Import tzcode 2022g

This commit is contained in:
Dag-Erling Smørgrav 2022-12-14 01:43:56 +01:00
parent cc16bfc34e
commit 85639444f4
24 changed files with 670 additions and 334 deletions

View File

@ -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

89
NEWS
View File

@ -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.

8
date.1
View File

@ -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.

View File

@ -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)

44
date.c
View File

@ -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);

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

202
private.h
View File

@ -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 _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
@ -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

View File

@ -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 @@ _fmt(const char *format, const struct tm *t, char *pt,
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);

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -1 +1 @@
2022f
2022g

12
zdump.8
View File

@ -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.

View File

@ -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)

101
zdump.c
View File

@ -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. */
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;
@ -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)) {

12
zic.8
View File

@ -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.

View File

@ -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)

287
zic.c
View File

@ -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)
{
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 <= 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)
{
#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_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
@ -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 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 erealloc(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 <= 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, ptrdiff_t itemsize, ptrdiff_t nitems,
ptrdiff_t *nitems_alloc)
{
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