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, 9 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