From fa7b09b795eadc8680fefba64f06cd9b3015ed07 Mon Sep 17 00:00:00 2001 From: christos Date: Sun, 11 Dec 2022 17:57:23 +0000 Subject: [PATCH] Merge in 2022g: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 to overflow checking by hand, as the latter has had obscure bugs. --- lib/libc/time/Makefile | 55 ++++--- lib/libc/time/NEWS | 89 ++++++++++- lib/libc/time/ctime.3 | 7 +- lib/libc/time/localtime.c | 102 ++++++++++--- lib/libc/time/private.h | 203 ++++++++++++++++++------- lib/libc/time/strftime.c | 25 +++- lib/libc/time/theory.html | 1 - lib/libc/time/time2posix.3 | 18 +-- lib/libc/time/tzfile.5 | 8 +- lib/libc/time/tzselect.8 | 8 +- lib/libc/time/tzset.3 | 6 +- lib/libc/time/version | 2 +- lib/libc/time/zdump.8 | 9 +- lib/libc/time/zdump.c | 111 ++++++++------ lib/libc/time/zic.8 | 9 +- lib/libc/time/zic.c | 300 ++++++++++++++++++++++--------------- 16 files changed, 645 insertions(+), 308 deletions(-) diff --git a/lib/libc/time/Makefile b/lib/libc/time/Makefile index 34cec49d3af4..afb9d538a203 100644 --- a/lib/libc/time/Makefile +++ b/lib/libc/time/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 does not declare asctime_r # -DHAVE_DECL_ENVIRON if declares 'environ' +# -DHAVE_DECL_TIMEGM=0 if does not declare timegm # -DHAVE_DIRECT_H if mkdir needs (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 +# -DHAVE_INTTYPES_H=0 if 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 * +# -DHAVE_STDCKDINT_H=0 if neither nor substitutes like +# __builtin_add_overflow work* +# -DHAVE_STDINT_H=0 if does not work* # -DHAVE_STRFTIME_L if 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 * +# -DHAVE_SYS_STAT_H=0 if does not work* # -DHAVE_TZSET=0 if your system lacks a tzset function -# -DHAVE_UNISTD_H=0 if your compiler lacks a * -# -DHAVE_UTMPX_H=0 if your compiler lacks a * +# -DHAVE_UNISTD_H=0 if does not work* +# -DHAVE_UTMPX_H=0 if 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/lib/libc/time/NEWS b/lib/libc/time/NEWS index 086151073a3b..701e490e4834 100644 --- a/lib/libc/time/NEWS +++ b/lib/libc/time/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 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/lib/libc/time/ctime.3 b/lib/libc/time/ctime.3 index 52335db993f1..7408064bbf6c 100644 --- a/lib/libc/time/ctime.3 +++ b/lib/libc/time/ctime.3 @@ -1,6 +1,7 @@ -.\" $NetBSD: ctime.3,v 1.68 2022/10/26 23:22:54 jschauma Exp $ +.\" $NetBSD: ctime.3,v 1.69 2022/12/11 17:57:23 christos Exp $ .\" -.\" XXX: License missing? +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. .\" .Dd October 22, 2022 .Dt CTIME 3 @@ -603,6 +604,4 @@ restricted to years in the range 1900 through 2099. To avoid this portability mess, new programs should use .Fn strftime instead. -.\" @(#)newctime.3 8.3 -.\" This file is in the public domain, so clarified as of .\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/localtime.c b/lib/libc/time/localtime.c index a5eb83113e0b..b24282156409 100644 --- a/lib/libc/time/localtime.c +++ b/lib/libc/time/localtime.c @@ -1,4 +1,4 @@ -/* $NetBSD: localtime.c,v 1.135 2022/10/29 13:55:50 christos Exp $ */ +/* $NetBSD: localtime.c,v 1.136 2022/12/11 17:57:23 christos Exp $ */ /* Convert timestamp from time_t to struct tm. */ @@ -12,7 +12,7 @@ #if 0 static char elsieid[] = "@(#)localtime.c 8.17"; #else -__RCSID("$NetBSD: localtime.c,v 1.135 2022/10/29 13:55:50 christos Exp $"); +__RCSID("$NetBSD: localtime.c,v 1.136 2022/12/11 17:57:23 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -485,8 +485,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 @@ -895,7 +894,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; @@ -916,7 +915,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; @@ -1182,13 +1181,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; dstname = NULL; /* XXX gcc */ @@ -1211,7 +1208,7 @@ tzparse(const char *name, struct state *sp, struct state *basep) if (name == NULL) return false; charcnt = stdlen + 1; - if (sizeof sp->chars < charcnt) + if ((ptrdiff_t)sizeof sp->chars < charcnt) return false; if (basep) { if (0 < basep->timecnt) @@ -1242,7 +1239,7 @@ tzparse(const char *name, struct state *sp, struct state *basep) if (!dstlen) return false; charcnt += dstlen + 1; - if (sizeof sp->chars < charcnt) + if ((ptrdiff_t)sizeof sp->chars < charcnt) return false; if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); @@ -1647,6 +1644,14 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, } 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; @@ -1658,6 +1663,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, return NULL; } result->tm_year = (int)newy; +#endif } return result; } @@ -1733,8 +1739,8 @@ localtime_r(const time_t * __restrict 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; @@ -1910,6 +1916,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 = (int)y; tmp->tm_year = signed_y - TM_YEAR_BASE; @@ -1920,6 +1932,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. @@ -2005,6 +2018,9 @@ ctime_rz(const timezone_t sp, 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; /* @@ -2017,22 +2033,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)) ...', @@ -2044,6 +2068,7 @@ increment_overflow_time(__time_t *tp, int_fast32_t j) return true; *tp += j; return false; +#endif } static bool @@ -2086,6 +2111,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 *, @@ -2110,7 +2152,8 @@ time2sub(struct tm *const tmp, struct tm yourtm, mytm; *okayp = false; - yourtm = *tmp; + mktmcpy(&yourtm, tmp); + #ifdef NO_ERROR_IN_DST_GAP again: #endif @@ -2155,14 +2198,19 @@ again: goto out_of_range; } } +#ifdef ckd_add + if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE)) + return WRONG; +#else if (increment_overflow32(&y, -TM_YEAR_BASE)) goto out_of_range; if (! (INT_MIN <= y && y <= INT_MAX)) goto out_of_range; yourtm.tm_year = (int)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 + TM_YEAR_BASE < EPOCH_YEAR) { /* ** We can't set tm_sec to 0, because that might push the ** time below the minimum representable time. @@ -2455,7 +2503,6 @@ mktime(struct tm *tmp) } #ifdef STD_INSPIRED - time_t timelocal_z(const timezone_t sp, struct tm *const tmp) { @@ -2471,13 +2518,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) { @@ -2487,7 +2530,18 @@ timeoff(struct tm *tmp, long offset) return time1(tmp, gmtsub, gmtptr, (int_fast32_t)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/lib/libc/time/private.h b/lib/libc/time/private.h index b25311a650ba..d9778ee4712b 100644 --- a/lib/libc/time/private.h +++ b/lib/libc/time/private.h @@ -1,6 +1,6 @@ /* Private header for tzdb code. */ -/* $NetBSD: private.h,v 1.63 2022/11/17 17:35:25 jakllsch Exp $ */ +/* $NetBSD: private.h,v 1.64 2022/12/11 17:57:23 christos Exp $ */ #ifndef PRIVATE_H #define PRIVATE_H @@ -29,6 +29,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 @@ -68,27 +72,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 __APPLE__ -# define HAVE_GETRANDOM false -#endif -#if !defined HAVE_GETRANDOM && defined __has_include -# if __has_include() -# 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() # define HAVE_GETTEXT true @@ -308,36 +298,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 @@ -383,24 +373,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; @@ -408,7 +401,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" @@ -419,23 +412,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() +# define HAVE_STDCKDINT_H true +# endif +#endif +#ifdef HAVE_STDCKDINT_H +# if HAVE_STDCKDINT_H +# include +# endif +#elif defined __EDG__ +/* Do nothing, to work around EDG bug . */ +#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)) #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 _Noreturn +# 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 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 @@ -562,7 +646,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 @@ -575,9 +659,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 @@ -617,9 +716,6 @@ void tzsetwall(void); # 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 @@ -637,6 +733,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 @@ -665,10 +762,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 __restrict, time_t) ATTRIBUTE_PURE; +time_t posix2time_z(timezone_t __restrict, time_t) ATTRIBUTE_REPRODUCIBLE; # endif # if TZ_TIME_T || !defined time2posix_z -time_t time2posix_z(timezone_t __restrict, time_t) ATTRIBUTE_PURE; +time_t time2posix_z(timezone_t __restrict, time_t) ATTRIBUTE_REPRODUCIBLE; # endif # endif #endif diff --git a/lib/libc/time/strftime.c b/lib/libc/time/strftime.c index 8a9bad1eb6d0..a9d2584b5984 100644 --- a/lib/libc/time/strftime.c +++ b/lib/libc/time/strftime.c @@ -1,4 +1,4 @@ -/* $NetBSD: strftime.c,v 1.50 2022/08/16 10:56:21 christos Exp $ */ +/* $NetBSD: strftime.c,v 1.51 2022/12/11 17:57:23 christos Exp $ */ /* Convert a broken-down timestamp to a string. */ @@ -35,7 +35,7 @@ static char elsieid[] = "@(#)strftime.c 7.64"; static char elsieid[] = "@(#)strftime.c 8.3"; #else -__RCSID("$NetBSD: strftime.c,v 1.50 2022/08/16 10:56:21 christos Exp $"); +__RCSID("$NetBSD: strftime.c,v 1.51 2022/12/11 17:57:23 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -135,7 +135,7 @@ strftime_z(const timezone_t sp, char * __restrict s, size_t maxsize, #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); @@ -374,12 +374,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_z(sp, &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. */ /* CONSTCOND */ if (TYPE_SIGNED(time_t)) { intmax_t n = mkt; diff --git a/lib/libc/time/theory.html b/lib/libc/time/theory.html index 298db866b39c..75e347f0f9d1 100644 --- a/lib/libc/time/theory.html +++ b/lib/libc/time/theory.html @@ -60,7 +60,6 @@ with current and future timestamps in the traditional North American mountain time zone can choose from the timezones America/Denver which observes US-style daylight saving time (DST), -America/Mazatlan which observes Mexican-style DST, and America/Phoenix which does not observe DST. Applications that also deal with past timestamps in the mountain time zone can choose from over a dozen timezones, such as diff --git a/lib/libc/time/time2posix.3 b/lib/libc/time/time2posix.3 index 928af0f621e1..0a0a32e17305 100644 --- a/lib/libc/time/time2posix.3 +++ b/lib/libc/time/time2posix.3 @@ -1,4 +1,7 @@ -.\" $NetBSD: time2posix.3,v 1.22 2017/10/29 06:07:48 abhinav Exp $ +.\" $NetBSD: time2posix.3,v 1.23 2022/12/11 17:57:23 christos Exp $ +.\" @(#)time2posix.3 7.7 +.\" This file is in the public domain, so clarified as of +.\" 1996-06-05 by Arthur David Olson. .Dd October 6, 2014 .Dt TIME2POSIX 3 .Os @@ -63,8 +66,8 @@ However, POSIX gives an arithmetic expression for directly computing a value from a given date/time, and the same relationship is assumed by some (usually older) applications. Any programs creating/dissecting -.Va time_t Ns 's -using such a relationship will typically not handle intervals over +.Va time_t +values using such a relationship will typically not handle intervals over leap seconds correctly. .Pp The @@ -132,10 +135,10 @@ A leap second deletion would look like... [Note: posix2time(B+1) => A+0 or A+1] .Pp If leap-second support is not enabled, local -.Va time_t Ns 's +.Va time_t and POSIX -.Va time_t Ns 's -are equivalent, and both +.Va time_t +values are equivalent, and both .Fn time2posix and .Fn posix2time @@ -149,6 +152,3 @@ degenerate to the identity function. .Xr mktime_z 3 , .Xr time 3 , .Xr tzalloc 3 -.\" @(#)time2posix.3 7.7 -.\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson. diff --git a/lib/libc/time/tzfile.5 b/lib/libc/time/tzfile.5 index 515d19dd1d6b..20e03b41f68a 100644 --- a/lib/libc/time/tzfile.5 +++ b/lib/libc/time/tzfile.5 @@ -1,7 +1,8 @@ -.\" $NetBSD: tzfile.5,v 1.32 2022/08/16 11:07:40 christos Exp $ +.\" $NetBSD: tzfile.5,v 1.33 2022/12/11 17:57:23 christos Exp $ .\" +.\" @(#)tzfile.5 8.3 .\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +.\" 2009-05-17 by Arthur David Olson. .Dd August 16, 2022 .Dt TZFILE 5 .Os @@ -514,6 +515,3 @@ Future changes to the format may append more data. .%U https://doi.org/10.17487/RFC8536 .%R RFC 8536 .Re -.\" @(#)tzfile.5 8.3 -.\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson. diff --git a/lib/libc/time/tzselect.8 b/lib/libc/time/tzselect.8 index 04223b3cc901..6d907dcacdd9 100644 --- a/lib/libc/time/tzselect.8 +++ b/lib/libc/time/tzselect.8 @@ -1,10 +1,12 @@ -.\" $NetBSD: tzselect.8,v 1.12 2022/08/16 11:07:40 christos Exp $ +.\" $NetBSD: tzselect.8,v 1.13 2022/12/11 17:57:23 christos Exp $ .\" +.\" 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 @@ -123,6 +125,4 @@ newctime(3), tzfile(5), zdump(8), zic(8) Applications should not assume that .BR tzselect 's output matches the user's political preferences. -.\" @(#)tzselect.8 8.2 -.\" This file is in the public domain, so clarified as of .\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/tzset.3 b/lib/libc/time/tzset.3 index c9f7aa3961ef..f5312561031a 100644 --- a/lib/libc/time/tzset.3 +++ b/lib/libc/time/tzset.3 @@ -1,4 +1,6 @@ -.\" $NetBSD: tzset.3,v 1.44 2022/12/04 11:25:09 uwe Exp $ +.\" $NetBSD: tzset.3,v 1.45 2022/12/11 17:57:23 christos Exp $ +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. .Dd Auguset 23, 2021 .Dt TZSET 3 .Os @@ -407,7 +409,7 @@ local timezone information directory .\" .It Pa /usr/share/zoneinfo/localtime .\" local timezone file .It Pa /usr/share/zoneinfo/posixrules -used with POSIX-style TZ's +used with POSIX-style TZ .It Pa /usr/share/zoneinfo/GMT for UTC leap seconds .El diff --git a/lib/libc/time/version b/lib/libc/time/version index 5c8fbb478a2c..b74fa117a223 100644 --- a/lib/libc/time/version +++ b/lib/libc/time/version @@ -1 +1 @@ -2022f +2022g diff --git a/lib/libc/time/zdump.8 b/lib/libc/time/zdump.8 index 4587c8237605..1eac839a2771 100644 --- a/lib/libc/time/zdump.8 +++ b/lib/libc/time/zdump.8 @@ -1,4 +1,8 @@ -.\" $NetBSD: zdump.8,v 1.21 2021/10/22 14:26:04 christos Exp $ +.\" $NetBSD: zdump.8,v 1.22 2022/12/11 17:57:23 christos Exp $ +.\" @(#)zdump.8 8.2 +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. +.TH zdump 8 .Dd October 22, 2021 .Dt ZDUMP 8 .Os @@ -217,6 +221,3 @@ introduction of UTC is problematic. .Xr localtime 3 , .Xr tzfile 5 , .Xr zic 8 -.\" @(#)zdump.8 8.2 -.\" This file is in the public domain, so clarified as of -.\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/zdump.c b/lib/libc/time/zdump.c index 3f4742b28c25..32308a9f64b8 100644 --- a/lib/libc/time/zdump.c +++ b/lib/libc/time/zdump.c @@ -1,4 +1,4 @@ -/* $NetBSD: zdump.c,v 1.58 2022/10/29 13:55:50 christos Exp $ */ +/* $NetBSD: zdump.c,v 1.59 2022/12/11 17:57:23 christos Exp $ */ /* Dump time zone data in a textual format. */ /* @@ -8,7 +8,7 @@ #include #ifndef lint -__RCSID("$NetBSD: zdump.c,v 1.58 2022/10/29 13:55:50 christos Exp $"); +__RCSID("$NetBSD: zdump.c,v 1.59 2022/12/11 17:57:23 christos Exp $"); #endif /* !defined lint */ #ifndef NETBSD_INSPIRED @@ -89,20 +89,20 @@ static time_t absolute_max_time = ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) : -1); static size_t 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 @@ -130,14 +130,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) - errx(EXIT_FAILURE, _("size overflow")); - return sum; +#ifdef ckd_add + ptrdiff_t sum; + if (!ckd_add(&sum, a, b) && sum <= PTRDIFF_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 @@ -237,21 +251,29 @@ 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 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. */ + ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */ - while (*e++) - continue; - initial_nenvptrs = e - environ; + 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; @@ -386,14 +408,14 @@ 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) return ab; else { size_t ablen = strlen(ab); - if (*bufalloc <= ablen) { + if (*bufalloc <= (ptrdiff_t)ablen) { free(*buf); /* Make the new buffer at least twice as long as the @@ -441,7 +463,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; int i; bool vflag; @@ -462,7 +484,7 @@ main(int argc, char *argv[]) # endif /* defined TEXTDOMAINDIR */ (void) 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) { (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); @@ -482,7 +504,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); } @@ -606,7 +628,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 +709,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 +717,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 +809,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 +881,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 +960,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 +983,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 +1012,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 +1054,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,11 +1067,11 @@ 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; + bool oversized = (ptrdiff_t)sizeof fbuf <= f_prefix_copy_size; char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf; memcpy(f_prefix_copy, f, f_prefix_len); strcpy(f_prefix_copy + f_prefix_len, "X"); @@ -1072,20 +1095,20 @@ istrftime(char *buf, size_t size, char const *time_fmt, { bool show_abbr; int offlen = format_utc_offset(b, s, tm, t); - if (! (0 <= offlen && (size_t)offlen < s)) + if (! (0 <= offlen && offlen < s)) return false; show_abbr = strcmp(b, ab) != 0; b += offlen, s -= offlen; if (show_abbr) { char const *abp; - size_t len; + ptrdiff_t len; if (s <= 1) return false; *b++ = '\t', s--; for (abp = ab; is_alpha(*abp); abp++) continue; len = (!*abp && *ab - ? (size_t)my_snprintf(b, s, "%s", ab) + ? my_snprintf(b, s, "%s", ab) : format_quoted_string(b, s, ab)); if (s <= len) return false; @@ -1117,7 +1140,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/lib/libc/time/zic.8 b/lib/libc/time/zic.8 index e02290a45d65..80c7e158d15b 100644 --- a/lib/libc/time/zic.8 +++ b/lib/libc/time/zic.8 @@ -1,4 +1,8 @@ -.\" $NetBSD: zic.8,v 1.40 2022/10/29 13:55:50 christos Exp $ +.\" $NetBSD: zic.8,v 1.41 2022/12/11 17:57:23 christos Exp $ +.\" @(#)zic.8 8.6 +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. +.TH zic 8 .Dd August 24, 2022 .Dt ZIC 8 .Os @@ -842,6 +846,3 @@ specifying transition instants using universal time. .Sh SEE ALSO .Xr tzfile 5 , .Xr zdump 8 -.\" @(#)zic.8 8.6 -.\" This file is in the public domain, so clarified as of -.\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/zic.c b/lib/libc/time/zic.c index 4ab642ce20e7..04b4717242bb 100644 --- a/lib/libc/time/zic.c +++ b/lib/libc/time/zic.c @@ -1,4 +1,4 @@ -/* $NetBSD: zic.c,v 1.85 2022/11/02 12:49:10 christos Exp $ */ +/* $NetBSD: zic.c,v 1.86 2022/12/11 17:57:23 christos Exp $ */ /* ** This file is in the public domain, so clarified as of ** 2006-07-17 by Arthur David Olson. @@ -11,7 +11,7 @@ #include #ifndef lint -__RCSID("$NetBSD: zic.c,v 1.85 2022/11/02 12:49:10 christos Exp $"); +__RCSID("$NetBSD: zic.c,v 1.86 2022/12/11 17:57:23 christos Exp $"); #endif /* !defined lint */ /* Use the system 'time' function, instead of any private replacement. @@ -31,6 +31,9 @@ __RCSID("$NetBSD: zic.c,v 1.85 2022/11/02 12:49:10 christos Exp $"); #include #include #include +#define emalloc zic_malloc +#define erealloc zic_realloc +#define estrdup zic_strdup typedef int_fast64_t zic_t; static zic_t const @@ -44,6 +47,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 # include @@ -51,7 +57,16 @@ static zic_t const # define mkdir(name, mode) _mkdir(name) #endif -#if HAVE_GETRANDOM +#ifndef HAVE_GETRANDOM +# ifdef __has_include +# if __has_include() +# include +# endif +# elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__) +# include +# endif +# define HAVE_GETRANDOM GRND_RANDOM +#elif HAVE_GETRANDOM # include #endif @@ -64,11 +79,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) @@ -462,29 +472,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) { - if (SIZE_MAX / itemsize < nitems) - memory_exhausted(_("size overflow")); - return nitems * itemsize; + memory_exhausted(_("size overflow")); } -static ATTRIBUTE_PURE size_t -align_to(size_t size, size_t alignment) +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t +size_sum(size_t a, size_t b) { - size_t aligned_size = size + alignment - 1; - aligned_size -= aligned_size % alignment; - if (aligned_size < size) - memory_exhausted(_("alignment overflow")); - return aligned_size; +#ifdef ckd_add + ptrdiff_t sum; + if (!ckd_add(&sum, a, b) && sum <= PTRDIFF_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) +{ +#ifdef ckd_mul + ptrdiff_t product; + if (!ckd_mul(&product, nitems, itemsize) && product <= PTRDIFF_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_REPRODUCIBLE ptrdiff_t +align_to(ptrdiff_t size, ptrdiff_t alignment) +{ + ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits); + return sum & ~lo_bits; } #if !HAVE_STRDUP @@ -505,35 +540,49 @@ memcheck(void *ptr) } static void * ATTRIBUTE_MALLOC -zic_malloc(size_t size) +emalloc(size_t size) { return memcheck(malloc(size)); } static void * -zic_realloc(void *ptr, size_t size) +erealloc(void *ptr, size_t size) { return memcheck(realloc(ptr, size)); } static char * ATTRIBUTE_MALLOC -ecpyalloc(char const *str) +estrdup(char const *str) { return memcheck(strdup(str)); } -static void * -growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc) +static ptrdiff_t +grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize) { - 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 zic_realloc(ptr, size_product(*nitems_alloc, 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 <= PTRDIFF_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) +{ + return (nitems < *nitems_alloc + ? ptr + : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize))); } /* @@ -630,7 +679,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, @@ -953,7 +1002,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")); @@ -1214,21 +1263,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; } } @@ -1237,13 +1277,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); @@ -1285,7 +1333,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'; @@ -1364,19 +1412,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] == '/') @@ -1384,7 +1433,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); @@ -1588,10 +1637,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; @@ -1602,7 +1650,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); } @@ -1617,7 +1665,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize) } } buf[linelen] = '\0'; - return linelen; + return true; } static void @@ -1639,13 +1687,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); @@ -1717,15 +1766,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) { @@ -1755,7 +1804,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) { @@ -1793,8 +1842,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); @@ -1851,7 +1900,7 @@ inzsub(char **fields, int nfields, bool iscont) char * cp; char * cp1; struct zone z; - size_t format_len; + int format_len; int i_stdoff, i_rule, i_format; int i_untilyear, i_untilmonth; int i_untilday, i_untiltime; @@ -1888,7 +1937,7 @@ inzsub(char **fields, int nfields, bool iscont) } z.z_format_specifier = cp ? *cp : '\0'; format_len = strlen(fields[i_format]); - if (max_format_len < format_len) + if ((ptrdiff_t)max_format_len < format_len) max_format_len = format_len; hasuntil = nfields > i_untilyear; if (hasuntil) { @@ -1918,9 +1967,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) @@ -1937,7 +1986,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) { const char * cp; const struct lookup * lp; @@ -2015,7 +2064,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) @@ -2043,7 +2092,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 @@ -2063,8 +2112,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; } @@ -2087,7 +2136,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)) { @@ -2166,7 +2215,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; @@ -2229,7 +2278,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]; @@ -2318,8 +2367,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; @@ -2752,13 +2803,13 @@ abbroffset(char *buf, zic_t offset) static char const disable_percent_s[] = ""; -static size_t +static ptrdiff_t doabbr(char *abbr, size_t abbrlen, struct zone const *zp, const char *letters, bool isdst, zic_t save, bool doquotes) { char * cp; char * slashp; - size_t len; + ptrdiff_t len; char const *format = zp->z_format; slashp = strchr(format, '/'); @@ -2928,9 +2979,9 @@ stringzone(char *result, int resultlen, const struct zone *const zpfirst, ptrdiff_t i; int compat = 0; 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]; @@ -3064,8 +3115,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 = zic_malloc(max_abbr_len + 1); ab = zic_malloc(max_abbr_len + 1); envvar = zic_malloc(max_envvar_len + 1); @@ -3572,7 +3625,7 @@ lowerit(char a) } /* case-insensitive equality */ -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool ciequal(const char *ap, const char *bp) { while (lowerit(*ap) == lowerit(*bp++)) @@ -3581,7 +3634,7 @@ ciequal(const char *ap, const char *bp) return false; } -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool itsabbr(const char *abbr, const char *word) { if (lowerit(*abbr) != lowerit(*word)) @@ -3597,7 +3650,7 @@ itsabbr(const char *abbr, 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 @@ -3700,38 +3753,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(); } /* @@ -3855,10 +3911,8 @@ mp = _("time zone abbreviation differs from POSIX standard"); static void mkdirs(char const *argname, bool ancestors) { - char * name; - 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