summaryrefslogtreecommitdiff
path: root/timezone/zic.c
diff options
context:
space:
mode:
Diffstat (limited to 'timezone/zic.c')
-rw-r--r--timezone/zic.c530
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++;