summaryrefslogtreecommitdiff
path: root/zic.c
diff options
context:
space:
mode:
Diffstat (limited to 'zic.c')
-rw-r--r--zic.c476
1 files changed, 290 insertions, 186 deletions
diff --git a/zic.c b/zic.c
index 2d1a187..a29f679 100644
--- a/zic.c
+++ b/zic.c
@@ -29,7 +29,7 @@ typedef int_fast64_t zic_t;
#define SCNdZIC SCNdFAST64
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
-#define ZIC_MAX_ABBR_LEN_WO_WARN 6
+# define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifdef HAVE_DIRECT_H
@@ -40,12 +40,12 @@ typedef int_fast64_t zic_t;
#endif
#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
+# include <sys/stat.h>
#endif
#ifdef S_IRUSR
-#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+# define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
#else
-#define MKDIR_UMASK 0755
+# define MKDIR_UMASK 0755
#endif
/* The maximum ptrdiff_t value, for pre-C99 platforms. */
@@ -58,6 +58,11 @@ static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
# define _Alignof(type) offsetof(struct { char a; type b; }, b)
#endif
+/* The maximum length of a text line, including the trailing newline. */
+#ifndef _POSIX2_LINE_MAX
+# define _POSIX2_LINE_MAX 2048
+#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. */
@@ -91,12 +96,13 @@ struct rule {
};
/*
-** r_dycode r_dayofmonth r_wday
+** 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) */
+enum {
+ DC_DOM, /* 1..31 */ /* unused */
+ DC_DOWGEQ, /* 1..31 */ /* 0..6 (Sun..Sat) */
+ DC_DOWLEQ /* 1..31 */ /* 0..6 (Sun..Sat) */
+};
struct zone {
const char * z_filename;
@@ -145,7 +151,7 @@ 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 int getfields(char *, char **, int);
static zic_t gethms(const char * string, const char * errstring);
static zic_t getsave(char *, bool *);
static void inexpires(char **, int);
@@ -208,86 +214,107 @@ static int unspecifiedtype;
** Line codes.
*/
-#define LC_RULE 0
-#define LC_ZONE 1
-#define LC_LINK 2
-#define LC_LEAP 3
-#define LC_EXPIRES 4
+enum {
+ LC_RULE,
+ LC_ZONE,
+ LC_LINK,
+ LC_LEAP,
+ LC_EXPIRES
+};
/*
** Which fields are which on a Zone line.
*/
-#define ZF_NAME 1
-#define ZF_STDOFF 2
-#define ZF_RULE 3
-#define ZF_FORMAT 4
-#define ZF_TILYEAR 5
-#define ZF_TILMONTH 6
-#define ZF_TILDAY 7
-#define ZF_TILTIME 8
-#define ZONE_MINFIELDS 5
-#define ZONE_MAXFIELDS 9
+enum {
+ ZF_NAME = 1,
+ ZF_STDOFF,
+ ZF_RULE,
+ ZF_FORMAT,
+ ZF_TILYEAR,
+ ZF_TILMONTH,
+ ZF_TILDAY,
+ ZF_TILTIME,
+ ZONE_MAXFIELDS,
+ ZONE_MINFIELDS = ZF_TILYEAR
+};
/*
** Which fields are which on a Zone continuation line.
*/
-#define ZFC_STDOFF 0
-#define ZFC_RULE 1
-#define ZFC_FORMAT 2
-#define ZFC_TILYEAR 3
-#define ZFC_TILMONTH 4
-#define ZFC_TILDAY 5
-#define ZFC_TILTIME 6
-#define ZONEC_MINFIELDS 3
-#define ZONEC_MAXFIELDS 7
+enum {
+ ZFC_STDOFF,
+ ZFC_RULE,
+ ZFC_FORMAT,
+ ZFC_TILYEAR,
+ ZFC_TILMONTH,
+ ZFC_TILDAY,
+ ZFC_TILTIME,
+ ZONEC_MAXFIELDS,
+ ZONEC_MINFIELDS = ZFC_TILYEAR
+};
/*
** Which files are which on a Rule line.
*/
-#define RF_NAME 1
-#define RF_LOYEAR 2
-#define RF_HIYEAR 3
-#define RF_COMMAND 4
-#define RF_MONTH 5
-#define RF_DAY 6
-#define RF_TOD 7
-#define RF_SAVE 8
-#define RF_ABBRVAR 9
-#define RULE_FIELDS 10
+enum {
+ RF_NAME = 1,
+ RF_LOYEAR,
+ RF_HIYEAR,
+ RF_COMMAND,
+ RF_MONTH,
+ RF_DAY,
+ RF_TOD,
+ RF_SAVE,
+ RF_ABBRVAR,
+ RULE_FIELDS
+};
/*
** Which fields are which on a Link line.
*/
-#define LF_TARGET 1
-#define LF_LINKNAME 2
-#define LINK_FIELDS 3
+enum {
+ LF_TARGET = 1,
+ LF_LINKNAME,
+ LINK_FIELDS
+};
/*
** Which fields are which on a Leap line.
*/
-#define LP_YEAR 1
-#define LP_MONTH 2
-#define LP_DAY 3
-#define LP_TIME 4
-#define LP_CORR 5
-#define LP_ROLL 6
-#define LEAP_FIELDS 7
+enum {
+ LP_YEAR = 1,
+ LP_MONTH,
+ LP_DAY,
+ LP_TIME,
+ LP_CORR,
+ LP_ROLL,
+ LEAP_FIELDS,
+
+ /* Expires lines are like Leap lines, except without CORR and ROLL fields. */
+ EXPIRES_FIELDS = LP_TIME + 1
+};
-/* Expires lines are like Leap lines, except without CORR and ROLL fields. */
-#define EXPIRES_FIELDS 5
+/* The maximum number of fields on any of the above lines.
+ (The "+"s pacify gcc -Wenum-compare.) */
+enum {
+ MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
+ max(+LEAP_FIELDS, +EXPIRES_FIELDS))
+};
/*
** Year synonyms.
*/
-#define YR_MINIMUM 0
-#define YR_MAXIMUM 1
-#define YR_ONLY 2
+enum {
+ YR_MINIMUM,
+ YR_MAXIMUM,
+ YR_ONLY
+};
static struct rule * rules;
static ptrdiff_t nrules; /* number of rules */
@@ -480,7 +507,7 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
return ptr;
else {
ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
- ptrdiff_t amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
+ ptrdiff_t amax = min(nitems_max, SIZE_MAX);
if ((amax - 1) / 3 * 2 < *nitems_alloc)
memory_exhausted(_("integer overflow"));
*nitems_alloc += (*nitems_alloc >> 1) + 1;
@@ -571,7 +598,8 @@ usage(FILE *stream, int status)
_("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
"\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
" [ -L leapseconds ] \\\n"
- "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -t localtime-link ] \\\n"
+ "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
+ "\t[ -t localtime-link ] \\\n"
"\t[ filename ... ]\n\n"
"Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO);
@@ -662,7 +690,7 @@ check_for_signal(void)
}
}
-#define TIME_T_BITS_IN_FILE 64
+enum { TIME_T_BITS_IN_FILE = 64 };
/* The minimum and maximum values representable in a TZif file. */
static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
@@ -673,6 +701,9 @@ 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 the -R option, defaulting to MIN_TIME. */
+static zic_t redundant_time = MINVAL(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;
@@ -699,11 +730,27 @@ timerange_option(char *timerange)
}
if (*hi_end || hi < lo || max_time < lo || hi < min_time)
return false;
- lo_time = lo < min_time ? min_time : lo;
- hi_time = max_time < hi ? max_time : hi;
+ lo_time = max(lo, min_time);
+ hi_time = min(hi, max_time);
return true;
}
+/* Generate redundant time stamps up to OPT. Return true if successful. */
+static bool
+redundant_time_option(char *opt)
+{
+ if (*opt == '@') {
+ intmax_t redundant;
+ char *opt_end;
+ redundant = strtoimax(opt + 1, &opt_end, 10);
+ if (opt_end != opt + 1 && !*opt_end) {
+ redundant_time = max(redundant_time, redundant);
+ return true;
+ }
+ }
+ return false;
+}
+
static const char * psxrules;
static const char * lcltime;
static const char * directory;
@@ -737,9 +784,9 @@ main(int argc, char **argv)
#endif
#if HAVE_GETTEXT
setlocale(LC_ALL, "");
-#ifdef TZ_DOMAINDIR
+# ifdef TZ_DOMAINDIR
bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* defined TEXTDOMAINDIR */
+# endif /* defined TEXTDOMAINDIR */
textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */
progname = argv[0];
@@ -756,7 +803,8 @@ main(int argc, char **argv)
} else if (strcmp(argv[k], "--help") == 0) {
usage(stdout, EXIT_SUCCESS);
}
- while ((c = getopt(argc, argv, "b:d:l:L:p:r:st:vy:")) != EOF && c != -1)
+ while ((c = getopt(argc, argv, "b:d:l:L:p:r:R:st:vy:")) != EOF
+ && c != -1)
switch (c) {
default:
usage(stderr, EXIT_FAILURE);
@@ -843,12 +891,23 @@ _("%s: invalid time range: %s\n"),
}
timerange_given = true;
break;
+ case 'R':
+ if (! redundant_time_option(optarg)) {
+ fprintf(stderr, _("%s: invalid time: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ break;
case 's':
warning(_("-s ignored"));
break;
}
if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
usage(stderr, EXIT_FAILURE); /* usage message by request */
+ if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
+ fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
+ return EXIT_FAILURE;
+ }
if (bloat == 0) {
static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
if (strcmp(bloat_default, "slim") == 0)
@@ -1304,17 +1363,47 @@ associate(void)
exit(EXIT_FAILURE);
}
+/* Read a text line from FP into BUF, which is of size BUFSIZE.
+ Terminate it with a NUL byte instead of a newline.
+ Return the line's length, not counting the NUL byte.
+ On EOF, return a negative number.
+ On error, report the error and exit. */
+static ptrdiff_t
+inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
+{
+ ptrdiff_t linelen = 0, ch;
+ while ((ch = getc(fp)) != '\n') {
+ if (ch < 0) {
+ if (ferror(fp)) {
+ error(_("input error"));
+ exit(EXIT_FAILURE);
+ }
+ if (linelen == 0)
+ return -1;
+ error(_("unterminated line"));
+ exit(EXIT_FAILURE);
+ }
+ if (!ch) {
+ error(_("NUL input byte"));
+ exit(EXIT_FAILURE);
+ }
+ buf[linelen++] = ch;
+ if (linelen == bufsize) {
+ error(_("line too long"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ buf[linelen] = '\0';
+ return linelen;
+}
+
static void
infile(const char *name)
{
register FILE * fp;
- register char ** fields;
- register char * cp;
register const struct lookup * lp;
- register int nfields;
register bool wantcont;
register lineno num;
- char buf[BUFSIZ];
if (strcmp(name, "-") == 0) {
name = _("standard input");
@@ -1328,24 +1417,16 @@ infile(const char *name)
}
wantcont = false;
for (num = 1; ; ++num) {
+ ptrdiff_t linelen;
+ char buf[_POSIX2_LINE_MAX];
+ int nfields;
+ char *fields[MAX_FIELDS];
eat(name, num);
- if (fgets(buf, sizeof buf, fp) != buf)
- break;
- cp = strchr(buf, '\n');
- if (cp == NULL) {
- error(_("line too long"));
- exit(EXIT_FAILURE);
- }
- *cp = '\0';
- fields = getfields(buf);
- nfields = 0;
- while (fields[nfields] != NULL) {
- static char nada;
-
- if (strcmp(fields[nfields], "-") == 0)
- fields[nfields] = &nada;
- ++nfields;
- }
+ linelen = inputline(fp, buf, sizeof buf);
+ if (linelen < 0)
+ break;
+ nfields = getfields(buf, fields,
+ sizeof fields / sizeof *fields);
if (nfields == 0) {
/* nothing to do */
} else if (wantcont) {
@@ -1379,7 +1460,6 @@ infile(const char *name)
default: UNREACHABLE();
}
}
- free(fields);
}
close_file(fp, NULL, filename, NULL);
if (wantcont)
@@ -1960,11 +2040,11 @@ struct timerange {
int defaulttype;
ptrdiff_t base, count;
int leapbase, leapcount;
- bool pretrans, leapexpiry;
+ bool leapexpiry;
};
static struct timerange
-limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
+limitrange(struct timerange r, zic_t lo, zic_t hi,
zic_t const *ats, unsigned char const *types)
{
/* Omit ordinary transitions < LO. */
@@ -1974,10 +2054,6 @@ limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
r.base++;
}
- /* "-00" before any -r low cutoff. */
- if (min_time < lo_time)
- r.defaulttype = unspecifiedtype;
-
/* Omit as many initial leap seconds as possible, such that the
first leap second in the truncated list is <= LO, and is a
positive leap second if and only if it has a positive correction.
@@ -2003,14 +2079,6 @@ limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
r.leapcount--;
}
- /* Determine whether to keep the last too-low transition if no
- transition is exactly at LO. The kept transition will be output
- as a LO "transition"; see "Output a LO_TIME transition" below.
- This is needed when the output is truncated at the start, and is
- also useful when catering to buggy 32-bit clients that do not use
- time type 0 for timestamps before the first transition. */
- r.pretrans = locut && r.base && ! (r.count && ats[r.base] == lo);
-
/* Determine whether to append an expiration to the leap second table. */
r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
@@ -2036,7 +2104,7 @@ writezone(const char *const name, const char *const string, char version,
_Alignof(zic_t)));
void *typesptr = ats + nats;
unsigned char *types = typesptr;
- struct timerange rangeall, range32, range64;
+ struct timerange rangeall = {0}, range32, range64;
/*
** Sort.
@@ -2120,14 +2188,13 @@ writezone(const char *const name, const char *const string, char version,
}
rangeall.defaulttype = defaulttype;
- rangeall.base = rangeall.leapbase = 0;
rangeall.count = timecnt;
rangeall.leapcount = leapcnt;
- rangeall.pretrans = rangeall.leapexpiry = false;
- range64 = limitrange(rangeall, min_time < lo_time,
- lo_time, hi_time, ats, types);
- range32 = limitrange(range64, true,
- INT32_MIN, INT32_MAX, ats, types);
+ range64 = limitrange(rangeall, lo_time,
+ max(hi_time,
+ redundant_time - (ZIC_MIN < redundant_time)),
+ ats, types);
+ range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
/* TZif version 4 is needed if a no-op transition is appended to
indicate the expiration of the leap second table, or if the first
@@ -2161,9 +2228,9 @@ writezone(const char *const name, const char *const string, char version,
register ptrdiff_t thistimei, thistimecnt, thistimelim;
register int thisleapi, thisleapcnt, thisleaplim;
struct tzhead tzh;
- int thisdefaulttype;
- bool hicut, pretrans, thisleapexpiry;
- zic_t lo;
+ int pretranstype = -1, thisdefaulttype;
+ bool locut, hicut, thisleapexpiry;
+ zic_t lo, thismin, thismax;
int old0;
char omittype[TZ_MAX_TYPES];
int typemap[TZ_MAX_TYPES];
@@ -2174,28 +2241,15 @@ writezone(const char *const name, const char *const string, char version,
int indmap[TZ_MAX_CHARS];
if (pass == 1) {
- /* Arguably the default time type in the 32-bit data
- should be range32.defaulttype, which is suited for
- timestamps just before INT32_MIN. However, zic
- traditionally used the time type of the indefinite
- past instead. Internet RFC 8532 says readers should
- ignore 32-bit data, so this discrepancy matters only
- to obsolete readers where the traditional type might
- be more appropriate even if it's "wrong". So, use
- the historical zic value, unless -r specifies a low
- cutoff that excludes some 32-bit timestamps. */
- thisdefaulttype = (lo_time <= INT32_MIN
- ? range64.defaulttype
- : range32.defaulttype);
-
+ thisdefaulttype = range32.defaulttype;
thistimei = range32.base;
thistimecnt = range32.count;
toomanytimes = thistimecnt >> 31 >> 1 != 0;
thisleapi = range32.leapbase;
thisleapcnt = range32.leapcount;
- pretrans = range32.pretrans;
thisleapexpiry = range32.leapexpiry;
- hicut = hi_time < INT32_MAX;
+ thismin = INT32_MIN;
+ thismax = INT32_MAX;
} else {
thisdefaulttype = range64.defaulttype;
thistimei = range64.base;
@@ -2203,17 +2257,46 @@ writezone(const char *const name, const char *const string, char version,
toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
thisleapi = range64.leapbase;
thisleapcnt = range64.leapcount;
- pretrans = range64.pretrans;
thisleapexpiry = range64.leapexpiry;
- hicut = hi_time < max_time;
+ thismin = min_time;
+ thismax = max_time;
}
if (toomanytimes)
error(_("too many transition times"));
+ locut = thismin < lo_time && lo_time <= thismax;
+ hicut = thismin <= hi_time && hi_time < thismax;
thistimelim = thistimei + thistimecnt;
memset(omittype, true, typecnt);
+
+ /* Determine whether to output a transition before the first
+ transition in range. This is needed when the output is
+ truncated at the start, and is also useful when catering to
+ buggy 32-bit clients that do not use time type 0 for
+ timestamps before the first transition. */
+ if ((locut || (pass == 1 && thistimei))
+ && ! (thistimecnt && ats[thistimei] == lo_time)) {
+ pretranstype = thisdefaulttype;
+ omittype[pretranstype] = false;
+ }
+
+ /* Arguably the default time type in the 32-bit data
+ should be range32.defaulttype, which is suited for
+ timestamps just before INT32_MIN. However, zic
+ traditionally used the time type of the indefinite
+ past instead. Internet RFC 8532 says readers should
+ ignore 32-bit data, so this discrepancy matters only
+ to obsolete readers where the traditional type might
+ be more appropriate even if it's "wrong". So, use
+ the historical zic value, unless -r specifies a low
+ cutoff that excludes some 32-bit timestamps. */
+ if (pass == 1 && lo_time <= thismin)
+ thisdefaulttype = range64.defaulttype;
+
+ if (locut)
+ thisdefaulttype = unspecifiedtype;
omittype[thisdefaulttype] = false;
- for (i = thistimei - pretrans; i < thistimelim; i++)
+ for (i = thistimei; i < thistimelim; i++)
omittype[types[i]] = false;
if (hicut)
omittype[unspecifiedtype] = false;
@@ -2237,7 +2320,13 @@ writezone(const char *const name, const char *const string, char version,
register int mrudst, mrustd, hidst, histd, type;
hidst = histd = mrudst = mrustd = -1;
- for (i = thistimei - pretrans; i < thistimelim; ++i)
+ if (0 <= pretranstype) {
+ if (isdsts[pretranstype])
+ mrudst = pretranstype;
+ else
+ mrustd = pretranstype;
+ }
+ for (i = thistimei; i < thistimelim; i++)
if (isdsts[types[i]])
mrudst = types[i];
else mrustd = types[i];
@@ -2307,7 +2396,8 @@ writezone(const char *const name, const char *const string, char version,
indmap[desigidx[i]] = j;
}
if (pass == 1 && !want_bloat()) {
- pretrans = hicut = thisleapexpiry = false;
+ hicut = thisleapexpiry = false;
+ pretranstype = -1;
thistimecnt = thisleapcnt = 0;
thistypecnt = thischarcnt = 1;
}
@@ -2318,7 +2408,8 @@ writezone(const char *const name, const char *const string, char version,
convert(utcnt, tzh.tzh_ttisutcnt);
convert(stdcnt, tzh.tzh_ttisstdcnt);
convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
- convert(pretrans + thistimecnt + hicut, tzh.tzh_timecnt);
+ convert((0 <= pretranstype) + thistimecnt + hicut,
+ tzh.tzh_timecnt);
convert(thistypecnt, tzh.tzh_typecnt);
convert(thischarcnt, tzh.tzh_charcnt);
DO(tzh_magic);
@@ -2345,14 +2436,16 @@ writezone(const char *const name, const char *const string, char version,
for this pass. */
lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time;
- if (pretrans)
+ if (0 <= pretranstype)
puttzcodepass(lo, fp, pass);
for (i = thistimei; i < thistimelim; ++i) {
puttzcodepass(ats[i], fp, pass);
}
if (hicut)
puttzcodepass(hi_time + 1, fp, pass);
- for (i = thistimei - pretrans; i < thistimelim; ++i)
+ if (0 <= pretranstype)
+ putc(typemap[pretranstype], fp);
+ for (i = thistimei; i < thistimelim; i++)
putc(typemap[types[i]], fp);
if (hicut)
putc(typemap[unspecifiedtype], fp);
@@ -2453,6 +2546,8 @@ abbroffset(char *buf, zic_t offset)
}
}
+static char const disable_percent_s[] = "";
+
static size_t
doabbr(char *abbr, struct zone const *zp, char const *letters,
bool isdst, zic_t save, bool doquotes)
@@ -2469,6 +2564,8 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
letters = abbroffset(letterbuf, zp->z_stdoff + save);
else if (!letters)
letters = "%s";
+ else if (letters == disable_percent_s)
+ return 0;
sprintf(abbr, format, letters);
} else if (isdst) {
strcpy(abbr, slashp + 1);
@@ -2739,18 +2836,10 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
static void
outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
{
- register const struct zone * zp;
- register struct rule * rp;
register ptrdiff_t i, j;
- register bool usestart, useuntil;
register zic_t starttime, untiltime;
- register zic_t stdoff;
- register zic_t save;
- register zic_t year;
- register zic_t startoff;
register bool startttisstd;
register bool startttisut;
- register int type;
register char * startbuf;
register char * ab;
register char * envvar;
@@ -2761,8 +2850,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
register bool do_extend;
register char version;
ptrdiff_t lastatmax = -1;
- zic_t one = 1;
- zic_t y2038_boundary = one << 31;
zic_t max_year0;
int defaulttype = -1;
@@ -2794,11 +2881,11 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
}
for (i = 0; i < zonecount; ++i) {
- zp = &zpfirst[i];
+ struct zone const *zp = &zpfirst[i];
if (i < zonecount - 1)
updateminmax(zp->z_untilrule.r_loyear);
for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
+ struct rule *rp = &zp->z_rules[j];
if (rp->r_lowasnum)
updateminmax(rp->r_loyear);
if (rp->r_hiwasnum)
@@ -2860,6 +2947,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
max_year = min_year + years_of_observations;
}
}
+ max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
+ + EPOCH_YEAR + 1));
max_year0 = max_year;
if (want_bloat()) {
/* For the benefit of older systems,
@@ -2875,22 +2964,23 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
for (i = 0; i < zonecount; ++i) {
struct rule *prevrp = NULL;
- zic_t prevktime;
- INITIALIZE(prevktime);
/*
** A guess that may well be corrected later.
*/
- save = 0;
- zp = &zpfirst[i];
- usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
- useuntil = i < (zonecount - 1);
+ zic_t save = 0;
+ struct zone const *zp = &zpfirst[i];
+ bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ bool useuntil = i < (zonecount - 1);
+ zic_t stdoff = zp->z_stdoff;
+ zic_t startoff = stdoff;
+ zic_t prevktime;
+ INITIALIZE(prevktime);
if (useuntil && zp->z_untiltime <= min_time)
continue;
- stdoff = zp->z_stdoff;
eat(zp->z_filename, zp->z_linenum);
*startbuf = '\0';
- startoff = zp->z_stdoff;
if (zp->z_nrules == 0) {
+ int type;
save = zp->z_save;
doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
type = addtype(oadd(zp->z_stdoff, save),
@@ -2901,7 +2991,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
usestart = false;
} else
defaulttype = type;
- } else for (year = min_year; year <= max_year; ++year) {
+ } else {
+ zic_t year;
+ for (year = min_year; year <= max_year; ++year) {
if (useuntil && year > zp->z_untilrule.r_hiyear)
break;
/*
@@ -2910,7 +3002,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
** The former TYPE field was also considered here.
*/
for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
+ zic_t one = 1;
+ zic_t y2038_boundary = one << 31;
+ struct rule *rp = &zp->z_rules[j];
eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum);
rp->r_todo = year >= rp->r_loyear &&
@@ -2926,6 +3020,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
register ptrdiff_t k;
register zic_t jtime, ktime;
register zic_t offset;
+ struct rule *rp;
+ int type;
INITIALIZE(ktime);
if (useuntil) {
@@ -2948,15 +3044,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
*/
k = -1;
for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
- if (!rp->r_todo)
+ struct rule *r = &zp->z_rules[j];
+ if (!r->r_todo)
continue;
eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
- offset = rp->r_todisut ? 0 : stdoff;
- if (!rp->r_todisstd)
+ r->r_filename, r->r_linenum);
+ offset = r->r_todisut ? 0 : stdoff;
+ if (!r->r_todisstd)
offset = oadd(offset, save);
- jtime = rp->r_temp;
+ jtime = r->r_temp;
if (jtime == min_time ||
jtime == max_time)
continue;
@@ -2968,11 +3064,11 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
char const *dup_rules_msg =
_("two rules for same instant");
eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
+ r->r_filename, r->r_linenum);
warning("%s", dup_rules_msg);
- rp = &zp->z_rules[k];
+ r = &zp->z_rules[k];
eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
+ r->r_filename, r->r_linenum);
error("%s", dup_rules_msg);
}
}
@@ -2980,8 +3076,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
break; /* go on to next year */
rp = &zp->z_rules[k];
rp->r_todo = false;
- if (useuntil && ktime >= untiltime)
+ if (useuntil && ktime >= untiltime) {
+ if (!*startbuf
+ && (oadd(zp->z_stdoff, rp->r_save)
+ == startoff))
+ doabbr(startbuf, zp, rp->r_abbrvar,
+ rp->r_isdst, rp->r_save,
+ false);
break;
+ }
save = rp->r_save;
if (usestart && ktime == starttime)
usestart = false;
@@ -3014,6 +3117,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
offset = oadd(zp->z_stdoff, rp->r_save);
if (!want_bloat() && !useuntil && !do_extend
&& prevrp && lo_time <= prevktime
+ && redundant_time <= ktime
&& rp->r_hiyear == ZIC_MAX
&& prevrp->r_hiyear == ZIC_MAX)
break;
@@ -3029,20 +3133,19 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
prevrp = rp;
prevktime = ktime;
}
+ }
}
if (usestart) {
- if (*startbuf == '\0' &&
- zp->z_format != NULL &&
- strchr(zp->z_format, '%') == NULL &&
- strchr(zp->z_format, '/') == NULL)
- strcpy(startbuf, zp->z_format);
+ bool isdst = startoff != zp->z_stdoff;
+ if (*startbuf == '\0' && zp->z_format)
+ doabbr(startbuf, zp, disable_percent_s,
+ isdst, save, false);
eat(zp->z_filename, zp->z_linenum);
if (*startbuf == '\0')
error(_("can't determine time zone abbreviation to use just after until time"));
else {
- bool isdst = startoff != zp->z_stdoff;
- type = addtype(startoff, startbuf, isdst,
- startttisstd, startttisut);
+ int type = addtype(startoff, startbuf, isdst,
+ startttisstd, startttisut);
if (defaulttype < 0 && !isdst)
defaulttype = type;
addtt(starttime, type);
@@ -3344,23 +3447,20 @@ byword(const char *word, const struct lookup *table)
return foundlp;
}
-static char **
-getfields(register char *cp)
+static int
+getfields(char *cp, char **array, int arrayelts)
{
register char * dp;
- register char ** array;
register int nsubs;
- if (cp == NULL)
- return NULL;
- array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
nsubs = 0;
for ( ; ; ) {
+ char *dstart;
while (is_space(*cp))
++cp;
if (*cp == '\0' || *cp == '#')
break;
- array[nsubs++] = dp = cp;
+ dstart = dp = cp;
do {
if ((*dp = *cp++) != '"')
++dp;
@@ -3375,9 +3475,13 @@ getfields(register char *cp)
if (is_space(*cp))
++cp;
*dp = '\0';
+ if (nsubs == arrayelts) {
+ error(_("Too many input fields"));
+ exit(EXIT_FAILURE);
+ }
+ array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
}
- array[nsubs] = NULL;
- return array;
+ return nsubs;
}
static _Noreturn void