Import tzcode 2022f
This commit is contained in:
parent
5644220527
commit
cc16bfc34e
40
Makefile
40
Makefile
@ -210,7 +210,8 @@ LDLIBS=
|
||||
# -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ'
|
||||
# -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows)
|
||||
# -DHAVE_GENERIC=0 if _Generic does not work
|
||||
# -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)
|
||||
# -DHAVE_GETRANDOM if getgrandom works (e.g., GNU/Linux)*
|
||||
# -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)*
|
||||
# -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).
|
||||
@ -222,16 +223,17 @@ LDLIBS=
|
||||
# -DHAVE_MALLOC_ERRNO=0 if malloc etc. do not set errno on failure.
|
||||
# -DHAVE_POSIX_DECLS=0 if your system's include files do not declare
|
||||
# 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_STDBOOL_H if you have a non-C99 compiler with <stdbool.h>
|
||||
# -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h>
|
||||
# -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h>*
|
||||
# -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 your compiler lacks a <sys/stat.h>*
|
||||
# -DHAVE_TZSET=0 if your system lacks a tzset function
|
||||
# -DHAVE_UNISTD_H=0 if your compiler lacks a <unistd.h>
|
||||
# -DHAVE_UNISTD_H=0 if your compiler lacks a <unistd.h>*
|
||||
# -DHAVE_UTMPX_H=0 if your compiler lacks a <utmpx.h>*
|
||||
# -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'.
|
||||
@ -254,14 +256,17 @@ LDLIBS=
|
||||
# Also set TZDOBJS=zdump.o and CHECK_TIME_T_ALTERNATIVES= below.
|
||||
# -DZIC_BLOAT_DEFAULT=\"fat\" to default zic's -b option to "fat", and
|
||||
# similarly for "slim". Fat TZif files work around incompatibilities
|
||||
# and bugs in some TZif readers, notably readers that mishandle 64-bit
|
||||
# data in TZif files. Slim TZif files are more efficient and do not
|
||||
# work around these incompatibilities and bugs. If not given, the
|
||||
# default is "slim".
|
||||
# and bugs in some TZif readers, notably older ones that
|
||||
# ignore or otherwise mishandle 64-bit data in TZif files;
|
||||
# however, fat TZif files may trigger bugs in newer TZif readers.
|
||||
# Slim TZif files are more efficient, and are the default.
|
||||
# -DZIC_MAX_ABBR_LEN_WO_WARN=3
|
||||
# (or some other number) to set the maximum time zone abbreviation length
|
||||
# that zic will accept without a warning (the default is 6)
|
||||
# $(GCC_DEBUG_FLAGS) if you are using recent GCC and want lots of checking
|
||||
#
|
||||
# * Options marked "*" can be omitted if your compiler is C23 compatible.
|
||||
#
|
||||
# Select instrumentation via "make GCC_INSTRUMENT='whatever'".
|
||||
GCC_INSTRUMENT = \
|
||||
-fsanitize=undefined -fsanitize-address-use-after-scope \
|
||||
@ -397,8 +402,9 @@ ZIC= $(zic) $(ZFLAGS)
|
||||
|
||||
# To shrink the size of installed TZif files,
|
||||
# append "-r @N" to omit data before N-seconds-after-the-Epoch.
|
||||
# To grow the files and work around older application bugs, append "-b fat";
|
||||
# see ZIC_BLOAT_DEFAULT above.
|
||||
# To grow the files and work around bugs in older applications,
|
||||
# possibly at the expense of introducing bugs in newer ones,
|
||||
# append "-b fat"; see ZIC_BLOAT_DEFAULT above.
|
||||
# See the zic man page for more about -b and -r.
|
||||
ZFLAGS=
|
||||
|
||||
@ -818,13 +824,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/ {print $$3}' backward | LC_ALL=C sort -cu
|
||||
$(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
|
||||
$(AWK) -f checklinks.awk $(TDATA_TO_CHECK)
|
||||
$(AWK) -f checklinks.awk tzdata.zi
|
||||
$(AWK) \
|
||||
-v DATAFORM=$(DATAFORM) \
|
||||
-v backcheck=backward \
|
||||
-f checklinks.awk $(TDATA_TO_CHECK)
|
||||
$(AWK) \
|
||||
-v DATAFORM=$(DATAFORM) \
|
||||
-f checklinks.awk tzdata.zi
|
||||
touch $@
|
||||
|
||||
check_tables: checktab.awk $(YDATA) backward $(ZONETABLES)
|
||||
|
101
NEWS
101
NEWS
@ -1,5 +1,106 @@
|
||||
News for the tz database
|
||||
|
||||
Release 2022f - 2022-10-28 18:04:57 -0700
|
||||
|
||||
Briefly:
|
||||
Mexico will no longer observe DST except near the US border.
|
||||
Chihuahua moves to year-round -06 on 2022-10-30.
|
||||
Fiji no longer observes DST.
|
||||
Move links to 'backward'.
|
||||
In vanguard form, GMT is now a Zone and Etc/GMT a link.
|
||||
zic now supports links to links, and vanguard form uses this.
|
||||
Simplify four Ontario zones.
|
||||
Fix a Y2438 bug when reading TZif data.
|
||||
Enable 64-bit time_t on 32-bit glibc platforms.
|
||||
Omit large-file support when no longer needed.
|
||||
In C code, use some C23 features if available.
|
||||
Remove no-longer-needed workaround for Qt bug 53071.
|
||||
|
||||
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.
|
||||
On 2022-10-30 at 02:00 the Mexican state of Chihuahua moves
|
||||
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.
|
||||
|
||||
Fiji will not observe DST in 2022/3. (Thanks to Shalvin Narayan.)
|
||||
For now, assume DST is suspended indefinitely.
|
||||
|
||||
Changes to data
|
||||
|
||||
Move links to 'backward' to ease and simplify link maintenance.
|
||||
This affects generated data only if you use 'make BACKWARD='.
|
||||
|
||||
GMT is now a Zone and Etc/GMT a link instead of vice versa,
|
||||
as GMT is needed for leap second support whereas Etc/GMT is not.
|
||||
However, this change exposes a bug in TZUpdater 2.3.2 so it is
|
||||
present only in vanguard form for now.
|
||||
|
||||
Vanguard form now uses links to links, as zic now supports this.
|
||||
|
||||
Changes to past timestamps
|
||||
|
||||
Simplify four Ontario zones, as most of the post-1970 differences
|
||||
seem to have been imaginary. (Problem reported by Chris Walton.)
|
||||
Move America/Nipigon, America/Rainy_River, and America/Thunder_Bay
|
||||
to 'backzone'; backward-compatibility links still work, albeit
|
||||
with some different timestamps before November 2005.
|
||||
|
||||
Changes to code
|
||||
|
||||
zic now supports links to links regardless of input line order.
|
||||
For example, if Australia/Sydney is a Zone, the lines
|
||||
Link Australia/Canberra Australia/ACT
|
||||
Link Australia/Sydney Australia/Canberra
|
||||
now work correctly, even though the shell commands
|
||||
ln Australia/Canberra Australia/ACT
|
||||
ln Australia/Sydney Australia/Canberra
|
||||
would fail because the first command attempts to use a link
|
||||
Australia/Canberra that does not exist until after the second
|
||||
command is executed. Previously, zic had unspecified behavior if
|
||||
a Link line's target was another link, and zic often misbehaved if
|
||||
a Link line's target was a later Link line.
|
||||
|
||||
Fix line number in zic's diagnostic for a link to a link.
|
||||
|
||||
Fix a bug that caused localtime to mishandle timestamps starting
|
||||
in the year 2438 when reading data generated by 'zic -b fat' when
|
||||
distant-future DST transitions occur at times given in standard
|
||||
time or in UT, not the usual case of local time. This occurs when
|
||||
the corresponding .zi Rule lines specify DST transitions with TO
|
||||
columns of 'max' and AT columns that end in 's' or 'u'. The
|
||||
number 2438 comes from the 32-bit limit in the year 2038, plus the
|
||||
400-year Gregorian cycle. (Problem reported by Bradley White.)
|
||||
|
||||
On glibc 2.34 and later, which optionally supports 64-bit time_t
|
||||
on platforms like x86 where time_t was traditionally 32 bits,
|
||||
default time_t to 64 instead of 32 bits. This lets functions like
|
||||
localtime support timestamps after the year 2038, and fixes
|
||||
year-2038 problems in zic when accessing files dated after 2038.
|
||||
To continue to limit time_t to 32 bits on these platforms, use
|
||||
"make CFLAGS='-D_TIME_BITS=32'".
|
||||
|
||||
In C code, do not enable large-file support on platforms like AIX
|
||||
and macOS that no longer need it now that tzcode does not use
|
||||
off_t or related functions like 'stat'. Large-file support is
|
||||
still enabled by default on GNU/Linux, as it is needed for 64-bit
|
||||
time_t support.
|
||||
|
||||
In C code, prefer C23 keywords to pre-C23 macros for alignof,
|
||||
bool, false, and true. Also, use the following C23 features if
|
||||
available: __has_include, unreachable.
|
||||
|
||||
zic no longer works around Qt bug 53071, as the relevant Qt
|
||||
releases have been out of support since 2019. This change affects
|
||||
only fat TZif files, as thin files never had the workaround.
|
||||
|
||||
zdump no longer modifies the environ vector when compiled on
|
||||
platforms lacking tm_zone or when compiled with -DUSE_LTZ=0.
|
||||
This avoid undefined behavior on POSIX platforms.
|
||||
|
||||
|
||||
Release 2022e - 2022-10-11 11:13:02 -0700
|
||||
|
||||
Briefly:
|
||||
|
@ -802,12 +802,14 @@ typesequiv(const struct state *sp, int a, int b)
|
||||
b < 0 || b >= sp->typecnt)
|
||||
result = false;
|
||||
else {
|
||||
/* Compare the relevant members of *AP and *BP.
|
||||
Ignore tt_ttisstd and tt_ttisut, as they are
|
||||
irrelevant now and counting them could cause
|
||||
sp->goahead to mistakenly remain false. */
|
||||
register const struct ttinfo * ap = &sp->ttis[a];
|
||||
register const struct ttinfo * bp = &sp->ttis[b];
|
||||
result = (ap->tt_utoff == bp->tt_utoff
|
||||
&& ap->tt_isdst == bp->tt_isdst
|
||||
&& ap->tt_ttisstd == bp->tt_ttisstd
|
||||
&& ap->tt_ttisut == bp->tt_ttisut
|
||||
&& (strcmp(&sp->chars[ap->tt_desigidx],
|
||||
&sp->chars[bp->tt_desigidx])
|
||||
== 0));
|
||||
@ -1096,7 +1098,7 @@ transtime(const int year, register const struct rule *const rulep,
|
||||
value += mon_lengths[leapyear][i] * SECSPERDAY;
|
||||
break;
|
||||
|
||||
default: UNREACHABLE();
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
/*
|
||||
|
110
private.h
110
private.h
@ -17,6 +17,15 @@
|
||||
** Thank you!
|
||||
*/
|
||||
|
||||
/* Define true, false and bool if they don't work out of the box. */
|
||||
#if __STDC_VERSION__ < 199901
|
||||
# define true 1
|
||||
# define false 0
|
||||
# define bool int
|
||||
#elif __STDC_VERSION__ < 202311
|
||||
# include <stdbool.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** zdump has been made independent of the rest of the time
|
||||
** conversion package to increase confidence in the verification it provides.
|
||||
@ -54,9 +63,25 @@
|
||||
# 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
|
||||
# endif
|
||||
#endif
|
||||
#ifndef HAVE_GETTEXT
|
||||
# define HAVE_GETTEXT 0
|
||||
#endif /* !defined HAVE_GETTEXT */
|
||||
# define HAVE_GETTEXT false
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_INCOMPATIBLE_CTIME_R
|
||||
# define HAVE_INCOMPATIBLE_CTIME_R 0
|
||||
@ -74,8 +99,8 @@
|
||||
# define HAVE_POSIX_DECLS 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STDBOOL_H
|
||||
# define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__)
|
||||
#ifndef HAVE_SETENV
|
||||
# define HAVE_SETENV 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
@ -90,17 +115,23 @@
|
||||
# define HAVE_SYMLINK 1
|
||||
#endif /* !defined HAVE_SYMLINK */
|
||||
|
||||
#if !defined HAVE_SYS_STAT_H && defined __has_include
|
||||
# if !__has_include(<sys/stat.h>)
|
||||
# define HAVE_SYS_STAT_H false
|
||||
# endif
|
||||
#endif
|
||||
#ifndef HAVE_SYS_STAT_H
|
||||
# define HAVE_SYS_STAT_H 1
|
||||
#endif /* !defined HAVE_SYS_STAT_H */
|
||||
# define HAVE_SYS_STAT_H true
|
||||
#endif
|
||||
|
||||
#if !defined HAVE_UNISTD_H && defined __has_include
|
||||
# if !__has_include(<unistd.h>)
|
||||
# define HAVE_UNISTD_H false
|
||||
# endif
|
||||
#endif
|
||||
#ifndef HAVE_UNISTD_H
|
||||
# define HAVE_UNISTD_H 1
|
||||
#endif /* !defined HAVE_UNISTD_H */
|
||||
|
||||
#ifndef HAVE_UTMPX_H
|
||||
# define HAVE_UTMPX_H 1
|
||||
#endif /* !defined HAVE_UTMPX_H */
|
||||
# define HAVE_UNISTD_H true
|
||||
#endif
|
||||
|
||||
#ifndef NETBSD_INSPIRED
|
||||
# define NETBSD_INSPIRED 1
|
||||
@ -118,15 +149,17 @@
|
||||
/* Enable strtoimax on pre-C99 Solaris 11. */
|
||||
#define __EXTENSIONS__ 1
|
||||
|
||||
/* To avoid having 'stat' fail unnecessarily with errno == EOVERFLOW,
|
||||
enable large files on GNUish systems ... */
|
||||
/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
|
||||
On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
|
||||
setting _TIME_BITS to 64 does not work. The code does not
|
||||
otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
|
||||
use off_t or functions like 'stat' that depend on off_t. */
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
/* ... and on AIX ... */
|
||||
#define _LARGE_FILES 1
|
||||
/* ... and enable large inode numbers on Mac OS X 10.5 and later. */
|
||||
#define _DARWIN_USE_64_BIT_INODE 1
|
||||
#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
|
||||
# define _TIME_BITS 64
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Nested includes
|
||||
@ -157,7 +190,7 @@
|
||||
#undef tzalloc
|
||||
#undef tzfree
|
||||
|
||||
#include <sys/types.h> /* for time_t */
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <limits.h> /* for CHAR_BIT et al. */
|
||||
#include <stdlib.h>
|
||||
@ -234,6 +267,9 @@
|
||||
** previously-included files. glibc 2.1 and Solaris 10 and later have
|
||||
** stdint.h, even with pre-C99 compilers.
|
||||
*/
|
||||
#if !defined HAVE_STDINT_H && defined __has_include
|
||||
# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
|
||||
#endif
|
||||
#ifndef HAVE_STDINT_H
|
||||
# define HAVE_STDINT_H \
|
||||
(199901 <= __STDC_VERSION__ \
|
||||
@ -360,13 +396,6 @@ typedef unsigned long uintmax_t;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef INT32_MAX
|
||||
# define INT32_MAX 0x7fffffff
|
||||
#endif /* !defined INT32_MAX */
|
||||
#ifndef INT32_MIN
|
||||
# define INT32_MIN (-1 - INT32_MAX)
|
||||
#endif /* !defined INT32_MIN */
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX ((size_t) -1)
|
||||
#endif
|
||||
@ -623,14 +652,6 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
|
||||
** Finally, some convenience items.
|
||||
*/
|
||||
|
||||
#if HAVE_STDBOOL_H
|
||||
# include <stdbool.h>
|
||||
#else
|
||||
# define true 1
|
||||
# define false 0
|
||||
# define bool int
|
||||
#endif
|
||||
|
||||
#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
|
||||
#define TYPE_SIGNED(type) (((type) -1) < 0)
|
||||
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
|
||||
@ -708,16 +729,19 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
# define UNREACHABLE() abort()
|
||||
#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
|
||||
# define UNREACHABLE() __builtin_unreachable()
|
||||
#elif defined __has_builtin
|
||||
# if __has_builtin(__builtin_unreachable)
|
||||
# define UNREACHABLE() __builtin_unreachable()
|
||||
# undef unreachable
|
||||
# define unreachable() abort()
|
||||
#elif !defined unreachable
|
||||
# ifdef __has_builtin
|
||||
# if __has_builtin(__builtin_unreachable)
|
||||
# define unreachable() __builtin_unreachable()
|
||||
# endif
|
||||
# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
|
||||
# define unreachable() __builtin_unreachable()
|
||||
# endif
|
||||
# ifndef unreachable
|
||||
# define unreachable() ((void) 0)
|
||||
# endif
|
||||
#endif
|
||||
#ifndef UNREACHABLE
|
||||
# define UNREACHABLE() ((void) 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -329,6 +329,7 @@ still supported.
|
||||
These legacy names are mostly defined in the file
|
||||
'<code>etcetera</code>'.
|
||||
Also, the file '<code>backward</code>' defines the legacy names
|
||||
'<code>Etc/GMT0</code>', '<code>Etc/GMT-0</code>', '<code>Etc/GMT+0</code>',
|
||||
'<code>GMT0</code>', '<code>GMT-0</code>' and '<code>GMT+0</code>',
|
||||
and the file '<code>northamerica</code>' defines the legacy names
|
||||
'<code>EST5EDT</code>', '<code>CST6CDT</code>',
|
||||
|
76
zdump.c
76
zdump.c
@ -56,21 +56,6 @@
|
||||
*/
|
||||
enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
|
||||
|
||||
#if !defined HAVE_GENERIC && defined __has_extension
|
||||
# if __has_extension(c_generic_selections)
|
||||
# define HAVE_GENERIC 1
|
||||
# else
|
||||
# define HAVE_GENERIC 0
|
||||
# endif
|
||||
#endif
|
||||
/* _Generic is buggy in pre-4.9 GCC. */
|
||||
#if !defined HAVE_GENERIC && defined __GNUC__
|
||||
# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
|
||||
#endif
|
||||
#ifndef HAVE_GENERIC
|
||||
# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
|
||||
#endif
|
||||
|
||||
#if HAVE_GETTEXT
|
||||
# include <locale.h> /* for setlocale */
|
||||
#endif /* HAVE_GETTEXT */
|
||||
@ -243,33 +228,56 @@ mktime_z(timezone_t tz, struct tm *tmp)
|
||||
static timezone_t
|
||||
tzalloc(char const *val)
|
||||
{
|
||||
# if HAVE_SETENV
|
||||
if (setenv("TZ", val, 1) != 0) {
|
||||
perror("setenv");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
tzset();
|
||||
return NULL;
|
||||
# else
|
||||
enum { TZeqlen = 3 };
|
||||
static char const TZeq[TZeqlen] = "TZ=";
|
||||
static char **fakeenv;
|
||||
char **env = fakeenv;
|
||||
char *env0;
|
||||
if (! env) {
|
||||
char **e = environ;
|
||||
int to;
|
||||
static size_t fakeenv0size;
|
||||
void *freeable = NULL;
|
||||
char **env = fakeenv, **initial_environ;
|
||||
size_t valsize = strlen(val) + 1;
|
||||
if (fakeenv0size < valsize) {
|
||||
char **e = environ, **to;
|
||||
ptrdiff_t initial_nenvptrs; /* Counting the trailing NULL pointer. */
|
||||
|
||||
while (*e++)
|
||||
continue;
|
||||
env = xmalloc(sumsize(sizeof *environ,
|
||||
(e - environ) * sizeof *environ));
|
||||
to = 1;
|
||||
for (e = environ; (env[to] = *e); e++)
|
||||
to += strncmp(*e, "TZ=", 3) != 0;
|
||||
initial_nenvptrs = e - environ;
|
||||
fakeenv0size = sumsize(valsize, valsize);
|
||||
fakeenv0size = max(fakeenv0size, 64);
|
||||
freeable = env;
|
||||
fakeenv = env =
|
||||
xmalloc(sumsize(sumsize(sizeof *environ,
|
||||
initial_nenvptrs * sizeof *environ),
|
||||
sumsize(TZeqlen, fakeenv0size)));
|
||||
to = env + 1;
|
||||
for (e = environ; (*to = *e); e++)
|
||||
to += strncmp(*e, TZeq, TZeqlen) != 0;
|
||||
env[0] = memcpy(to + 1, TZeq, TZeqlen);
|
||||
}
|
||||
env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
|
||||
env[0] = strcat(strcpy(env0, "TZ="), val);
|
||||
environ = fakeenv = env;
|
||||
memcpy(env[0] + TZeqlen, val, valsize);
|
||||
initial_environ = environ;
|
||||
environ = env;
|
||||
tzset();
|
||||
return env;
|
||||
free(freeable);
|
||||
return initial_environ;
|
||||
# endif
|
||||
}
|
||||
|
||||
static void
|
||||
tzfree(timezone_t env)
|
||||
tzfree(timezone_t initial_environ)
|
||||
{
|
||||
environ = env + 1;
|
||||
free(env[0]);
|
||||
# if !HAVE_SETENV
|
||||
environ = initial_environ;
|
||||
tzset();
|
||||
# endif
|
||||
}
|
||||
#endif /* ! USE_LOCALTIME_RZ */
|
||||
|
||||
@ -281,9 +289,9 @@ gmtzinit(void)
|
||||
if (USE_LOCALTIME_RZ) {
|
||||
/* Try "GMT" first to find out whether this is one of the rare
|
||||
platforms where time_t counts leap seconds; this works due to
|
||||
the "Link Etc/GMT GMT" line in the "etcetera" file. If "GMT"
|
||||
the "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT"
|
||||
fails, fall back on "GMT0" which might be similar due to the
|
||||
"Link Etc/GMT GMT0" line in the "backward" file, and which
|
||||
"Link GMT GMT0" line in the "backward" file, and which
|
||||
should work on all POSIX platforms. The rest of zdump does not
|
||||
use the "GMT" abbreviation that comes from this setting, so it
|
||||
is OK to use "GMT" here rather than the more-modern "UTC" which
|
||||
|
29
zic.8
29
zic.8
@ -190,7 +190,10 @@ the named file rather than in the standard location.
|
||||
Be more verbose, and complain about the following situations:
|
||||
.RS
|
||||
.PP
|
||||
The input specifies a link to a link.
|
||||
The input specifies a link to a link,
|
||||
something not supported by some older parsers, including
|
||||
.B zic
|
||||
itself through release 2022e.
|
||||
.PP
|
||||
A year that appears in a data file is outside the range
|
||||
of representable years.
|
||||
@ -691,19 +694,37 @@ The
|
||||
.B TARGET
|
||||
field should appear as the
|
||||
.B NAME
|
||||
field in some zone line.
|
||||
field in some zone line or as the
|
||||
.B LINK-NAME
|
||||
field in some link line.
|
||||
The
|
||||
.B LINK-NAME
|
||||
field is used as an alternative name for that zone;
|
||||
it has the same syntax as a zone line's
|
||||
.B NAME
|
||||
field.
|
||||
Links can chain together, although the behavior is unspecified if a
|
||||
chain of one or more links does not terminate in a Zone name.
|
||||
A link line can appear before the line that defines the link target.
|
||||
For example:
|
||||
.sp
|
||||
.ne 3
|
||||
.nf
|
||||
.in +2m
|
||||
.ta \w'Zone\0\0'u +\w'Greenwich\0\0'u
|
||||
Link Greenwich G_M_T
|
||||
Link Etc/GMT Greenwich
|
||||
Zone Etc/GMT\0\00\0\0\*-\0\0GMT
|
||||
.sp
|
||||
.in
|
||||
.fi
|
||||
The two links are chained together, and G_M_T, Greenwich, and Etc/GMT
|
||||
all name the same zone.
|
||||
.PP
|
||||
Except for continuation lines,
|
||||
lines may appear in any order in the input.
|
||||
However, the behavior is unspecified if multiple zone or link lines
|
||||
define the same name, or if the source of one link line is the target
|
||||
of another.
|
||||
define the same name.
|
||||
.PP
|
||||
The file that describes leap seconds can have leap lines and an
|
||||
expiration line.
|
||||
|
23
zic.8.txt
23
zic.8.txt
@ -93,7 +93,8 @@ OPTIONS
|
||||
|
||||
-v Be more verbose, and complain about the following situations:
|
||||
|
||||
The input specifies a link to a link.
|
||||
The input specifies a link to a link, something not supported by
|
||||
some older parsers, including zic itself through release 2022e.
|
||||
|
||||
A year that appears in a data file is outside the range of
|
||||
representable years.
|
||||
@ -386,14 +387,24 @@ FILES
|
||||
|
||||
Link Europe/Istanbul Asia/Istanbul
|
||||
|
||||
The TARGET field should appear as the NAME field in some zone line.
|
||||
The LINK-NAME field is used as an alternative name for that zone; it
|
||||
has the same syntax as a zone line's NAME field.
|
||||
The TARGET field should appear as the NAME field in some zone line or
|
||||
as the LINK-NAME field in some link line. The LINK-NAME field is used
|
||||
as an alternative name for that zone; it has the same syntax as a zone
|
||||
line's NAME field. Links can chain together, although the behavior is
|
||||
unspecified if a chain of one or more links does not terminate in a
|
||||
Zone name. A link line can appear before the line that defines the
|
||||
link target. For example:
|
||||
|
||||
Link Greenwich G_M_T
|
||||
Link Etc/GMT Greenwich
|
||||
Zone Etc/GMT 0 - GMT
|
||||
|
||||
The two links are chained together, and G_M_T, Greenwich, and Etc/GMT
|
||||
all name the same zone.
|
||||
|
||||
Except for continuation lines, lines may appear in any order in the
|
||||
input. However, the behavior is unspecified if multiple zone or link
|
||||
lines define the same name, or if the source of one link line is the
|
||||
target of another.
|
||||
lines define the same name.
|
||||
|
||||
The file that describes leap seconds can have leap lines and an
|
||||
expiration line. Leap lines have the following form:
|
||||
|
445
zic.c
445
zic.c
@ -20,12 +20,14 @@
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef int_fast64_t zic_t;
|
||||
#define ZIC_MIN INT_FAST64_MIN
|
||||
#define ZIC_MAX INT_FAST64_MAX
|
||||
static zic_t const
|
||||
ZIC_MIN = INT_FAST64_MIN,
|
||||
ZIC_MAX = INT_FAST64_MAX,
|
||||
ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
|
||||
ZIC32_MAX = 0x7fffffff;
|
||||
#define SCNdZIC SCNdFAST64
|
||||
|
||||
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
|
||||
@ -39,6 +41,10 @@ typedef int_fast64_t zic_t;
|
||||
# define mkdir(name, mode) _mkdir(name)
|
||||
#endif
|
||||
|
||||
#if HAVE_GETRANDOM
|
||||
# include <sys/random.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_STAT_H
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
@ -53,9 +59,11 @@ typedef int_fast64_t zic_t;
|
||||
static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
|
||||
#endif
|
||||
|
||||
/* The minimum alignment of a type, for pre-C11 platforms. */
|
||||
/* The minimum alignment of a type, for pre-C23 platforms. */
|
||||
#if __STDC_VERSION__ < 201112
|
||||
# define _Alignof(type) offsetof(struct { char a; type b; }, b)
|
||||
# define alignof(type) offsetof(struct { char a; type b; }, b)
|
||||
#elif __STDC_VERSION__ < 202311
|
||||
# include <stdalign.h>
|
||||
#endif
|
||||
|
||||
/* The maximum length of a text line, including the trailing newline. */
|
||||
@ -69,7 +77,7 @@ static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
|
||||
typedef intmax_t lineno;
|
||||
|
||||
struct rule {
|
||||
const char * r_filename;
|
||||
int r_filenum;
|
||||
lineno r_linenum;
|
||||
const char * r_name;
|
||||
|
||||
@ -105,7 +113,7 @@ enum {
|
||||
};
|
||||
|
||||
struct zone {
|
||||
const char * z_filename;
|
||||
int z_filenum;
|
||||
lineno z_linenum;
|
||||
|
||||
const char * z_name;
|
||||
@ -132,17 +140,28 @@ extern char * optarg;
|
||||
extern int optind;
|
||||
#endif
|
||||
|
||||
#if ! HAVE_LINK
|
||||
# define link(target, linkname) (errno = ENOTSUP, -1)
|
||||
#endif
|
||||
#if ! HAVE_SYMLINK
|
||||
# define readlink(file, buf, size) (errno = ENOTSUP, -1)
|
||||
# define symlink(target, linkname) (errno = ENOTSUP, -1)
|
||||
# define S_ISLNK(m) 0
|
||||
static ssize_t
|
||||
readlink(char const *restrict file, char *restrict buf, size_t size)
|
||||
{
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
static int
|
||||
symlink(char const *target, char const *linkname)
|
||||
{
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#ifndef AT_SYMLINK_FOLLOW
|
||||
# define linkat(targetdir, target, linknamedir, linkname, flag) \
|
||||
(itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname))
|
||||
# if HAVE_LINK
|
||||
# define linkat(targetdir, target, linknamedir, linkname, flag) \
|
||||
(itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname))
|
||||
# else
|
||||
# define linkat(targetdir, target, linknamedir, linkname, flag) \
|
||||
(errno = ENOTSUP, -1)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void addtt(zic_t starttime, int type);
|
||||
@ -155,7 +174,7 @@ static int getfields(char *, char **, int);
|
||||
static zic_t gethms(const char * string, const char * errstring);
|
||||
static zic_t getsave(char *, bool *);
|
||||
static void inexpires(char **, int);
|
||||
static void infile(const char * filename);
|
||||
static void infile(int, char const *);
|
||||
static void inleap(char ** fields, int nfields);
|
||||
static void inlink(char ** fields, int nfields);
|
||||
static void inrule(char ** fields, int nfields);
|
||||
@ -179,19 +198,10 @@ static zic_t tadd(zic_t t1, zic_t t2);
|
||||
/* Bound on length of what %z can expand to. */
|
||||
enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
|
||||
|
||||
/* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles
|
||||
TZif files whose POSIX-TZ-style strings contain '<'; see
|
||||
QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>. This
|
||||
workaround will no longer be needed when Qt 5.6.1 and earlier are
|
||||
obsolete, say in the year 2021. */
|
||||
#ifndef WORK_AROUND_QTBUG_53071
|
||||
enum { WORK_AROUND_QTBUG_53071 = true };
|
||||
#endif
|
||||
|
||||
static int charcnt;
|
||||
static bool errors;
|
||||
static bool warnings;
|
||||
static const char * filename;
|
||||
static int filenum;
|
||||
static int leapcnt;
|
||||
static bool leapseen;
|
||||
static zic_t leapminyear;
|
||||
@ -202,9 +212,11 @@ static int max_format_len;
|
||||
static zic_t max_year;
|
||||
static zic_t min_year;
|
||||
static bool noise;
|
||||
static const char * rfilename;
|
||||
static int rfilenum;
|
||||
static lineno rlinenum;
|
||||
static const char * progname;
|
||||
static char const * leapsec;
|
||||
static char *const * main_argv;
|
||||
static ptrdiff_t timecnt;
|
||||
static ptrdiff_t timecnt_alloc;
|
||||
static int typecnt;
|
||||
@ -325,7 +337,7 @@ static ptrdiff_t nzones; /* number of zones */
|
||||
static ptrdiff_t nzones_alloc;
|
||||
|
||||
struct link {
|
||||
const char * l_filename;
|
||||
int l_filenum;
|
||||
lineno l_linenum;
|
||||
const char * l_target;
|
||||
const char * l_linkname;
|
||||
@ -506,8 +518,7 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
|
||||
if (nitems < *nitems_alloc)
|
||||
return ptr;
|
||||
else {
|
||||
ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
|
||||
ptrdiff_t amax = min(nitems_max, SIZE_MAX);
|
||||
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;
|
||||
@ -519,19 +530,36 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
|
||||
** Error handling.
|
||||
*/
|
||||
|
||||
static void
|
||||
eats(char const *name, lineno num, char const *rname, lineno rnum)
|
||||
/* In most of the code, an input file name is represented by its index
|
||||
into the main argument vector, except that LEAPSEC_FILENUM stands
|
||||
for leapsec and COMMAND_LINE_FILENUM stands for the command line. */
|
||||
enum { LEAPSEC_FILENUM = -2, COMMAND_LINE_FILENUM = -1 };
|
||||
|
||||
/* Return the name of the Ith input file, for diagnostics. */
|
||||
static char const *
|
||||
filename(int i)
|
||||
{
|
||||
filename = name;
|
||||
if (i == COMMAND_LINE_FILENUM)
|
||||
return _("command line");
|
||||
else {
|
||||
char const *fname = i == LEAPSEC_FILENUM ? leapsec : main_argv[i];
|
||||
return strcmp(fname, "-") == 0 ? _("standard input") : fname;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
eats(int fnum, lineno num, int rfnum, lineno rnum)
|
||||
{
|
||||
filenum = fnum;
|
||||
linenum = num;
|
||||
rfilename = rname;
|
||||
rfilenum = rfnum;
|
||||
rlinenum = rnum;
|
||||
}
|
||||
|
||||
static void
|
||||
eat(char const *name, lineno num)
|
||||
eat(int fnum, lineno num)
|
||||
{
|
||||
eats(name, num, NULL, -1);
|
||||
eats(fnum, num, 0, -1);
|
||||
}
|
||||
|
||||
static void ATTRIBUTE_FORMAT((printf, 1, 0))
|
||||
@ -542,12 +570,13 @@ verror(const char *const string, va_list args)
|
||||
** zic ... 2>&1 | error -t "*" -v
|
||||
** on BSD systems.
|
||||
*/
|
||||
if (filename)
|
||||
fprintf(stderr, _("\"%s\", line %"PRIdMAX": "), filename, linenum);
|
||||
if (filenum)
|
||||
fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
|
||||
filename(filenum), linenum);
|
||||
vfprintf(stderr, string, args);
|
||||
if (rfilename != NULL)
|
||||
if (rfilenum)
|
||||
fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
|
||||
rfilename, rlinenum);
|
||||
filename(rfilenum), rlinenum);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
@ -628,6 +657,131 @@ change_directory(char const *dir)
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare the two links A and B, for a stable sort by link name. */
|
||||
static int
|
||||
qsort_linkcmp(void const *a, void const *b)
|
||||
{
|
||||
struct link const *l = a;
|
||||
struct link const *m = b;
|
||||
int cmp = strcmp(l->l_linkname, m->l_linkname);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
/* The link names are the same. Make the sort stable by comparing
|
||||
file numbers (where subtraction cannot overflow) and possibly
|
||||
line numbers (where it can). */
|
||||
cmp = l->l_filenum - m->l_filenum;
|
||||
if (cmp)
|
||||
return cmp;
|
||||
return (l->l_linenum > m->l_linenum) - (l->l_linenum < m->l_linenum);
|
||||
}
|
||||
|
||||
/* Compare the string KEY to the link B, for bsearch. */
|
||||
static int
|
||||
bsearch_linkcmp(void const *key, void const *b)
|
||||
{
|
||||
struct link const *m = b;
|
||||
return strcmp(key, m->l_linkname);
|
||||
}
|
||||
|
||||
/* Make the links specified by the Link lines. */
|
||||
static void
|
||||
make_links(void)
|
||||
{
|
||||
ptrdiff_t i, j, nalinks, pass_size;
|
||||
if (1 < nlinks)
|
||||
qsort(links, nlinks, sizeof *links, qsort_linkcmp);
|
||||
|
||||
/* Ignore each link superseded by a later link with the same name. */
|
||||
j = 0;
|
||||
for (i = 0; i < nlinks; i++) {
|
||||
while (i + 1 < nlinks
|
||||
&& strcmp(links[i].l_linkname, links[i + 1].l_linkname) == 0)
|
||||
i++;
|
||||
links[j++] = links[i];
|
||||
}
|
||||
nlinks = pass_size = j;
|
||||
|
||||
/* Walk through the link array making links. However,
|
||||
if a link's target has not been made yet, append a copy to the
|
||||
end of the array. The end of the array will gradually fill
|
||||
up with a small sorted subsequence of not-yet-made links.
|
||||
nalinks counts all the links in the array, including copies.
|
||||
When we reach the copied subsequence, it may still contain
|
||||
a link to a not-yet-made link, so the process repeats.
|
||||
At any given point in time, the link array consists of the
|
||||
following subregions, where 0 <= i <= j <= nalinks and
|
||||
0 <= nlinks <= nalinks:
|
||||
|
||||
0 .. (i - 1):
|
||||
links that either have been made, or have been copied to a
|
||||
later point point in the array (this later point can be in
|
||||
any of the three subregions)
|
||||
i .. (j - 1):
|
||||
not-yet-made links for this pass
|
||||
j .. (nalinks - 1):
|
||||
not-yet-made links that this pass has skipped because
|
||||
they were links to not-yet-made links
|
||||
|
||||
The first subregion might not be sorted if nlinks < i;
|
||||
the other two subregions are sorted. This algorithm does
|
||||
not alter entries 0 .. (nlinks - 1), which remain sorted.
|
||||
|
||||
If there are L links, this algorithm is O(C*L*log(L)) where
|
||||
C is the length of the longest link chain. Usually C is
|
||||
short (e.g., 3) though its worst-case value is L. */
|
||||
|
||||
j = nalinks = nlinks;
|
||||
|
||||
for (i = 0; i < nalinks; i++) {
|
||||
struct link *l;
|
||||
|
||||
eat(links[i].l_filenum, links[i].l_linenum);
|
||||
|
||||
/* If this pass examined all its links, start the next pass. */
|
||||
if (i == j) {
|
||||
if (nalinks - i == pass_size) {
|
||||
error(_("\"Link %s %s\" is part of a link cycle"),
|
||||
links[i].l_target, links[i].l_linkname);
|
||||
break;
|
||||
}
|
||||
j = nalinks;
|
||||
pass_size = nalinks - i;
|
||||
}
|
||||
|
||||
/* Diagnose self links, which the cycle detection algorithm would not
|
||||
otherwise catch. */
|
||||
if (strcmp(links[i].l_target, links[i].l_linkname) == 0) {
|
||||
error(_("link %s targets itself"), links[i].l_target);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make this link unless its target has not been made yet. */
|
||||
l = bsearch(links[i].l_target, &links[i + 1], j - (i + 1),
|
||||
sizeof *links, bsearch_linkcmp);
|
||||
if (!l)
|
||||
l = bsearch(links[i].l_target, &links[j], nalinks - j,
|
||||
sizeof *links, bsearch_linkcmp);
|
||||
if (!l)
|
||||
dolink(links[i].l_target, links[i].l_linkname, false);
|
||||
else {
|
||||
/* The link target has not been made yet; copy the link to the end. */
|
||||
links = growalloc(links, sizeof *links, nalinks, &nlinks_alloc);
|
||||
links[nalinks++] = links[i];
|
||||
}
|
||||
|
||||
if (noise && i < nlinks) {
|
||||
if (l)
|
||||
warning(_("link %s targeting link %s mishandled by pre-2023 zic"),
|
||||
links[i].l_linkname, links[i].l_target);
|
||||
else if (bsearch(links[i].l_target, links, nlinks, sizeof *links,
|
||||
bsearch_linkcmp))
|
||||
warning(_("link %s targeting link %s"),
|
||||
links[i].l_linkname, links[i].l_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple signal handling: just set a flag that is checked
|
||||
periodically outside critical sections. To set up the handler,
|
||||
prefer sigaction if available to close a signal race. */
|
||||
@ -754,7 +908,6 @@ redundant_time_option(char *opt)
|
||||
static const char * psxrules;
|
||||
static const char * lcltime;
|
||||
static const char * directory;
|
||||
static const char * leapsec;
|
||||
static const char * tzdefault;
|
||||
|
||||
/* -1 if the TZif output file should be slim, 0 if default, 1 if the
|
||||
@ -789,6 +942,7 @@ main(int argc, char **argv)
|
||||
# endif /* defined TEXTDOMAINDIR */
|
||||
textdomain(TZ_DOMAIN);
|
||||
#endif /* HAVE_GETTEXT */
|
||||
main_argv = argv;
|
||||
progname = argv[0];
|
||||
if (TYPE_BIT(zic_t) < 64) {
|
||||
fprintf(stderr, "%s: %s\n", progname,
|
||||
@ -923,12 +1077,12 @@ _("%s: invalid time range: %s\n"),
|
||||
tzdefault = TZDEFAULT;
|
||||
|
||||
if (optind < argc && leapsec != NULL) {
|
||||
infile(leapsec);
|
||||
infile(LEAPSEC_FILENUM, leapsec);
|
||||
adjleap();
|
||||
}
|
||||
|
||||
for (k = optind; k < argc; k++)
|
||||
infile(argv[k]);
|
||||
infile(k, argv[k]);
|
||||
if (errors)
|
||||
return EXIT_FAILURE;
|
||||
associate();
|
||||
@ -942,24 +1096,13 @@ _("%s: invalid time range: %s\n"),
|
||||
continue;
|
||||
outzone(&zones[i], j - i);
|
||||
}
|
||||
/*
|
||||
** Make links.
|
||||
*/
|
||||
for (i = 0; i < nlinks; ++i) {
|
||||
eat(links[i].l_filename, links[i].l_linenum);
|
||||
dolink(links[i].l_target, links[i].l_linkname, false);
|
||||
if (noise)
|
||||
for (j = 0; j < nlinks; ++j)
|
||||
if (strcmp(links[i].l_linkname,
|
||||
links[j].l_target) == 0)
|
||||
warning(_("link to link"));
|
||||
}
|
||||
make_links();
|
||||
if (lcltime != NULL) {
|
||||
eat(_("command line"), 1);
|
||||
eat(COMMAND_LINE_FILENUM, 1);
|
||||
dolink(lcltime, tzdefault, true);
|
||||
}
|
||||
if (psxrules != NULL) {
|
||||
eat(_("command line"), 1);
|
||||
eat(COMMAND_LINE_FILENUM, 1);
|
||||
dolink(psxrules, TZDEFRULES, true);
|
||||
}
|
||||
if (warnings && (ferror(stderr) || fclose(stderr) != 0))
|
||||
@ -1038,6 +1181,63 @@ namecheck(const char *name)
|
||||
return componentcheck(name, component, cp);
|
||||
}
|
||||
|
||||
/* Return a random uint_fast64_t. */
|
||||
static uint_fast64_t
|
||||
get_rand_u64(void)
|
||||
{
|
||||
#if HAVE_GETRANDOM
|
||||
static uint_fast64_t entropy_buffer[max(1, 256 / sizeof(uint_fast64_t))];
|
||||
static int nwords;
|
||||
if (!nwords) {
|
||||
ssize_t s;
|
||||
do
|
||||
s = getrandom(entropy_buffer, sizeof entropy_buffer, 0);
|
||||
while (s < 0 && errno == EINTR);
|
||||
|
||||
nwords = s < 0 ? -1 : s / sizeof *entropy_buffer;
|
||||
}
|
||||
if (0 < nwords)
|
||||
return entropy_buffer[--nwords];
|
||||
#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. */
|
||||
{
|
||||
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);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a random number if rand() yields a random number and in
|
||||
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. */
|
||||
r = 0, rmax = 0;
|
||||
do {
|
||||
uint_fast64_t rmax1 = rmax * multiplier + rand_max;
|
||||
r = r * multiplier + rand();
|
||||
rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX;
|
||||
} while (rmax < UINT_FAST64_MAX);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a randomish name in the same directory as *NAME. If
|
||||
*NAMEALLOC, put the name into *NAMEALLOC which is assumed to be
|
||||
that returned by a previous call and is thus already almost set up
|
||||
@ -1057,8 +1257,19 @@ random_dirent(char const **name, char **namealloc)
|
||||
int suffixlen = 6;
|
||||
char const *lastslash = strrchr(src, '/');
|
||||
ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0;
|
||||
static unsigned short initialized;
|
||||
int i;
|
||||
uint_fast64_t r;
|
||||
uint_fast64_t base = alphabetlen;
|
||||
|
||||
/* BASE**6 */
|
||||
uint_fast64_t base__6 = base * base * base * base * base * base;
|
||||
|
||||
/* The largest uintmax_t that is a multiple of BASE**6. Any random
|
||||
uintmax_t value that is this value or greater, yields a biased
|
||||
remainder when divided by BASE**6. UNFAIR_MIN equals the
|
||||
mathematical value of ((UINTMAX_MAX + 1) - (UINTMAX_MAX + 1) % BASE**6)
|
||||
computed without overflow. */
|
||||
uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
|
||||
|
||||
if (!dst) {
|
||||
dst = emalloc(dirlen + prefixlen + suffixlen + 1);
|
||||
@ -1068,13 +1279,14 @@ random_dirent(char const **name, char **namealloc)
|
||||
*name = *namealloc = dst;
|
||||
}
|
||||
|
||||
/* This randomization is not the best, but is portable to C89. */
|
||||
if (!initialized++) {
|
||||
unsigned now = time(NULL);
|
||||
srand(rand() ^ now);
|
||||
do
|
||||
r = get_rand_u64();
|
||||
while (unfair_min <= r);
|
||||
|
||||
for (i = 0; i < suffixlen; i++) {
|
||||
dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
|
||||
r /= alphabetlen;
|
||||
}
|
||||
for (i = 0; i < suffixlen; i++)
|
||||
dst[dirlen + prefixlen + i] = alphabet[rand() % alphabetlen];
|
||||
}
|
||||
|
||||
/* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
|
||||
@ -1297,28 +1509,26 @@ associate(void)
|
||||
register struct rule * rp;
|
||||
register ptrdiff_t i, j, base, out;
|
||||
|
||||
if (nrules != 0) {
|
||||
if (1 < nrules) {
|
||||
qsort(rules, nrules, sizeof *rules, rcomp);
|
||||
for (i = 0; i < nrules - 1; ++i) {
|
||||
if (strcmp(rules[i].r_name,
|
||||
rules[i + 1].r_name) != 0)
|
||||
continue;
|
||||
if (strcmp(rules[i].r_filename,
|
||||
rules[i + 1].r_filename) == 0)
|
||||
if (rules[i].r_filenum == rules[i + 1].r_filenum)
|
||||
continue;
|
||||
eat(rules[i].r_filename, rules[i].r_linenum);
|
||||
eat(rules[i].r_filenum, rules[i].r_linenum);
|
||||
warning(_("same rule name in multiple files"));
|
||||
eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
|
||||
eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum);
|
||||
warning(_("same rule name in multiple files"));
|
||||
for (j = i + 2; j < nrules; ++j) {
|
||||
if (strcmp(rules[i].r_name,
|
||||
rules[j].r_name) != 0)
|
||||
break;
|
||||
if (strcmp(rules[i].r_filename,
|
||||
rules[j].r_filename) == 0)
|
||||
if (rules[i].r_filenum == rules[j].r_filenum)
|
||||
continue;
|
||||
if (strcmp(rules[i + 1].r_filename,
|
||||
rules[j].r_filename) == 0)
|
||||
if (rules[i + 1].r_filenum
|
||||
== rules[j].r_filenum)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
@ -1349,7 +1559,7 @@ associate(void)
|
||||
/*
|
||||
** Maybe we have a local standard time offset.
|
||||
*/
|
||||
eat(zp->z_filename, zp->z_linenum);
|
||||
eat(zp->z_filenum, zp->z_linenum);
|
||||
zp->z_save = getsave(zp->z_rule, &zp->z_isdst);
|
||||
/*
|
||||
** Note, though, that if there's no rule,
|
||||
@ -1398,7 +1608,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
|
||||
}
|
||||
|
||||
static void
|
||||
infile(const char *name)
|
||||
infile(int fnum, char const *name)
|
||||
{
|
||||
register FILE * fp;
|
||||
register const struct lookup * lp;
|
||||
@ -1406,7 +1616,6 @@ infile(const char *name)
|
||||
register lineno num;
|
||||
|
||||
if (strcmp(name, "-") == 0) {
|
||||
name = _("standard input");
|
||||
fp = stdin;
|
||||
} else if ((fp = fopen(name, "r")) == NULL) {
|
||||
const char *e = strerror(errno);
|
||||
@ -1421,7 +1630,7 @@ infile(const char *name)
|
||||
char buf[_POSIX2_LINE_MAX];
|
||||
int nfields;
|
||||
char *fields[MAX_FIELDS];
|
||||
eat(name, num);
|
||||
eat(fnum, num);
|
||||
linelen = inputline(fp, buf, sizeof buf);
|
||||
if (linelen < 0)
|
||||
break;
|
||||
@ -1433,7 +1642,7 @@ infile(const char *name)
|
||||
wantcont = inzcont(fields, nfields);
|
||||
} else {
|
||||
struct lookup const *line_codes
|
||||
= name == leapsec ? leap_line_codes : zi_line_codes;
|
||||
= fnum < 0 ? leap_line_codes : zi_line_codes;
|
||||
lp = byword(fields[0], line_codes);
|
||||
if (lp == NULL)
|
||||
error(_("input line of unknown type"));
|
||||
@ -1457,11 +1666,11 @@ infile(const char *name)
|
||||
inexpires(fields, nfields);
|
||||
wantcont = false;
|
||||
break;
|
||||
default: UNREACHABLE();
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
close_file(fp, NULL, filename, NULL);
|
||||
close_file(fp, NULL, filename(fnum), NULL);
|
||||
if (wantcont)
|
||||
error(_("expected continuation line not found"));
|
||||
}
|
||||
@ -1564,7 +1773,7 @@ inrule(char **fields, int nfields)
|
||||
error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
|
||||
return;
|
||||
}
|
||||
r.r_filename = filename;
|
||||
r.r_filenum = filenum;
|
||||
r.r_linenum = linenum;
|
||||
r.r_save = getsave(fields[RF_SAVE], &r.r_isdst);
|
||||
if (!rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR],
|
||||
@ -1605,9 +1814,9 @@ _("\"Zone %s\" line and -p option are mutually exclusive"),
|
||||
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
|
||||
error(_("duplicate zone name %s"
|
||||
" (file \"%s\", line %"PRIdMAX")"),
|
||||
fields[ZF_NAME],
|
||||
zones[i].z_filename,
|
||||
zones[i].z_linenum);
|
||||
fields[ZF_NAME],
|
||||
filename(zones[i].z_filenum),
|
||||
zones[i].z_linenum);
|
||||
return false;
|
||||
}
|
||||
return inzsub(fields, nfields, false);
|
||||
@ -1654,7 +1863,7 @@ inzsub(char **fields, int nfields, bool iscont)
|
||||
i_untilday = ZF_TILDAY;
|
||||
i_untiltime = ZF_TILTIME;
|
||||
}
|
||||
z.z_filename = filename;
|
||||
z.z_filenum = filenum;
|
||||
z.z_linenum = linenum;
|
||||
z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
|
||||
if ((cp = strchr(fields[i_format], '%')) != 0) {
|
||||
@ -1670,7 +1879,7 @@ inzsub(char **fields, int nfields, bool iscont)
|
||||
max_format_len = format_len;
|
||||
hasuntil = nfields > i_untilyear;
|
||||
if (hasuntil) {
|
||||
z.z_untilrule.r_filename = filename;
|
||||
z.z_untilrule.r_filenum = filenum;
|
||||
z.z_untilrule.r_linenum = linenum;
|
||||
if (!rulesub(
|
||||
&z.z_untilrule,
|
||||
@ -1839,7 +2048,7 @@ inlink(char **fields, int nfields)
|
||||
}
|
||||
if (! namecheck(fields[LF_LINKNAME]))
|
||||
return;
|
||||
l.l_filename = filename;
|
||||
l.l_filenum = filenum;
|
||||
l.l_linenum = linenum;
|
||||
l.l_target = ecpyalloc(fields[LF_TARGET]);
|
||||
l.l_linkname = ecpyalloc(fields[LF_LINKNAME]);
|
||||
@ -1903,7 +2112,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
|
||||
case YR_MAXIMUM:
|
||||
rp->r_loyear = ZIC_MAX;
|
||||
break;
|
||||
default: UNREACHABLE();
|
||||
default: unreachable();
|
||||
} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
|
||||
error(_("invalid starting year"));
|
||||
return false;
|
||||
@ -1921,7 +2130,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
|
||||
case YR_ONLY:
|
||||
rp->r_hiyear = rp->r_loyear;
|
||||
break;
|
||||
default: UNREACHABLE();
|
||||
default: unreachable();
|
||||
} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
|
||||
error(_("invalid ending year"));
|
||||
return false;
|
||||
@ -2092,17 +2301,14 @@ writezone(const char *const name, const char *const string, char version,
|
||||
register FILE * fp;
|
||||
register ptrdiff_t i, j;
|
||||
register int pass;
|
||||
zic_t one = 1;
|
||||
zic_t y2038_boundary = one << 31;
|
||||
ptrdiff_t nats = timecnt + WORK_AROUND_QTBUG_53071;
|
||||
char *tempname = NULL;
|
||||
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(nats, sizeof *ats + 1),
|
||||
_Alignof(zic_t)));
|
||||
void *typesptr = ats + nats;
|
||||
zic_t *ats = emalloc(align_to(size_product(timecnt, sizeof *ats + 1),
|
||||
alignof(zic_t)));
|
||||
void *typesptr = ats + timecnt;
|
||||
unsigned char *types = typesptr;
|
||||
struct timerange rangeall = {0}, range32, range64;
|
||||
|
||||
@ -2172,21 +2378,6 @@ writezone(const char *const name, const char *const string, char version,
|
||||
}
|
||||
}
|
||||
|
||||
/* Work around QTBUG-53071 for timestamps less than y2038_boundary - 1,
|
||||
by inserting a no-op transition at time y2038_boundary - 1.
|
||||
This works only for timestamps before the boundary, which
|
||||
should be good enough in practice as QTBUG-53071 should be
|
||||
long-dead by 2038. Do this after correcting for leap
|
||||
seconds, as the idea is to insert a transition just before
|
||||
32-bit time_t rolls around, and this occurs at a slightly
|
||||
different moment if transitions are leap-second corrected. */
|
||||
if (WORK_AROUND_QTBUG_53071 && timecnt != 0 && want_bloat()
|
||||
&& ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) {
|
||||
ats[timecnt] = y2038_boundary - 1;
|
||||
types[timecnt] = types[timecnt - 1];
|
||||
timecnt++;
|
||||
}
|
||||
|
||||
rangeall.defaulttype = defaulttype;
|
||||
rangeall.count = timecnt;
|
||||
rangeall.leapcount = leapcnt;
|
||||
@ -2194,7 +2385,7 @@ writezone(const char *const name, const char *const string, char version,
|
||||
max(hi_time,
|
||||
redundant_time - (ZIC_MIN < redundant_time)),
|
||||
ats, types);
|
||||
range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
|
||||
range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
|
||||
|
||||
/* TZif version 4 is needed if a no-op transition is appended to
|
||||
indicate the expiration of the leap second table, or if the first
|
||||
@ -2248,8 +2439,8 @@ writezone(const char *const name, const char *const string, char version,
|
||||
thisleapi = range32.leapbase;
|
||||
thisleapcnt = range32.leapcount;
|
||||
thisleapexpiry = range32.leapexpiry;
|
||||
thismin = INT32_MIN;
|
||||
thismax = INT32_MAX;
|
||||
thismin = ZIC32_MIN;
|
||||
thismax = ZIC32_MAX;
|
||||
} else {
|
||||
thisdefaulttype = range64.defaulttype;
|
||||
thistimei = range64.base;
|
||||
@ -2282,7 +2473,7 @@ writezone(const char *const name, const char *const string, char version,
|
||||
|
||||
/* Arguably the default time type in the 32-bit data
|
||||
should be range32.defaulttype, which is suited for
|
||||
timestamps just before INT32_MIN. However, zic
|
||||
timestamps just before ZIC32_MIN. However, zic
|
||||
traditionally used the time type of the indefinite
|
||||
past instead. Internet RFC 8532 says readers should
|
||||
ignore 32-bit data, so this discrepancy matters only
|
||||
@ -2434,7 +2625,7 @@ writezone(const char *const name, const char *const string, char version,
|
||||
/* Output a LO_TIME transition if needed; see limitrange.
|
||||
But do not go below the minimum representable value
|
||||
for this pass. */
|
||||
lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time;
|
||||
lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
|
||||
|
||||
if (0 <= pretranstype)
|
||||
puttzcodepass(lo, fp, pass);
|
||||
@ -2977,7 +3168,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
|
||||
INITIALIZE(prevktime);
|
||||
if (useuntil && zp->z_untiltime <= min_time)
|
||||
continue;
|
||||
eat(zp->z_filename, zp->z_linenum);
|
||||
eat(zp->z_filenum, zp->z_linenum);
|
||||
*startbuf = '\0';
|
||||
if (zp->z_nrules == 0) {
|
||||
int type;
|
||||
@ -3005,8 +3196,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
|
||||
zic_t one = 1;
|
||||
zic_t y2038_boundary = one << 31;
|
||||
struct rule *rp = &zp->z_rules[j];
|
||||
eats(zp->z_filename, zp->z_linenum,
|
||||
rp->r_filename, rp->r_linenum);
|
||||
eats(zp->z_filenum, zp->z_linenum,
|
||||
rp->r_filenum, rp->r_linenum);
|
||||
rp->r_todo = year >= rp->r_loyear &&
|
||||
year <= rp->r_hiyear;
|
||||
if (rp->r_todo) {
|
||||
@ -3047,8 +3238,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
|
||||
struct rule *r = &zp->z_rules[j];
|
||||
if (!r->r_todo)
|
||||
continue;
|
||||
eats(zp->z_filename, zp->z_linenum,
|
||||
r->r_filename, r->r_linenum);
|
||||
eats(zp->z_filenum, zp->z_linenum,
|
||||
r->r_filenum, r->r_linenum);
|
||||
offset = r->r_todisut ? 0 : stdoff;
|
||||
if (!r->r_todisstd)
|
||||
offset = oadd(offset, save);
|
||||
@ -3063,12 +3254,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
|
||||
} else if (jtime == ktime) {
|
||||
char const *dup_rules_msg =
|
||||
_("two rules for same instant");
|
||||
eats(zp->z_filename, zp->z_linenum,
|
||||
r->r_filename, r->r_linenum);
|
||||
eats(zp->z_filenum, zp->z_linenum,
|
||||
r->r_filenum, r->r_linenum);
|
||||
warning("%s", dup_rules_msg);
|
||||
r = &zp->z_rules[k];
|
||||
eats(zp->z_filename, zp->z_linenum,
|
||||
r->r_filename, r->r_linenum);
|
||||
eats(zp->z_filenum, zp->z_linenum,
|
||||
r->r_filenum, r->r_linenum);
|
||||
error("%s", dup_rules_msg);
|
||||
}
|
||||
}
|
||||
@ -3110,8 +3301,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
|
||||
false);
|
||||
}
|
||||
}
|
||||
eats(zp->z_filename, zp->z_linenum,
|
||||
rp->r_filename, rp->r_linenum);
|
||||
eats(zp->z_filenum, zp->z_linenum,
|
||||
rp->r_filenum, rp->r_linenum);
|
||||
doabbr(ab, zp, rp->r_abbrvar,
|
||||
rp->r_isdst, rp->r_save, false);
|
||||
offset = oadd(zp->z_stdoff, rp->r_save);
|
||||
@ -3140,7 +3331,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
|
||||
if (*startbuf == '\0' && zp->z_format)
|
||||
doabbr(startbuf, zp, disable_percent_s,
|
||||
isdst, save, false);
|
||||
eat(zp->z_filename, zp->z_linenum);
|
||||
eat(zp->z_filenum, zp->z_linenum);
|
||||
if (*startbuf == '\0')
|
||||
error(_("can't determine time zone abbreviation to use just after until time"));
|
||||
else {
|
||||
|
Loading…
Reference in New Issue
Block a user