WordPress.org

Make WordPress Core

Ticket #10406: php-5.3.0-systzdata-v5.patch

File php-5.3.0-systzdata-v5.patch, 13.2 KB (added by demonicpagan, 5 years ago)

Patch file from http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=535770

  • php-5.3.0/ext/date/lib/parse_tz.c

    Add support for use of the system timezone database, rather
    than embedding a copy.  Discussed upstream but was not desired.
    
    History:
    r5: reverts addition of "System/Localtime" fake tzname.
        updated for 5.3.0, parses zone.tab to pick up mapping between
        timezone name, country code and long/lat coords
    r4: added "System/Localtime" tzname which uses /etc/localtime
    r3: fix a crash if /usr/share/zoneinfo doesn't exist (Raphael Geissert)
    r2: add filesystem trawl to set up name alias index
    r1: initial revision
    
    old new  
    2020 
    2121#include "timelib.h" 
    2222 
     23#ifdef HAVE_SYSTEM_TZDATA 
     24#include <sys/mman.h> 
     25#include <sys/stat.h> 
     26#include <limits.h> 
     27#include <fcntl.h> 
     28#include <unistd.h> 
     29 
     30#include "php_scandir.h" 
     31#endif 
     32 
    2333#include <stdio.h> 
    2434 
    2535#ifdef HAVE_LOCALE_H 
     
    3141#else 
    3242#include <strings.h> 
    3343#endif 
     44 
     45#ifndef HAVE_SYSTEM_TZDATA 
    3446#include "timezonedb.h" 
     47#endif 
     48 
     49#include <ctype.h> 
    3550 
    3651#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__)) 
    3752# if defined(__LITTLE_ENDIAN__) 
    void timelib_dump_tzinfo(timelib_tzinfo  
    253268        } 
    254269} 
    255270 
     271#ifdef HAVE_SYSTEM_TZDATA 
     272 
     273#ifdef HAVE_SYSTEM_TZDATA_PREFIX 
     274#define ZONEINFO_PREFIX HAVE_SYSTEM_TZDATA_PREFIX 
     275#else 
     276#define ZONEINFO_PREFIX "/usr/share/zoneinfo" 
     277#endif 
     278 
     279/* System timezone database pointer. */ 
     280static const timelib_tzdb *timezonedb_system = NULL; 
     281 
     282/* Hash table entry for the cache of the zone.tab mapping table. */ 
     283struct location_info { 
     284    char code[2]; 
     285    double latitude, longitude; 
     286    char name[64]; 
     287    char *comment; 
     288    struct location_info *next; 
     289}; 
     290 
     291/* Cache of zone.tab. */ 
     292static struct location_info **system_zone_info; 
     293 
     294/* Size of the zone.tab hash table; a random-ish prime big enough to 
     295 * prevent too many collisions. */ 
     296#define LOCINFO_HASH_SIZE (1021) 
     297 
     298static uint32_t tz_hash(const char *str) 
     299{ 
     300    const unsigned char *p = (const unsigned char *)str; 
     301    uint32_t hash = 5381; 
     302    int c; 
     303     
     304    while ((c = *p++) != '\0') { 
     305        hash = (hash << 5) ^ hash ^ c; 
     306    } 
     307     
     308    return hash % LOCINFO_HASH_SIZE; 
     309} 
     310 
     311/* Parse an ISO-6709 date as used in zone.tab. Returns end of the 
     312 * parsed string on success, or NULL on parse error.  On success, 
     313 * writes the parsed number to *result. */ 
     314static char *parse_iso6709(char *p, double *result) 
     315{ 
     316    double v, sign; 
     317    char *pend; 
     318    size_t len; 
     319 
     320    if (*p == '+') 
     321        sign = 1.0; 
     322    else if (*p == '-') 
     323        sign = -1.0; 
     324    else 
     325        return NULL; 
     326 
     327    p++; 
     328    for (pend = p; *pend >= '0' && *pend <= '9'; pend++) 
     329        ;; 
     330 
     331    /* Annoying encoding used by zone.tab has no decimal point, so use 
     332     * the length to determine the format: 
     333     *  
     334     * 4 = DDMM 
     335     * 5 = DDDMM 
     336     * 6 = DDMMSS 
     337     * 7 = DDDMMSS 
     338     */ 
     339    len = pend - p; 
     340    if (len < 4 || len > 7) { 
     341        return NULL; 
     342    } 
     343 
     344    /* p => [D]DD */ 
     345    v = (p[0] - '0') * 10.0 + (p[1] - '0'); 
     346    p += 2; 
     347    if (len == 5 || len == 7) 
     348        v = v * 10.0 + (*p++ - '0'); 
     349    /* p => MM[SS] */ 
     350    v += (10.0 * (p[0] - '0') 
     351          + p[1] - '0') / 60.0; 
     352    p += 2; 
     353    /* p => [SS] */ 
     354    if (len > 5) { 
     355        v += (10.0 * (p[0] - '0') 
     356              + p[1] - '0') / 3600.0; 
     357        p += 2; 
     358    } 
     359 
     360    /* Round to 3 decimal places. */ 
     361    *result = round(v * sign * 1000.00)/1000.00; 
     362 
     363    return p; 
     364} 
     365 
     366/* This function parses the zone.tab file to build up the mapping of 
     367 * timezone to country code and geographic location, and returns a 
     368 * hash table.  The hash table is indexed by the function: 
     369 * 
     370 *   tz_hash(timezone-name) 
     371 */ 
     372static struct location_info **load_zone_table(void) 
     373{ 
     374    struct location_info **li, *i; 
     375    char zone_tab[PATH_MAX]; 
     376    char line[512]; 
     377    FILE *fp; 
     378 
     379    strncpy(zone_tab, ZONEINFO_PREFIX "/zone.tab", sizeof zone_tab); 
     380 
     381    fp = fopen(zone_tab, "r"); 
     382    if (!fp) { 
     383        return NULL; 
     384    } 
     385 
     386    li = calloc(LOCINFO_HASH_SIZE, sizeof *li); 
     387 
     388    while (fgets(line, sizeof line, fp)) { 
     389        char *p = line, *code, *name, *comment; 
     390        uint32_t hash; 
     391        double latitude, longitude; 
     392 
     393        while (isspace(*p)) 
     394            p++; 
     395 
     396        if (*p == '#' || *p == '\0' || *p == '\n') 
     397            continue; 
     398         
     399        if (!isalpha(p[0]) || !isalpha(p[1]) || p[2] != '\t') 
     400            continue; 
     401         
     402        /* code => AA */ 
     403        code = p; 
     404        p[2] = 0; 
     405        p += 3; 
     406 
     407        /* coords => [+-][D]DDMM[SS][+-][D]DDMM[SS] */ 
     408        p = parse_iso6709(p, &latitude); 
     409        if (!p) { 
     410            continue; 
     411        } 
     412        p = parse_iso6709(p, &longitude); 
     413        if (!p) { 
     414            continue; 
     415        } 
     416 
     417        if (!p || *p != '\t') { 
     418            continue; 
     419        } 
     420 
     421        /* name = string */ 
     422        name = ++p; 
     423        while (*p != '\t' && *p && *p != '\n') 
     424            p++; 
     425 
     426        *p++ = '\0'; 
     427 
     428        /* comment = string */ 
     429        comment = p; 
     430        while (*p != '\t' && *p && *p != '\n') 
     431            p++; 
     432 
     433        if (*p == '\n' || *p == '\t') 
     434            *p = '\0'; 
     435         
     436        hash = tz_hash(name); 
     437        i = malloc(sizeof *i); 
     438        memcpy(i->code, code, 2); 
     439        strncpy(i->name, name, sizeof i->name); 
     440        i->comment = strdup(comment); 
     441        i->longitude = longitude; 
     442        i->latitude = latitude; 
     443        i->next = li[hash]; 
     444        li[hash] = i; 
     445        /* printf("%s [%u, %f, %f]\n", name, hash, longitude, latitude); */ 
     446    } 
     447                  
     448    return li; 
     449} 
     450 
     451/* Return location info from hash table, using given timezone name. 
     452 * Returns NULL if the name could not be found. */ 
     453const struct location_info *find_zone_info(struct location_info **li,  
     454                                           const char *name) 
     455{ 
     456    uint32_t hash = tz_hash(name); 
     457    const struct location_info *l; 
     458 
     459    if (!li) { 
     460        return NULL; 
     461    } 
     462 
     463    for (l = li[hash]; l; l = l->next) { 
     464        if (strcasecmp(l->name, name) == 0) 
     465            return l; 
     466    } 
     467 
     468    return NULL; 
     469}     
     470 
     471/* Since 5.3, php_date.c:timezone_identifiers_list() peeks directly 
     472 * into the data array at position + 4, which is the BC flag, so, we 
     473 * need to fake one up.  For each timezone entry, the 'pos' entry will 
     474 * be set to either 0 or 1.  pos + 4 will hence be either \1 or \0, 
     475 * respectively.  Somewhat gross, but means we can avoid patching 
     476 * php_date.c.  */ 
     477static const unsigned char system_fake_data[] = "1234\1\0"; 
     478#define SYSTEM_FAKE_BC_POS (0) 
     479#define SYSTEM_FAKE_NONBC_POS (1) 
     480 
     481/* Filter out some non-tzdata files and the posix/right databases, if 
     482 * present. */ 
     483static int index_filter(const struct dirent *ent) 
     484{ 
     485        return strcmp(ent->d_name, ".") != 0 
     486                && strcmp(ent->d_name, "..") != 0 
     487                && strcmp(ent->d_name, "posix") != 0 
     488                && strcmp(ent->d_name, "posixrules") != 0 
     489                && strcmp(ent->d_name, "right") != 0 
     490                && strstr(ent->d_name, ".tab") == NULL; 
     491} 
     492 
     493/* Create the zone identifier index by trawling the filesystem. */ 
     494static void create_zone_index(timelib_tzdb *db) 
     495{ 
     496        size_t dirstack_size,  dirstack_top; 
     497        size_t index_size, index_next; 
     498        timelib_tzdb_index_entry *db_index; 
     499        char **dirstack; 
     500 
     501        /* LIFO stack to hold directory entries to scan; each slot is a 
     502         * directory name relative to the zoneinfo prefix. */ 
     503        dirstack_size = 32; 
     504        dirstack = malloc(dirstack_size * sizeof *dirstack); 
     505        dirstack_top = 1; 
     506        dirstack[0] = strdup(""); 
     507         
     508        /* Index array. */ 
     509        index_size = 64; 
     510        db_index = malloc(index_size * sizeof *db_index); 
     511        index_next = 0; 
     512 
     513        do { 
     514                struct dirent **ents; 
     515                char name[PATH_MAX], *top; 
     516                int count; 
     517 
     518                /* Pop the top stack entry, and iterate through its contents. */ 
     519                top = dirstack[--dirstack_top]; 
     520                snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s", top); 
     521 
     522                count = php_scandir(name, &ents, index_filter, php_alphasort); 
     523 
     524                while (count > 0) { 
     525                        struct stat st; 
     526                        const char *leaf = ents[count - 1]->d_name; 
     527 
     528                        snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s/%s",  
     529                                 top, leaf); 
     530                         
     531                        if (strlen(name) && stat(name, &st) == 0) { 
     532                                /* Name, relative to the zoneinfo prefix. */ 
     533                                const char *root = top; 
     534 
     535                                if (root[0] == '/') root++; 
     536 
     537                                snprintf(name, sizeof name, "%s%s%s", root,  
     538                                         *root ? "/": "", leaf); 
     539 
     540                                if (S_ISDIR(st.st_mode)) { 
     541                                        if (dirstack_top == dirstack_size) { 
     542                                                dirstack_size *= 2; 
     543                                                dirstack = realloc(dirstack,  
     544                                                                   dirstack_size * sizeof *dirstack); 
     545                                        } 
     546                                        dirstack[dirstack_top++] = strdup(name); 
     547                                } 
     548                                else { 
     549                                        const struct location_info *li; 
     550 
     551                                        if (index_next == index_size) { 
     552                                                index_size *= 2; 
     553                                                db_index = realloc(db_index, 
     554                                                                   index_size * sizeof *db_index); 
     555                                        } 
     556 
     557                                        db_index[index_next].id = strdup(name); 
     558                                         
     559                                        /* Look up the timezone in the zone.tab cache, and see 
     560                                         * whether this is a "BC" name or not; fake the pos 
     561                                         * pointer appropriately. */ 
     562                                        li = find_zone_info(system_zone_info, name); 
     563                                        if (li) { 
     564                                            db_index[index_next].pos = SYSTEM_FAKE_NONBC_POS; 
     565                                        } 
     566                                        else { 
     567                                            db_index[index_next].pos = SYSTEM_FAKE_BC_POS; 
     568                                        } 
     569 
     570                                        index_next++; 
     571                                } 
     572                        } 
     573 
     574                        free(ents[--count]); 
     575                } 
     576                 
     577                if (count != -1) free(ents); 
     578                free(top); 
     579        } while (dirstack_top); 
     580 
     581        db->index = db_index; 
     582        db->index_size = index_next; 
     583        db->data = system_fake_data; 
     584 
     585        free(dirstack); 
     586} 
     587 
     588/* Return the mmap()ed tzfile if found, else NULL.  On success, the 
     589 * length of the mapped data is placed in *length. */ 
     590static char *map_tzfile(const char *timezone, size_t *length) 
     591{ 
     592        char fname[PATH_MAX]; 
     593        struct stat st; 
     594        char *p; 
     595        int fd; 
     596         
     597        if (strstr(timezone, "..") != NULL) { 
     598                return NULL; 
     599        } 
     600 
     601        snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", timezone); 
     602         
     603        fd = open(fname, O_RDONLY); 
     604        if (fd == -1) { 
     605                return NULL; 
     606        } else if (fstat(fd, &st) != 0 || st.st_size < 21) { 
     607                close(fd); 
     608                return NULL; 
     609        } 
     610 
     611        *length = st.st_size; 
     612        p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 
     613        close(fd); 
     614         
     615        return p != MAP_FAILED ? p : NULL; 
     616} 
     617 
     618const timelib_tzdb *timelib_builtin_db(void) 
     619{ 
     620        if (timezonedb_system == NULL) { 
     621                timelib_tzdb *tmp = malloc(sizeof *tmp); 
     622 
     623                tmp->version = "0.system"; 
     624                tmp->data = NULL; 
     625                create_zone_index(tmp); 
     626                system_zone_info = load_zone_table();         
     627                timezonedb_system = tmp; 
     628        } 
     629                         
     630        return timezonedb_system; 
     631} 
     632 
     633const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count) 
     634{ 
     635        *count = timezonedb_system->index_size; 
     636        return timezonedb_system->index; 
     637} 
     638 
     639int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb) 
     640{ 
     641        char fname[PATH_MAX]; 
     642 
     643        if (strstr(timezone, "..") != NULL) { 
     644                return 0; 
     645        } 
     646         
     647        snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", timezone); 
     648 
     649        return access(fname, R_OK) == 0 ? 1 : 0; 
     650} 
     651 
     652timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb) 
     653{ 
     654        char *orig; 
     655        const unsigned char *tzf; 
     656        timelib_tzinfo *tmp; 
     657        size_t len; 
     658        const struct location_info *li; 
     659 
     660        orig = map_tzfile(timezone, &len); 
     661        if (orig == NULL) { 
     662                return NULL; 
     663        } 
     664 
     665        tmp = timelib_tzinfo_ctor(timezone); 
     666 
     667        tzf = (const unsigned char *)orig + 20; 
     668        read_header(&tzf, tmp); 
     669        read_transistions(&tzf, tmp); 
     670        read_types(&tzf, tmp); 
     671         
     672        if ((li = find_zone_info(system_zone_info, timezone)) != NULL) { 
     673            tmp->location.comments = strdup(li->comment); 
     674            strncpy(tmp->location.country_code, li->code, 2); 
     675            tmp->location.longitude = li->longitude; 
     676            tmp->location.latitude = li->latitude; 
     677            tmp->bc = 1; 
     678        } 
     679        else { 
     680            strcpy(tmp->location.country_code, "??"); 
     681            tmp->bc = 0; 
     682            tmp->location.comments = strdup(""); 
     683        } 
     684 
     685        munmap(orig, len); 
     686 
     687        return tmp; 
     688} 
     689 
     690#else /* !HAVE_SYSTEM_TZDATA */ 
     691 
    256692static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb) 
    257693{ 
    258694        int left = 0, right = tzdb->index_size - 1; 
    timelib_tzinfo *timelib_parse_tzfile(cha 
    328764 
    329765        return tmp; 
    330766} 
     767#endif 
    331768 
    332769static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time) 
    333770{ 
  • php-5.3.0/ext/date/lib/timelib.m4

    old new stdlib.h 
    7878 
    7979dnl Check for strtoll, atoll 
    8080AC_CHECK_FUNCS(strtoll atoll strftime) 
     81 
     82PHP_ARG_WITH(system-tzdata, for use of system timezone data, 
     83[  --with-system-tzdata[=DIR]      to specify use of system timezone data], 
     84no, no) 
     85 
     86if test "$PHP_SYSTEM_TZDATA" != "no"; then 
     87   AC_DEFINE(HAVE_SYSTEM_TZDATA, 1, [Define if system timezone data is used]) 
     88 
     89   if test "$PHP_SYSTEM_TZDATA" != "yes"; then 
     90      AC_DEFINE_UNQUOTED(HAVE_SYSTEM_TZDATA_PREFIX, "$PHP_SYSTEM_TZDATA", 
     91                         [Define for location of system timezone data]) 
     92   fi 
     93fi 
     94