]> git.pld-linux.org Git - packages/php.git/blame - php-systzdata.patch
This commit was manufactured by cvs2git to create branch 'DEVEL'.
[packages/php.git] / php-systzdata.patch
CommitLineData
c0240cb1 1
2Add support for use of the system timezone database, rather
3than embedding a copy. Discussed upstream but was not desired.
4
5History:
6r7: improve check for valid timezone id to exclude directories
7r6: fix fd leak in r5, fix country code/BC flag use in
8 timezone_identifiers_list() using system db,
9 fix use of PECL timezonedb to override system db,
10r5: reverts addition of "System/Localtime" fake tzname.
11 updated for 5.3.0, parses zone.tab to pick up mapping between
12 timezone name, country code and long/lat coords
13r4: added "System/Localtime" tzname which uses /etc/localtime
14r3: fix a crash if /usr/share/zoneinfo doesn't exist (Raphael Geissert)
15r2: add filesystem trawl to set up name alias index
16r1: initial revision
17
18--- php-5.3.1/ext/date/lib/parse_tz.c.systzdata
19+++ php-5.3.1/ext/date/lib/parse_tz.c
20@@ -20,6 +20,16 @@
21
22 #include "timelib.h"
23
24+#ifdef HAVE_SYSTEM_TZDATA
25+#include <sys/mman.h>
26+#include <sys/stat.h>
27+#include <limits.h>
28+#include <fcntl.h>
29+#include <unistd.h>
30+
31+#include "php_scandir.h"
32+#endif
33+
34 #include <stdio.h>
35
36 #ifdef HAVE_LOCALE_H
37@@ -31,7 +41,12 @@
38 #else
39 #include <strings.h>
40 #endif
41+
42+#ifndef HAVE_SYSTEM_TZDATA
43 #include "timezonedb.h"
44+#endif
45+
46+#include <ctype.h>
47
48 #if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
49 # if defined(__LITTLE_ENDIAN__)
50@@ -51,9 +66,14 @@
51
52 static void read_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
53 {
54- /* skip ID */
55- *tzf += 4;
56-
57+ if (memcmp(tzf, "TZif", 4) == 0) {
58+ *tzf += 20;
59+ return;
60+ }
61+
62+ /* skip ID */
63+ *tzf += 4;
64+
65 /* read BC flag */
66 tz->bc = (**tzf == '\1');
67 *tzf += 1;
68@@ -253,7 +273,397 @@ void timelib_dump_tzinfo(timelib_tzinfo
69 }
70 }
71
72-static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
73+#ifdef HAVE_SYSTEM_TZDATA
74+
75+#ifdef HAVE_SYSTEM_TZDATA_PREFIX
76+#define ZONEINFO_PREFIX HAVE_SYSTEM_TZDATA_PREFIX
77+#else
78+#define ZONEINFO_PREFIX "/usr/share/zoneinfo"
79+#endif
80+
81+/* System timezone database pointer. */
82+static const timelib_tzdb *timezonedb_system = NULL;
83+
84+/* Hash table entry for the cache of the zone.tab mapping table. */
85+struct location_info {
86+ char code[2];
87+ double latitude, longitude;
88+ char name[64];
89+ char *comment;
90+ struct location_info *next;
91+};
92+
93+/* Cache of zone.tab. */
94+static struct location_info **system_location_table;
95+
96+/* Size of the zone.tab hash table; a random-ish prime big enough to
97+ * prevent too many collisions. */
98+#define LOCINFO_HASH_SIZE (1021)
99+
100+static uint32_t tz_hash(const char *str)
101+{
102+ const unsigned char *p = (const unsigned char *)str;
103+ uint32_t hash = 5381;
104+ int c;
105+
106+ while ((c = *p++) != '\0') {
107+ hash = (hash << 5) ^ hash ^ c;
108+ }
109+
110+ return hash % LOCINFO_HASH_SIZE;
111+}
112+
113+/* Parse an ISO-6709 date as used in zone.tab. Returns end of the
114+ * parsed string on success, or NULL on parse error. On success,
115+ * writes the parsed number to *result. */
116+static char *parse_iso6709(char *p, double *result)
117+{
118+ double v, sign;
119+ char *pend;
120+ size_t len;
121+
122+ if (*p == '+')
123+ sign = 1.0;
124+ else if (*p == '-')
125+ sign = -1.0;
126+ else
127+ return NULL;
128+
129+ p++;
130+ for (pend = p; *pend >= '0' && *pend <= '9'; pend++)
131+ ;;
132+
133+ /* Annoying encoding used by zone.tab has no decimal point, so use
134+ * the length to determine the format:
135+ *
136+ * 4 = DDMM
137+ * 5 = DDDMM
138+ * 6 = DDMMSS
139+ * 7 = DDDMMSS
140+ */
141+ len = pend - p;
142+ if (len < 4 || len > 7) {
143+ return NULL;
144+ }
145+
146+ /* p => [D]DD */
147+ v = (p[0] - '0') * 10.0 + (p[1] - '0');
148+ p += 2;
149+ if (len == 5 || len == 7)
150+ v = v * 10.0 + (*p++ - '0');
151+ /* p => MM[SS] */
152+ v += (10.0 * (p[0] - '0')
153+ + p[1] - '0') / 60.0;
154+ p += 2;
155+ /* p => [SS] */
156+ if (len > 5) {
157+ v += (10.0 * (p[0] - '0')
158+ + p[1] - '0') / 3600.0;
159+ p += 2;
160+ }
161+
162+ /* Round to five decimal place, not because it's a good idea,
163+ * but, because the builtin data uses rounded data, so, match
164+ * that. */
165+ *result = round(v * sign * 100000.0) / 100000.0;
166+
167+ return p;
168+}
169+
170+/* This function parses the zone.tab file to build up the mapping of
171+ * timezone to country code and geographic location, and returns a
172+ * hash table. The hash table is indexed by the function:
173+ *
174+ * tz_hash(timezone-name)
175+ */
176+static struct location_info **create_location_table(void)
177+{
178+ struct location_info **li, *i;
179+ char zone_tab[PATH_MAX];
180+ char line[512];
181+ FILE *fp;
182+
183+ strncpy(zone_tab, ZONEINFO_PREFIX "/zone.tab", sizeof zone_tab);
184+
185+ fp = fopen(zone_tab, "r");
186+ if (!fp) {
187+ return NULL;
188+ }
189+
190+ li = calloc(LOCINFO_HASH_SIZE, sizeof *li);
191+
192+ while (fgets(line, sizeof line, fp)) {
193+ char *p = line, *code, *name, *comment;
194+ uint32_t hash;
195+ double latitude, longitude;
196+
197+ while (isspace(*p))
198+ p++;
199+
200+ if (*p == '#' || *p == '\0' || *p == '\n')
201+ continue;
202+
203+ if (!isalpha(p[0]) || !isalpha(p[1]) || p[2] != '\t')
204+ continue;
205+
206+ /* code => AA */
207+ code = p;
208+ p[2] = 0;
209+ p += 3;
210+
211+ /* coords => [+-][D]DDMM[SS][+-][D]DDMM[SS] */
212+ p = parse_iso6709(p, &latitude);
213+ if (!p) {
214+ continue;
215+ }
216+ p = parse_iso6709(p, &longitude);
217+ if (!p) {
218+ continue;
219+ }
220+
221+ if (!p || *p != '\t') {
222+ continue;
223+ }
224+
225+ /* name = string */
226+ name = ++p;
227+ while (*p != '\t' && *p && *p != '\n')
228+ p++;
229+
230+ *p++ = '\0';
231+
232+ /* comment = string */
233+ comment = p;
234+ while (*p != '\t' && *p && *p != '\n')
235+ p++;
236+
237+ if (*p == '\n' || *p == '\t')
238+ *p = '\0';
239+
240+ hash = tz_hash(name);
241+ i = malloc(sizeof *i);
242+ memcpy(i->code, code, 2);
243+ strncpy(i->name, name, sizeof i->name);
244+ i->comment = strdup(comment);
245+ i->longitude = longitude;
246+ i->latitude = latitude;
247+ i->next = li[hash];
248+ li[hash] = i;
249+ /* printf("%s [%u, %f, %f]\n", name, hash, latitude, longitude); */
250+ }
251+
252+ fclose(fp);
253+
254+ return li;
255+}
256+
257+/* Return location info from hash table, using given timezone name.
258+ * Returns NULL if the name could not be found. */
259+const struct location_info *find_zone_info(struct location_info **li,
260+ const char *name)
261+{
262+ uint32_t hash = tz_hash(name);
263+ const struct location_info *l;
264+
265+ if (!li) {
266+ return NULL;
267+ }
268+
269+ for (l = li[hash]; l; l = l->next) {
270+ if (strcasecmp(l->name, name) == 0)
271+ return l;
272+ }
273+
274+ return NULL;
275+}
276+
277+/* Filter out some non-tzdata files and the posix/right databases, if
278+ * present. */
279+static int index_filter(const struct dirent *ent)
280+{
281+ return strcmp(ent->d_name, ".") != 0
282+ && strcmp(ent->d_name, "..") != 0
283+ && strcmp(ent->d_name, "posix") != 0
284+ && strcmp(ent->d_name, "posixrules") != 0
285+ && strcmp(ent->d_name, "right") != 0
286+ && strstr(ent->d_name, ".tab") == NULL;
287+}
288+
289+static int sysdbcmp(const void *first, const void *second)
290+{
291+ const timelib_tzdb_index_entry *alpha = first, *beta = second;
292+
293+ return strcmp(alpha->id, beta->id);
294+}
295+
296+
297+/* Create the zone identifier index by trawling the filesystem. */
298+static void create_zone_index(timelib_tzdb *db)
299+{
300+ size_t dirstack_size, dirstack_top;
301+ size_t index_size, index_next;
302+ timelib_tzdb_index_entry *db_index;
303+ char **dirstack;
304+
305+ /* LIFO stack to hold directory entries to scan; each slot is a
306+ * directory name relative to the zoneinfo prefix. */
307+ dirstack_size = 32;
308+ dirstack = malloc(dirstack_size * sizeof *dirstack);
309+ dirstack_top = 1;
310+ dirstack[0] = strdup("");
311+
312+ /* Index array. */
313+ index_size = 64;
314+ db_index = malloc(index_size * sizeof *db_index);
315+ index_next = 0;
316+
317+ do {
318+ struct dirent **ents;
319+ char name[PATH_MAX], *top;
320+ int count;
321+
322+ /* Pop the top stack entry, and iterate through its contents. */
323+ top = dirstack[--dirstack_top];
324+ snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s", top);
325+
326+ count = php_scandir(name, &ents, index_filter, php_alphasort);
327+
328+ while (count > 0) {
329+ struct stat st;
330+ const char *leaf = ents[count - 1]->d_name;
331+
332+ snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s/%s",
333+ top, leaf);
334+
335+ if (strlen(name) && stat(name, &st) == 0) {
336+ /* Name, relative to the zoneinfo prefix. */
337+ const char *root = top;
338+
339+ if (root[0] == '/') root++;
340+
341+ snprintf(name, sizeof name, "%s%s%s", root,
342+ *root ? "/": "", leaf);
343+
344+ if (S_ISDIR(st.st_mode)) {
345+ if (dirstack_top == dirstack_size) {
346+ dirstack_size *= 2;
347+ dirstack = realloc(dirstack,
348+ dirstack_size * sizeof *dirstack);
349+ }
350+ dirstack[dirstack_top++] = strdup(name);
351+ }
352+ else {
353+ if (index_next == index_size) {
354+ index_size *= 2;
355+ db_index = realloc(db_index,
356+ index_size * sizeof *db_index);
357+ }
358+
359+ db_index[index_next++].id = strdup(name);
360+ }
361+ }
362+
363+ free(ents[--count]);
364+ }
365+
366+ if (count != -1) free(ents);
367+ free(top);
368+ } while (dirstack_top);
369+
370+ qsort(db_index, index_next, sizeof *db_index, sysdbcmp);
371+
372+ db->index = db_index;
373+ db->index_size = index_next;
374+
375+ free(dirstack);
376+}
377+
378+#define FAKE_HEADER "1234\0??\1??"
379+#define FAKE_UTC_POS (7 - 4)
380+
381+/* Create a fake data segment for database 'sysdb'. */
382+static void fake_data_segment(timelib_tzdb *sysdb,
383+ struct location_info **info)
384+{
385+ size_t n;
386+ char *data, *p;
387+
388+ data = malloc(3 * sysdb->index_size + 7);
389+
390+ p = mempcpy(data, FAKE_HEADER, sizeof(FAKE_HEADER) - 1);
391+
392+ for (n = 0; n < sysdb->index_size; n++) {
393+ const struct location_info *li;
394+ timelib_tzdb_index_entry *ent;
395+
396+ ent = (timelib_tzdb_index_entry *)&sysdb->index[n];
397+
398+ /* Lookup the timezone name in the hash table. */
399+ if (strcmp(ent->id, "UTC") == 0) {
400+ ent->pos = FAKE_UTC_POS;
401+ continue;
402+ }
403+
404+ li = find_zone_info(info, ent->id);
405+ if (li) {
406+ /* If found, append the BC byte and the
407+ * country code; set the position for this
408+ * section of timezone data. */
409+ ent->pos = (p - data) - 4;
410+ *p++ = '\1';
411+ *p++ = li->code[0];
412+ *p++ = li->code[1];
413+ }
414+ else {
415+ /* If not found, the timezone data can
416+ * point at the header. */
417+ ent->pos = 0;
418+ }
419+ }
420+
421+ sysdb->data = (unsigned char *)data;
422+}
423+
424+/* Returns true if the passed-in stat structure describes a
425+ * probably-valid timezone file. */
426+static int is_valid_tzfile(const struct stat *st)
427+{
428+ return S_ISREG(st->st_mode) && st->st_size > 20;
429+}
430+
431+/* Return the mmap()ed tzfile if found, else NULL. On success, the
432+ * length of the mapped data is placed in *length. */
433+static char *map_tzfile(const char *timezone, size_t *length)
434+{
435+ char fname[PATH_MAX];
436+ struct stat st;
437+ char *p;
438+ int fd;
439+
440+ if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
441+ return NULL;
442+ }
443+
444+ snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", timezone);
445+
446+ fd = open(fname, O_RDONLY);
447+ if (fd == -1) {
448+ return NULL;
449+ } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st)) {
450+ close(fd);
451+ return NULL;
452+ }
453+
454+ *length = st.st_size;
455+ p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
456+ close(fd);
457+
458+ return p != MAP_FAILED ? p : NULL;
459+}
460+
461+#endif
462+
463+static int inmem_seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
464 {
465 int left = 0, right = tzdb->index_size - 1;
466 #ifdef HAVE_SETLOCALE
467@@ -292,36 +702,125 @@ static int seek_to_tz_position(const uns
468 return 0;
469 }
470
471+static int seek_to_tz_position(const unsigned char **tzf, char *timezone,
472+ char **map, size_t *maplen,
473+ const timelib_tzdb *tzdb)
474+{
475+ if (tzdb == timezonedb_system) {
476+ char *orig;
477+
478+ orig = map_tzfile(timezone, maplen);
479+ if (orig == NULL) {
480+ return 0;
481+ }
482+
483+ (*tzf) = (unsigned char *)orig ;
484+ *map = orig;
485+
486+ return 1;
487+ }
488+ else {
489+ return inmem_seek_to_tz_position(tzf, timezone, tzdb);
490+ }
491+}
492+
493 const timelib_tzdb *timelib_builtin_db(void)
494 {
495+#ifdef HAVE_SYSTEM_TZDATA
496+ if (timezonedb_system == NULL) {
497+ timelib_tzdb *tmp = malloc(sizeof *tmp);
498+
499+ tmp->version = "0.system";
500+ tmp->data = NULL;
501+ create_zone_index(tmp);
502+ system_location_table = create_location_table();
503+ fake_data_segment(tmp, system_location_table);
504+ timezonedb_system = tmp;
505+ }
506+
507+
508+ return timezonedb_system;
509+#else
510 return &timezonedb_builtin;
511+#endif
512 }
513
514 const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count)
515 {
516+#ifdef HAVE_SYSTEM_TZDATA
517+ *count = timezonedb_system->index_size;
518+ return timezonedb_system->index;
519+#else
520 *count = sizeof(timezonedb_idx_builtin) / sizeof(*timezonedb_idx_builtin);
521 return timezonedb_idx_builtin;
522+#endif
523 }
524
525 int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb)
526 {
527 const unsigned char *tzf;
528- return (seek_to_tz_position(&tzf, timezone, tzdb));
529+
530+#ifdef HAVE_SYSTEM_TZDATA
531+ if (tzdb == timezonedb_system) {
532+ char fname[PATH_MAX];
533+ struct stat st;
534+
535+ if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
536+ return 0;
537+ }
538+
539+ snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", timezone);
540+
541+ return stat(fname, &st) == 0 && is_valid_tzfile(&st);
542+ }
543+#endif
544+
545+ return (inmem_seek_to_tz_position(&tzf, timezone, tzdb));
546 }
547
548 timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb)
549 {
550 const unsigned char *tzf;
551+ char *memmap = NULL;
552+ size_t maplen;
553 timelib_tzinfo *tmp;
554
555- if (seek_to_tz_position(&tzf, timezone, tzdb)) {
556+ if (seek_to_tz_position(&tzf, timezone, &memmap, &maplen, tzdb)) {
557 tmp = timelib_tzinfo_ctor(timezone);
558
559 read_preamble(&tzf, tmp);
560 read_header(&tzf, tmp);
561 read_transistions(&tzf, tmp);
562 read_types(&tzf, tmp);
563- read_location(&tzf, tmp);
564+
565+#ifdef HAVE_SYSTEM_TZDATA
566+ if (memmap) {
567+ const struct location_info *li;
568+
569+ /* TZif-style - grok the location info from the system database,
570+ * if possible. */
571+
572+ if ((li = find_zone_info(system_location_table, timezone)) != NULL) {
573+ tmp->location.comments = strdup(li->comment);
574+ strncpy(tmp->location.country_code, li->code, 2);
575+ tmp->location.longitude = li->longitude;
576+ tmp->location.latitude = li->latitude;
577+ tmp->bc = 1;
578+ }
579+ else {
580+ strcpy(tmp->location.country_code, "??");
581+ tmp->bc = 0;
582+ tmp->location.comments = strdup("");
583+ }
584+
585+ /* Now done with the mmap segment - discard it. */
586+ munmap(memmap, maplen);
587+#endif
588+ }
589+ else {
590+ /* PHP-style - use the embedded info. */
591+ read_location(&tzf, tmp);
592+ }
593 } else {
594 tmp = NULL;
595 }
596--- php-5.3.1/ext/date/lib/timelib.m4.systzdata
597+++ php-5.3.1/ext/date/lib/timelib.m4
598@@ -78,3 +78,17 @@ stdlib.h
599
600 dnl Check for strtoll, atoll
601 AC_CHECK_FUNCS(strtoll atoll strftime)
602+
603+PHP_ARG_WITH(system-tzdata, for use of system timezone data,
604+[ --with-system-tzdata[=DIR] to specify use of system timezone data],
605+no, no)
606+
607+if test "$PHP_SYSTEM_TZDATA" != "no"; then
608+ AC_DEFINE(HAVE_SYSTEM_TZDATA, 1, [Define if system timezone data is used])
609+
610+ if test "$PHP_SYSTEM_TZDATA" != "yes"; then
611+ AC_DEFINE_UNQUOTED(HAVE_SYSTEM_TZDATA_PREFIX, "$PHP_SYSTEM_TZDATA",
612+ [Define for location of system timezone data])
613+ fi
614+fi
615+
This page took 0.146252 seconds and 4 git commands to generate.