aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2022-12-14 00:43:56 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2022-12-14 00:43:56 +0000
commit85639444f44f168af982f59143b53efbba37669e (patch)
tree05feba73aad64e84b00d35c2eeb48216bbbe4f7b
parentcc16bfc34e07fede47a989d9552413c7b4d8e78e (diff)
Import tzcode 2022gvendor/tzcode/tzcode2022g
-rw-r--r--Makefile55
-rw-r--r--NEWS89
-rw-r--r--date.18
-rw-r--r--date.1.txt4
-rw-r--r--date.c44
-rw-r--r--localtime.c94
-rw-r--r--newctime.36
-rw-r--r--newstrftime.34
-rw-r--r--newtzset.38
-rw-r--r--newtzset.3.txt2
-rw-r--r--private.h202
-rw-r--r--strftime.c21
-rw-r--r--theory.html1
-rw-r--r--time2posix.316
-rw-r--r--time2posix.3.txt16
-rw-r--r--tzfile.56
-rw-r--r--tzselect.86
-rw-r--r--version2
-rw-r--r--zdump.812
-rw-r--r--zdump.8.txt4
-rw-r--r--zdump.c103
-rw-r--r--zic.812
-rw-r--r--zic.8.txt4
-rw-r--r--zic.c285
24 files changed, 670 insertions, 334 deletions
diff --git a/Makefile b/Makefile
index 34cec49d3af4..afb9d538a203 100644
--- a/Makefile
+++ b/Makefile
@@ -196,6 +196,7 @@ PACKRATLIST=
UTF8_LOCALE= en_US.utf8
# Non-default libraries needed to link.
+# On some hosts, this should have -lintl unless CFLAGS has -DHAVE_GETTEXT=0.
LDLIBS=
# Add the following to the end of the "CFLAGS=" line as needed to override
@@ -208,14 +209,18 @@ LDLIBS=
# For example, N is 252460800 on AmigaOS.
# -DHAVE_DECL_ASCTIME_R=0 if <time.h> does not declare asctime_r
# -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ'
+# -DHAVE_DECL_TIMEGM=0 if <time.h> does not declare timegm
# -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows)
-# -DHAVE_GENERIC=0 if _Generic does not work
-# -DHAVE_GETRANDOM if getgrandom works (e.g., GNU/Linux)*
-# -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)*
+# -DHAVE_GENERIC=0 if _Generic does not work*
+# -DHAVE_GETRANDOM if getrandom works (e.g., GNU/Linux),
+# -DHAVE_GETRANDOM=0 to avoid using getrandom
+# -DHAVE_GETTEXT if gettext works (e.g., GNU/Linux, FreeBSD, Solaris),
+# where LDLIBS also needs to contain -lintl on some hosts;
+# -DHAVE_GETTEXT=0 to avoid using gettext
# -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares
# ctime_r and asctime_r incompatibly with the POSIX standard
# (Solaris when _POSIX_PTHREAD_SEMANTICS is not defined).
-# -DHAVE_INTTYPES_H if you have a non-C99 compiler with <inttypes.h>
+# -DHAVE_INTTYPES_H=0 if <inttypes.h> does not work*
# -DHAVE_LINK=0 if your system lacks a link function
# -DHAVE_LOCALTIME_R=0 if your system lacks a localtime_r function
# -DHAVE_LOCALTIME_RZ=0 if you do not want zdump to use localtime_rz
@@ -225,15 +230,17 @@ LDLIBS=
# functions like 'link' or variables like 'tzname' required by POSIX
# -DHAVE_SETENV=0 if your system lacks the setenv function
# -DHAVE_SNPRINTF=0 if your system lacks the snprintf function
-# -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h>*
+# -DHAVE_STDCKDINT_H=0 if neither <stdckdint.h> nor substitutes like
+# __builtin_add_overflow work*
+# -DHAVE_STDINT_H=0 if <stdint.h> does not work*
# -DHAVE_STRFTIME_L if <time.h> declares locale_t and strftime_l
# -DHAVE_STRDUP=0 if your system lacks the strdup function
# -DHAVE_STRTOLL=0 if your system lacks the strtoll function
# -DHAVE_SYMLINK=0 if your system lacks the symlink function
-# -DHAVE_SYS_STAT_H=0 if your compiler lacks a <sys/stat.h>*
+# -DHAVE_SYS_STAT_H=0 if <sys/stat.h> does not work*
# -DHAVE_TZSET=0 if your system lacks a tzset function
-# -DHAVE_UNISTD_H=0 if your compiler lacks a <unistd.h>*
-# -DHAVE_UTMPX_H=0 if your compiler lacks a <utmpx.h>*
+# -DHAVE_UNISTD_H=0 if <unistd.h> does not work*
+# -DHAVE_UTMPX_H=0 if <utmpx.h> does not work*
# -Dlocale_t=XXX if your system uses XXX instead of locale_t
# -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers
# with external linkage, e.g., applications cannot define 'localtime'.
@@ -280,7 +287,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
-Wdeclaration-after-statement -Wdouble-promotion \
-Wduplicated-branches -Wduplicated-cond \
-Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
- -Winit-self -Wlogical-op \
+ -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op \
-Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
-Wnull-dereference \
-Wold-style-definition -Woverlength-strings -Wpointer-arith \
@@ -293,7 +300,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
-Wtrampolines -Wundef -Wuninitialized -Wunused-macros -Wuse-after-free=3 \
-Wvariadic-macros -Wvla -Wwrite-strings \
-Wno-address -Wno-format-nonliteral -Wno-sign-compare \
- -Wno-type-limits -Wno-unused-parameter
+ -Wno-type-limits
#
# If your system has a "GMT offset" field in its "struct tm"s
# (or if you decide to add such a field in your system's "time.h" file),
@@ -340,14 +347,11 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
# If you want functions that were inspired by early versions of X3J11's work,
# add
# -DSTD_INSPIRED
-# to the end of the "CFLAGS=" line. This arranges for the functions
-# "offtime", "timelocal", "timegm", "timeoff",
-# "posix2time", and "time2posix" to be added to the time conversion library.
+# to the end of the "CFLAGS=" line. This arranges for the following
+# functions to be added to the time conversion library.
# "offtime" is like "gmtime" except that it accepts a second (long) argument
# that gives an offset to add to the time_t when converting it.
# "timelocal" is equivalent to "mktime".
-# "timegm" is like "timelocal" except that it turns a struct tm into
-# a time_t using UT (rather than local time as "timelocal" does).
# "timeoff" is like "timegm" except that it accepts a second (long) argument
# that gives an offset to use when converting to a time_t.
# "posix2time" and "time2posix" are described in an included manual page.
@@ -495,6 +499,11 @@ TARFLAGS= `if tar $(GNUTARFLAGS) --version >/dev/null 2>&1; \
# Flags to give 'gzip' when making a distribution.
GZIPFLAGS= -9n
+# When comparing .tzs files, use GNU diff's -F'^TZ=' option if supported.
+# This makes it easier to see which Zone has been affected.
+DIFF_TZS= diff -u$$(! diff -u -F'^TZ=' - - <>/dev/null >&0 2>&1 \
+ || echo ' -F^TZ=')
+
###############################################################################
#MAKE= make
@@ -773,7 +782,8 @@ tzselect: tzselect.ksh version
chmod +x $@.out
mv $@.out $@
-check: check_character_set check_white_space check_links \
+check: check_back check_mild
+check_mild: check_character_set check_white_space check_links \
check_name_lengths check_slashed_abbrs check_sorted \
check_tables check_web check_ziguard check_zishrink check_tzs
@@ -824,16 +834,19 @@ check_slashed_abbrs: $(TDATA_TO_CHECK)
CHECK_CC_LIST = { n = split($$1,a,/,/); for (i=2; i<=n; i++) print a[1], a[i]; }
check_sorted: backward backzone iso3166.tab zone.tab zone1970.tab
- $(AWK) '/^Link/ {printf "%.5d %s\n", g, $$3} /^$$/ {g++}' \
+ $(AWK) '/^Link/ {printf "%.5d %s\n", g, $$3} !/./ {g++}' \
backward | LC_ALL=C sort -cu
$(AWK) '/^Zone/ {print $$2}' backzone | LC_ALL=C sort -cu
touch $@
-check_links: checklinks.awk $(TDATA_TO_CHECK) tzdata.zi
+check_back: checklinks.awk $(TDATA_TO_CHECK)
$(AWK) \
-v DATAFORM=$(DATAFORM) \
-v backcheck=backward \
-f checklinks.awk $(TDATA_TO_CHECK)
+ touch $@
+
+check_links: checklinks.awk tzdata.zi
$(AWK) \
-v DATAFORM=$(DATAFORM) \
-f checklinks.awk tzdata.zi
@@ -849,7 +862,7 @@ check_tables: checktab.awk $(YDATA) backward $(ZONETABLES)
check_tzs: $(TZS) $(TZS_NEW)
if test -s $(TZS); then \
- diff -u $(TZS) $(TZS_NEW); \
+ $(DIFF_TZS) $(TZS) $(TZS_NEW); \
else \
cp $(TZS_NEW) $(TZS); \
fi
@@ -1050,7 +1063,7 @@ $(TIME_T_ALTERNATIVES): $(VERSION_DEPS)
TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
D=$$wd/$@.dir \
to$$range.tzs) && \
- diff -u $(TIME_T_ALTERNATIVES_HEAD).dir/to$$range.tzs \
+ $(DIFF_TZS) $(TIME_T_ALTERNATIVES_HEAD).dir/to$$range.tzs \
$@.dir/to$$range.tzs && \
if diff -q Makefile Makefile 2>/dev/null; then \
quiet_option='-q'; \
@@ -1220,7 +1233,7 @@ zdump.o: version.h
zic.o: private.h tzfile.h version.h
.PHONY: ALL INSTALL all
-.PHONY: check check_time_t_alternatives
+.PHONY: check check_mild check_time_t_alternatives
.PHONY: check_web check_zishrink
.PHONY: clean clean_misc dummy.zd force_tzs
.PHONY: install install_data maintainer-clean names
diff --git a/NEWS b/NEWS
index 086151073a3b..701e490e4834 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,91 @@
News for the tz database
+Release 2022g - 2022-11-29 08:58:31 -0800
+
+ Briefly:
+ The northern edge of Chihuahua changes to US timekeeping.
+ Much of Greenland stops changing clocks after March 2023.
+ Fix some pre-1996 timestamps in northern Canada.
+ C89 is now deprecated; please use C99 or later.
+ Portability fixes for AIX, libintl, MS-Windows, musl, z/OS
+ In C code, use more C23 features if available.
+ C23 timegm now supported by default
+ Fixes for unlikely integer overflows
+
+ Changes to future timestamps
+
+ In the Mexican state of Chihuahua, the border strip near the US
+ will change to agree with nearby US locations on 2022-11-30.
+ The strip's western part, represented by Ciudad Juárez, switches
+ from -06 all year to -07/-06 with US DST rules, like El Paso, TX.
+ The eastern part, represented by Ojinaga, will observe US DST next
+ year, like Presidio, TX. (Thanks to Heitor David Pinto.)
+ A new Zone America/Ciudad_Juarez splits from America/Ojinaga.
+
+ Much of Greenland, represented by America/Nuuk, stops observing
+ winter time after March 2023, so its daylight saving time becomes
+ standard time. (Thanks to Jonas Nyrup and Jürgen Appel.)
+
+ Changes to past timestamps
+
+ Changes for pre-1996 northern Canada (thanks to Chris Walton):
+
+ Merge America/Iqaluit and America/Pangnirtung into the former,
+ with a backward compatibility link for the latter name.
+ There is no good evidence the two locations differ since 1970.
+ This change affects pre-1996 America/Pangnirtung timestamps.
+
+ Cambridge Bay, Inuvik, Iqaluit, Rankin Inlet, Resolute and
+ Yellowknife did not observe DST in 1965, and did observe DST
+ from 1972 through 1979.
+
+ Whitehorse moved from -09 to -08 on 1966-02-27, not 1967-05-28.
+
+ Colombia's 1993 fallback was 02-06 24:00, not 04-04 00:00.
+ (Thanks to Alois Treindl.)
+
+ Singapore's 1981-12-31 change was at 16:00 UTC (23:30 local time),
+ not 24:00 local time. (Thanks to Geoff Clare via Robert Elz.)
+
+ Changes to code
+
+ Although tzcode still works with C89, bugs found in recent routine
+ maintenance indicate that bitrot has set in and that in practice
+ C89 is no longer used to build tzcode. As it is a maintenance
+ burden, support for C89 is planned to be removed soon. Instead,
+ please use compilers compatible with C99, C11, C17, or C23.
+
+ timegm, which tzcode implemented in 1989, will finally be
+ standardized 34 years later as part of C23, so timegm is now
+ supported even if STD_INSPIRED is not defined.
+
+ Fix bug in zdump's tzalloc emulation on hosts that lack tm_zone.
+ (Problem reported by Đoàn Trần Công Danh.)
+
+ Fix bug in zic on hosts where malloc(0) yields NULL on success.
+ (Problem reported by Tim McBrayer for AIX 6.1.)
+
+ Fix zic configuration to avoid linkage failures on some platforms.
+ (Problems reported by Gilmore Davidson and Igor Ivanov.)
+
+ Work around MS-Windows nmake incompatibility with POSIX.
+ (Problem reported by Manuela Friedrich.)
+
+ Port mktime and strftime to debugging platforms where accessing
+ uninitialized data has undefined behavior (strftime problem
+ reported by Robert Elz).
+
+ Check more carefully for unlikely integer overflows, preferring
+ C23 <stdckdint.h> to overflow checking by hand, as the latter has
+ had obscure bugs.
+
+ Changes to build procedure
+
+ New Makefile rule check_mild that skips checking whether Link
+ lines are in the file 'backward'. (Inspired by a suggestion from
+ Stephen Colebourne.)
+
+
Release 2022f - 2022-10-28 18:04:57 -0700
Briefly:
@@ -16,7 +102,7 @@ Release 2022f - 2022-10-28 18:04:57 -0700
In C code, use some C23 features if available.
Remove no-longer-needed workaround for Qt bug 53071.
- Changes to future timestamps.
+ Changes to future timestamps
Mexico will no longer observe DST after 2022, except for areas
near the US border that continue to observe US DST rules.
@@ -24,6 +110,7 @@ Release 2022f - 2022-10-28 18:04:57 -0700
from -07 (-06 with DST) to year-round -06, thus not changing
its clocks that day. The new law states that Chihuahua
near the US border no longer observes US DST.
+ (Thanks to gera for the heads-up about Chihuahua.)
Fiji will not observe DST in 2022/3. (Thanks to Shalvin Narayan.)
For now, assume DST is suspended indefinitely.
diff --git a/date.1 b/date.1
index 1ecd63a3d90c..043e568117df 100644
--- a/date.1
+++ b/date.1
@@ -1,10 +1,12 @@
-.TH DATE 1
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH date 1
.SH NAME
date \- show and set date and time
.SH SYNOPSIS
.if n .nh
.if n .na
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B date
[
@@ -163,5 +165,3 @@ If
is absent,
UTC leap seconds are loaded from
.BR /usr/share/zoneinfo/posixrules .
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/date.1.txt b/date.1.txt
index 5affca23b2f0..dcb4c4098a53 100644
--- a/date.1.txt
+++ b/date.1.txt
@@ -1,4 +1,4 @@
-DATE(1) General Commands Manual DATE(1)
+date(1) General Commands Manual date(1)
NAME
date - show and set date and time
@@ -104,4 +104,4 @@ FILES
If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
/usr/share/zoneinfo/posixrules.
- DATE(1)
+ date(1)
diff --git a/date.c b/date.c
index 4e4b355af03f..11c5e5fe8d49 100644
--- a/date.c
+++ b/date.c
@@ -42,7 +42,7 @@ static void display(const char *, time_t);
static void dogmt(void);
static void errensure(void);
static void timeout(FILE *, const char *, const struct tm *);
-static _Noreturn void usage(void);
+static ATTRIBUTE_NORETURN void usage(void);
int
main(const int argc, char *argv[])
@@ -117,14 +117,19 @@ dogmt(void)
static char ** fakeenv;
if (fakeenv == NULL) {
- register int from;
- register int to;
- register int n;
static char tzeutc0[] = "TZ=UTC0";
+ ptrdiff_t from, to, n;
for (n = 0; environ[n] != NULL; ++n)
continue;
- fakeenv = malloc((n + 2) * sizeof *fakeenv);
+#if defined ckd_add && defined ckd_mul
+ if (!ckd_add(&n, n, 2) && !ckd_mul(&n, n, sizeof *fakeenv)
+ && n <= SIZE_MAX)
+ fakeenv = malloc(n);
+#else
+ if (n <= min(PTRDIFF_MAX, SIZE_MAX) / sizeof *fakeenv - 2)
+ fakeenv = malloc((n + 2) * sizeof *fakeenv);
+#endif
if (fakeenv == NULL) {
fprintf(stderr, _("date: Memory exhausted\n"));
errensure();
@@ -183,33 +188,28 @@ display(char const *format, time_t now)
static void
timeout(FILE *fp, char const *format, struct tm const *tmp)
{
- char * cp;
- size_t result;
- size_t size;
- struct tm tm;
- int INCR = 1024;
+ char *cp = NULL;
+ ptrdiff_t result;
+ ptrdiff_t size = 1024 / 2;
- if (!tmp) {
- fprintf(stderr, _("date: error: time out of range\n"));
- errensure();
- return;
- }
- tm = *tmp;
- tmp = &tm;
- size = INCR;
- cp = malloc(size);
for ( ; ; ) {
- if (cp == NULL) {
+#ifdef ckd_mul
+ bool bigger = !ckd_mul(&size, size, 2) && size <= SIZE_MAX;
+#else
+ bool bigger = (size <= min(PTRDIFF_MAX, SIZE_MAX) / 2
+ && (size *= 2, true));
+#endif
+ char *newcp = bigger ? realloc(cp, size) : NULL;
+ if (!newcp) {
fprintf(stderr,
_("date: error: can't get memory\n"));
errensure();
exit(retval);
}
+ cp = newcp;
result = strftime(cp, size, format, tmp);
if (result != 0)
break;
- size += INCR;
- cp = realloc(cp, size);
}
fwrite(cp + 1, 1, result - 1, fp);
free(cp);
diff --git a/localtime.c b/localtime.c
index c556531a0a2e..1d22d35146b7 100644
--- a/localtime.c
+++ b/localtime.c
@@ -425,8 +425,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
#endif
if (!doaccess) {
char const *dot;
- size_t namelen = strlen(name);
- if (sizeof lsp->fullname - sizeof tzdirslash <= namelen)
+ if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name))
return ENAMETOOLONG;
/* Create a string "TZDIR/NAME". Using sprintf here
@@ -839,7 +838,7 @@ is_digit(char c)
** Return a pointer to that character.
*/
-static ATTRIBUTE_PURE const char *
+static ATTRIBUTE_REPRODUCIBLE const char *
getzname(register const char *strp)
{
register char c;
@@ -860,7 +859,7 @@ getzname(register const char *strp)
** We don't do any checking here; checking is done later in common-case code.
*/
-static ATTRIBUTE_PURE const char *
+static ATTRIBUTE_REPRODUCIBLE const char *
getqzname(register const char *strp, const int delim)
{
register int c;
@@ -1120,13 +1119,11 @@ tzparse(const char *name, struct state *sp, struct state *basep)
{
const char * stdname;
const char * dstname;
- size_t stdlen;
- size_t dstlen;
- size_t charcnt;
int_fast32_t stdoffset;
int_fast32_t dstoffset;
register char * cp;
register bool load_ok;
+ ptrdiff_t stdlen, dstlen, charcnt;
time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
stdname = name;
@@ -1568,6 +1565,14 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
return NULL; /* "cannot happen" */
result = localsub(sp, &newt, setname, tmp);
if (result) {
+#if defined ckd_add && defined ckd_sub
+ if (t < sp->ats[0]
+ ? ckd_sub(&result->tm_year,
+ result->tm_year, years)
+ : ckd_add(&result->tm_year,
+ result->tm_year, years))
+ return NULL;
+#else
register int_fast64_t newy;
newy = result->tm_year;
@@ -1577,6 +1582,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
if (! (INT_MIN <= newy && newy <= INT_MAX))
return NULL;
result->tm_year = newy;
+#endif
}
return result;
}
@@ -1656,8 +1662,8 @@ localtime_r(const time_t *timep, struct tm *tmp)
*/
static struct tm *
-gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
- struct tm *tmp)
+gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
+ int_fast32_t offset, struct tm *tmp)
{
register struct tm * result;
@@ -1786,6 +1792,12 @@ timesub(const time_t *timep, int_fast32_t offset,
y = newy;
}
+#ifdef ckd_add
+ if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+#else
if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) {
int signed_y = y;
tmp->tm_year = signed_y - TM_YEAR_BASE;
@@ -1796,6 +1808,7 @@ timesub(const time_t *timep, int_fast32_t offset,
errno = EOVERFLOW;
return NULL;
}
+#endif
tmp->tm_yday = idays;
/*
** The "extra" mods below avoid overflow problems.
@@ -1870,6 +1883,9 @@ ctime_r(const time_t *timep, char *buf)
static bool
increment_overflow(int *ip, int j)
{
+#ifdef ckd_add
+ return ckd_add(ip, *ip, j);
+#else
register int const i = *ip;
/*
@@ -1882,22 +1898,30 @@ increment_overflow(int *ip, int j)
return true;
*ip += j;
return false;
+#endif
}
static bool
increment_overflow32(int_fast32_t *const lp, int const m)
{
+#ifdef ckd_add
+ return ckd_add(lp, *lp, m);
+#else
register int_fast32_t const l = *lp;
if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
return true;
*lp += m;
return false;
+#endif
}
static bool
increment_overflow_time(time_t *tp, int_fast32_t j)
{
+#ifdef ckd_add
+ return ckd_add(tp, *tp, j);
+#else
/*
** This is like
** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
@@ -1909,6 +1933,7 @@ increment_overflow_time(time_t *tp, int_fast32_t j)
return true;
*tp += j;
return false;
+#endif
}
static bool
@@ -1951,6 +1976,23 @@ tmcomp(register const struct tm *const atmp,
return result;
}
+/* Copy to *DEST from *SRC. Copy only the members needed for mktime,
+ as other members might not be initialized. */
+static void
+mktmcpy(struct tm *dest, struct tm const *src)
+{
+ dest->tm_sec = src->tm_sec;
+ dest->tm_min = src->tm_min;
+ dest->tm_hour = src->tm_hour;
+ dest->tm_mday = src->tm_mday;
+ dest->tm_mon = src->tm_mon;
+ dest->tm_year = src->tm_year;
+ dest->tm_isdst = src->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ dest->TM_GMTOFF = src->TM_GMTOFF;
+#endif
+}
+
static time_t
time2sub(struct tm *const tmp,
struct tm *(*funcp)(struct state const *, time_t const *,
@@ -1972,7 +2014,8 @@ time2sub(struct tm *const tmp,
struct tm yourtm, mytm;
*okayp = false;
- yourtm = *tmp;
+ mktmcpy(&yourtm, tmp);
+
if (do_norm_secs) {
if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
SECSPERMIN))
@@ -2014,14 +2057,19 @@ time2sub(struct tm *const tmp,
return WRONG;
}
}
+#ifdef ckd_add
+ if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE))
+ return WRONG;
+#else
if (increment_overflow32(&y, -TM_YEAR_BASE))
return WRONG;
if (! (INT_MIN <= y && y <= INT_MAX))
return WRONG;
yourtm.tm_year = y;
+#endif
if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
saved_seconds = 0;
- else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) {
/*
** We can't set tm_sec to 0, because that might push the
** time below the minimum representable time.
@@ -2278,7 +2326,6 @@ mktime(struct tm *tmp)
}
#ifdef STD_INSPIRED
-
time_t
timelocal(struct tm *tmp)
{
@@ -2286,13 +2333,9 @@ timelocal(struct tm *tmp)
tmp->tm_isdst = -1; /* in case it wasn't initialized */
return mktime(tmp);
}
-
-time_t
-timegm(struct tm *tmp)
-{
- return timeoff(tmp, 0);
-}
-
+#else
+static
+#endif
time_t
timeoff(struct tm *tmp, long offset)
{
@@ -2302,7 +2345,18 @@ timeoff(struct tm *tmp, long offset)
return time1(tmp, gmtsub, gmtptr, offset);
}
-#endif /* defined STD_INSPIRED */
+time_t
+timegm(struct tm *tmp)
+{
+ time_t t;
+ struct tm tmcpy;
+ mktmcpy(&tmcpy, tmp);
+ tmcpy.tm_wday = -1;
+ t = timeoff(&tmcpy, 0);
+ if (0 <= tmcpy.tm_wday)
+ *tmp = tmcpy;
+ return t;
+}
static int_fast32_t
leapcorr(struct state const *sp, time_t t)
diff --git a/newctime.3 b/newctime.3
index 866154986dab..e25d841ef537 100644
--- a/newctime.3
+++ b/newctime.3
@@ -1,9 +1,11 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
.TH NEWCTIME 3
.SH NAME
asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
@@ -340,5 +342,3 @@ restricted to years in the range 1900 through 2099.
To avoid this portability mess, new programs should use
.B strftime
instead.
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/newstrftime.3 b/newstrftime.3
index d68bc33783a1..d5d8ee104d9f 100644
--- a/newstrftime.3
+++ b/newstrftime.3
@@ -40,7 +40,7 @@
strftime \- format date and time
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
@@ -55,7 +55,7 @@ strftime \- format date and time
.ie '\(rq'' .ds rq \&"\"
.el .ds rq \(rq\"
.de c
-.ie \n(.g \f(CW\\$1\fP\\$2
+.ie \n(.g \f(CR\\$1\fP\\$2
.el \\$1\\$2
..
.de q
diff --git a/newtzset.3 b/newtzset.3
index 8aaa0ff41732..1e75acf0e6e3 100644
--- a/newtzset.3
+++ b/newtzset.3
@@ -1,9 +1,11 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
.TH NEWTZSET 3
.SH NAME
tzset \- initialize time conversion information
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
@@ -331,7 +333,7 @@ from the rest of the specification.
.br
/usr/share/zoneinfo/localtime local timezone file
.br
-/usr/share/zoneinfo/posixrules used with POSIX-style TZ's
+/usr/share/zoneinfo/posixrules used with POSIX-style TZ
.br
/usr/share/zoneinfo/GMT for UTC leap seconds
.sp
@@ -346,5 +348,3 @@ newctime(3),
newstrftime(3),
time(2),
tzfile(5)
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/newtzset.3.txt b/newtzset.3.txt
index 171e8291f8ce..67cf610d2388 100644
--- a/newtzset.3.txt
+++ b/newtzset.3.txt
@@ -187,7 +187,7 @@ DESCRIPTION
FILES
/usr/share/zoneinfo timezone information directory
/usr/share/zoneinfo/localtime local timezone file
- /usr/share/zoneinfo/posixrules used with POSIX-style TZ's
+ /usr/share/zoneinfo/posixrules used with POSIX-style TZ
/usr/share/zoneinfo/GMT for UTC leap seconds
If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
diff --git a/private.h b/private.h
index 18f6a055ad15..7a73eff7c576 100644
--- a/private.h
+++ b/private.h
@@ -17,6 +17,10 @@
** Thank you!
*/
+#ifndef __STDC_VERSION__
+# define __STDC_VERSION__ 0
+#endif
+
/* Define true, false and bool if they don't work out of the box. */
#if __STDC_VERSION__ < 199901
# define true 1
@@ -56,24 +60,13 @@
# endif
#endif
/* _Generic is buggy in pre-4.9 GCC. */
-#if !defined HAVE_GENERIC && defined __GNUC__
+#if !defined HAVE_GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__
# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
#endif
#ifndef HAVE_GENERIC
# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
#endif
-#if !defined HAVE_GETRANDOM && defined __has_include
-# if __has_include(<sys/random.h>)
-# define HAVE_GETRANDOM true
-# else
-# define HAVE_GETRANDOM false
-# endif
-#endif
-#ifndef HAVE_GETRANDOM
-# define HAVE_GETRANDOM (2 < __GLIBC__ + (25 <= __GLIBC_MINOR__))
-#endif
-
#if !defined HAVE_GETTEXT && defined __has_include
# if __has_include(<libintl.h>)
# define HAVE_GETTEXT true
@@ -289,36 +282,36 @@
#endif
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
-#ifdef __LONG_LONG_MAX__
+#if defined __LONG_LONG_MAX__ && !defined __STRICT_ANSI__
# ifndef LLONG_MAX
# define LLONG_MAX __LONG_LONG_MAX__
# endif
# ifndef LLONG_MIN
# define LLONG_MIN (-1 - LLONG_MAX)
# endif
+# ifndef ULLONG_MAX
+# define ULLONG_MAX (LLONG_MAX * 2ull + 1)
+# endif
#endif
#ifndef INT_FAST64_MAX
-# ifdef LLONG_MAX
-typedef long long int_fast64_t;
-# define INT_FAST64_MIN LLONG_MIN
-# define INT_FAST64_MAX LLONG_MAX
-# else
-# if LONG_MAX >> 31 < 0xffffffff
-Please use a compiler that supports a 64-bit integer type (or wider);
-you may need to compile with "-DHAVE_STDINT_H".
-# endif
-typedef long int_fast64_t;
+# if 1 <= LONG_MAX >> 31 >> 31
+typedef long int_fast64_t;
# define INT_FAST64_MIN LONG_MIN
# define INT_FAST64_MAX LONG_MAX
+# else
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef long long int_fast64_t;
+# define INT_FAST64_MIN LLONG_MIN
+# define INT_FAST64_MAX LLONG_MAX
# endif
#endif
#ifndef PRIdFAST64
-# if INT_FAST64_MAX == LLONG_MAX
-# define PRIdFAST64 "lld"
-# else
+# if INT_FAST64_MAX == LONG_MAX
# define PRIdFAST64 "ld"
+# else
+# define PRIdFAST64 "lld"
# endif
#endif
@@ -364,24 +357,27 @@ typedef long intmax_t;
# endif
#endif
+#ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t))
+#endif
+
#ifndef UINT_FAST32_MAX
typedef unsigned long uint_fast32_t;
#endif
#ifndef UINT_FAST64_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
-typedef unsigned long long uint_fast64_t;
+# if 3 <= ULONG_MAX >> 31 >> 31
+typedef unsigned long uint_fast64_t;
+# define UINT_FAST64_MAX ULONG_MAX
# else
-# if ULONG_MAX >> 31 >> 1 < 0xffffffff
-Please use a compiler that supports a 64-bit integer type (or wider);
-you may need to compile with "-DHAVE_STDINT_H".
-# endif
-typedef unsigned long uint_fast64_t;
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef unsigned long long uint_fast64_t;
+# define UINT_FAST64_MAX ULLONG_MAX
# endif
#endif
#ifndef UINTMAX_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef ULLONG_MAX
typedef unsigned long long uintmax_t;
# else
typedef unsigned long uintmax_t;
@@ -389,7 +385,7 @@ typedef unsigned long uintmax_t;
#endif
#ifndef PRIuMAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef ULLONG_MAX
# define PRIuMAX "llu"
# else
# define PRIuMAX "lu"
@@ -400,23 +396,114 @@ typedef unsigned long uintmax_t;
# define SIZE_MAX ((size_t) -1)
#endif
+/* Support ckd_add, ckd_sub, ckd_mul on C23 or recent-enough GCC-like
+ hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
+#if !defined HAVE_STDCKDINT_H && defined __has_include
+# if __has_include(<stdckdint.h>)
+# define HAVE_STDCKDINT_H true
+# endif
+#endif
+#ifdef HAVE_STDCKDINT_H
+# if HAVE_STDCKDINT_H
+# include <stdckdint.h>
+# endif
+#elif defined __EDG__
+/* Do nothing, to work around EDG bug <https://bugs.gnu.org/53256>. */
+#elif defined __has_builtin
+# if __has_builtin(__builtin_add_overflow)
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_sub_overflow)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_mul_overflow)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+# endif
+#elif 7 <= __GNUC__
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+#endif
+
#if 3 <= __GNUC__
-# define ATTRIBUTE_CONST __attribute__((const))
-# define ATTRIBUTE_MALLOC __attribute__((__malloc__))
-# define ATTRIBUTE_PURE __attribute__((__pure__))
-# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
+# define ATTRIBUTE_MALLOC __attribute__((malloc))
+# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
#else
-# define ATTRIBUTE_CONST /* empty */
# define ATTRIBUTE_MALLOC /* empty */
-# define ATTRIBUTE_PURE /* empty */
# define ATTRIBUTE_FORMAT(spec) /* empty */
#endif
-#if !defined _Noreturn && __STDC_VERSION__ < 201112
-# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
-# define _Noreturn __attribute__((__noreturn__))
+#if (defined __has_c_attribute \
+ && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__))
+# define HAVE_HAS_C_ATTRIBUTE true
+#else
+# define HAVE_HAS_C_ATTRIBUTE false
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(fallthrough)
+# define ATTRIBUTE_FALLTHROUGH [[fallthrough]]
+# endif
+#endif
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if 7 <= __GNUC__
+# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough))
+# else
+# define ATTRIBUTE_FALLTHROUGH ((void) 0)
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(maybe_unused)
+# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
+# endif
+#endif
+#ifndef ATTRIBUTE_MAYBE_UNUSED
+# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused))
+# else
+# define ATTRIBUTE_MAYBE_UNUSED /* empty */
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(noreturn)
+# define ATTRIBUTE_NORETURN [[noreturn]]
+# endif
+#endif
+#ifndef ATTRIBUTE_NORETURN
+# if 201112 <= __STDC_VERSION__
+# define ATTRIBUTE_NORETURN _Noreturn
+# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
+# define ATTRIBUTE_NORETURN __attribute__((noreturn))
# else
-# define _Noreturn
+# define ATTRIBUTE_NORETURN /* empty */
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(reproducible)
+# define ATTRIBUTE_REPRODUCIBLE [[reproducible]]
+# endif
+#endif
+#ifndef ATTRIBUTE_REPRODUCIBLE
+# if 3 <= __GNUC__
+# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure))
+# else
+# define ATTRIBUTE_REPRODUCIBLE /* empty */
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(unsequenced)
+# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
+# endif
+#endif
+#ifndef ATTRIBUTE_UNSEQUENCED
+# if 3 <= __GNUC__
+# define ATTRIBUTE_UNSEQUENCED __attribute__((const))
+# else
+# define ATTRIBUTE_UNSEQUENCED /* empty */
# endif
#endif
@@ -541,7 +628,7 @@ char *asctime(struct tm const *);
char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime(time_t const *);
char *ctime_r(time_t const *, char *);
-double difftime(time_t, time_t) ATTRIBUTE_CONST;
+double difftime(time_t, time_t) ATTRIBUTE_UNSEQUENCED;
size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict);
# if HAVE_STRFTIME_L
@@ -554,9 +641,24 @@ struct tm *localtime(time_t const *);
struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
time_t mktime(struct tm *);
time_t time(time_t *);
+time_t timegm(struct tm *);
void tzset(void);
#endif
+#ifndef HAVE_DECL_TIMEGM
+# if (202311 <= __STDC_VERSION__ \
+ || defined __GLIBC__ || defined __tm_zone /* musl */ \
+ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# define HAVE_DECL_TIMEGM true
+# else
+# define HAVE_DECL_TIMEGM false
+# endif
+#endif
+#if !HAVE_DECL_TIMEGM && !defined timegm
+time_t timegm(struct tm *);
+#endif
+
#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
extern char *asctime_r(struct tm const *restrict, char *restrict);
#endif
@@ -593,9 +695,6 @@ extern long altzone;
# if TZ_TIME_T || !defined offtime
struct tm *offtime(time_t const *, long);
# endif
-# if TZ_TIME_T || !defined timegm
-time_t timegm(struct tm *);
-# endif
# if TZ_TIME_T || !defined timelocal
time_t timelocal(struct tm *);
# endif
@@ -613,6 +712,7 @@ time_t posix2time(time_t);
/* Infer TM_ZONE on systems where this information is known, but suppress
guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
#if (defined __GLIBC__ \
+ || defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
@@ -640,10 +740,10 @@ timezone_t tzalloc(char const *);
void tzfree(timezone_t);
# ifdef STD_INSPIRED
# if TZ_TIME_T || !defined posix2time_z
-time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
+time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_REPRODUCIBLE;
# endif
# if TZ_TIME_T || !defined time2posix_z
-time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
+time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_REPRODUCIBLE;
# endif
# endif
#endif
diff --git a/strftime.c b/strftime.c
index deba2b5e7a48..b23b6101b150 100644
--- a/strftime.c
+++ b/strftime.c
@@ -117,7 +117,7 @@ static char * _yconv(int, int, bool, bool, char *, char const *);
#if HAVE_STRFTIME_L
size_t
strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
- locale_t locale)
+ ATTRIBUTE_MAYBE_UNUSED locale_t locale)
{
/* Just call strftime, as only the C locale is supported. */
return strftime(s, maxsize, format, t);
@@ -319,12 +319,21 @@ label:
time_t) + 1];
time_t mkt;
- tm = *t;
+ tm.tm_sec = t->tm_sec;
+ tm.tm_min = t->tm_min;
+ tm.tm_hour = t->tm_hour;
+ tm.tm_mday = t->tm_mday;
+ tm.tm_mon = t->tm_mon;
+ tm.tm_year = t->tm_year;
+ tm.tm_isdst = t->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ tm.TM_GMTOFF = t->TM_GMTOFF;
+#endif
mkt = mktime(&tm);
- /* There is no portable, definitive
- test for whether whether mktime
- succeeded, so treat (time_t) -1 as
- the success that it might be. */
+ /* If mktime fails, %s expands to the
+ value of (time_t) -1 as a failure
+ marker; this is better in practice
+ than strftime failing. */
if (TYPE_SIGNED(time_t)) {
intmax_t n = mkt;
sprintf(buf, "%"PRIdMAX, n);
diff --git a/theory.html b/theory.html
index 298db866b39c..75e347f0f9d1 100644
--- a/theory.html
+++ b/theory.html
@@ -60,7 +60,6 @@ with current and future timestamps in the traditional North
American mountain time zone can choose from the timezones
<code>America/Denver</code> which observes US-style daylight saving
time (<abbr>DST</abbr>),
-<code>America/Mazatlan</code> which observes Mexican-style <abbr>DST</abbr>,
and <code>America/Phoenix</code> which does not observe <abbr>DST</abbr>.
Applications that also deal with past timestamps in the mountain time
zone can choose from over a dozen timezones, such as
diff --git a/time2posix.3 b/time2posix.3
index 1fcdf9e55733..e13c431c85b9 100644
--- a/time2posix.3
+++ b/time2posix.3
@@ -1,9 +1,11 @@
-.TH TIME2POSIX 3
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
+.TH time2posix 3
.SH NAME
time2posix, posix2time \- convert seconds since the Epoch
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
@@ -58,7 +60,7 @@ expression for directly computing a time_t value from a given date/time,
and the same relationship is assumed by some
(usually older)
applications.
-Any programs creating/dissecting time_t's
+Any programs creating/dissecting time_t values
using such a relationship will typically not handle intervals
over leap seconds correctly.
.PP
@@ -93,7 +95,7 @@ Both of these are good indicators of the inferiority of the
POSIX representation.
.PP
The following table summarizes the relationship between a time
-T and it's conversion to,
+T and its conversion to,
and back from,
the POSIX representation over the leap second inserted at the end of June,
1993.
@@ -117,8 +119,8 @@ DATE TIME T X=time2posix(T) posix2time(X)
.fi
.PP
If leap-second support is not enabled,
-local time_t's and
-POSIX time_t's are equivalent,
+local time_t and
+POSIX time_t values are equivalent,
and both
.B time2posix
and
@@ -129,5 +131,3 @@ difftime(3),
localtime(3),
mktime(3),
time(2)
-.\" This file is in the public domain, so clarified as of
-.\" 1996-06-05 by Arthur David Olson.
diff --git a/time2posix.3.txt b/time2posix.3.txt
index aae7e1190584..95ffd5676e17 100644
--- a/time2posix.3.txt
+++ b/time2posix.3.txt
@@ -1,4 +1,4 @@
-TIME2POSIX(3) Library Functions Manual TIME2POSIX(3)
+time2posix(3) Library Functions Manual time2posix(3)
NAME
time2posix, posix2time - convert seconds since the Epoch
@@ -30,8 +30,8 @@ DESCRIPTION
difftime(3). However, POSIX gives an arithmetic expression for
directly computing a time_t value from a given date/time, and the same
relationship is assumed by some (usually older) applications. Any
- programs creating/dissecting time_t's using such a relationship will
- typically not handle intervals over leap seconds correctly.
+ programs creating/dissecting time_t values using such a relationship
+ will typically not handle intervals over leap seconds correctly.
The time2posix and posix2time functions are provided to address this
time_t mismatch by converting between local time_t values and their
@@ -49,7 +49,7 @@ DESCRIPTION
indicators of the inferiority of the POSIX representation.
The following table summarizes the relationship between a time T and
- it's conversion to, and back from, the POSIX representation over the
+ its conversion to, and back from, the POSIX representation over the
leap second inserted at the end of June, 1993.
DATE TIME T X=time2posix(T) posix2time(X)
93/06/30 23:59:59 A+0 B+0 A+0
@@ -66,11 +66,11 @@ DESCRIPTION
[Note: posix2time(B+1) => A+0 or A+1]
- If leap-second support is not enabled, local time_t's and POSIX
- time_t's are equivalent, and both time2posix and posix2time degenerate
- to the identity function.
+ If leap-second support is not enabled, local time_t and POSIX time_t
+ values are equivalent, and both time2posix and posix2time degenerate to
+ the identity function.
SEE ALSO
difftime(3), localtime(3), mktime(3), time(2)
- TIME2POSIX(3)
+ time2posix(3)
diff --git a/tzfile.5 b/tzfile.5
index 280e8d8a40ff..c3e86c62ad71 100644
--- a/tzfile.5
+++ b/tzfile.5
@@ -1,3 +1,5 @@
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
.TH TZFILE 5
.SH NAME
tzfile \- timezone information
@@ -9,7 +11,7 @@ tzfile \- timezone information
.de q
\\$3\*(lq\\$1\*(rq\\$2
..
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
The timezone information files used by
.BR tzset (3)
@@ -492,5 +494,3 @@ Internet RFC 8536
.UR https://\:doi.org/\:10.17487/\:RFC8536
doi:10.17487/RFC8536
.UE .
-.\" This file is in the public domain, so clarified as of
-.\" 1996-06-05 by Arthur David Olson.
diff --git a/tzselect.8 b/tzselect.8
index 1a5ce110f47d..846b867be1c0 100644
--- a/tzselect.8
+++ b/tzselect.8
@@ -1,8 +1,10 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
.TH TZSELECT 8
.SH NAME
tzselect \- select a timezone
.SH SYNOPSIS
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.ds d " degrees
.ds m " minutes
@@ -121,5 +123,3 @@ newctime(3), tzfile(5), zdump(8), zic(8)
Applications should not assume that
.BR tzselect 's
output matches the user's political preferences.
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/version b/version
index 5c8fbb478a2c..b74fa117a223 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2022f
+2022g
diff --git a/zdump.8 b/zdump.8
index 131a6cbd269c..170e18d6f94e 100644
--- a/zdump.8
+++ b/zdump.8
@@ -1,4 +1,6 @@
-.TH ZDUMP 8
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH zdump 8
.SH NAME
zdump \- timezone dumper
.SH SYNOPSIS
@@ -16,7 +18,7 @@ zdump \- timezone dumper
.de q
\\$3\*(lq\\$1\*(rq\\$2
..
-.ie \n(.g .ds - \f(CW-\fP
+.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
The
.B zdump
@@ -149,7 +151,7 @@ Here is an example of the output, with the leading empty line omitted.
tabbed columns line up.)
.nf
.sp
-.if \n(.g .ft CW
+.if \n(.g .ft CR
.if t .in +.5i
.if n .in +2
.nr w \w'1896-01-13 'u+\n(.i
@@ -182,7 +184,7 @@ UT, a standard time abbreviated HST.
Here are excerpts from another example:
.nf
.sp
-.if \n(.g .ft CW
+.if \n(.g .ft CR
.if t .in +.5i
.if n .in +2
TZ="Europe/Astrakhan"
@@ -227,5 +229,3 @@ introduction of UTC is problematic.
.SH SEE ALSO
.BR tzfile (5),
.BR zic (8)
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/zdump.8.txt b/zdump.8.txt
index 65b6a287f3f8..8201c0166565 100644
--- a/zdump.8.txt
+++ b/zdump.8.txt
@@ -1,4 +1,4 @@
-ZDUMP(8) System Manager's Manual ZDUMP(8)
+zdump(8) System Manager's Manual zdump(8)
NAME
zdump - timezone dumper
@@ -141,4 +141,4 @@ LIMITATIONS
SEE ALSO
tzfile(5), zic(8)
- ZDUMP(8)
+ zdump(8)
diff --git a/zdump.c b/zdump.c
index ffb321a0369e..7acb3e2d9481 100644
--- a/zdump.c
+++ b/zdump.c
@@ -84,20 +84,20 @@ static time_t const absolute_max_time =
? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
: -1);
static int longest;
-static char * progname;
+static char const *progname;
static bool warned;
static bool errout;
static char const *abbr(struct tm const *);
-static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
+static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_REPRODUCIBLE;
static void dumptime(struct tm const *);
-static time_t hunt(timezone_t, char *, time_t, time_t, bool);
+static time_t hunt(timezone_t, time_t, time_t, bool);
static void show(timezone_t, char *, time_t, bool);
static void showextrema(timezone_t, char *, time_t, struct tm *, time_t);
static void showtrans(char const *, struct tm const *, time_t, char const *,
char const *);
static const char *tformat(void);
-static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
+static time_t yeartot(intmax_t) ATTRIBUTE_REPRODUCIBLE;
/* Is C an ASCII digit? */
static bool
@@ -125,16 +125,28 @@ is_alpha(char a)
}
}
-/* Return A + B, exiting if the result would overflow. */
-static size_t
+static ATTRIBUTE_NORETURN void
+size_overflow(void)
+{
+ fprintf(stderr, _("%s: size overflow\n"), progname);
+ exit(EXIT_FAILURE);
+}
+
+/* Return A + B, exiting if the result would overflow either ptrdiff_t
+ or size_t. */
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
sumsize(size_t a, size_t b)
{
- size_t sum = a + b;
- if (sum < a) {
- fprintf(stderr, _("%s: size overflow\n"), progname);
- exit(EXIT_FAILURE);
- }
- return sum;
+#ifdef ckd_add
+ ptrdiff_t sum;
+ if (!ckd_add(&sum, a, b) && sum <= SIZE_MAX)
+ return sum;
+#else
+ ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX);
+ if (a <= sum_max && b <= sum_max - a)
+ return a + b;
+#endif
+ size_overflow();
}
/* Return a pointer to a newly allocated buffer of size SIZE, exiting
@@ -234,22 +246,30 @@ tzalloc(char const *val)
exit(EXIT_FAILURE);
}
tzset();
- return NULL;
+ return &optarg; /* Any valid non-null char ** will do. */
# else
enum { TZeqlen = 3 };
static char const TZeq[TZeqlen] = "TZ=";
static char **fakeenv;
- static size_t fakeenv0size;
+ static ptrdiff_t fakeenv0size;
void *freeable = NULL;
char **env = fakeenv, **initial_environ;
size_t valsize = strlen(val) + 1;
if (fakeenv0size < valsize) {
char **e = environ, **to;
- ptrdiff_t initial_nenvptrs; /* Counting the trailing NULL pointer. */
-
- while (*e++)
- continue;
- initial_nenvptrs = e - environ;
+ ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */
+
+ while (*e++) {
+# ifdef ckd_add
+ if (ckd_add(&initial_nenvptrs, initial_envptrs, 1)
+ || SIZE_MAX < initial_envptrs)
+ size_overflow();
+# else
+ if (initial_nenvptrs == min(PTRDIFF_MAX, SIZE_MAX) / sizeof *environ)
+ size_overflow();
+ initial_nenvptrs++;
+# endif
+ }
fakeenv0size = sumsize(valsize, valsize);
fakeenv0size = max(fakeenv0size, 64);
freeable = env;
@@ -385,7 +405,7 @@ abbrok(const char *const abbrp, const char *const zone)
return the abbreviation. Get the abbreviation from TMP.
Exit on memory allocation failure. */
static char const *
-saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp)
+saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp)
{
char const *ab = abbr(tmp);
if (HAVE_LOCALTIME_RZ)
@@ -442,7 +462,7 @@ main(int argc, char *argv[])
{
/* These are static so that they're initially zero. */
static char * abbrev;
- static size_t abbrevsize;
+ static ptrdiff_t abbrevsize;
register int i;
register bool vflag;
@@ -463,7 +483,7 @@ main(int argc, char *argv[])
# endif /* defined TEXTDOMAINDIR */
textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */
- progname = argv[0];
+ progname = argv[0] ? argv[0] : "zdump";
for (i = 1; i < argc; ++i)
if (strcmp(argv[i], "--version") == 0) {
printf("zdump %s%s\n", PKGVERSION, TZVERSION);
@@ -483,7 +503,7 @@ main(int argc, char *argv[])
case -1:
if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
goto arg_processing_done;
- /* Fall through. */
+ ATTRIBUTE_FALLTHROUGH;
default:
usage(stderr, EXIT_FAILURE);
}
@@ -607,7 +627,7 @@ main(int argc, char *argv[])
|| (ab && (delta(&newtm, &tm) != newt - t
|| newtm.tm_isdst != tm.tm_isdst
|| strcmp(abbr(&newtm), ab) != 0))) {
- newt = hunt(tz, argv[i], t, newt, false);
+ newt = hunt(tz, t, newt, false);
newtmp = localtime_rz(tz, &newt, &newtm);
newtm_ok = newtmp != NULL;
if (iflag)
@@ -687,7 +707,7 @@ yeartot(intmax_t y)
return t;
}
-/* Search for a discontinuity in timezone TZ with name NAME, in the
+/* Search for a discontinuity in timezone TZ, in the
timestamps ranging from LOT through HIT. LOT and HIT disagree
about some aspect of timezone. If ONLY_OK, search only for
definedness changes, i.e., localtime succeeds on one side of the
@@ -695,10 +715,10 @@ yeartot(intmax_t y)
before the transition from LOT's settings. */
static time_t
-hunt(timezone_t tz, char *name, time_t lot, time_t hit, bool only_ok)
+hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok)
{
static char * loab;
- static size_t loabsize;
+ static ptrdiff_t loabsize;
struct tm lotm;
struct tm tm;
@@ -787,7 +807,8 @@ adjusted_yday(struct tm const *a, struct tm const *b)
my_gmtime_r and use its result instead of B. Otherwise, B is the
possibly nonnull result of an earlier call to my_gmtime_r. */
static long
-gmtoff(struct tm const *a, time_t *t, struct tm const *b)
+gmtoff(struct tm const *a, ATTRIBUTE_MAYBE_UNUSED time_t *t,
+ ATTRIBUTE_MAYBE_UNUSED struct tm const *b)
{
#ifdef TM_GMTOFF
return a->TM_GMTOFF;
@@ -858,7 +879,7 @@ static void
showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
{
struct tm localtm[2], gmtm[2];
- time_t t, boundary = hunt(tz, zone, lo, hi, true);
+ time_t t, boundary = hunt(tz, lo, hi, true);
bool old = false;
hi = (SECSPERDAY < hi - boundary
? boundary + SECSPERDAY
@@ -937,7 +958,7 @@ my_snprintf(char *s, size_t size, char const *format, ...)
fit, return the length that the string would have been if it had
fit; do not overrun the output buffer. */
static int
-format_local_time(char *buf, size_t size, struct tm const *tm)
+format_local_time(char *buf, ptrdiff_t size, struct tm const *tm)
{
int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
return (ss
@@ -960,7 +981,7 @@ format_local_time(char *buf, size_t size, struct tm const *tm)
the length that the string would have been if it had fit; do not
overrun the output buffer. */
static int
-format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
+format_utc_offset(char *buf, ptrdiff_t size, struct tm const *tm, time_t t)
{
long off = gmtoff(tm, &t, NULL);
char sign = ((off < 0
@@ -989,11 +1010,11 @@ format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
If the representation's length is less than SIZE, return the
length; the representation is not null terminated. Otherwise
return SIZE, to indicate that BUF is too small. */
-static size_t
-format_quoted_string(char *buf, size_t size, char const *p)
+static ptrdiff_t
+format_quoted_string(char *buf, ptrdiff_t size, char const *p)
{
char *b = buf;
- size_t s = size;
+ ptrdiff_t s = size;
if (!s)
return size;
*b++ = '"', s--;
@@ -1031,11 +1052,11 @@ format_quoted_string(char *buf, size_t size, char const *p)
and omit any trailing tabs. */
static bool
-istrftime(char *buf, size_t size, char const *time_fmt,
+istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
struct tm const *tm, time_t t, char const *ab, char const *zone_name)
{
char *b = buf;
- size_t s = size;
+ ptrdiff_t s = size;
char const *f = time_fmt, *p;
for (p = f; ; p++)
@@ -1044,9 +1065,9 @@ istrftime(char *buf, size_t size, char const *time_fmt,
else if (!*p
|| (*p == '%'
&& (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
- size_t formatted_len;
- size_t f_prefix_len = p - f;
- size_t f_prefix_copy_size = p - f + 2;
+ ptrdiff_t formatted_len;
+ ptrdiff_t f_prefix_len = p - f;
+ ptrdiff_t f_prefix_copy_size = sumsize(f_prefix_len, 2);
char fbuf[100];
bool oversized = sizeof fbuf <= f_prefix_copy_size;
char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
@@ -1078,7 +1099,7 @@ istrftime(char *buf, size_t size, char const *time_fmt,
b += offlen, s -= offlen;
if (show_abbr) {
char const *abp;
- size_t len;
+ ptrdiff_t len;
if (s <= 1)
return false;
*b++ = '\t', s--;
@@ -1117,7 +1138,7 @@ showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
putchar('\n');
} else {
char stackbuf[1000];
- size_t size = sizeof stackbuf;
+ ptrdiff_t size = sizeof stackbuf;
char *buf = stackbuf;
char *bufalloc = NULL;
while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
diff --git a/zic.8 b/zic.8
index f79148f4a08a..019a289cf452 100644
--- a/zic.8
+++ b/zic.8
@@ -1,4 +1,6 @@
-.TH ZIC 8
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH zic 8
.SH NAME
zic \- timezone compiler
.SH SYNOPSIS
@@ -22,7 +24,7 @@ zic \- timezone compiler
.el .ds > \(ra
.ie \n(.g \{\
. ds : \:
-. ds - \f(CW-\fP
+. ds - \f(CR-\fP
.\}
.el \{\
. ds :
@@ -347,7 +349,9 @@ nor
.q + .
To allow for future extensions,
an unquoted name should not contain characters from the set
-.q !$%&'()*,/:;<=>?@[\e]^`{|}~ .
+.ie \n(.g .q \f(CR!$%&\(aq()*,/:;<=>?@[\e]\(ha\(ga{|}\(ti\fP .
+.el .ie t .q \f(CW!$%&'()*,/:;<=>?@[\e]^\(ga{|}~\fP .
+.el .q !$%&'()*,/:;<=>?@[\e]^`{|}~ .
.TP
.B FROM
Gives the first year in which the rule applies.
@@ -894,5 +898,3 @@ specifying transition instants using universal time.
.SH SEE ALSO
.BR tzfile (5),
.BR zdump (8)
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/zic.8.txt b/zic.8.txt
index 89a551dcd375..e224b980ab6d 100644
--- a/zic.8.txt
+++ b/zic.8.txt
@@ -1,4 +1,4 @@
-ZIC(8) System Manager's Manual ZIC(8)
+zic(8) System Manager's Manual zic(8)
NAME
zic - timezone compiler
@@ -513,4 +513,4 @@ NOTES
SEE ALSO
tzfile(5), zdump(8)
- ZIC(8)
+ zic(8)
diff --git a/zic.c b/zic.c
index 501718f459d3..892414af7bf5 100644
--- a/zic.c
+++ b/zic.c
@@ -34,6 +34,9 @@ static zic_t const
# define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
+/* An upper bound on how much a format might grow due to concatenation. */
+enum { FORMAT_LEN_GROWTH_BOUND = 5 };
+
#ifdef HAVE_DIRECT_H
# include <direct.h>
# include <io.h>
@@ -41,7 +44,16 @@ static zic_t const
# define mkdir(name, mode) _mkdir(name)
#endif
-#if HAVE_GETRANDOM
+#ifndef HAVE_GETRANDOM
+# ifdef __has_include
+# if __has_include(<sys/random.h>)
+# include <sys/random.h>
+# endif
+# elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__)
+# include <sys/random.h>
+# endif
+# define HAVE_GETRANDOM GRND_RANDOM
+#elif HAVE_GETRANDOM
# include <sys/random.h>
#endif
@@ -54,11 +66,6 @@ static zic_t const
# define MKDIR_UMASK 0755
#endif
-/* 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-C23 platforms. */
#if __STDC_VERSION__ < 201112
# define alignof(type) offsetof(struct { char a; type b; }, b)
@@ -452,29 +459,54 @@ static char roll[TZ_MAX_LEAPS];
** Memory allocation.
*/
-static _Noreturn void
+static ATTRIBUTE_NORETURN void
memory_exhausted(const char *msg)
{
fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
exit(EXIT_FAILURE);
}
-static ATTRIBUTE_PURE size_t
-size_product(size_t nitems, size_t itemsize)
+static ATTRIBUTE_NORETURN void
+size_overflow(void)
+{
+ memory_exhausted(_("size overflow"));
+}
+
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+size_sum(size_t a, size_t b)
+{
+#ifdef ckd_add
+ ptrdiff_t sum;
+ if (!ckd_add(&sum, a, b) && sum <= SIZE_MAX)
+ return sum;
+#else
+ ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX);
+ if (a <= sum_max && b <= sum_max - a)
+ return a + b;
+#endif
+ size_overflow();
+}
+
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
{
- if (SIZE_MAX / itemsize < nitems)
- memory_exhausted(_("size overflow"));
- return nitems * itemsize;
+#ifdef ckd_mul
+ ptrdiff_t product;
+ if (!ckd_mul(&product, nitems, itemsize) && product <= SIZE_MAX)
+ return product;
+#else
+ ptrdiff_t nitems_max = min(PTRDIFF_MAX, SIZE_MAX) / itemsize;
+ if (nitems <= nitems_max)
+ return nitems * itemsize;
+#endif
+ size_overflow();
}
-static ATTRIBUTE_PURE size_t
-align_to(size_t size, size_t alignment)
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+align_to(ptrdiff_t size, ptrdiff_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;
+ ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits);
+ return sum & ~lo_bits;
}
#if !HAVE_STRDUP
@@ -507,23 +539,37 @@ erealloc(void *ptr, size_t size)
}
static char * ATTRIBUTE_MALLOC
-ecpyalloc(char const *str)
+estrdup(char const *str)
{
return memcheck(strdup(str));
}
+static ptrdiff_t
+grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize)
+{
+ ptrdiff_t addend = (*nitems_alloc >> 1) + 1;
+#if defined ckd_add && defined ckd_mul
+ ptrdiff_t product;
+ if (!ckd_add(nitems_alloc, *nitems_alloc, addend)
+ && !ckd_mul(&product, *nitems_alloc, itemsize) && product <= SIZE_MAX)
+ return product;
+#else
+ ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX);
+ if (*nitems_alloc <= ((amax - 1) / 3 * 2) / itemsize) {
+ *nitems_alloc += addend;
+ return *nitems_alloc * itemsize;
+ }
+#endif
+ memory_exhausted(_("integer overflow"));
+}
+
static void *
-growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
+growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
+ ptrdiff_t *nitems_alloc)
{
- if (nitems < *nitems_alloc)
- return ptr;
- else {
- ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX);
- if ((amax - 1) / 3 * 2 < *nitems_alloc)
- memory_exhausted(_("integer overflow"));
- *nitems_alloc += (*nitems_alloc >> 1) + 1;
- return erealloc(ptr, size_product(*nitems_alloc, itemsize));
- }
+ return (nitems < *nitems_alloc
+ ? ptr
+ : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
}
/*
@@ -620,7 +666,7 @@ close_file(FILE *stream, char const *dir, char const *name,
}
}
-static _Noreturn void
+static ATTRIBUTE_NORETURN void
usage(FILE *stream, int status)
{
fprintf(stream,
@@ -943,7 +989,7 @@ main(int argc, char **argv)
textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */
main_argv = argv;
- progname = argv[0];
+ progname = argv[0] ? argv[0] : "zic";
if (TYPE_BIT(zic_t) < 64) {
fprintf(stderr, "%s: %s\n", progname,
_("wild compilation-time specification of zic_t"));
@@ -1201,21 +1247,12 @@ get_rand_u64(void)
#endif
/* getrandom didn't work, so fall back on portable code that is
- not the best because the seed doesn't necessarily have enough bits,
- the seed isn't cryptographically random on platforms lacking
- getrandom, and 'rand' might not be cryptographically secure. */
+ not the best because the seed isn't cryptographically random and
+ 'rand' might not be cryptographically secure. */
{
static bool initialized;
if (!initialized) {
- unsigned seed;
-#ifdef CLOCK_REALTIME
- struct timespec now;
- clock_gettime (CLOCK_REALTIME, &now);
- seed = now.tv_sec ^ now.tv_nsec;
-#else
- seed = time(NULL);
-#endif
- srand(seed);
+ srand(time(NULL));
initialized = true;
}
}
@@ -1224,13 +1261,21 @@ get_rand_u64(void)
the typical case where RAND_MAX is one less than a power of two.
In other cases this code yields a sort-of-random number. */
{
- uint_fast64_t
- rand_max = RAND_MAX,
- multiplier = rand_max + 1, /* It's OK if this overflows to 0. */
+ uint_fast64_t rand_max = RAND_MAX,
+ nrand = rand_max < UINT_FAST64_MAX ? rand_max + 1 : 0,
+ rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1,
r = 0, rmax = 0;
+
do {
- uint_fast64_t rmax1 = rmax * multiplier + rand_max;
- r = r * multiplier + rand();
+ uint_fast64_t rmax1 = rmax;
+ if (rmod) {
+ /* Avoid signed integer overflow on theoretical platforms
+ where uint_fast64_t promotes to int. */
+ rmax1 %= rmod;
+ r %= rmod;
+ }
+ rmax1 = nrand * rmax1 + rand_max;
+ r = nrand * r + rand();
rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX;
} while (rmax < UINT_FAST64_MAX);
@@ -1272,7 +1317,7 @@ random_dirent(char const **name, char **namealloc)
uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
if (!dst) {
- dst = emalloc(dirlen + prefixlen + suffixlen + 1);
+ dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
memcpy(dst, src, dirlen);
memcpy(dst + dirlen, prefix, prefixlen);
dst[dirlen + prefixlen + suffixlen] = '\0';
@@ -1351,19 +1396,20 @@ rename_dest(char *tempname, char const *name)
static char *
relname(char const *target, char const *linkname)
{
- size_t i, taillen, dotdotetcsize;
- size_t dir_len = 0, dotdots = 0, linksize = SIZE_MAX;
+ size_t i, taillen, dir_len = 0, dotdots = 0;
+ ptrdiff_t dotdotetcsize, linksize = min(PTRDIFF_MAX, SIZE_MAX);
char const *f = target;
char *result = NULL;
if (*linkname == '/') {
/* Make F absolute too. */
size_t len = strlen(directory);
- bool needslash = len && directory[len - 1] != '/';
- linksize = len + needslash + strlen(target) + 1;
+ size_t lenslash = len + (len && directory[len - 1] != '/');
+ size_t targetsize = strlen(target) + 1;
+ linksize = size_sum(lenslash, targetsize);
f = result = emalloc(linksize);
- strcpy(result, directory);
+ memcpy(result, directory, len);
result[len] = '/';
- strcpy(result + len + needslash, target);
+ memcpy(result + lenslash, target, targetsize);
}
for (i = 0; f[i] && f[i] == linkname[i]; i++)
if (f[i] == '/')
@@ -1371,7 +1417,7 @@ relname(char const *target, char const *linkname)
for (; linkname[i]; i++)
dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
taillen = strlen(f + dir_len);
- dotdotetcsize = 3 * dotdots + taillen + 1;
+ dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
if (dotdotetcsize <= linksize) {
if (!result)
result = emalloc(dotdotetcsize);
@@ -1575,10 +1621,9 @@ associate(void)
/* 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.
+ Return true if successful, false if EOF.
On error, report the error and exit. */
-static ptrdiff_t
+static bool
inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
{
ptrdiff_t linelen = 0, ch;
@@ -1589,7 +1634,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
exit(EXIT_FAILURE);
}
if (linelen == 0)
- return -1;
+ return false;
error(_("unterminated line"));
exit(EXIT_FAILURE);
}
@@ -1604,7 +1649,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
}
}
buf[linelen] = '\0';
- return linelen;
+ return true;
}
static void
@@ -1626,13 +1671,14 @@ infile(int fnum, char const *name)
}
wantcont = false;
for (num = 1; ; ++num) {
- ptrdiff_t linelen;
- char buf[_POSIX2_LINE_MAX];
+ enum { bufsize_bound
+ = (min(INT_MAX, min(PTRDIFF_MAX, SIZE_MAX))
+ / FORMAT_LEN_GROWTH_BOUND) };
+ char buf[min(_POSIX2_LINE_MAX, bufsize_bound)];
int nfields;
char *fields[MAX_FIELDS];
eat(fnum, num);
- linelen = inputline(fp, buf, sizeof buf);
- if (linelen < 0)
+ if (!inputline(fp, buf, sizeof buf))
break;
nfields = getfields(buf, fields,
sizeof fields / sizeof *fields);
@@ -1704,15 +1750,15 @@ gethms(char const *string, char const *errstring)
default: ok = false; break;
case 8:
ok = '0' <= xr && xr <= '9';
- /* fallthrough */
+ ATTRIBUTE_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 */
+ ATTRIBUTE_FALLTHROUGH;
+ case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH;
+ case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH;
case 1: break;
}
if (!ok) {
@@ -1742,7 +1788,7 @@ getsave(char *field, bool *isdst)
{
int dst = -1;
zic_t save;
- size_t fieldlen = strlen(field);
+ ptrdiff_t fieldlen = strlen(field);
if (fieldlen != 0) {
char *ep = field + fieldlen - 1;
switch (*ep) {
@@ -1780,8 +1826,8 @@ inrule(char **fields, int nfields)
fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
fields[RF_TOD]))
return;
- r.r_name = ecpyalloc(fields[RF_NAME]);
- r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+ r.r_name = estrdup(fields[RF_NAME]);
+ r.r_abbrvar = estrdup(fields[RF_ABBRVAR]);
if (max_abbrvar_len < strlen(r.r_abbrvar))
max_abbrvar_len = strlen(r.r_abbrvar);
rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
@@ -1838,7 +1884,7 @@ inzsub(char **fields, int nfields, bool iscont)
register char * cp;
char * cp1;
struct zone z;
- size_t format_len;
+ int format_len;
register int i_stdoff, i_rule, i_format;
register int i_untilyear, i_untilmonth;
register int i_untilday, i_untiltime;
@@ -1905,9 +1951,9 @@ inzsub(char **fields, int nfields, bool iscont)
return false;
}
}
- z.z_name = iscont ? NULL : ecpyalloc(fields[ZF_NAME]);
- z.z_rule = ecpyalloc(fields[i_rule]);
- z.z_format = cp1 = ecpyalloc(fields[i_format]);
+ z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]);
+ z.z_rule = estrdup(fields[i_rule]);
+ z.z_format = cp1 = estrdup(fields[i_format]);
if (z.z_format_specifier == 'z') {
cp1[cp - fields[i_format]] = 's';
if (noise)
@@ -1924,7 +1970,7 @@ inzsub(char **fields, int nfields, bool iscont)
}
static zic_t
-getleapdatetime(char **fields, int nfields, bool expire_line)
+getleapdatetime(char **fields, bool expire_line)
{
register const char * cp;
register const struct lookup * lp;
@@ -2002,7 +2048,7 @@ inleap(char **fields, int nfields)
if (nfields != LEAP_FIELDS)
error(_("wrong number of fields on Leap line"));
else {
- zic_t t = getleapdatetime(fields, nfields, false);
+ zic_t t = getleapdatetime(fields, false);
if (0 <= t) {
struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
if (!lp)
@@ -2030,7 +2076,7 @@ inexpires(char **fields, int nfields)
else if (0 <= leapexpires)
error(_("multiple Expires lines"));
else
- leapexpires = getleapdatetime(fields, nfields, true);
+ leapexpires = getleapdatetime(fields, true);
}
static void
@@ -2050,8 +2096,8 @@ inlink(char **fields, int nfields)
return;
l.l_filenum = filenum;
l.l_linenum = linenum;
- l.l_target = ecpyalloc(fields[LF_TARGET]);
- l.l_linkname = ecpyalloc(fields[LF_LINKNAME]);
+ l.l_target = estrdup(fields[LF_TARGET]);
+ l.l_linkname = estrdup(fields[LF_LINKNAME]);
links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
links[nlinks++] = l;
}
@@ -2074,7 +2120,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
rp->r_month = lp->l_value;
rp->r_todisstd = false;
rp->r_todisut = false;
- dp = ecpyalloc(timep);
+ dp = estrdup(timep);
if (*dp != '\0') {
ep = dp + strlen(dp) - 1;
switch (lowerit(*ep)) {
@@ -2153,7 +2199,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
** Sun<=20
** Sun>=7
*/
- dp = ecpyalloc(dayp);
+ dp = estrdup(dayp);
if ((lp = byword(dp, lasts)) != NULL) {
rp->r_dycode = DC_DOWLEQ;
rp->r_wday = lp->l_value;
@@ -2216,7 +2262,7 @@ convert64(uint_fast64_t val, char *buf)
}
static void
-puttzcode(const int_fast32_t val, FILE *const fp)
+puttzcode(zic_t val, FILE *fp)
{
char buf[4];
@@ -2305,8 +2351,10 @@ writezone(const char *const name, const char *const string, char version,
char const *outname = name;
/* Allocate the ATS and TYPES arrays via a single malloc,
- as this is a bit faster. */
- zic_t *ats = emalloc(align_to(size_product(timecnt, sizeof *ats + 1),
+ as this is a bit faster. Do not malloc(0) if !timecnt,
+ as that might return NULL even on success. */
+ zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt,
+ sizeof *ats + 1),
alignof(zic_t)));
void *typesptr = ats + timecnt;
unsigned char *types = typesptr;
@@ -2739,13 +2787,13 @@ abbroffset(char *buf, zic_t offset)
static char const disable_percent_s[] = "";
-static size_t
+static ptrdiff_t
doabbr(char *abbr, struct zone const *zp, char const *letters,
bool isdst, zic_t save, bool doquotes)
{
register char * cp;
register char * slashp;
- register size_t len;
+ ptrdiff_t len;
char const *format = zp->z_format;
slashp = strchr(format, '/');
@@ -2911,9 +2959,9 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
register ptrdiff_t i;
register int compat = 0;
register int c;
- size_t len;
int offsetlen;
struct rule stdr, dstr;
+ ptrdiff_t len;
int dstcmp;
struct rule *lastrp[2] = { NULL, NULL };
struct zone zstr[2];
@@ -3046,8 +3094,10 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
check_for_signal();
+ /* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND. */
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
+
startbuf = emalloc(max_abbr_len + 1);
ab = emalloc(max_abbr_len + 1);
envvar = emalloc(max_envvar_len + 1);
@@ -3547,7 +3597,7 @@ lowerit(char a)
}
/* case-insensitive equality */
-static ATTRIBUTE_PURE bool
+static ATTRIBUTE_REPRODUCIBLE bool
ciequal(register const char *ap, register const char *bp)
{
while (lowerit(*ap) == lowerit(*bp++))
@@ -3556,7 +3606,7 @@ ciequal(register const char *ap, register const char *bp)
return false;
}
-static ATTRIBUTE_PURE bool
+static ATTRIBUTE_REPRODUCIBLE bool
itsabbr(register const char *abbr, register const char *word)
{
if (lowerit(*abbr) != lowerit(*word))
@@ -3572,7 +3622,7 @@ itsabbr(register const char *abbr, register const char *word)
/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
-static ATTRIBUTE_PURE bool
+static ATTRIBUTE_REPRODUCIBLE bool
ciprefix(char const *abbr, char const *word)
{
do
@@ -3675,38 +3725,41 @@ getfields(char *cp, char **array, int arrayelts)
return nsubs;
}
-static _Noreturn void
+static ATTRIBUTE_NORETURN void
time_overflow(void)
{
error(_("time overflow"));
exit(EXIT_FAILURE);
}
-static ATTRIBUTE_PURE zic_t
+static ATTRIBUTE_REPRODUCIBLE zic_t
oadd(zic_t t1, zic_t t2)
{
- if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2)
- time_overflow();
- return t1 + t2;
+#ifdef ckd_add
+ zic_t sum;
+ if (!ckd_add(&sum, t1, t2))
+ return sum;
+#else
+ if (t1 < 0 ? ZIC_MIN - t1 <= t2 : t2 <= ZIC_MAX - t1)
+ return t1 + t2;
+#endif
+ time_overflow();
}
-static ATTRIBUTE_PURE zic_t
+static ATTRIBUTE_REPRODUCIBLE zic_t
tadd(zic_t t1, zic_t t2)
{
- if (t1 < 0) {
- if (t2 < min_time - t1) {
- if (t1 != min_time)
- time_overflow();
- return min_time;
- }
- } else {
- if (max_time - t1 < t2) {
- if (t1 != max_time)
- time_overflow();
- return max_time;
- }
- }
- return t1 + t2;
+#ifdef ckd_add
+ zic_t sum;
+ if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time)
+ return sum;
+#else
+ if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1)
+ return t1 + t2;
+#endif
+ if (t1 == min_time || t1 == max_time)
+ return t1;
+ time_overflow();
}
/*
@@ -3830,10 +3883,8 @@ mp = _("time zone abbreviation differs from POSIX standard");
static void
mkdirs(char const *argname, bool ancestors)
{
- register char * name;
- register char * cp;
-
- cp = name = ecpyalloc(argname);
+ char *name = estrdup(argname);
+ char *cp = name;
/* On MS-Windows systems, do not worry about drive letters or
backslashes, as this should suffice in practice. Time zone