summaryrefslogtreecommitdiff
path: root/zic.c
diff options
context:
space:
mode:
Diffstat (limited to 'zic.c')
-rw-r--r--zic.c229
1 files changed, 128 insertions, 101 deletions
diff --git a/zic.c b/zic.c
index 81a1c5a883f6..c10be426d66b 100644
--- a/zic.c
+++ b/zic.c
@@ -31,8 +31,8 @@ typedef int_fast64_t zic_t;
static zic_t const
ZIC_MIN = INT_FAST64_MIN,
ZIC_MAX = INT_FAST64_MAX,
- ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
- ZIC32_MAX = 0x7fffffff;
+ ZIC32_MIN = -1 - (zic_t) TWO_31_MINUS_1,
+ ZIC32_MAX = TWO_31_MINUS_1;
#define SCNdZIC SCNdFAST64
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
@@ -164,6 +164,11 @@ static uid_t output_owner = -1;
# include <stdalign.h>
#endif
+/* The name used for the file implementing the obsolete -p option. */
+#ifndef TZDEFRULES
+# define TZDEFRULES "posixrules"
+#endif
+
/* The maximum length of a text line, including the trailing newline. */
#ifndef _POSIX2_LINE_MAX
# define _POSIX2_LINE_MAX 2048
@@ -271,6 +276,7 @@ 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 zic_t omul(zic_t, zic_t);
static void outzone(const struct zone * zp, ptrdiff_t ntzones);
static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static bool rulesub(struct rule * rp,
@@ -293,7 +299,8 @@ static int charcnt;
static bool errors;
static bool warnings;
static int filenum;
-static int leapcnt;
+static ptrdiff_t leapcnt;
+static ptrdiff_t leap_alloc;
static bool leapseen;
static zic_t leapminyear;
static zic_t leapmaxyear;
@@ -534,9 +541,11 @@ static unsigned char desigidx[TZ_MAX_TYPES];
static bool ttisstds[TZ_MAX_TYPES];
static bool ttisuts[TZ_MAX_TYPES];
static char chars[TZ_MAX_CHARS];
-static zic_t trans[TZ_MAX_LEAPS];
-static zic_t corr[TZ_MAX_LEAPS];
-static char roll[TZ_MAX_LEAPS];
+static struct {
+ zic_t trans;
+ zic_t corr;
+ char roll;
+} *leap;
/*
** Memory allocation.
@@ -555,7 +564,8 @@ size_overflow(void)
memory_exhausted(_("size overflow"));
}
-ATTRIBUTE_PURE_114833 static ptrdiff_t
+ATTRIBUTE_PURE_114833_HACK
+static ptrdiff_t
size_sum(size_t a, size_t b)
{
#ifdef ckd_add
@@ -569,7 +579,8 @@ size_sum(size_t a, size_t b)
size_overflow();
}
-ATTRIBUTE_PURE_114833 static ptrdiff_t
+ATTRIBUTE_PURE_114833_HACK
+static ptrdiff_t
size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
{
#ifdef ckd_mul
@@ -584,7 +595,8 @@ size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
size_overflow();
}
-ATTRIBUTE_PURE_114833 static ptrdiff_t
+ATTRIBUTE_PURE_114833_HACK
+static ptrdiff_t
align_to(ptrdiff_t size, ptrdiff_t alignment)
{
ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits);
@@ -814,8 +826,8 @@ close_file(FILE *stream, char const *dir, char const *name,
char const *tempname)
{
char const *e = (ferror(stream) ? _("I/O error")
- : (fflush(stream) < 0
- || (tempname && chmetadata(stream) < 0)
+ : ((tempname
+ && (fflush(stream) < 0 || chmetadata(stream) < 0))
|| fclose(stream) < 0)
? strerror(errno) : NULL);
if (e) {
@@ -1283,6 +1295,9 @@ main(int argc, char **argv)
case 'p':
if (psxrules)
duplicate_options("-p");
+ if (strcmp(optarg, "-") != 0)
+ warning(_("-p is obsolete"
+ " and likely ineffective"));
psxrules = optarg;
break;
case 't':
@@ -1691,7 +1706,8 @@ relname(char const *target, char const *linkname)
/* Return true if A and B must have the same parent dir if A and B exist.
Return false if this is not necessarily true (though it might be true).
Keep it simple, and do not inspect the file system. */
-ATTRIBUTE_PURE_114833 static bool
+ATTRIBUTE_PURE_114833
+static bool
same_parent_dirs(char const *a, char const *b)
{
for (; *a == *b; a++, b++)
@@ -2073,15 +2089,11 @@ gethms(char const *string, char const *errstring)
error("%s", errstring);
return 0;
}
- if (ZIC_MAX / SECSPERHOUR < hh) {
- 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"));
- return oadd(sign * hh * SECSPERHOUR,
+ return oadd(omul(hh, sign * SECSPERHOUR),
sign * (mm * SECSPERMIN + ss));
}
@@ -2241,10 +2253,6 @@ inzsub(char **fields, int nfields, bool iscont)
z.z_untiltime = rpytime(&z.z_untilrule,
z.z_untilrule.r_loyear);
if (iscont && nzones > 0 &&
- z.z_untiltime > min_time &&
- z.z_untiltime < max_time &&
- zones[nzones - 1].z_untiltime > min_time &&
- zones[nzones - 1].z_untiltime < max_time &&
zones[nzones - 1].z_untiltime >= z.z_untiltime) {
error(_("Zone continuation line end time is"
" not after end time of previous line"));
@@ -2326,15 +2334,7 @@ getleapdatetime(char **fields, bool expire_line)
return -1;
}
dayoff = oadd(dayoff, day - 1);
- if (dayoff < min_time / SECSPERDAY) {
- error(_("time too small"));
- return -1;
- }
- if (dayoff > max_time / SECSPERDAY) {
- error(_("time too large"));
- return -1;
- }
- t = dayoff * SECSPERDAY;
+ t = omul(dayoff, SECSPERDAY);
tod = gethms(fields[LP_TIME], _("invalid time of day"));
t = tadd(t, tod);
if (t < 0)
@@ -2594,7 +2594,7 @@ atcomp(const void *avp, const void *bvp)
struct timerange {
int defaulttype;
ptrdiff_t base, count;
- int leapbase, leapcount;
+ ptrdiff_t leapbase, leapcount;
bool leapexpiry;
};
@@ -2614,13 +2614,13 @@ limitrange(struct timerange r, zic_t lo, zic_t hi,
positive leap second if and only if it has a positive correction.
This supports common TZif readers that assume that the first leap
second is positive if and only if its correction is positive. */
- while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) {
+ while (1 < r.leapcount && leap[r.leapbase + 1].trans <= lo) {
r.leapcount--;
r.leapbase++;
}
while (0 < r.leapbase
- && ((corr[r.leapbase - 1] < corr[r.leapbase])
- != (0 < corr[r.leapbase]))) {
+ && ((leap[r.leapbase - 1].corr < leap[r.leapbase].corr)
+ != (0 < leap[r.leapbase].corr))) {
r.leapcount++;
r.leapbase--;
}
@@ -2630,7 +2630,7 @@ limitrange(struct timerange r, zic_t lo, zic_t hi,
if (hi < max_time) {
while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
r.count--;
- while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
+ while (0 < r.leapcount && hi + 1 < leap[r.leapbase + r.leapcount - 1].trans)
r.leapcount--;
}
@@ -2666,7 +2666,7 @@ writezone(const char *const name, const char *const string, char version,
if (timecnt > 1)
qsort(attypes, timecnt, sizeof *attypes, atcomp);
/*
- ** Optimize.
+ ** Optimize and skip unwanted transitions.
*/
{
ptrdiff_t fromi, toi;
@@ -2674,16 +2674,28 @@ writezone(const char *const name, const char *const string, char version,
toi = 0;
fromi = 0;
for ( ; fromi < timecnt; ++fromi) {
- if (toi != 0
- && ((attypes[fromi].at
+ if (toi != 0) {
+ /* Skip the previous transition if it is unwanted
+ because its local time is not earlier.
+ The UT offset additions can't overflow because
+ of how the times were calculated. */
+ unsigned char type_2 =
+ toi == 1 ? 0 : attypes[toi - 2].type;
+ if ((attypes[fromi].at
+ utoffs[attypes[toi - 1].type])
- <= (attypes[toi - 1].at
- + utoffs[toi == 1 ? 0
- : attypes[toi - 2].type]))) {
+ <= attypes[toi - 1].at + utoffs[type_2]) {
+ if (attypes[fromi].type == type_2)
+ toi--;
+ else
attypes[toi - 1].type =
attypes[fromi].type;
- continue;
+ continue;
+ }
}
+
+ /* Use a transition if it is the first one,
+ or if it cannot be merged for other reasons,
+ or if it transitions to different timekeeping. */
if (toi == 0
|| attypes[fromi].dontmerge
|| (utoffs[attypes[toi - 1].type]
@@ -2697,14 +2709,19 @@ writezone(const char *const name, const char *const string, char version,
timecnt = toi;
}
- if (noise && timecnt > 1200) {
- if (timecnt > TZ_MAX_TIMES)
+ if (noise) {
+ if (1200 < timecnt) {
+ if (TZ_MAX_TIMES < timecnt)
warning(_("reference clients mishandle"
" more than %d transition times"),
TZ_MAX_TIMES);
- else
+ else
warning(_("pre-2014 clients may mishandle"
" more than 1200 transition times"));
+ }
+ if (TZ_MAX_LEAPS < leapcnt)
+ warning(_("reference clients mishandle more than %d leap seconds"),
+ TZ_MAX_LEAPS);
}
/*
** Transfer.
@@ -2720,8 +2737,8 @@ writezone(const char *const name, const char *const string, char version,
for (i = 0; i < timecnt; ++i) {
j = leapcnt;
while (--j >= 0)
- if (ats[i] > trans[j] - corr[j]) {
- ats[i] = tadd(ats[i], corr[j]);
+ if (leap[j].trans - leap[j].corr < ats[i]) {
+ ats[i] = tadd(ats[i], leap[j].corr);
break;
}
}
@@ -2750,7 +2767,7 @@ writezone(const char *const name, const char *const string, char version,
version = '4';
}
if (0 < r->leapcount
- && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) {
+ && leap[r->leapbase].corr != 1 && leap[r->leapbase].corr != -1) {
if (noise)
warning(_("%s: pre-2021b clients may mishandle"
" leap second table truncation"),
@@ -2765,7 +2782,7 @@ 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;
+ register ptrdiff_t thisleapi, thisleapcnt, thisleaplim;
struct tzhead tzh;
int pretranstype = -1, thisdefaulttype;
bool locut, hicut, thisleapexpiry;
@@ -2970,6 +2987,11 @@ writezone(const char *const name, const char *const string, char version,
continue;
}
+ if (pass == 2 && noise && 50 < thischarcnt)
+ warning(_("%s: pre-2026 reference clients mishandle"
+ " more than 50 bytes of abbreviations"),
+ name);
+
/* Output a LO_TIME transition if needed; see limitrange.
But do not go below the minimum representable value
for this pass. */
@@ -3005,8 +3027,8 @@ writezone(const char *const name, const char *const string, char version,
for (i = thisleapi; i < thisleaplim; ++i) {
register zic_t todo;
- if (roll[i]) {
- if (timecnt == 0 || trans[i] < ats[0]) {
+ if (leap[i].roll) {
+ if (timecnt == 0 || leap[i].trans < ats[0]) {
j = 0;
while (isdsts[j])
if (++j >= typecnt) {
@@ -3016,14 +3038,14 @@ writezone(const char *const name, const char *const string, char version,
} else {
j = 1;
while (j < timecnt &&
- trans[i] >= ats[j])
+ ats[j] <= leap[i].trans)
++j;
j = types[j - 1];
}
- todo = tadd(trans[i], -utoffs[j]);
- } else todo = trans[i];
+ todo = tadd(leap[i].trans, -utoffs[j]);
+ } else todo = leap[i].trans;
puttzcodepass(todo, fp, pass);
- puttzcode(corr[i], fp);
+ puttzcode(leap[i].corr, fp);
}
if (thisleapexpiry) {
/* Append a no-op leap correction indicating when the leap
@@ -3032,7 +3054,7 @@ writezone(const char *const name, const char *const string, char version,
the plan is to amend the RFC to allow this in version 4
TZif files. */
puttzcodepass(leapexpires, fp, pass);
- puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp);
+ puttzcode(thisleaplim ? leap[thisleaplim - 1].corr : 0, fp);
}
if (stdcnt != 0)
for (i = old0; i < typecnt; i++)
@@ -3092,11 +3114,10 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
bool isdst, zic_t save, bool doquotes)
{
register char * cp;
- register char * slashp;
ptrdiff_t len;
char const *format = zp->z_format;
+ char const *slashp = strchr(format, '/');
- slashp = strchr(format, '/');
if (slashp == NULL) {
char letterbuf[PERCENT_Z_LEN_BOUND + 1];
if (zp->z_format_specifier == 'z')
@@ -3570,9 +3591,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
if (!r->r_todisstd)
offset = oadd(offset, save);
jtime = r->r_temp;
- if (jtime == min_time ||
- jtime == max_time)
- continue;
jtime = tadd(jtime, -offset);
if (k < 0 || jtime < ktime) {
k = j;
@@ -3752,7 +3770,8 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
{
register int i, j;
- if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) {
+ /* RFC 9636 section 3.2 specifies this range for utoff. */
+ if (! (-TWO_31_MINUS_1 <= utoff && utoff <= TWO_31_MINUS_1)) {
error(_("UT offset out of range"));
exit(EXIT_FAILURE);
}
@@ -3791,32 +3810,27 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
static void
leapadd(zic_t t, int correction, int rolling)
{
- register int i;
+ register ptrdiff_t i;
- if (TZ_MAX_LEAPS <= leapcnt) {
- error(_("too many leap seconds"));
- exit(EXIT_FAILURE);
- }
if (rolling && (lo_time != min_time || hi_time != max_time)) {
error(_("Rolling leap seconds not supported with -r"));
exit(EXIT_FAILURE);
}
+ leap = growalloc(leap, sizeof *leap, leapcnt, &leap_alloc);
for (i = 0; i < leapcnt; ++i)
- if (t <= trans[i])
+ if (t <= leap[i].trans)
break;
- 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;
+ memmove(&leap[i + 1], &leap[i], (leapcnt - i) * sizeof *leap);
+ leap[i].trans = t;
+ leap[i].corr = correction;
+ leap[i].roll = rolling;
++leapcnt;
}
static void
adjleap(void)
{
- register int i;
+ register ptrdiff_t i;
register zic_t last = 0;
register zic_t prevtrans = 0;
@@ -3824,18 +3838,18 @@ adjleap(void)
** propagate leap seconds forward
*/
for (i = 0; i < leapcnt; ++i) {
- if (trans[i] - prevtrans < 28 * SECSPERDAY) {
+ if (leap[i].trans - 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;
+ prevtrans = leap[i].trans;
+ leap[i].trans = tadd(prevtrans, last);
+ last = leap[i].corr += last;
}
if (0 <= leapexpires) {
leapexpires = oadd(leapexpires, last);
- if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) {
+ if (! (leapcnt == 0 || (leap[leapcnt - 1].trans < leapexpires))) {
error(_("last Leap time does not precede Expires time"));
exit(EXIT_FAILURE);
}
@@ -3893,7 +3907,8 @@ lowerit(char a)
}
/* case-insensitive equality */
-ATTRIBUTE_PURE_114833 static bool
+ATTRIBUTE_PURE_114833
+static bool
ciequal(register const char *ap, register const char *bp)
{
while (lowerit(*ap) == lowerit(*bp++))
@@ -3902,7 +3917,8 @@ ciequal(register const char *ap, register const char *bp)
return false;
}
-ATTRIBUTE_PURE_114833 static bool
+ATTRIBUTE_PURE_114833
+static bool
itsabbr(register const char *abbr, register const char *word)
{
if (lowerit(*abbr) != lowerit(*word))
@@ -3918,7 +3934,8 @@ itsabbr(register const char *abbr, register const char *word)
/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
-ATTRIBUTE_PURE_114833 static bool
+ATTRIBUTE_PURE_114833
+static bool
ciprefix(char const *abbr, char const *word)
{
do
@@ -4028,7 +4045,9 @@ time_overflow(void)
exit(EXIT_FAILURE);
}
-ATTRIBUTE_PURE_114833 static zic_t
+/* Return T1 + T2, but diagnose any overflow and exit. */
+ATTRIBUTE_PURE_114833_HACK
+static zic_t
oadd(zic_t t1, zic_t t2)
{
#ifdef ckd_add
@@ -4042,25 +4061,41 @@ oadd(zic_t t1, zic_t t2)
time_overflow();
}
-ATTRIBUTE_PURE_114833 static zic_t
+/* Return T1 + T2, but diagnose any overflow and exit.
+ This is like oadd, except the result must fit in min_time..max_time range,
+ which on oddball machines can be a smaller range than ZIC_MIN..ZIC_MAX. */
+ATTRIBUTE_PURE_114833_HACK
+static zic_t
tadd(zic_t t1, zic_t t2)
{
-#ifdef ckd_add
- zic_t sum;
- if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time)
+ zic_t sum = oadd(t1, t2);
+ if (min_time <= sum && sum <= max_time)
return sum;
+ time_overflow();
+}
+
+/* Return T1 * T2, but diagnose any overflow and exit. */
+ATTRIBUTE_PURE_114833_HACK
+static zic_t
+omul(zic_t t1, zic_t t2)
+{
+#ifdef ckd_mul
+ zic_t product;
+ if (!ckd_mul(&product, t1, t2))
+ return product;
#else
- if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1)
- return t1 + t2;
+ if (t2 < 0
+ ? ZIC_MAX / t2 <= t1 && (t2 == -1 || t1 <= ZIC_MIN / t2)
+ : t2 == 0 || (ZIC_MIN / t2 <= t1 && t1 <= ZIC_MAX / t2))
+ return t1 * t2;
#endif
- if (t1 == min_time || t1 == max_time)
- return t1;
time_overflow();
}
/*
** Given a rule, and a year, compute the date (in seconds since January 1,
** 1970, 00:00 LOCAL time) in that year that the rule refers to.
+** Do not count leap seconds. On error, diagnose and exit.
*/
static zic_t
@@ -4071,10 +4106,6 @@ rpytime(const struct rule *rp, zic_t wantedy)
register zic_t t, y;
int yrem;
- if (wantedy == ZIC_MIN)
- return min_time;
- if (wantedy == ZIC_MAX)
- return max_time;
m = TM_JANUARY;
y = EPOCH_YEAR;
@@ -4132,11 +4163,7 @@ rpytime(const struct rule *rp, zic_t wantedy)
will not work with pre-2004 versions of zic"));
}
}
- if (dayoff < min_time / SECSPERDAY)
- return min_time;
- if (dayoff > max_time / SECSPERDAY)
- return max_time;
- t = (zic_t) dayoff * SECSPERDAY;
+ t = omul(dayoff, SECSPERDAY);
return tadd(t, rp->r_tod);
}