diff options
Diffstat (limited to 'src/timezone/zic.c')
-rw-r--r-- | src/timezone/zic.c | 169 |
1 files changed, 105 insertions, 64 deletions
diff --git a/src/timezone/zic.c b/src/timezone/zic.c index 9df81824a0f..e5a3ca26f42 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -125,13 +125,14 @@ static void warning(const char *string,...) pg_attribute_printf(1, 2); static void usage(FILE *stream, int status) pg_attribute_noreturn(); static void addtt(zic_t starttime, int type); static int addtype(zic_t, char const *, bool, bool, bool); -static void leapadd(zic_t, bool, int, int); +static void leapadd(zic_t, int, int); 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); static zic_t getsave(char *, bool *); +static void inexpires(char **, int); static void infile(const char *filename); static void inleap(char **fields, int nfields); static void inlink(char **fields, int nfields); @@ -202,6 +203,7 @@ static int typecnt; #define LC_ZONE 1 #define LC_LINK 2 #define LC_LEAP 3 +#define LC_EXPIRES 4 /* * Which fields are which on a Zone line. @@ -267,6 +269,9 @@ static int typecnt; #define LP_ROLL 6 #define LEAP_FIELDS 7 +/* Expires lines are like Leap lines, except without CORR and ROLL fields. */ +#define EXPIRES_FIELDS 5 + /* * Year synonyms. */ @@ -312,6 +317,7 @@ static struct lookup const zi_line_codes[] = { }; static struct lookup const leap_line_codes[] = { {"Leap", LC_LEAP}, + {"Expires", LC_EXPIRES}, {NULL, 0} }; @@ -584,6 +590,12 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); +/* The time specified by an Expires line, or negative if no such line. */ +static zic_t leapexpires = -1; + +/* The time specified by an #expires comment, or negative if no such line. */ +static zic_t comment_leapexpires = -1; + /* Set the time range of the output to TIMERANGE. Return true if successful. */ static bool @@ -1279,7 +1291,8 @@ infile(const char *name) } if (nfields == 0) { - /* nothing to do */ + if (name == leapsec && *buf == '#') + sscanf(buf, "#expires " INT64_FORMAT, &comment_leapexpires); } else if (wantcont) { @@ -1311,6 +1324,10 @@ infile(const char *name) inleap(fields, nfields); wantcont = false; break; + case LC_EXPIRES: + inexpires(fields, nfields); + wantcont = false; + break; default: /* "cannot happen" */ fprintf(stderr, _("%s: panic: Invalid l_value %d\n"), @@ -1634,8 +1651,8 @@ inzsub(char **fields, int nfields, bool iscont) return hasuntil; } -static void -inleap(char **fields, int nfields) +static zic_t +getleapdatetime(char **fields, int nfields, bool expire_line) { const char *cp; const struct lookup *lp; @@ -1651,11 +1668,6 @@ inleap(char **fields, int nfields) zic_t t; char xs; - if (nfields != LEAP_FIELDS) - { - error(_("wrong number of fields on Leap line")); - return; - } dayoff = 0; cp = fields[LP_YEAR]; if (sscanf(cp, "%d%c", &year, &xs) != 1) @@ -1664,13 +1676,16 @@ inleap(char **fields, int nfields) * Leapin' Lizards! */ error(_("invalid leaping year")); - return; + return -1; + } + if (!expire_line) + { + if (!leapseen || leapmaxyear < year) + leapmaxyear = year; + if (!leapseen || leapminyear > year) + leapminyear = year; + leapseen = true; } - if (!leapseen || leapmaxyear < year) - leapmaxyear = year; - if (!leapseen || leapminyear > year) - leapminyear = year; - leapseen = true; j = EPOCH_YEAR; while (j != year) { @@ -1689,7 +1704,7 @@ inleap(char **fields, int nfields) if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { error(_("invalid month name")); - return; + return -1; } month = lp->l_value; j = TM_JANUARY; @@ -1704,57 +1719,71 @@ inleap(char **fields, int nfields) day <= 0 || day > len_months[isleap(year)][month]) { error(_("invalid day of month")); - return; + return -1; } dayoff = oadd(dayoff, day - 1); if (dayoff < min_time / SECSPERDAY) { error(_("time too small")); - return; + return -1; } if (dayoff > max_time / SECSPERDAY) { error(_("time too large")); - return; + return -1; } t = dayoff * SECSPERDAY; tod = gethms(fields[LP_TIME], _("invalid time of day")); - cp = fields[LP_CORR]; + t = tadd(t, tod); + if (t < 0) + error(_("leap second precedes Epoch")); + return t; +} + +static void +inleap(char **fields, int nfields) +{ + if (nfields != LEAP_FIELDS) + error(_("wrong number of fields on Leap line")); + else { - bool positive; - int count; + zic_t t = getleapdatetime(fields, nfields, false); - if (strcmp(cp, "") == 0) - { /* infile() turns "-" into "" */ - positive = false; - count = 1; - } - else if (strcmp(cp, "+") == 0) + if (0 <= t) { - positive = true; - count = 1; - } - else - { - error(_("illegal CORRECTION field on Leap line")); - return; - } - if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) - { - error(_("illegal Rolling/Stationary field on Leap line")); - return; - } - t = tadd(t, tod); - if (t < 0) - { - error(_("leap second precedes Epoch")); - return; + struct lookup const *lp = byword(fields[LP_ROLL], leap_types); + + if (!lp) + error(_("invalid Rolling/Stationary field on Leap line")); + else + { + int correction = 0; + + if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */ + correction = -1; + else if (strcmp(fields[LP_CORR], "+") == 0) + correction = 1; + else + error(_("invalid CORRECTION field on Leap line")); + if (correction) + leapadd(t, correction, lp->l_value); + } } - leapadd(t, positive, lp->l_value, count); } } static void +inexpires(char **fields, int nfields) +{ + if (nfields != EXPIRES_FIELDS) + error(_("wrong number of fields on Expires line")); + else if (0 <= leapexpires) + error(_("multiple Expires lines")); + else + leapexpires = getleapdatetime(fields, nfields, true); +} + +static void inlink(char **fields, int nfields) { struct link l; @@ -3369,12 +3398,11 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) } static void -leapadd(zic_t t, bool positive, int rolling, int count) +leapadd(zic_t t, int correction, int rolling) { - int i, - j; + int i; - if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) + if (TZ_MAX_LEAPS <= leapcnt) { error(_("too many leap seconds")); exit(EXIT_FAILURE); @@ -3382,19 +3410,13 @@ leapadd(zic_t t, bool positive, int rolling, int count) for (i = 0; i < leapcnt; ++i) if (t <= trans[i]) break; - do - { - for (j = leapcnt; j > i; --j) - { - trans[j] = trans[j - 1]; - corr[j] = corr[j - 1]; - roll[j] = roll[j - 1]; - } - trans[i] = t; - corr[i] = positive ? 1 : -count; - roll[i] = rolling; - ++leapcnt; - } while (positive && --count != 0); + memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans); + memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr); + memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll); + trans[i] = t; + corr[i] = correction; + roll[i] = rolling; + ++leapcnt; } static void @@ -3418,6 +3440,25 @@ adjleap(void) trans[i] = tadd(trans[i], last); last = corr[i] += last; } + + if (leapexpires < 0) + { + leapexpires = comment_leapexpires; + if (0 <= leapexpires) + warning(_("\"#expires\" is obsolescent; use \"Expires\"")); + } + + if (0 <= leapexpires) + { + leapexpires = oadd(leapexpires, last); + if (!(leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) + { + error(_("last Leap time does not precede Expires time")); + exit(EXIT_FAILURE); + } + if (leapexpires <= hi_time) + hi_time = leapexpires - 1; + } } static char * |