diff options
author | Joseph Myers <joseph@codesourcery.com> | 2018-12-10 13:11:58 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2018-12-10 13:11:58 +0000 |
commit | 0afcb29d50312409f0b698001d1ee2b535d06a3c (patch) | |
tree | 810bf1bbcabd4ad6cc535fb15de39c22b7aeac2e /timezone/zic.c | |
parent | b93f4052fc171f456c8f594d4ae4aeb02ec5b3b6 (diff) | |
download | glibc-0afcb29d50312409f0b698001d1ee2b535d06a3c.tar.gz glibc-0afcb29d50312409f0b698001d1ee2b535d06a3c.tar.xz glibc-0afcb29d50312409f0b698001d1ee2b535d06a3c.zip |
Update timezone code from tzcode 2018g.
This patch updates files coming from tzcode to the versions in tzcode 2018g. No changes elsewhere in glibc were needed. Tested for x86_64. * timezone/private.h: Update from tzcode 2018g. * timezone/tzfile.h: Likewise. * timezone/tzselect.ksh: Likewise. * timezone/zdump.c: Likewise. * timezone/zic.c: Likewise.
Diffstat (limited to 'timezone/zic.c')
-rw-r--r-- | timezone/zic.c | 530 |
1 files changed, 322 insertions, 208 deletions
diff --git a/timezone/zic.c b/timezone/zic.c index e738386600..cb1bf28bfb 100644 --- a/timezone/zic.c +++ b/timezone/zic.c @@ -1,3 +1,5 @@ +/* Compile .zi time zone data into TZif binary files. */ + /* ** This file is in the public domain, so clarified as of ** 2006-07-17 by Arthur David Olson. @@ -11,6 +13,7 @@ #include <locale.h> #include <stdarg.h> #include <stddef.h> +#include <stdio.h> #define ZIC_VERSION_PRE_2013 '2' #define ZIC_VERSION '3' @@ -40,12 +43,32 @@ typedef int_fast64_t zic_t; #else #define MKDIR_UMASK 0755 #endif +/* Port to native MS-Windows and to ancient UNIX. */ +#if !defined S_ISDIR && defined S_IFDIR && defined S_IFMT +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */ +#endif /* HAVE_SYS_WAIT_H */ + +#ifndef WIFEXITED +#define WIFEXITED(status) (((status) & 0xff) == 0) +#endif /* !defined WIFEXITED */ +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status) >> 8) & 0xff) +#endif /* !defined WEXITSTATUS */ /* The maximum ptrdiff_t value, for pre-C99 platforms. */ #ifndef PTRDIFF_MAX static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t)); #endif +/* The minimum alignment of a type, for pre-C11 platforms. */ +#if __STDC_VERSION__ < 201112 +# define _Alignof(type) offsetof(struct { char a; type b; }, b) +#endif + /* The type for line numbers. Use PRIdMAX to format them; formerly there was also "#define PRIdLINENO PRIdMAX" and formats used PRIdLINENO, but xgettext cannot grok that. */ @@ -73,7 +96,9 @@ struct rule { /* or wall clock time if 0 */ bool r_todisgmt; /* above is GMT if 1 */ /* or local time if 0 */ - zic_t r_stdoff; /* offset from standard time */ + bool r_isdst; /* is this daylight saving time? */ + zic_t r_stdoff; /* offset from default time (which is + usually standard time) */ const char * r_abbrvar; /* variable part of abbreviation */ bool r_todo; /* a rule to do (used in outzone) */ @@ -94,10 +119,11 @@ struct zone { const char * z_name; zic_t z_gmtoff; - const char * z_rule; + char * z_rule; const char * z_format; char z_format_specifier; + bool z_isdst; zic_t z_stdoff; struct rule * z_rules; @@ -135,8 +161,8 @@ static void adjleap(void); static void associate(void); static void dolink(const char *, const char *, bool); static char ** getfields(char * buf); -static zic_t gethms(const char * string, const char * errstring, - bool); +static zic_t gethms(const char * string, const char * errstring); +static zic_t getstdoff(char *, bool *); static void infile(const char * filename); static void inleap(char ** fields, int nfields); static void inlink(char ** fields, int nfields); @@ -164,11 +190,13 @@ static bool yearistype(zic_t year, const char * type); enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 }; /* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles - tz binary files whose POSIX-TZ-style strings contain '<'; see + TZif files whose POSIX-TZ-style strings contain '<'; see QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>. This workaround will no longer be needed when Qt 5.6.1 and earlier are obsolete, say in the year 2021. */ +#ifndef WORK_AROUND_QTBUG_53071 enum { WORK_AROUND_QTBUG_53071 = true }; +#endif static int charcnt; static bool errors; @@ -299,10 +327,13 @@ struct lookup { static struct lookup const * byword(const char * string, const struct lookup * lp); -static struct lookup const line_codes[] = { +static struct lookup const zi_line_codes[] = { { "Rule", LC_RULE }, { "Zone", LC_ZONE }, { "Link", LC_LINK }, + { NULL, 0 } +}; +static struct lookup const leap_line_codes[] = { { "Leap", LC_LEAP }, { NULL, 0} }; @@ -407,6 +438,16 @@ size_product(size_t nitems, size_t itemsize) return nitems * itemsize; } +static ATTRIBUTE_PURE size_t +align_to(size_t size, size_t alignment) +{ + size_t aligned_size = size + alignment - 1; + aligned_size -= aligned_size % alignment; + if (aligned_size < size) + memory_exhausted(_("alignment overflow")); + return aligned_size; +} + #if !HAVE_STRDUP static char * strdup(char const *str) @@ -416,7 +457,7 @@ strdup(char const *str) } #endif -static ATTRIBUTE_PURE void * +static void * memcheck(void *ptr) { if (ptr == NULL) @@ -424,7 +465,7 @@ memcheck(void *ptr) return ptr; } -static void * +static void * ATTRIBUTE_MALLOC emalloc(size_t size) { return memcheck(malloc(size)); @@ -436,7 +477,7 @@ erealloc(void *ptr, size_t size) return memcheck(realloc(ptr, size)); } -static char * +static char * ATTRIBUTE_MALLOC ecpyalloc (char const *str) { return memcheck(strdup(str)); @@ -534,7 +575,7 @@ usage(FILE *stream, int status) fprintf(stream, _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n" "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n" - "\t[ -L leapseconds ] [ filename ... ]\n\n" + "\t[ -t localtime-link ] [ -L leapseconds ] [ filename ... ]\n\n" "Report bugs to %s.\n"), progname, progname, REPORT_BUGS_TO); if (status == EXIT_SUCCESS) @@ -566,6 +607,7 @@ static const char * psxrules; static const char * lcltime; static const char * directory; static const char * leapsec; +static const char * tzdefault; static const char * yitcommand; int @@ -598,7 +640,7 @@ main(int argc, char **argv) } else if (strcmp(argv[k], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } - while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) + while ((c = getopt(argc, argv, "d:l:L:p:st:vy:")) != EOF && c != -1) switch (c) { default: usage(stderr, EXIT_FAILURE); @@ -632,10 +674,21 @@ _("%s: More than one -p option specified\n"), return EXIT_FAILURE; } break; + case 't': + if (tzdefault != NULL) { + fprintf(stderr, + _("%s: More than one -t option" + " specified\n"), + progname); + return EXIT_FAILURE; + } + tzdefault = optarg; + break; case 'y': - if (yitcommand == NULL) + if (yitcommand == NULL) { + warning(_("-y is obsolescent")); yitcommand = optarg; - else { + } else { fprintf(stderr, _("%s: More than one -y option specified\n"), progname); @@ -663,6 +716,8 @@ _("%s: More than one -L option specified\n"), usage(stderr, EXIT_FAILURE); /* usage message by request */ if (directory == NULL) directory = TZDIR; + if (tzdefault == NULL) + tzdefault = TZDEFAULT; if (yitcommand == NULL) yitcommand = "yearistype"; @@ -699,7 +754,7 @@ _("%s: More than one -L option specified\n"), } if (lcltime != NULL) { eat(_("command line"), 1); - dolink(lcltime, TZDEFAULT, true); + dolink(lcltime, tzdefault, true); } if (psxrules != NULL) { eat(_("command line"), 1); @@ -864,9 +919,11 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink) char *linkalloc = absolute ? NULL : relname(fromfield, tofield); char const *contents = absolute ? fromfield : linkalloc; int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; - if (symlink_errno == ENOENT && !todirs_made) { + if (!todirs_made + && (symlink_errno == ENOENT || symlink_errno == ENOTSUP)) { mkdirs(tofield, true); - symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; + if (symlink_errno == ENOENT) + symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; } free(linkalloc); if (symlink_errno == 0) { @@ -909,44 +966,6 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink) static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); -/* Estimated time of the Big Bang, in seconds since the POSIX epoch. - rounded downward to the negation of a power of two that is - comfortably outside the error bounds. - - For the time of the Big Bang, see: - - Ade PAR, Aghanim N, Armitage-Caplan C et al. Planck 2013 results. - I. Overview of products and scientific results. - arXiv:1303.5062 2013-03-20 20:10:01 UTC - <http://arxiv.org/pdf/1303.5062v1> [PDF] - - Page 36, Table 9, row Age/Gyr, column Planck+WP+highL+BAO 68% limits - gives the value 13.798 plus-or-minus 0.037 billion years. - Multiplying this by 1000000000 and then by 31557600 (the number of - seconds in an astronomical year) gives a value that is comfortably - less than 2**59, so BIG_BANG is - 2**59. - - BIG_BANG is approximate, and may change in future versions. - Please do not rely on its exact value. */ - -#ifndef BIG_BANG -#define BIG_BANG (- (1LL << 59)) -#endif - -/* If true, work around GNOME bug 730332 - <https://bugzilla.gnome.org/show_bug.cgi?id=730332> - by refusing to output time stamps before BIG_BANG. - Such time stamps are physically suspect anyway. - - The GNOME bug is scheduled to be fixed in GNOME 3.22, and if so - this workaround will no longer be needed when GNOME 3.21 and - earlier are obsolete, say in the year 2021. */ -enum { WORK_AROUND_GNOME_BUG_730332 = true }; - -static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332 - ? BIG_BANG - : MINVAL(zic_t, TIME_T_BITS_IN_FILE)); - /* Return true if NAME is a directory. */ static bool itsdir(char const *name) @@ -1053,8 +1072,7 @@ associate(void) ** Maybe we have a local standard time offset. */ eat(zp->z_filename, zp->z_linenum); - zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), - true); + zp->z_stdoff = getstdoff(zp->z_rule, &zp->z_isdst); /* ** Note, though, that if there's no rule, ** a '%s' in the format is a bad thing. @@ -1114,6 +1132,8 @@ infile(const char *name) } else if (wantcont) { wantcont = inzcont(fields, nfields); } else { + struct lookup const *line_codes + = name == leapsec ? leap_line_codes : zi_line_codes; lp = byword(fields[0], line_codes); if (lp == NULL) error(_("input line of unknown type")); @@ -1130,11 +1150,7 @@ infile(const char *name) wantcont = false; break; case LC_LEAP: - if (name != leapsec) - warning(_("%s: Leap line in non leap" - " seconds file %s"), - progname, name); - else inleap(fields, nfields); + inleap(fields, nfields); wantcont = false; break; default: /* "cannot happen" */ @@ -1160,26 +1176,38 @@ _("%s: panic: Invalid l_value %d\n"), */ static zic_t -gethms(char const *string, char const *errstring, bool signable) +gethms(char const *string, char const *errstring) { zic_t hh; - int mm, ss, sign; - char xs; + int sign, mm = 0, ss = 0; + char hhx, mmx, ssx, xr = '0', xs; + int tenths = 0; + bool ok = true; if (string == NULL || *string == '\0') return 0; - if (!signable) - sign = 1; - else if (*string == '-') { + if (*string == '-') { sign = -1; ++string; } else sign = 1; - if (sscanf(string, "%"SCNdZIC"%c", &hh, &xs) == 1) - mm = ss = 0; - else if (sscanf(string, "%"SCNdZIC":%d%c", &hh, &mm, &xs) == 2) - ss = 0; - else if (sscanf(string, "%"SCNdZIC":%d:%d%c", &hh, &mm, &ss, &xs) - != 3) { + switch (sscanf(string, + "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c", + &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) { + default: ok = false; break; + case 8: + ok = '0' <= xr && xr <= '9'; + /* fallthrough */ + case 7: + ok &= ssx == '.'; + if (ok && noise) + warning(_("fractional seconds rejected by" + " pre-2018 versions of zic")); + /* fallthrough */ + case 5: ok &= mmx == ':'; /* fallthrough */ + case 3: ok &= hhx == ':'; /* fallthrough */ + case 1: break; + } + if (!ok) { error("%s", errstring); return 0; } @@ -1193,6 +1221,7 @@ gethms(char const *string, char const *errstring, bool signable) error(_("time overflow")); return 0; } + ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */ if (noise && (hh > HOURSPERDAY || (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) warning(_("values over 24 hours not handled by pre-2007 versions of zic")); @@ -1200,6 +1229,24 @@ warning(_("values over 24 hours not handled by pre-2007 versions of zic")); sign * (mm * SECSPERMIN + ss)); } +static zic_t +getstdoff(char *field, bool *isdst) +{ + int dst = -1; + zic_t stdoff; + size_t fieldlen = strlen(field); + if (fieldlen != 0) { + char *ep = field + fieldlen - 1; + switch (*ep) { + case 'd': dst = 1; *ep = '\0'; break; + case 's': dst = 0; *ep = '\0'; break; + } + } + stdoff = gethms(field, _("invalid saved time")); + *isdst = dst < 0 ? stdoff != 0 : dst; + return stdoff; +} + static void inrule(char **fields, int nfields) { @@ -1209,13 +1256,18 @@ inrule(char **fields, int nfields) error(_("wrong number of fields on Rule line")); return; } - if (*fields[RF_NAME] == '\0') { - error(_("nameless rule")); + switch (*fields[RF_NAME]) { + case '\0': + case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': + case '+': case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + error(_("Invalid rule name \"%s\""), fields[RF_NAME]); return; } r.r_filename = filename; r.r_linenum = linenum; - r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true); + r.r_stdoff = getstdoff(fields[RF_STDOFF], &r.r_isdst); rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); r.r_name = ecpyalloc(fields[RF_NAME]); @@ -1235,10 +1287,10 @@ inzone(char **fields, int nfields) error(_("wrong number of fields on Zone line")); return false; } - if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { + if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) { error( _("\"Zone %s\" line and -l option are mutually exclusive"), - TZDEFAULT); + tzdefault); return false; } if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { @@ -1304,7 +1356,7 @@ inzsub(char **fields, int nfields, bool iscont) } z.z_filename = filename; z.z_linenum = linenum; - z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true); + z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset")); if ((cp = strchr(fields[i_format], '%')) != 0) { if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') || strchr(fields[i_format], '/')) { @@ -1426,7 +1478,7 @@ inleap(char **fields, int nfields) return; } t = dayoff * SECSPERDAY; - tod = gethms(fields[LP_TIME], _("invalid time of day"), false); + tod = gethms(fields[LP_TIME], _("invalid time of day")); cp = fields[LP_CORR]; { register bool positive; @@ -1435,15 +1487,9 @@ inleap(char **fields, int nfields) if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ positive = false; count = 1; - } else if (strcmp(cp, "--") == 0) { - positive = false; - count = 2; } else if (strcmp(cp, "+") == 0) { positive = true; count = 1; - } else if (strcmp(cp, "++") == 0) { - positive = true; - count = 2; } else { error(_("illegal CORRECTION field on Leap line")); return; @@ -1455,8 +1501,8 @@ inleap(char **fields, int nfields) return; } t = tadd(t, tod); - if (t < early_time) { - error(_("leap second precedes Big Bang")); + if (t < 0) { + error(_("leap second precedes Epoch")); return; } leapadd(t, positive, lp->l_value, count); @@ -1527,7 +1573,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, break; } } - rp->r_tod = gethms(dp, _("invalid time of day"), false); + rp->r_tod = gethms(dp, _("invalid time of day")); free(dp); /* ** Year work. @@ -1584,13 +1630,16 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, error(_("typed single year")); return; } + warning(_("year type \"%s\" is obsolete; use \"-\" instead"), + typep); rp->r_yrtype = ecpyalloc(typep); } /* ** Day work. ** Accept things such as: ** 1 - ** last-Sunday + ** lastSunday + ** last-Sunday (undocumented; warn about this) ** Sun<=20 ** Sun>=7 */ @@ -1682,14 +1731,20 @@ atcomp(const void *avp, const void *bvp) return (a < b) ? -1 : (a > b); } -static bool -is32(const zic_t x) +static void +swaptypes(int i, int j) { - return INT32_MIN <= x && x <= INT32_MAX; + { zic_t t = gmtoffs[i]; gmtoffs[i] = gmtoffs[j]; gmtoffs[j] = t; } + { char t = isdsts[i]; isdsts[i] = isdsts[j]; isdsts[j] = t; } + { unsigned char t = abbrinds[i]; abbrinds[i] = abbrinds[j]; + abbrinds[j] = t; } + { bool t = ttisstds[i]; ttisstds[i] = ttisstds[j]; ttisstds[j] = t; } + { bool t = ttisgmts[i]; ttisgmts[i] = ttisgmts[j]; ttisgmts[j] = t; } } static void -writezone(const char *const name, const char *const string, char version) +writezone(const char *const name, const char *const string, char version, + int defaulttype) { register FILE * fp; register ptrdiff_t i, j; @@ -1702,7 +1757,11 @@ writezone(const char *const name, const char *const string, char version) zic_t one = 1; zic_t y2038_boundary = one << 31; ptrdiff_t nats = timecnt + WORK_AROUND_QTBUG_53071; - zic_t *ats = emalloc(size_product(nats, sizeof *ats + 1)); + + /* Allocate the ATS and TYPES arrays via a single malloc, + as this is a bit faster. */ + zic_t *ats = emalloc(align_to(size_product(nats, sizeof *ats + 1), + _Alignof(zic_t))); void *typesptr = ats + nats; unsigned char *types = typesptr; @@ -1719,13 +1778,11 @@ writezone(const char *const name, const char *const string, char version) toi = 0; fromi = 0; - while (fromi < timecnt && attypes[fromi].at < early_time) - ++fromi; for ( ; fromi < timecnt; ++fromi) { - if (toi > 1 && ((attypes[fromi].at + + if (toi != 0 && ((attypes[fromi].at + gmtoffs[attypes[toi - 1].type]) <= - (attypes[toi - 1].at + - gmtoffs[attypes[toi - 2].type]))) { + (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 + : attypes[toi - 2].type]))) { attypes[toi - 1].type = attypes[fromi].type; continue; @@ -1755,18 +1812,6 @@ writezone(const char *const name, const char *const string, char version) types[i] = attypes[i].type; } - /* Work around QTBUG-53071 for time stamps less than y2038_boundary - 1, - by inserting a no-op transition at time y2038_boundary - 1. - This works only for timestamps before the boundary, which - should be good enough in practice as QTBUG-53071 should be - long-dead by 2038. */ - if (WORK_AROUND_QTBUG_53071 && timecnt != 0 - && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) { - ats[timecnt] = y2038_boundary - 1; - types[timecnt] = types[timecnt - 1]; - timecnt++; - } - /* ** Correct for leap seconds. */ @@ -1778,6 +1823,22 @@ writezone(const char *const name, const char *const string, char version) break; } } + + /* Work around QTBUG-53071 for timestamps less than y2038_boundary - 1, + by inserting a no-op transition at time y2038_boundary - 1. + This works only for timestamps before the boundary, which + should be good enough in practice as QTBUG-53071 should be + long-dead by 2038. Do this after correcting for leap + seconds, as the idea is to insert a transition just before + 32-bit time_t rolls around, and this occurs at a slightly + different moment if transitions are leap-second corrected. */ + if (WORK_AROUND_QTBUG_53071 && timecnt != 0 + && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) { + ats[timecnt] = y2038_boundary - 1; + types[timecnt] = types[timecnt - 1]; + timecnt++; + } + /* ** Figure out 32-bit-limited starts and counts. */ @@ -1785,22 +1846,22 @@ writezone(const char *const name, const char *const string, char version) timei32 = 0; leapcnt32 = leapcnt; leapi32 = 0; - while (timecnt32 > 0 && !is32(ats[timecnt32 - 1])) + while (0 < timecnt32 && INT32_MAX < ats[timecnt32 - 1]) --timecnt32; - while (timecnt32 > 0 && !is32(ats[timei32])) { + while (1 < timecnt32 && ats[timei32] < INT32_MIN + && ats[timei32 + 1] <= INT32_MIN) { + /* Discard too-low transitions, except keep any last too-low + transition if no transition is exactly at INT32_MIN. + The kept transition will be output as an INT32_MIN + "transition" appropriate for buggy 32-bit clients that do + not use time type 0 for timestamps before the first + transition; see below. */ --timecnt32; ++timei32; } - /* - ** Output an INT32_MIN "transition" if appropriate; see below. - */ - if (timei32 > 0 && ats[timei32] > INT32_MIN) { - --timei32; - ++timecnt32; - } - while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) + while (0 < leapcnt32 && INT32_MAX < trans[leapcnt32 - 1]) --leapcnt32; - while (leapcnt32 > 0 && !is32(trans[leapi32])) { + while (0 < leapcnt32 && trans[leapi32] < INT32_MIN) { --leapcnt32; ++leapi32; } @@ -1833,7 +1894,8 @@ writezone(const char *const name, const char *const string, char version) for (pass = 1; pass <= 2; ++pass) { register ptrdiff_t thistimei, thistimecnt, thistimelim; register int thisleapi, thisleapcnt, thisleaplim; - int writetype[TZ_MAX_TYPES]; + int old0; + char omittype[TZ_MAX_TYPES]; int typemap[TZ_MAX_TYPES]; register int thistypecnt; char thischars[TZ_MAX_CHARS]; @@ -1858,25 +1920,18 @@ writezone(const char *const name, const char *const string, char version) error(_("too many transition times")); thistimelim = thistimei + thistimecnt; thisleaplim = thisleapi + thisleapcnt; - for (i = 0; i < typecnt; ++i) - writetype[i] = thistimecnt == timecnt; - if (thistimecnt == 0) { - /* - ** No transition times fall in the current - ** (32- or 64-bit) window. - */ - if (typecnt != 0) - writetype[typecnt - 1] = true; - } else { - for (i = thistimei - 1; i < thistimelim; ++i) - if (i >= 0) - writetype[types[i]] = true; - /* - ** For America/Godthab and Antarctica/Palmer - */ - if (thistimei == 0) - writetype[0] = true; - } + memset(omittype, true, typecnt); + omittype[defaulttype] = false; + for (i = thistimei; i < thistimelim; i++) + omittype[types[i]] = false; + + /* Reorder types to make DEFAULTTYPE type 0. + Use TYPEMAP to swap OLD0 and DEFAULTTYPE so that + DEFAULTTYPE appears as type 0 in the output instead + of OLD0. TYPEMAP also omits unused types. */ + old0 = strlen(omittype); + swaptypes(old0, defaulttype); + #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH /* ** For some pre-2011 systems: if the last-to-be-written @@ -1894,8 +1949,8 @@ writezone(const char *const name, const char *const string, char version) if (isdsts[types[i]]) mrudst = types[i]; else mrustd = types[i]; - for (i = 0; i < typecnt; ++i) - if (writetype[i]) { + for (i = old0; i < typecnt; i++) + if (!omittype[i]) { if (isdsts[i]) hidst = i; else histd = i; @@ -1909,7 +1964,7 @@ writezone(const char *const name, const char *const string, char version) ttisstds[mrudst], ttisgmts[mrudst]); isdsts[mrudst] = 1; - writetype[type] = true; + omittype[type] = false; } if (histd >= 0 && mrustd >= 0 && histd != mrustd && gmtoffs[histd] != gmtoffs[mrustd]) { @@ -1920,20 +1975,24 @@ writezone(const char *const name, const char *const string, char version) ttisstds[mrustd], ttisgmts[mrustd]); isdsts[mrustd] = 0; - writetype[type] = true; + omittype[type] = false; } } #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ thistypecnt = 0; - for (i = 0; i < typecnt; ++i) - typemap[i] = writetype[i] ? thistypecnt++ : -1; + for (i = old0; i < typecnt; i++) + if (!omittype[i]) + typemap[i == old0 ? defaulttype + : i == defaulttype ? old0 : i] + = thistypecnt++; + for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i) indmap[i] = -1; thischarcnt = 0; - for (i = 0; i < typecnt; ++i) { + for (i = old0; i < typecnt; i++) { register char * thisabbr; - if (!writetype[i]) + if (omittype[i]) continue; if (indmap[abbrinds[i]] >= 0) continue; @@ -1982,8 +2041,8 @@ writezone(const char *const name, const char *const string, char version) uc = typemap[types[i]]; fwrite(&uc, sizeof uc, 1, fp); } - for (i = 0; i < typecnt; ++i) - if (writetype[i]) { + for (i = old0; i < typecnt; i++) + if (!omittype[i]) { puttzcode(gmtoffs[i], fp); putc(isdsts[i], fp); putc((unsigned char) indmap[abbrinds[i]], fp); @@ -2016,12 +2075,13 @@ writezone(const char *const name, const char *const string, char version) else puttzcode64(todo, fp); puttzcode(corr[i], fp); } - for (i = 0; i < typecnt; ++i) - if (writetype[i]) + for (i = old0; i < typecnt; i++) + if (!omittype[i]) putc(ttisstds[i], fp); - for (i = 0; i < typecnt; ++i) - if (writetype[i]) + for (i = old0; i < typecnt; i++) + if (!omittype[i]) putc(ttisgmts[i], fp); + swaptypes(old0, defaulttype); } fprintf(fp, "\n%s\n", string); close_file(fp, directory, name); @@ -2044,7 +2104,7 @@ abbroffset(char *buf, zic_t offset) minutes = offset % MINSPERHOUR; offset /= MINSPERHOUR; if (100 <= offset) { - error(_("%%z UTC offset magnitude exceeds 99:59:59")); + error(_("%%z UT offset magnitude exceeds 99:59:59")); return "%z"; } else { char *p = buf; @@ -2066,7 +2126,7 @@ abbroffset(char *buf, zic_t offset) static size_t doabbr(char *abbr, struct zone const *zp, char const *letters, - zic_t stdoff, bool doquotes) + bool isdst, zic_t stdoff, bool doquotes) { register char * cp; register char * slashp; @@ -2081,7 +2141,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters, else if (!letters) letters = "%s"; sprintf(abbr, format, letters); - } else if (stdoff != 0) { + } else if (isdst) { strcpy(abbr, slashp + 1); } else { memcpy(abbr, format, slashp - format); @@ -2192,7 +2252,7 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff, } if (rp->r_todisgmt) tod += gmtoff; - if (rp->r_todisstd && rp->r_stdoff == 0) + if (rp->r_todisstd && !rp->r_isdst) tod += dstoff; if (tod != 2 * SECSPERMIN * MINSPERHOUR) { *result++ = '/'; @@ -2249,7 +2309,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) continue; if (rp->r_yrtype != NULL) continue; - if (rp->r_stdoff == 0) { + if (!rp->r_isdst) { if (stdrp == NULL) stdrp = rp; else return -1; @@ -2268,7 +2328,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) register struct rule *stdabbrrp = NULL; for (i = 0; i < zp->z_nrules; ++i) { rp = &zp->z_rules[i]; - if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0) + if (!rp->r_isdst && rule_cmp(stdabbrrp, rp) < 0) stdabbrrp = rp; if (rule_cmp(stdrp, rp) < 0) stdrp = rp; @@ -2281,13 +2341,14 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) if (stdrp != NULL && stdrp->r_hiyear == 2037) return YEAR_BY_YEAR_ZONE; - if (stdrp != NULL && stdrp->r_stdoff != 0) { + if (stdrp != NULL && stdrp->r_isdst) { /* Perpetual DST. */ dstr.r_month = TM_JANUARY; dstr.r_dycode = DC_DOM; dstr.r_dayofmonth = 1; dstr.r_tod = 0; dstr.r_todisstd = dstr.r_todisgmt = false; + dstr.r_isdst = stdrp->r_isdst; dstr.r_stdoff = stdrp->r_stdoff; dstr.r_abbrvar = stdrp->r_abbrvar; stdr.r_month = TM_DECEMBER; @@ -2295,6 +2356,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) stdr.r_dayofmonth = 31; stdr.r_tod = SECSPERDAY + stdrp->r_stdoff; stdr.r_todisstd = stdr.r_todisgmt = false; + stdr.r_isdst = false; stdr.r_stdoff = 0; stdr.r_abbrvar = (stdabbrrp ? stdabbrrp->r_abbrvar : ""); @@ -2302,10 +2364,10 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) stdrp = &stdr; } } - if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) + if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_isdst)) return -1; abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; - len = doabbr(result, zp, abbrvar, 0, true); + len = doabbr(result, zp, abbrvar, false, 0, true); offsetlen = stringoffset(result + len, -zp->z_gmtoff); if (! offsetlen) { result[0] = '\0'; @@ -2314,7 +2376,8 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) len += offsetlen; if (dstrp == NULL) return compat; - len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true); + len += doabbr(result + len, zp, dstrp->r_abbrvar, + dstrp->r_isdst, dstrp->r_stdoff, true); if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) { offsetlen = stringoffset(result + len, -(zp->z_gmtoff + dstrp->r_stdoff)); @@ -2372,6 +2435,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) zic_t one = 1; zic_t y2038_boundary = one << 31; zic_t max_year0; + int defaulttype = -1; max_abbr_len = 2 + max_format_len + max_abbrvar_len; max_envvar_len = 2 * max_abbr_len + 5 * 9; @@ -2480,9 +2544,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) */ stdoff = 0; zp = &zpfirst[i]; - usestart = i > 0 && (zp - 1)->z_untiltime > early_time; + usestart = i > 0 && (zp - 1)->z_untiltime > min_time; useuntil = i < (zonecount - 1); - if (useuntil && zp->z_untiltime <= early_time) + if (useuntil && zp->z_untiltime <= min_time) continue; gmtoff = zp->z_gmtoff; eat(zp->z_filename, zp->z_linenum); @@ -2490,14 +2554,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) startoff = zp->z_gmtoff; if (zp->z_nrules == 0) { stdoff = zp->z_stdoff; - doabbr(startbuf, zp, NULL, stdoff, false); + doabbr(startbuf, zp, NULL, zp->z_isdst, stdoff, false); type = addtype(oadd(zp->z_gmtoff, stdoff), - startbuf, stdoff != 0, startttisstd, + startbuf, zp->z_isdst, startttisstd, startttisgmt); if (usestart) { addtt(starttime, type); usestart = false; - } else addtt(early_time, type); + } else + defaulttype = type; } else for (year = min_year; year <= max_year; ++year) { if (useuntil && year > zp->z_untilrule.r_hiyear) break; @@ -2588,6 +2653,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) stdoff); doabbr(startbuf, zp, rp->r_abbrvar, + rp->r_isdst, rp->r_stdoff, false); continue; @@ -2598,6 +2664,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) doabbr(startbuf, zp, rp->r_abbrvar, + rp->r_isdst, rp->r_stdoff, false); } @@ -2605,10 +2672,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) eats(zp->z_filename, zp->z_linenum, rp->r_filename, rp->r_linenum); doabbr(ab, zp, rp->r_abbrvar, - rp->r_stdoff, false); + rp->r_isdst, rp->r_stdoff, false); offset = oadd(zp->z_gmtoff, rp->r_stdoff); - type = addtype(offset, ab, rp->r_stdoff != 0, + type = addtype(offset, ab, rp->r_isdst, rp->r_todisstd, rp->r_todisgmt); + if (defaulttype < 0 && !rp->r_isdst) + defaulttype = type; if (rp->r_hiyear == ZIC_MAX && ! (0 <= lastatmax && ktime < attypes[lastatmax].at)) @@ -2625,11 +2694,14 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) eat(zp->z_filename, zp->z_linenum); if (*startbuf == '\0') error(_("can't determine time zone abbreviation to use just after until time")); - else addtt(starttime, - addtype(startoff, startbuf, - startoff != zp->z_gmtoff, - startttisstd, - startttisgmt)); + else { + bool isdst = startoff != zp->z_gmtoff; + type = addtype(startoff, startbuf, isdst, + startttisstd, startttisgmt); + if (defaulttype < 0 && !isdst) + defaulttype = type; + addtt(starttime, type); + } } /* ** Now we may get to set starttime for the next zone line. @@ -2644,6 +2716,8 @@ error(_("can't determine time zone abbreviation to use just after until time")); starttime = tadd(starttime, -gmtoff); } } + if (defaulttype < 0) + defaulttype = 0; if (0 <= lastatmax) attypes[lastatmax].dontmerge = true; if (do_extend) { @@ -2671,7 +2745,7 @@ error(_("can't determine time zone abbreviation to use just after until time")); attypes[timecnt - 1].dontmerge = true; } } - writezone(zpfirst->z_name, envvar, version); + writezone(zpfirst->z_name, envvar, version, defaulttype); free(startbuf); free(ab); free(envvar); @@ -2680,20 +2754,6 @@ error(_("can't determine time zone abbreviation to use just after until time")); static void addtt(zic_t starttime, int type) { - if (starttime <= early_time - || (timecnt == 1 && attypes[0].at < early_time)) { - gmtoffs[0] = gmtoffs[type]; - isdsts[0] = isdsts[type]; - ttisstds[0] = ttisstds[type]; - ttisgmts[0] = ttisgmts[type]; - if (abbrinds[type] != 0) - strcpy(chars, &chars[abbrinds[type]]); - abbrinds[0] = 0; - charcnt = strlen(chars) + 1; - typecnt = 1; - timecnt = 0; - type = 0; - } attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc); attypes[timecnt].at = starttime; attypes[timecnt].dontmerge = false; @@ -2754,13 +2814,8 @@ leapadd(zic_t t, bool positive, int rolling, int count) exit(EXIT_FAILURE); } for (i = 0; i < leapcnt; ++i) - if (t <= trans[i]) { - if (t == trans[i]) { - error(_("repeated leap second moment")); - exit(EXIT_FAILURE); - } + if (t <= trans[i]) break; - } do { for (j = leapcnt; j > i; --j) { trans[j] = trans[j - 1]; @@ -2779,11 +2834,17 @@ adjleap(void) { register int i; register zic_t last = 0; + register zic_t prevtrans = 0; /* ** propagate leap seconds forward */ for (i = 0; i < leapcnt; ++i) { + if (trans[i] - prevtrans < 28 * SECSPERDAY) { + error(_("Leap seconds too close together")); + exit(EXIT_FAILURE); + } + prevtrans = trans[i]; trans[i] = tadd(trans[i], last); last = corr[i] += last; } @@ -2884,7 +2945,7 @@ lowerit(char a) } /* case-insensitive equality */ -static ATTRIBUTE_PURE bool +static bool ciequal(register const char *ap, register const char *bp) { while (lowerit(*ap) == lowerit(*bp++)) @@ -2893,7 +2954,7 @@ ciequal(register const char *ap, register const char *bp) return false; } -static ATTRIBUTE_PURE bool +static bool itsabbr(register const char *abbr, register const char *word) { if (lowerit(*abbr) != lowerit(*word)) @@ -2907,7 +2968,20 @@ itsabbr(register const char *abbr, register const char *word) return true; } -static ATTRIBUTE_PURE const struct lookup * +/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */ + +static bool +ciprefix(char const *abbr, char const *word) +{ + do + if (!*abbr) + return true; + while (lowerit(*abbr++) == lowerit(*word++)); + + return false; +} + +static const struct lookup * byword(const char *word, const struct lookup *table) { register const struct lookup * foundlp; @@ -2915,6 +2989,20 @@ byword(const char *word, const struct lookup *table) if (word == NULL || table == NULL) return NULL; + + /* If TABLE is LASTS and the word starts with "last" followed + by a non-'-', skip the "last" and look in WDAY_NAMES instead. + Warn about any usage of the undocumented prefix "last-". */ + if (table == lasts && ciprefix("last", word) && word[4]) { + if (word[4] == '-') + warning(_("\"%s\" is undocumented; use \"last%s\" instead"), + word, word + 5); + else { + word += 4; + table = wday_names; + } + } + /* ** Look for exact match. */ @@ -2926,11 +3014,25 @@ byword(const char *word, const struct lookup *table) */ foundlp = NULL; for (lp = table; lp->l_word != NULL; ++lp) - if (itsabbr(word, lp->l_word)) { + if (ciprefix(word, lp->l_word)) { if (foundlp == NULL) foundlp = lp; else return NULL; /* multiple inexact matches */ } + + /* Warn about any backward-compatibility issue with pre-2017c zic. */ + if (foundlp) { + bool pre_2017c_match = false; + for (lp = table; lp->l_word; lp++) + if (itsabbr(word, lp->l_word)) { + if (pre_2017c_match) { + warning(_("\"%s\" is ambiguous in pre-2017c zic"), word); + break; + } + pre_2017c_match = true; + } + } + return foundlp; } @@ -3023,6 +3125,15 @@ rpytime(const struct rule *rp, zic_t wantedy) dayoff = 0; m = TM_JANUARY; y = EPOCH_YEAR; + if (y < wantedy) { + wantedy -= y; + dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY); + wantedy %= YEARSPERREPEAT; + wantedy += y; + } else if (wantedy < 0) { + dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY); + wantedy %= YEARSPERREPEAT; + } while (wantedy != y) { if (wantedy > y) { i = len_years[isleap(y)]; @@ -3134,11 +3245,14 @@ mkdirs(char const *argname, bool ancestors) cp = name = ecpyalloc(argname); + /* On MS-Windows systems, do not worry about drive letters or + backslashes, as this should suffice in practice. Time zone + names do not use drive letters and backslashes. If the -d + option of zic does not name an already-existing directory, + it can use slashes to separate the already-existing + ancestor prefix from the to-be-created subdirectories. */ + /* Do not mkdir a root directory, as it must exist. */ -#ifdef HAVE_DOS_FILE_NAMES - if (is_alpha(name[0]) && name[1] == ':') - cp += 2; -#endif while (*cp == '/') cp++; |