Ignore:
Timestamp:
Apr 17, 2017, 12:18:15 PM (6 weeks ago)
Author:
brainslayer
Message:

update php

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/router/php7/ext/date/lib/parse_tz.c

    r31877 r31879  
    2525#include "timelib.h"
    2626
     27#ifdef HAVE_SYSTEM_TZDATA
     28#include <sys/mman.h>
     29#include <sys/stat.h>
     30#include <limits.h>
     31#include <fcntl.h>
     32#include <unistd.h>
     33
     34#include "php_scandir.h"
     35#endif
     36
    2737#include <stdio.h>
    2838
     
    3747#endif
    3848
     49#ifndef HAVE_SYSTEM_TZDATA
    3950#define TIMELIB_SUPPORTS_V2DATA
    4051#include "timezonedb.h"
     52#endif
     53
     54#include <ctype.h>
    4155
    4256#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
     
    5973{
    6074        uint32_t version;
     75
     76        if (memcmp(*tzf, "TZif", 4) == 0) {
     77                *tzf += 20;
     78                return 0;
     79        }
    6180
    6281        /* read ID */
     
    303322}
    304323
    305 static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
     324#ifdef HAVE_SYSTEM_TZDATA
     325
     326#ifdef HAVE_SYSTEM_TZDATA_PREFIX
     327#define ZONEINFO_PREFIX HAVE_SYSTEM_TZDATA_PREFIX
     328#else
     329#define ZONEINFO_PREFIX "/usr/share/zoneinfo"
     330#endif
     331
     332/* System timezone database pointer. */
     333static const timelib_tzdb *timezonedb_system;
     334
     335/* Hash table entry for the cache of the zone.tab mapping table. */
     336struct location_info {
     337        char code[2];
     338        double latitude, longitude;
     339        char name[64];
     340        char *comment;
     341        struct location_info *next;
     342};
     343
     344/* Cache of zone.tab. */
     345static struct location_info **system_location_table;
     346
     347/* Size of the zone.tab hash table; a random-ish prime big enough to
     348 * prevent too many collisions. */
     349#define LOCINFO_HASH_SIZE (1021)
     350
     351/* Compute a case insensitive hash of str */
     352static uint32_t tz_hash(const char *str)
     353{
     354    const unsigned char *p = (const unsigned char *)str;
     355    uint32_t hash = 5381;
     356    int c;
     357
     358    while ((c = tolower(*p++)) != '\0') {
     359        hash = (hash << 5) ^ hash ^ c;
     360    }
     361
     362    return hash % LOCINFO_HASH_SIZE;
     363}
     364
     365/* Parse an ISO-6709 date as used in zone.tab. Returns end of the
     366 * parsed string on success, or NULL on parse error.  On success,
     367 * writes the parsed number to *result. */
     368static char *parse_iso6709(char *p, double *result)
     369{
     370    double v, sign;
     371    char *pend;
     372    size_t len;
     373
     374    if (*p == '+')
     375        sign = 1.0;
     376    else if (*p == '-')
     377        sign = -1.0;
     378    else
     379        return NULL;
     380
     381    p++;
     382    for (pend = p; *pend >= '0' && *pend <= '9'; pend++)
     383        ;;
     384
     385    /* Annoying encoding used by zone.tab has no decimal point, so use
     386     * the length to determine the format:
     387     *
     388     * 4 = DDMM
     389     * 5 = DDDMM
     390     * 6 = DDMMSS
     391     * 7 = DDDMMSS
     392     */
     393    len = pend - p;
     394    if (len < 4 || len > 7) {
     395        return NULL;
     396    }
     397
     398    /* p => [D]DD */
     399    v = (p[0] - '0') * 10.0 + (p[1] - '0');
     400    p += 2;
     401    if (len == 5 || len == 7)
     402        v = v * 10.0 + (*p++ - '0');
     403    /* p => MM[SS] */
     404    v += (10.0 * (p[0] - '0')
     405          + p[1] - '0') / 60.0;
     406    p += 2;
     407    /* p => [SS] */
     408    if (len > 5) {
     409        v += (10.0 * (p[0] - '0')
     410              + p[1] - '0') / 3600.0;
     411        p += 2;
     412    }
     413
     414    /* Round to five decimal place, not because it's a good idea,
     415     * but, because the builtin data uses rounded data, so, match
     416     * that. */
     417    *result = trunc(v * sign * 100000.0) / 100000.0;
     418
     419    return p;
     420}
     421
     422/* This function parses the zone.tab file to build up the mapping of
     423 * timezone to country code and geographic location, and returns a
     424 * hash table.  The hash table is indexed by the function:
     425 *
     426 *   tz_hash(timezone-name)
     427 */
     428static struct location_info **create_location_table(void)
     429{
     430    struct location_info **li, *i;
     431    char zone_tab[PATH_MAX];
     432    char line[512];
     433    FILE *fp;
     434
     435    strncpy(zone_tab, ZONEINFO_PREFIX "/zone.tab", sizeof zone_tab);
     436
     437    fp = fopen(zone_tab, "r");
     438    if (!fp) {
     439        return NULL;
     440    }
     441
     442    li = calloc(LOCINFO_HASH_SIZE, sizeof *li);
     443
     444    while (fgets(line, sizeof line, fp)) {
     445        char *p = line, *code, *name, *comment;
     446        uint32_t hash;
     447        double latitude, longitude;
     448
     449        while (isspace(*p))
     450            p++;
     451
     452        if (*p == '#' || *p == '\0' || *p == '\n')
     453            continue;
     454       
     455        if (!isalpha(p[0]) || !isalpha(p[1]) || p[2] != '\t')
     456            continue;
     457       
     458        /* code => AA */
     459        code = p;
     460        p[2] = 0;
     461        p += 3;
     462
     463        /* coords => [+-][D]DDMM[SS][+-][D]DDMM[SS] */
     464        p = parse_iso6709(p, &latitude);
     465        if (!p) {
     466            continue;
     467        }
     468        p = parse_iso6709(p, &longitude);
     469        if (!p) {
     470            continue;
     471        }
     472
     473        if (!p || *p != '\t') {
     474            continue;
     475        }
     476
     477        /* name = string */
     478        name = ++p;
     479        while (*p != '\t' && *p && *p != '\n')
     480            p++;
     481
     482        *p++ = '\0';
     483
     484        /* comment = string */
     485        comment = p;
     486        while (*p != '\t' && *p && *p != '\n')
     487            p++;
     488
     489        if (*p == '\n' || *p == '\t')
     490            *p = '\0';
     491       
     492        hash = tz_hash(name);
     493        i = malloc(sizeof *i);
     494        memcpy(i->code, code, 2);
     495        strncpy(i->name, name, sizeof i->name);
     496        i->comment = strdup(comment);
     497        i->longitude = longitude;
     498        i->latitude = latitude;
     499        i->next = li[hash];
     500        li[hash] = i;
     501        /* printf("%s [%u, %f, %f]\n", name, hash, latitude, longitude); */
     502    }
     503
     504    fclose(fp);
     505
     506    return li;
     507}
     508
     509/* Return location info from hash table, using given timezone name.
     510 * Returns NULL if the name could not be found. */
     511const struct location_info *find_zone_info(struct location_info **li,
     512                                           const char *name)
     513{
     514    uint32_t hash = tz_hash(name);
     515    const struct location_info *l;
     516
     517    if (!li) {
     518        return NULL;
     519    }
     520
     521    for (l = li[hash]; l; l = l->next) {
     522        if (strcasecmp(l->name, name) == 0)
     523            return l;
     524    }
     525
     526    return NULL;
     527}   
     528
     529/* Filter out some non-tzdata files and the posix/right databases, if
     530 * present. */
     531static int index_filter(const struct dirent *ent)
     532{
     533        return strcmp(ent->d_name, ".") != 0
     534                && strcmp(ent->d_name, "..") != 0
     535                && strcmp(ent->d_name, "posix") != 0
     536                && strcmp(ent->d_name, "posixrules") != 0
     537                && strcmp(ent->d_name, "right") != 0
     538                && strstr(ent->d_name, ".list") == NULL
     539                && strstr(ent->d_name, ".tab") == NULL;
     540}
     541
     542static int sysdbcmp(const void *first, const void *second)
     543{
     544        const timelib_tzdb_index_entry *alpha = first, *beta = second;
     545
     546        return strcasecmp(alpha->id, beta->id);
     547}
     548
     549
     550/* Create the zone identifier index by trawling the filesystem. */
     551static void create_zone_index(timelib_tzdb *db)
     552{
     553        size_t dirstack_size,  dirstack_top;
     554        size_t index_size, index_next;
     555        timelib_tzdb_index_entry *db_index;
     556        char **dirstack;
     557
     558        /* LIFO stack to hold directory entries to scan; each slot is a
     559         * directory name relative to the zoneinfo prefix. */
     560        dirstack_size = 32;
     561        dirstack = malloc(dirstack_size * sizeof *dirstack);
     562        dirstack_top = 1;
     563        dirstack[0] = strdup("");
     564       
     565        /* Index array. */
     566        index_size = 64;
     567        db_index = malloc(index_size * sizeof *db_index);
     568        index_next = 0;
     569
     570        do {
     571                struct dirent **ents;
     572                char name[PATH_MAX], *top;
     573                int count;
     574
     575                /* Pop the top stack entry, and iterate through its contents. */
     576                top = dirstack[--dirstack_top];
     577                snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s", top);
     578
     579                count = php_scandir(name, &ents, index_filter, php_alphasort);
     580
     581                while (count > 0) {
     582                        struct stat st;
     583                        const char *leaf = ents[count - 1]->d_name;
     584
     585                        snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s/%s",
     586                                 top, leaf);
     587                       
     588                        if (strlen(name) && stat(name, &st) == 0) {
     589                                /* Name, relative to the zoneinfo prefix. */
     590                                const char *root = top;
     591
     592                                if (root[0] == '/') root++;
     593
     594                                snprintf(name, sizeof name, "%s%s%s", root,
     595                                         *root ? "/": "", leaf);
     596
     597                                if (S_ISDIR(st.st_mode)) {
     598                                        if (dirstack_top == dirstack_size) {
     599                                                dirstack_size *= 2;
     600                                                dirstack = realloc(dirstack,
     601                                                                   dirstack_size * sizeof *dirstack);
     602                                        }
     603                                        dirstack[dirstack_top++] = strdup(name);
     604                                }
     605                                else {
     606                                        if (index_next == index_size) {
     607                                                index_size *= 2;
     608                                                db_index = realloc(db_index,
     609                                                                   index_size * sizeof *db_index);
     610                                        }
     611
     612                                        db_index[index_next++].id = strdup(name);
     613                                }
     614                        }
     615
     616                        free(ents[--count]);
     617                }
     618               
     619                if (count != -1) free(ents);
     620                free(top);
     621        } while (dirstack_top);
     622
     623        qsort(db_index, index_next, sizeof *db_index, sysdbcmp);
     624
     625        db->index = db_index;
     626        db->index_size = index_next;
     627
     628        free(dirstack);
     629}
     630
     631#define FAKE_HEADER "1234\0??\1??"
     632#define FAKE_UTC_POS (7 - 4)
     633
     634/* Create a fake data segment for database 'sysdb'. */
     635static void fake_data_segment(timelib_tzdb *sysdb,
     636                              struct location_info **info)
     637{
     638        size_t n;
     639        char *data, *p;
     640       
     641        data = malloc(3 * sysdb->index_size + 7);
     642
     643        p = mempcpy(data, FAKE_HEADER, sizeof(FAKE_HEADER) - 1);
     644
     645        for (n = 0; n < sysdb->index_size; n++) {
     646                const struct location_info *li;
     647                timelib_tzdb_index_entry *ent;
     648
     649                ent = (timelib_tzdb_index_entry *)&sysdb->index[n];
     650
     651                /* Lookup the timezone name in the hash table. */
     652                if (strcmp(ent->id, "UTC") == 0) {
     653                        ent->pos = FAKE_UTC_POS;
     654                        continue;
     655                }
     656
     657                li = find_zone_info(info, ent->id);
     658                if (li) {
     659                        /* If found, append the BC byte and the
     660                         * country code; set the position for this
     661                         * section of timezone data.  */
     662                        ent->pos = (p - data) - 4;
     663                        *p++ = '\1';
     664                        *p++ = li->code[0];
     665                        *p++ = li->code[1];
     666                }
     667                else {
     668                        /* If not found, the timezone data can
     669                         * point at the header. */
     670                        ent->pos = 0;
     671                }
     672        }
     673       
     674        sysdb->data = (unsigned char *)data;
     675}
     676
     677/* Returns true if the passed-in stat structure describes a
     678 * probably-valid timezone file. */
     679static int is_valid_tzfile(const struct stat *st, int fd)
     680{
     681        if (fd) {
     682                char buf[20];
     683                if (read(fd, buf, 20)!=20) {
     684                        return 0;
     685                }
     686                lseek(fd, SEEK_SET, 0);
     687                if (memcmp(buf, "TZif", 4)) {
     688                        return 0;
     689                }
     690        }
     691        return S_ISREG(st->st_mode) && st->st_size > 20;
     692}
     693
     694/* To allow timezone names to be used case-insensitively, find the
     695 * canonical name for this timezone, if possible. */
     696static const char *canonical_tzname(const char *timezone)
     697{
     698    if (timezonedb_system) {
     699        timelib_tzdb_index_entry *ent, lookup;
     700
     701        lookup.id = (char *)timezone;
     702
     703        ent = bsearch(&lookup, timezonedb_system->index,
     704                      timezonedb_system->index_size, sizeof lookup,
     705                      sysdbcmp);
     706        if (ent) {
     707            return ent->id;
     708        }
     709    }
     710
     711    return timezone;
     712}
     713
     714/* Return the mmap()ed tzfile if found, else NULL.  On success, the
     715 * length of the mapped data is placed in *length. */
     716static char *map_tzfile(const char *timezone, size_t *length)
     717{
     718        char fname[PATH_MAX];
     719        struct stat st;
     720        char *p;
     721        int fd;
     722       
     723        if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
     724                return NULL;
     725        }
     726
     727        snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", canonical_tzname(timezone));
     728
     729        fd = open(fname, O_RDONLY);
     730        if (fd == -1) {
     731                return NULL;
     732        } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st, fd)) {
     733                close(fd);
     734                return NULL;
     735        }
     736
     737        *length = st.st_size;
     738        p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
     739        close(fd);
     740       
     741        return p != MAP_FAILED ? p : NULL;
     742}
     743
     744#endif
     745
     746static int inmem_seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
    306747{
    307748        int left = 0, right = tzdb->index_size - 1;
     
    342783}
    343784
     785static int seek_to_tz_position(const unsigned char **tzf, char *timezone,
     786                               char **map, size_t *maplen,
     787                               const timelib_tzdb *tzdb)
     788{
     789#ifdef HAVE_SYSTEM_TZDATA
     790        if (tzdb == timezonedb_system) {
     791                char *orig;
     792
     793                orig = map_tzfile(timezone, maplen);
     794                if (orig == NULL) {
     795                        return 0;
     796                }
     797
     798                (*tzf) = (unsigned char *)orig;
     799                *map = orig;
     800        return 1;
     801        }
     802        else
     803#endif
     804        {
     805                return inmem_seek_to_tz_position(tzf, timezone, tzdb);
     806        }
     807}
     808
    344809const timelib_tzdb *timelib_builtin_db(void)
    345810{
     811#ifdef HAVE_SYSTEM_TZDATA
     812        if (timezonedb_system == NULL) {
     813                timelib_tzdb *tmp = malloc(sizeof *tmp);
     814
     815                tmp->version = "0.system";
     816                tmp->data = NULL;
     817                create_zone_index(tmp);
     818                system_location_table = create_location_table();
     819                fake_data_segment(tmp, system_location_table);
     820                timezonedb_system = tmp;
     821        }
     822
     823        return timezonedb_system;
     824#else
    346825        return &timezonedb_builtin;
     826#endif
    347827}
    348828
    349829const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count)
    350830{
     831#ifdef HAVE_SYSTEM_TZDATA
     832        *count = timezonedb_system->index_size;
     833        return timezonedb_system->index;
     834#else
    351835        *count = sizeof(timezonedb_idx_builtin) / sizeof(*timezonedb_idx_builtin);
    352836        return timezonedb_idx_builtin;
     837#endif
    353838}
    354839
     
    356841{
    357842        const unsigned char *tzf;
    358         return (seek_to_tz_position(&tzf, timezone, tzdb));
     843
     844#ifdef HAVE_SYSTEM_TZDATA
     845        if (tzdb == timezonedb_system) {
     846                char fname[PATH_MAX];
     847                struct stat st;
     848
     849                if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
     850                        return 0;
     851                }
     852
     853                if (system_location_table) {
     854                        if (find_zone_info(system_location_table, timezone) != NULL) {
     855                                /* found in cache */
     856                                return 1;
     857                        }
     858                }
     859
     860                snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", canonical_tzname(timezone));
     861
     862                return stat(fname, &st) == 0 && is_valid_tzfile(&st, 0);
     863        }
     864#endif
     865
     866        return (inmem_seek_to_tz_position(&tzf, timezone, tzdb));
    359867}
    360868
     
    381889{
    382890        const unsigned char *tzf;
     891        char *memmap = NULL;
     892        size_t maplen;
    383893        timelib_tzinfo *tmp;
    384894        int version;
    385895
    386         if (seek_to_tz_position(&tzf, timezone, tzdb)) {
     896        if (seek_to_tz_position(&tzf, timezone, &memmap, &maplen, tzdb)) {
    387897                tmp = timelib_tzinfo_ctor(timezone);
    388898
     
    391901                read_transistions(&tzf, tmp);
    392902                read_types(&tzf, tmp);
    393                 if (version == 2) {
    394                         skip_64bit_preamble(&tzf, tmp);
    395                         read_64bit_header(&tzf, tmp);
    396                         skip_64bit_transistions(&tzf, tmp);
    397                         skip_64bit_types(&tzf, tmp);
    398                         skip_posix_string(&tzf, tmp);
    399                 }
    400                 read_location(&tzf, tmp);
     903
     904#ifdef HAVE_SYSTEM_TZDATA
     905                if (memmap) {
     906                        const struct location_info *li;
     907
     908                        /* TZif-style - grok the location info from the system database,
     909                         * if possible. */
     910
     911                        if ((li = find_zone_info(system_location_table, timezone)) != NULL) {
     912                                tmp->location.comments = timelib_strdup(li->comment);
     913                                strncpy(tmp->location.country_code, li->code, 2);
     914                                tmp->location.longitude = li->longitude;
     915                                tmp->location.latitude = li->latitude;
     916                                tmp->bc = 1;
     917                        }
     918                        else {
     919                                strcpy(tmp->location.country_code, "??");
     920                                tmp->bc = 0;
     921                                tmp->location.comments = timelib_strdup("");
     922                        }
     923
     924                        /* Now done with the mmap segment - discard it. */
     925                        munmap(memmap, maplen);
     926                } else
     927#endif
     928                {
     929                        /* PHP-style - use the embedded info. */
     930                        if (version == 2) {
     931                                skip_64bit_preamble(&tzf, tmp);
     932                                read_64bit_header(&tzf, tmp);
     933                                skip_64bit_transistions(&tzf, tmp);
     934                                skip_64bit_types(&tzf, tmp);
     935                                skip_posix_string(&tzf, tmp);
     936                        }
     937                        read_location(&tzf, tmp);
     938                }
    401939        } else {
    402940                tmp = NULL;
Note: See TracChangeset for help on using the changeset viewer.