diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 04105b2c968..d946e882aa6 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -26,15 +26,15 @@ #ifndef WILDABBR /* * Someone might make incorrect use of a time zone abbreviation: - * 1. They might reference tzname[0] before calling tzset (explicitly + * 1. They might reference tzname[0] before calling tzset (explicitly * or implicitly). - * 2. They might reference tzname[1] before calling tzset (explicitly + * 2. They might reference tzname[1] before calling tzset (explicitly * or implicitly). - * 3. They might reference tzname[1] after setting to a time zone + * 3. They might reference tzname[1] after setting to a time zone * in which Daylight Saving Time is never observed. - * 4. They might reference tzname[0] after setting to a time zone + * 4. They might reference tzname[0] after setting to a time zone * in which Standard Time is never observed. - * 5. They might reference tm.TM_ZONE after calling offtime. + * 5. They might reference tm.TM_ZONE after calling offtime. * What's best to do in the above cases is open to debate; * for now, we just set things up so that in any of the five cases * WILDABBR is used. Another possibility: initialize tzname[0] to the @@ -44,18 +44,14 @@ * that tzname[0] has the "normal" length of three characters). */ #define WILDABBR " " -#endif /* !defined WILDABBR */ +#endif /* !defined WILDABBR */ static const char wildabbr[] = WILDABBR; static const char gmt[] = "GMT"; -/* The minimum and maximum finite time values. This assumes no padding. */ -static const pg_time_t time_t_min = MINVAL(pg_time_t, TYPE_BIT(pg_time_t)); -static const pg_time_t time_t_max = MAXVAL(pg_time_t, TYPE_BIT(pg_time_t)); - /* - * We cache the result of trying to load the TZDEFRULES zone here. + * PG: We cache the result of trying to load the TZDEFRULES zone here. * tzdefrules_loaded is 0 if not tried yet, +1 if good, -1 if failed. */ static struct state tzdefrules_s; @@ -63,12 +59,12 @@ static int tzdefrules_loaded = 0; /* * The DST rules to use if TZ has no rules and we can't load TZDEFRULES. - * We default to US rules as of 1999-08-17. + * Default to US rules as of 2017-05-07. * POSIX 1003.1 section 8.1.1 says that the default DST rules are * implementation dependent; for historical reasons, US rules are a * common default. */ -#define TZDEFRULESTRING ",M4.1.0,M10.5.0" +#define TZDEFRULESTRING ",M3.2.0,M11.1.0" /* structs ttinfo, lsinfo, state have been moved to pgtz.h */ @@ -112,7 +108,7 @@ static struct pg_tm tm; /* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */ static void -init_ttinfo(struct ttinfo * s, int32 gmtoff, bool isdst, int abbrind) +init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind) { s->tt_gmtoff = gmtoff; s->tt_isdst = isdst; @@ -189,16 +185,14 @@ union input_buffer /* The entire buffer. */ char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state) - + 4 * TZ_MAX_TIMES]; + + 4 * TZ_MAX_TIMES]; }; /* Local storage needed for 'tzloadbody'. */ union local_storage { - /* We don't need the "fullname" member */ - /* The results of analyzing the file's contents after it is opened. */ - struct + struct file_analysis { /* The input buffer. */ union input_buffer u; @@ -206,6 +200,8 @@ union local_storage /* A temporary state used for parsing a TZ string in the file. */ struct state st; } u; + + /* We don't need the "fullname" member */ }; /* Load tz data from the file named NAME into *SP. Read extended @@ -215,8 +211,8 @@ union local_storage * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!). */ static int -tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, - union local_storage * lsp) +tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, + union local_storage *lsp) { int i; int fid; @@ -255,6 +251,8 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, { int32 ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt); int32 ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt); + int64 prevtr = 0; + int32 prevcorr = 0; int32 leapcnt = detzcode(up->tzhead.tzh_leapcnt); int32 timecnt = detzcode(up->tzhead.tzh_timecnt); int32 typecnt = detzcode(up->tzhead.tzh_typecnt); @@ -270,7 +268,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, return EINVAL; if (nread < (tzheadsize /* struct tzhead */ - + timecnt * stored /* ats */ + + timecnt * stored /* ats */ + timecnt /* types */ + typecnt * 6 /* ttinfos */ + charcnt /* chars */ @@ -285,8 +283,8 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, /* * Read transitions, discarding those out of pg_time_t range. But - * pretend the last transition before time_t_min occurred at - * time_t_min. + * pretend the last transition before TIME_T_MIN occurred at + * TIME_T_MIN. */ timecnt = 0; for (i = 0; i < sp->timecnt; ++i) @@ -294,12 +292,12 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, int64 at = stored == 4 ? detzcode(p) : detzcode64(p); - sp->types[i] = at <= time_t_max; + sp->types[i] = at <= TIME_T_MAX; if (sp->types[i]) { pg_time_t attime - = ((TYPE_SIGNED(pg_time_t) ? at < time_t_min : at < 0) - ? time_t_min : at); + = ((TYPE_SIGNED(pg_time_t) ? at < TIME_T_MIN : at < 0) + ? TIME_T_MIN : at); if (timecnt && attime <= sp->ats[timecnt - 1]) { @@ -354,20 +352,22 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, int32 corr = detzcode(p + stored); p += stored + 4; - if (tr <= time_t_max) + /* Leap seconds cannot occur before the Epoch. */ + if (tr < 0) + return EINVAL; + if (tr <= TIME_T_MAX) { - pg_time_t trans - = ((TYPE_SIGNED(pg_time_t) ? tr < time_t_min : tr < 0) - ? time_t_min : tr); - - if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans) - { - if (trans < sp->lsis[leapcnt - 1].ls_trans) - return EINVAL; - leapcnt--; - } - sp->lsis[leapcnt].ls_trans = trans; - sp->lsis[leapcnt].ls_corr = corr; + /* + * Leap seconds cannot occur more than once per UTC month, and + * UTC months are at least 28 days long (minus 1 second for a + * negative leap second). Each leap second's correction must + * differ from the previous one's by 1 second. + */ + if (tr - prevtr < 28 * SECSPERDAY - 1 + || (corr != prevcorr - 1 && corr != prevcorr + 1)) + return EINVAL; + sp->lsis[leapcnt].ls_trans = prevtr = tr; + sp->lsis[leapcnt].ls_corr = prevcorr = corr; leapcnt++; } } @@ -508,7 +508,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, } /* - * If type 0 is is unused in transitions, it's the type to use for early + * If type 0 is unused in transitions, it's the type to use for early * times. */ for (i = 0; i < sp->timecnt; ++i) @@ -553,7 +553,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!). */ int -tzload(const char *name, char *canonname, struct state * sp, bool doextend) +tzload(const char *name, char *canonname, struct state *sp, bool doextend) { union local_storage *lsp = malloc(sizeof *lsp); @@ -569,7 +569,7 @@ tzload(const char *name, char *canonname, struct state * sp, bool doextend) } static bool -typesequiv(const struct state * sp, int a, int b) +typesequiv(const struct state *sp, int a, int b) { bool result; @@ -737,7 +737,7 @@ getoffset(const char *strp, int32 *offsetp) * Otherwise, return a pointer to the first character not part of the rule. */ static const char * -getrule(const char *strp, struct rule * rulep) +getrule(const char *strp, struct rule *rulep) { if (*strp == 'J') { @@ -788,7 +788,7 @@ getrule(const char *strp, struct rule * rulep) strp = getoffset(strp, &rulep->r_time); } else - rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ + rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } @@ -797,7 +797,7 @@ getrule(const char *strp, struct rule * rulep) * effect, calculate the year-relative time that rule takes effect. */ static int32 -transtime(int year, const struct rule * rulep, +transtime(int year, const struct rule *rulep, int32 offset) { bool leapyear; @@ -894,7 +894,7 @@ transtime(int year, const struct rule * rulep, * Returns true on success, false on failure. */ bool -tzparse(const char *name, struct state * sp, bool lastditch) +tzparse(const char *name, struct state *sp, bool lastditch) { const char *stdname; const char *dstname = NULL; @@ -921,7 +921,7 @@ tzparse(const char *name, struct state * sp, bool lastditch) stdlen = (sizeof sp->chars) - 1; charcnt = stdlen + 1; stdoffset = 0; - sp->goback = sp->goahead = false; /* simulate failed tzload() */ + sp->goback = sp->goahead = false; /* simulate failed tzload() */ load_ok = false; } else @@ -1217,7 +1217,7 @@ tzparse(const char *name, struct state * sp, bool lastditch) } static void -gmtload(struct state * sp) +gmtload(struct state *sp) { if (tzload(gmt, NULL, sp, true) != 0) tzparse(gmt, sp, true); @@ -1231,8 +1231,8 @@ gmtload(struct state * sp) * but it *is* desirable.) */ static struct pg_tm * -localsub(struct state const * sp, pg_time_t const * timep, - struct pg_tm * tmp) +localsub(struct state const *sp, pg_time_t const *timep, + struct pg_tm *tmp) { const struct ttinfo *ttisp; int i; @@ -1323,7 +1323,7 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz) * Except we have a private "struct state" for GMT, so no sp is passed in. */ static struct pg_tm * -gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp) +gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp) { struct pg_tm *result; @@ -1361,16 +1361,23 @@ pg_gmtime(const pg_time_t *timep) * Return the number of leap years through the end of the given year * where, to make the math easy, the answer for year zero is defined as zero. */ +static int +leaps_thru_end_of_nonneg(int y) +{ + return y / 4 - y / 100 + y / 400; +} + static int leaps_thru_end_of(const int y) { - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); + return (y < 0 + ? -1 - leaps_thru_end_of_nonneg(-1 - y) + : leaps_thru_end_of_nonneg(y)); } static struct pg_tm * timesub(const pg_time_t *timep, int32 offset, - const struct state * sp, struct pg_tm * tmp) + const struct state *sp, struct pg_tm *tmp) { const struct lsinfo *lp; pg_time_t tdays; @@ -1390,22 +1397,9 @@ timesub(const pg_time_t *timep, int32 offset, lp = &sp->lsis[i]; if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) - { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) - { - ++hit; - --i; - } - } corr = lp->ls_corr; + hit = (*timep == lp->ls_trans + && (i == 0 ? 0 : lp[-1].ls_corr) < corr); break; } } @@ -1529,13 +1523,13 @@ increment_overflow_time(pg_time_t *tp, int32 j) { /*---------- * This is like - * 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...', + * 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', * except that it does the right thing even if *tp + j would overflow. *---------- */ if (!(j < 0 - ? (TYPE_SIGNED(pg_time_t) ? time_t_min - j <= *tp : -1 - j < *tp) - : *tp <= time_t_max - j)) + ? (TYPE_SIGNED(pg_time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp) + : *tp <= TIME_T_MAX - j)) return true; *tp += j; return false; diff --git a/src/timezone/private.h b/src/timezone/private.h index f78053660e8..701112ec5b8 100644 --- a/src/timezone/private.h +++ b/src/timezone/private.h @@ -38,26 +38,9 @@ #define EOVERFLOW EINVAL #endif -#ifndef WIFEXITED -#define WIFEXITED(status) (((status) & 0xff) == 0) -#endif /* !defined WIFEXITED */ -#ifndef WEXITSTATUS -#define WEXITSTATUS(status) (((status) >> 8) & 0xff) -#endif /* !defined WEXITSTATUS */ - /* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) -/* - * SunOS 4.1.1 libraries lack remove. - */ - -#ifndef remove -extern int unlink(const char *filename); - -#define remove unlink -#endif /* !defined remove */ - /* * Finally, some convenience items. @@ -78,6 +61,10 @@ extern int unlink(const char *filename); #define MINVAL(t, b) \ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) +/* The extreme time values, assuming no padding. */ +#define TIME_T_MIN MINVAL(pg_time_t, TYPE_BIT(pg_time_t)) +#define TIME_T_MAX MAXVAL(pg_time_t, TYPE_BIT(pg_time_t)) + /* * 302 / 1000 is log10(2.0) rounded up. * Subtract one for the sign bit if the type is signed; @@ -91,7 +78,7 @@ extern int unlink(const char *filename); /* * INITIALIZE(x) */ -#define INITIALIZE(x) ((x) = 0) +#define INITIALIZE(x) ((x) = 0) #undef _ #define _(msgid) (msgid) @@ -146,7 +133,7 @@ extern int unlink(const char *filename); * or * isleap(a + b) == isleap(a % 400 + b % 400) * This is true even if % means modulo rather than Fortran remainder - * (which is allowed by C89 but not C99). + * (which is allowed by C89 but not by C99 or later). * We use this to avoid addition overflow problems. */ @@ -162,4 +149,4 @@ extern int unlink(const char *filename); ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR) #define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ -#endif /* !defined PRIVATE_H */ +#endif /* !defined PRIVATE_H */ diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c index 2f32cf8bdb7..e1c64834430 100644 --- a/src/timezone/strftime.c +++ b/src/timezone/strftime.c @@ -41,7 +41,6 @@ #include "postgres.h" #include -#include #include "private.h" @@ -83,17 +82,17 @@ static const struct lc_time_T C_time_locale = { /* * x_fmt * - * C99 requires this format. Using just numbers (as here) makes Quakers - * happier; it's also compatible with SVR4. + * C99 and later require this format. Using just numbers (as here) makes + * Quakers happier; it's also compatible with SVR4. */ "%m/%d/%y", /* * c_fmt * - * C99 requires this format. Previously this code used "%D %X", but we now - * conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by Solaris - * 2.3. + * C99 and later require this format. Previously this code used "%D %X", + * but we now conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by + * Solaris 2.3. */ "%a %b %e %T %Y", @@ -107,26 +106,25 @@ static const struct lc_time_T C_time_locale = { "%a %b %e %H:%M:%S %Z %Y" }; +enum warn +{ + IN_NONE, IN_SOME, IN_THIS, IN_ALL +}; + static char *_add(const char *, char *, const char *); static char *_conv(int, const char *, char *, const char *); -static char *_fmt(const char *, const struct pg_tm *, char *, - const char *, int *); +static char *_fmt(const char *, const struct pg_tm *, char *, const char *, + enum warn *); static char *_yconv(int, int, bool, bool, char *, const char *); -#define IN_NONE 0 -#define IN_SOME 1 -#define IN_THIS 2 -#define IN_ALL 3 - size_t pg_strftime(char *s, size_t maxsize, const char *format, - const struct pg_tm * t) + const struct pg_tm *t) { char *p; - int warn; + enum warn warn = IN_NONE; - warn = IN_NONE; p = _fmt(format, t, s, s + maxsize, &warn); if (p == s + maxsize) return 0; @@ -135,8 +133,8 @@ pg_strftime(char *s, size_t maxsize, const char *format, } static char * -_fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, - int *warnp) +_fmt(const char *format, const struct pg_tm *t, char *pt, + const char *ptlim, enum warn *warnp) { for (; *format; ++format) { @@ -185,7 +183,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, continue; case 'c': { - int warn2 = IN_SOME; + enum warn warn2 = IN_SOME; pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) @@ -204,9 +202,9 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, case 'O': /* - * C99 locale modifiers. The sequences %Ec %EC %Ex %EX - * %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU %OV %Ow - * %OW %Oy are supposed to provide alternate + * Locale modifiers of C99 and later. The sequences %Ec + * %EC %Ex %EX %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU + * %OV %Ow %OW %Oy are supposed to provide alternate * representations. */ goto label; @@ -246,7 +244,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, */ pt = _add("kitchen sink", pt, ptlim); continue; -#endif /* defined KITCHEN_SINK */ +#endif /* defined KITCHEN_SINK */ case 'l': /* @@ -418,7 +416,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, continue; case 'x': { - int warn2 = IN_SOME; + enum warn warn2 = IN_SOME; pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) @@ -443,8 +441,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, pt = _add(t->tm_zone, pt, ptlim); /* - * C99 says that %Z must be replaced by the empty string - * if the time zone is not determinable. + * C99 and later say that %Z must be replaced by the empty + * string if the time zone is not determinable. */ continue; case 'z': diff --git a/src/timezone/tzfile.h b/src/timezone/tzfile.h index 56a5b434727..2843833e494 100644 --- a/src/timezone/tzfile.h +++ b/src/timezone/tzfile.h @@ -34,9 +34,9 @@ struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ - char tzh_reserved[15]; /* reserved; must be zero */ - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_reserved[15]; /* reserved; must be zero */ + char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_timecnt[4]; /* coded number of transition times */ char tzh_typecnt[4]; /* coded number of local time types */ @@ -100,4 +100,4 @@ struct tzhead #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#endif /* !defined TZFILE_H */ +#endif /* !defined TZFILE_H */ diff --git a/src/timezone/zic.c b/src/timezone/zic.c index f6beedcab2a..db119265c35 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -9,10 +9,8 @@ #include "postgres_fe.h" #include -#include #include #include -#include #include "pg_getopt.h" @@ -28,7 +26,7 @@ typedef int64 zic_t; #ifndef ZIC_MAX_ABBR_LEN_WO_WARN #define ZIC_MAX_ABBR_LEN_WO_WARN 6 -#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ +#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ #ifndef WIN32 #ifdef S_IRUSR @@ -47,9 +45,12 @@ typedef int64 zic_t; static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t)); #endif -/* The type and printf format for line numbers. */ +/* + * The type for line numbers. In Postgres, use %d to format them; upstream + * uses PRIdMAX but we prefer not to rely on that, not least because it + * results in platform-dependent strings to be translated. + */ typedef int lineno_t; -#define PRIdLINENO "d" struct rule { @@ -84,9 +85,9 @@ struct rule * r_dycode r_dayofmonth r_wday */ -#define DC_DOM 0 /* 1..31 */ /* unused */ -#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ -#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ +#define DC_DOM 0 /* 1..31 */ /* unused */ +#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ +#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ struct zone { @@ -138,9 +139,9 @@ static char lowerit(char); static void mkdirs(char const *, bool); static void newabbr(const char *abbr); static zic_t oadd(zic_t t1, zic_t t2); -static void outzone(const struct zone * zp, ptrdiff_t ntzones); -static zic_t rpytime(const struct rule * rp, zic_t wantedy); -static void rulesub(struct rule * rp, +static void outzone(const struct zone *zp, ptrdiff_t ntzones); +static zic_t rpytime(const struct rule *rp, zic_t wantedy); +static void rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, const char *typep, const char *monthp, const char *dayp, const char *timep); @@ -292,12 +293,15 @@ struct lookup }; static struct lookup const *byword(const char *string, - const struct lookup * lp); + 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} }; @@ -373,7 +377,7 @@ static struct attype zic_t at; bool dontmerge; unsigned char type; -} *attypes; +} *attypes; static zic_t gmtoffs[TZ_MAX_TYPES]; static char isdsts[TZ_MAX_TYPES]; static unsigned char abbrinds[TZ_MAX_TYPES]; @@ -424,7 +428,7 @@ erealloc(void *ptr, size_t size) } static char * -ecpyalloc(char const * str) +ecpyalloc(char const *str) { return memcheck(strdup(str)); } @@ -436,7 +440,8 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc) return ptr; else { - ptrdiff_t amax = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071; + ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071; + ptrdiff_t amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX; if ((amax - 1) / 3 * 2 < *nitems_alloc) memory_exhausted(_("integer overflow")); @@ -450,7 +455,7 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc) */ static void -eats(char const * name, lineno_t num, char const * rname, lineno_t rnum) +eats(char const *name, lineno_t num, char const *rname, lineno_t rnum) { filename = name; linenum = num; @@ -459,7 +464,7 @@ eats(char const * name, lineno_t num, char const * rname, lineno_t rnum) } static void -eat(char const * name, lineno_t num) +eat(char const *name, lineno_t num) { eats(name, num, NULL, -1); } @@ -472,10 +477,10 @@ verror(const char *string, va_list args) * "*" -v on BSD systems. */ if (filename) - fprintf(stderr, _("\"%s\", line %" PRIdLINENO ": "), filename, linenum); + fprintf(stderr, _("\"%s\", line %d: "), filename, linenum); vfprintf(stderr, string, args); if (rfilename != NULL) - fprintf(stderr, _(" (rule from \"%s\", line %" PRIdLINENO ")"), + fprintf(stderr, _(" (rule from \"%s\", line %d)"), rfilename, rlinenum); fprintf(stderr, "\n"); } @@ -504,7 +509,7 @@ warning(const char *string,...) } static void -close_file(FILE *stream, char const * dir, char const * name) +close_file(FILE *stream, char const *dir, char const *name) { char const *e = (ferror(stream) ? _("I/O error") : fclose(stream) != 0 ? strerror(errno) : NULL); @@ -537,7 +542,7 @@ usage(FILE *stream, int status) ancestors. After this is done, all files are accessed with names relative to DIR. */ static void -change_directory(char const * dir) +change_directory(char const *dir) { if (chdir(dir) != 0) { @@ -564,7 +569,7 @@ static const char *leapsec; static const char *yitcommand; int -main(int argc, char *argv[]) +main(int argc, char **argv) { int c, k; @@ -573,7 +578,7 @@ main(int argc, char *argv[]) #ifndef WIN32 umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); -#endif /* !WIN32 */ +#endif progname = argv[0]; if (TYPE_BIT(zic_t) <64) { @@ -632,7 +637,10 @@ main(int argc, char *argv[]) break; case 'y': if (yitcommand == NULL) + { + warning(_("-y is obsolescent")); yitcommand = strdup(optarg); + } else { fprintf(stderr, @@ -721,8 +729,8 @@ main(int argc, char *argv[]) } static bool -componentcheck(char const * name, char const * component, - char const * component_end) +componentcheck(char const *name, char const *component, + char const *component_end) { enum { @@ -813,7 +821,7 @@ namecheck(const char *name) */ #ifdef HAVE_SYMLINK static char * -relname(char const * from, char const * to) +relname(char const *from, char const *to) { size_t i, taillen, @@ -853,12 +861,12 @@ relname(char const * from, char const * to) } return result; } -#endif /* HAVE_SYMLINK */ +#endif /* HAVE_SYMLINK */ /* Hard link FROM to TO, following any symbolic links. Return 0 if successful, an error number otherwise. */ static int -hardlinkerr(char const * from, char const * to) +hardlinkerr(char const *from, char const *to) { int r = linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW); @@ -866,7 +874,7 @@ hardlinkerr(char const * from, char const * to) } static void -dolink(char const * fromfield, char const * tofield, bool staysymlink) +dolink(char const *fromfield, char const *tofield, bool staysymlink) { bool todirs_made = false; int link_errno; @@ -921,7 +929,7 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink) strerror(link_errno)); } else -#endif /* HAVE_SYMLINK */ +#endif /* HAVE_SYMLINK */ { FILE *fp, *tp; @@ -1010,7 +1018,7 @@ static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332 /* Return true if NAME is a directory. */ static bool -itsdir(char const * name) +itsdir(char const *name) { struct stat st; int res = stat(name, &st); @@ -1035,7 +1043,7 @@ itsdir(char const * name) /* Return true if NAME is a symbolic link. */ static bool -itssymlink(char const * name) +itssymlink(char const *name) { #ifdef HAVE_SYMLINK char c; @@ -1203,6 +1211,9 @@ infile(const char *name) 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")); @@ -1221,12 +1232,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" */ @@ -1251,7 +1257,7 @@ infile(const char *name) * Call error with errstring and return zero on errors. */ static zic_t -gethms(char const * string, char const * errstring, bool signable) +gethms(char const *string, char const *errstring, bool signable) { /* PG: make hh be int not zic_t to avoid sscanf portability issues */ int hh; @@ -1360,7 +1366,7 @@ inzone(char **fields, int nfields) strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { error(_("duplicate zone name %s" - " (file \"%s\", line %" PRIdLINENO ")"), + " (file \"%s\", line %d)"), fields[ZF_NAME], zones[i].z_filename, zones[i].z_linenum); @@ -1574,21 +1580,11 @@ inleap(char **fields, int nfields) 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")); @@ -1600,9 +1596,9 @@ inleap(char **fields, int nfields) return; } t = tadd(t, tod); - if (t < early_time) + if (t < 0) { - error(_("leap second precedes Big Bang")); + error(_("leap second precedes Epoch")); return; } leapadd(t, positive, lp->l_value, count); @@ -1635,7 +1631,7 @@ inlink(char **fields, int nfields) } static void -rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, +rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, const char *typep, const char *monthp, const char *dayp, const char *timep) { @@ -1754,11 +1750,14 @@ 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 Sun<=20 Sun>=7 + * Day work. Accept things such as: 1 lastSunday last-Sunday + * (undocumented; warn about this) Sun<=20 Sun>=7 */ dp = ecpyalloc(dayp); if ((lp = byword(dp, lasts)) != NULL) @@ -2141,7 +2140,7 @@ writezone(const char *const name, const char *const string, char version) writetype[type] = true; } } -#endif /* !defined +#endif /* !defined * LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ thistypecnt = 0; for (i = 0; i < typecnt; ++i) @@ -2324,7 +2323,7 @@ abbroffset(char *buf, zic_t offset) } static size_t -doabbr(char *abbr, struct zone const * zp, char const * letters, +doabbr(char *abbr, struct zone const *zp, char const *letters, zic_t stdoff, bool doquotes) { char *cp; @@ -2410,7 +2409,7 @@ stringoffset(char *result, zic_t offset) } static int -stringrule(char *result, const struct rule * const rp, const zic_t dstoff, +stringrule(char *result, const struct rule *const rp, const zic_t dstoff, const zic_t gmtoff) { zic_t tod = rp->r_tod; @@ -2492,7 +2491,7 @@ stringrule(char *result, const struct rule * const rp, const zic_t dstoff, } static int -rule_cmp(struct rule const * a, struct rule const * b) +rule_cmp(struct rule const *a, struct rule const *b) { if (!a) return -!!b; @@ -2510,7 +2509,7 @@ enum YEAR_BY_YEAR_ZONE = 1}; static int -stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount) +stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) { const struct zone *zp; struct rule *rp; @@ -2645,7 +2644,7 @@ stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount) } static void -outzone(const struct zone * zpfirst, ptrdiff_t zonecount) +outzone(const struct zone *zpfirst, ptrdiff_t zonecount) { const struct zone *zp; struct rule *rp; @@ -2851,9 +2850,10 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount) { ptrdiff_t k; zic_t jtime, - ktime = 0; + ktime; zic_t offset; + INITIALIZE(ktime); if (useuntil) { /* @@ -2930,7 +2930,8 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount) continue; } if (*startbuf == '\0' && - startoff == oadd(zp->z_gmtoff, stdoff)) + startoff == oadd(zp->z_gmtoff, + stdoff)) { doabbr(startbuf, zp, @@ -3046,7 +3047,7 @@ addtt(zic_t starttime, int type) } static int -addtype(zic_t gmtoff, char const * abbr, bool isdst, bool ttisstd, bool ttisgmt) +addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt) { int i, j; @@ -3105,14 +3106,7 @@ leapadd(zic_t t, bool positive, int rolling, int count) } for (i = 0; i < leapcnt; ++i) if (t <= trans[i]) - { - if (t == trans[i]) - { - error(_("repeated leap second moment")); - exit(EXIT_FAILURE); - } break; - } do { for (j = leapcnt; j > i; --j) @@ -3133,19 +3127,26 @@ adjleap(void) { int i; zic_t last = 0; + 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; } } static char * -shellquote(char *b, char const * s) +shellquote(char *b, char const *s) { *b++ = '\''; while (*s) @@ -3192,7 +3193,7 @@ yearistype(zic_t year, const char *type) exit(EXIT_FAILURE); } -/* Is A a space character in the C locale? */ +/* Is A a space character in the C locale? */ static bool is_space(char a) { @@ -3363,8 +3364,21 @@ itsabbr(const char *abbr, const char *word) return true; } +/* 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) +byword(const char *word, const struct lookup *table) { const struct lookup *foundlp; const struct lookup *lp; @@ -3372,6 +3386,23 @@ 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. */ @@ -3384,13 +3415,31 @@ 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; } @@ -3479,7 +3528,7 @@ tadd(zic_t t1, zic_t t2) */ static zic_t -rpytime(const struct rule * rp, zic_t wantedy) +rpytime(const struct rule *rp, zic_t wantedy) { int m, i; @@ -3571,7 +3620,7 @@ will not work with pre-2004 versions of zic")); return min_time; if (dayoff > max_time / SECSPERDAY) return max_time; - t = (zic_t) dayoff *SECSPERDAY; + t = (zic_t) dayoff * SECSPERDAY; return tadd(t, rp->r_tod); } @@ -3615,18 +3664,22 @@ newabbr(const char *string) do it for ARGNAME too. Exit with failure if there is trouble. Do not consider an existing non-directory to be trouble. */ static void -mkdirs(char const * argname, bool ancestors) +mkdirs(char const *argname, bool ancestors) { char *name; char *cp; 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 WIN32 - if (is_alpha(name[0]) && name[1] == ':') - cp += 2; -#endif while (*cp == '/') cp++;