Index: tz/create-2022f-version-patch/file.list
===================================================================
--- tz/create-2022f-version-patch/file.list (revision 384)
+++ tz/create-2022f-version-patch/file.list (nonexistent)
@@ -1 +0,0 @@
-Makefile
Index: tz/create-2022f-version-patch/timezone-new/Makefile
===================================================================
--- tz/create-2022f-version-patch/timezone-new/Makefile (revision 384)
+++ tz/create-2022f-version-patch/timezone-new/Makefile (nonexistent)
@@ -1,1237 +0,0 @@
-# Make and install tzdb code and data.
-
-# This file is in the public domain, so clarified as of
-# 2009-05-17 by Arthur David Olson.
-
-# Package name for the code distribution.
-PACKAGE= tzcode
-
-# Version number for the distribution, overridden in the 'tarballs' rule below.
-VERSION= 2022f
-
-# Email address for bug reports.
-BUGEMAIL= tz@iana.org
-
-# DATAFORM selects the data format.
-# Available formats represent essentially the same data, albeit
-# possibly with minor discrepancies that users are not likely to notice.
-# To get new features and the best data right away, use:
-# DATAFORM= vanguard
-# To wait a while before using new features, to give downstream users
-# time to upgrade zic (the default), use:
-# DATAFORM= main
-# To wait even longer for new features, use:
-# DATAFORM= rearguard
-# Rearguard users might also want "ZFLAGS = -b fat"; see below.
-DATAFORM= main
-
-# Change the line below for your timezone (after finding the one you want in
-# one of the $(TDATA) source files, or adding it to a source file).
-# Alternatively, if you discover you've got the wrong timezone, you can just
-# 'zic -l -' to remove it, or 'zic -l rightzone' to change it.
-# Use the command
-# make zonenames
-# to get a list of the values you can use for LOCALTIME.
-
-LOCALTIME= Factory
-
-# The POSIXRULES macro controls interpretation of nonstandard and obsolete
-# POSIX-like TZ settings like TZ='EET-2EEST' that lack DST transition rules.
-# Such a setting uses the rules in a template file to determine
-# "spring forward" and "fall back" days and times; the environment
-# variable itself specifies UT offsets of standard and daylight saving time.
-#
-# If POSIXRULES is '-', no template is installed; this is the default.
-#
-# Any other value for POSIXRULES is obsolete and should not be relied on, as:
-# * It does not work correctly in popular implementations such as GNU/Linux.
-# * It does not work even in tzcode, except for historical timestamps
-# that precede the last explicit transition in the POSIXRULES file.
-# Hence it typically does not work for current and future timestamps.
-# In short, software should avoid ruleless settings like TZ='EET-2EEST'
-# and so should not depend on the value of POSIXRULES.
-#
-# If, despite the above, you want a template for handling these settings,
-# you can change the line below (after finding the timezone you want in the
-# one of the $(TDATA) source files, or adding it to a source file).
-# Alternatively, if you discover you've got the wrong timezone, you can just
-# 'zic -p -' to remove it, or 'zic -p rightzone' to change it.
-# Use the command
-# make zonenames
-# to get a list of the values you can use for POSIXRULES.
-
-POSIXRULES= -
-
-# Also see TZDEFRULESTRING below, which takes effect only
-# if the time zone files cannot be accessed.
-
-
-# Installation locations.
-#
-# The defaults are suitable for Debian, except that if REDO is
-# posix_right or right_posix then files that Debian puts under
-# /usr/share/zoneinfo/posix and /usr/share/zoneinfo/right are instead
-# put under /usr/share/zoneinfo-posix and /usr/share/zoneinfo-leaps,
-# respectively. Problems with the Debian approach are discussed in
-# the commentary for the right_posix rule (below).
-
-# Destination directory, which can be used for staging.
-# 'make DESTDIR=/stage install' installs under /stage (e.g., to
-# /stage/etc/localtime instead of to /etc/localtime). Files under
-# /stage are not intended to work as-is, but can be copied by hand to
-# the root directory later. If DESTDIR is empty, 'make install' does
-# not stage, but installs directly into production locations.
-DESTDIR =
-
-# Everything is installed into subdirectories of TOPDIR, and used there.
-# TOPDIR should be empty (meaning the root directory),
-# or a directory name that does not end in "/".
-# TOPDIR should be empty or an absolute name unless you're just testing.
-TOPDIR =
-
-# The default local timezone is taken from the file TZDEFAULT.
-TZDEFAULT = $(TOPDIR)/etc/localtime
-
-# The subdirectory containing installed program and data files, and
-# likewise for installed files that can be shared among architectures.
-# These should be relative file names.
-USRDIR = usr
-USRSHAREDIR = $(USRDIR)/share
-
-# "Compiled" timezone information is placed in the "TZDIR" directory
-# (and subdirectories).
-# TZDIR_BASENAME should not contain "/" and should not be ".", ".." or empty.
-TZDIR_BASENAME= zoneinfo
-TZDIR = $(TOPDIR)/$(USRSHAREDIR)/$(TZDIR_BASENAME)
-
-# The "tzselect" and (if you do "make INSTALL") "date" commands go in:
-BINDIR = $(TOPDIR)/$(USRDIR)/bin
-
-# The "zdump" command goes in:
-ZDUMPDIR = $(BINDIR)
-
-# The "zic" command goes in:
-ZICDIR = $(TOPDIR)/$(USRDIR)/sbin
-
-# Manual pages go in subdirectories of. . .
-MANDIR = $(TOPDIR)/$(USRSHAREDIR)/man
-
-# Library functions are put in an archive in LIBDIR.
-LIBDIR = $(TOPDIR)/$(USRDIR)/lib
-
-
-# Types to try, as an alternative to time_t.
-TIME_T_ALTERNATIVES = $(TIME_T_ALTERNATIVES_HEAD) $(TIME_T_ALTERNATIVES_TAIL)
-TIME_T_ALTERNATIVES_HEAD = int_least64_t
-TIME_T_ALTERNATIVES_TAIL = int_least32_t uint_least32_t uint_least64_t
-
-# What kind of TZif data files to generate. (TZif is the binary time
-# zone data format that zic generates; see Internet RFC 8536.)
-# If you want only POSIX time, with time values interpreted as
-# seconds since the epoch (not counting leap seconds), use
-# REDO= posix_only
-# below. If you want only "right" time, with values interpreted
-# as seconds since the epoch (counting leap seconds), use
-# REDO= right_only
-# below. If you want both sets of data available, with leap seconds not
-# counted normally, use
-# REDO= posix_right
-# below. If you want both sets of data available, with leap seconds counted
-# normally, use
-# REDO= right_posix
-# below. POSIX mandates that leap seconds not be counted; for compatibility
-# with it, use "posix_only" or "posix_right". Use POSIX time on systems with
-# leap smearing; this can work better than unsmeared "right" time with
-# applications that are not leap second aware, and is closer to unsmeared
-# "right" time than unsmeared POSIX time is (e.g., 0.5 vs 1.0 s max error).
-
-REDO= posix_right
-
-# Whether to put an "Expires" line in the leapseconds file.
-# Use EXPIRES_LINE=1 to put the line in, 0 to omit it.
-# The EXPIRES_LINE value matters only if REDO's value contains "right".
-# If you change EXPIRES_LINE, remove the leapseconds file before running "make".
-# zic's support for the Expires line was introduced in tzdb 2020a,
-# and was modified in tzdb 2021b to generate version 4 TZif files.
-# EXPIRES_LINE defaults to 0 for now so that the leapseconds file
-# can be given to pre-2020a zic implementations and so that TZif files
-# built by newer zic implementations can be read by pre-2021b libraries.
-EXPIRES_LINE= 0
-
-# To install data in text form that has all the information of the TZif data,
-# (optionally incorporating leap second information), use
-# TZDATA_TEXT= tzdata.zi leapseconds
-# To install text data without leap second information (e.g., because
-# REDO='posix_only'), use
-# TZDATA_TEXT= tzdata.zi
-# To avoid installing text data, use
-# TZDATA_TEXT=
-
-TZDATA_TEXT= leapseconds tzdata.zi
-
-# For backward-compatibility links for old zone names, use
-# BACKWARD= backward
-# To omit these links, use
-# BACKWARD=
-
-BACKWARD= backward
-
-# If you want out-of-scope and often-wrong data from the file 'backzone',
-# but only for entries listed in the backward-compatibility file zone.tab, use
-# PACKRATDATA= backzone
-# PACKRATLIST= zone.tab
-# If you want all the 'backzone' data, use
-# PACKRATDATA= backzone
-# PACKRATLIST=
-# To omit this data, use
-# PACKRATDATA=
-# PACKRATLIST=
-
-PACKRATDATA=
-PACKRATLIST=
-
-# The name of a locale using the UTF-8 encoding, used during self-tests.
-# The tests are skipped if the name does not appear to work on this system.
-
-UTF8_LOCALE= en_US.utf8
-
-# Non-default libraries needed to link.
-LDLIBS=
-
-# Add the following to the end of the "CFLAGS=" line as needed to override
-# defaults specified in the source code. "-DFOO" is equivalent to "-DFOO=1".
-# -DDEPRECATE_TWO_DIGIT_YEARS for optional runtime warnings about strftime
-# formats that generate only the last two digits of year numbers
-# -DEPOCH_LOCAL if the 'time' function returns local time not UT
-# -DEPOCH_OFFSET=N if the 'time' function returns a value N greater
-# than what POSIX specifies, assuming local time is UT.
-# 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_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_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_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
-# localtime_rz can make zdump significantly faster, but is nonstandard.
-# -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_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_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>*
-# -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'.
-# -Dssize_t=long on hosts like MS-Windows that lack ssize_t
-# -DSUPPRESS_TZDIR to not prepend TZDIR to file names; this has
-# security implications and is not recommended for general use
-# -DTHREAD_SAFE to make localtime.c thread-safe, as POSIX requires;
-# not needed by the main-program tz code, which is single-threaded.
-# Append other compiler flags as needed, e.g., -pthread on GNU/Linux.
-# -Dtime_tz=\"T\" to use T as the time_t type, rather than the system time_t
-# This is intended for internal use only; it mangles external names.
-# -DTZ_DOMAIN=\"foo\" to use "foo" for gettext domain name; default is "tz"
-# -DTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
-# the default is system-supplied, typically "/usr/lib/locale"
-# -DTZDEFRULESTRING=\",date/time,date/time\" to default to the specified
-# DST transitions if the time zone files cannot be accessed
-# -DUNINIT_TRAP if reading uninitialized storage can cause problems
-# other than simply getting garbage data
-# -DUSE_LTZ=0 to build zdump with the system time zone library
-# 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 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 \
- -fsanitize-undefined-trap-on-error -fstack-protector
-# Omit -fanalyzer from GCC_DEBUG_FLAGS, as it makes GCC too slow.
-GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
- $(GCC_INSTRUMENT) \
- -Wall -Wextra \
- -Walloc-size-larger-than=100000 -Warray-bounds=2 \
- -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wdate-time \
- -Wdeclaration-after-statement -Wdouble-promotion \
- -Wduplicated-branches -Wduplicated-cond \
- -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
- -Winit-self -Wlogical-op \
- -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
- -Wnull-dereference \
- -Wold-style-definition -Woverlength-strings -Wpointer-arith \
- -Wshadow -Wshift-overflow=2 -Wstrict-overflow \
- -Wstrict-prototypes -Wstringop-overflow=4 \
- -Wstringop-truncation -Wsuggest-attribute=cold \
- -Wsuggest-attribute=const -Wsuggest-attribute=format \
- -Wsuggest-attribute=malloc \
- -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure \
- -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
-#
-# 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),
-# add the name to a define such as
-# -DTM_GMTOFF=tm_gmtoff
-# to the end of the "CFLAGS=" line. If not defined, the code attempts to
-# guess TM_GMTOFF from other macros; define NO_TM_GMTOFF to suppress this.
-# Similarly, if your system has a "zone abbreviation" field, define
-# -DTM_ZONE=tm_zone
-# and define NO_TM_ZONE to suppress any guessing. Although these two fields
-# not required by POSIX, a future version of POSIX is planned to require them
-# and they are widely available on GNU/Linux and BSD systems.
-#
-# The next batch of options control support for external variables
-# exported by tzcode. In practice these variables are less useful
-# than TM_GMTOFF and TM_ZONE. However, most of them are standardized.
-# #
-# # To omit or support the external variable "tzname", add one of:
-# # -DHAVE_TZNAME=0 # do not support "tzname"
-# # -DHAVE_TZNAME=1 # support "tzname", which is defined by system library
-# # -DHAVE_TZNAME=2 # support and define "tzname"
-# # to the "CFLAGS=" line. "tzname" is required by POSIX 1988 and later.
-# # If not defined, the code attempts to guess HAVE_TZNAME from other macros.
-# # Warning: unless time_tz is also defined, HAVE_TZNAME=1 can cause
-# # crashes when combined with some platforms' standard libraries,
-# # presumably due to memory allocation issues.
-# #
-# # To omit or support the external variables "timezone" and "daylight", add
-# # -DUSG_COMPAT=0 # do not support
-# # -DUSG_COMPAT=1 # support, and variables are defined by system library
-# # -DUSG_COMPAT=2 # support and define variables
-# # to the "CFLAGS=" line; "timezone" and "daylight" are inspired by
-# # Unix Systems Group code and are required by POSIX 2008 (with XSI) and later.
-# # If not defined, the code attempts to guess USG_COMPAT from other macros.
-# #
-# # To support the external variable "altzone", add
-# # -DALTZONE=0 # do not support
-# # -DALTZONE=1 # support "altzone", which is defined by system library
-# # -DALTZONE=2 # support and define "altzone"
-# # to the end of the "CFLAGS=" line; although "altzone" appeared in
-# # System V Release 3.1 it has not been standardized.
-# # If not defined, the code attempts to guess ALTZONE from other macros.
-#
-# 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.
-# "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.
-# X3J11's work does not describe any of these functions.
-# These functions may well disappear in future releases of the time
-# conversion package.
-#
-# If you don't want functions that were inspired by NetBSD, add
-# -DNETBSD_INSPIRED=0
-# to the end of the "CFLAGS=" line. Otherwise, the functions
-# "localtime_rz", "mktime_z", "tzalloc", and "tzfree" are added to the
-# time library, and if STD_INSPIRED is also defined the functions
-# "posix2time_z" and "time2posix_z" are added as well.
-# The functions ending in "_z" (or "_rz") are like their unsuffixed
-# (or suffixed-by-"_r") counterparts, except with an extra first
-# argument of opaque type timezone_t that specifies the timezone.
-# "tzalloc" allocates a timezone_t value, and "tzfree" frees it.
-#
-# If you want to allocate state structures in localtime, add
-# -DALL_STATE
-# to the end of the "CFLAGS=" line. Storage is obtained by calling malloc.
-#
-# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
-# out by the National Institute of Standards and Technology
-# which claims to test C and Posix conformance. If you want to pass PCTS, add
-# -DPCTS
-# to the end of the "CFLAGS=" line.
-#
-# If you want strict compliance with XPG4 as of 1994-04-09, add
-# -DXPG4_1994_04_09
-# to the end of the "CFLAGS=" line. This causes "strftime" to always return
-# 53 as a week number (rather than 52 or 53) for January days before
-# January's first Monday when a "%V" format is used and January 1
-# falls on a Friday, Saturday, or Sunday.
-
-CFLAGS=
-
-# Linker flags. Default to $(LFLAGS) for backwards compatibility
-# to release 2012h and earlier.
-
-LDFLAGS= $(LFLAGS)
-
-# For leap seconds, this Makefile uses LEAPSECONDS='-L leapseconds' in
-# submake command lines. The default is no leap seconds.
-
-LEAPSECONDS=
-
-# The zic command and its arguments.
-
-zic= ./zic
-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 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=
-
-# How to use zic to install TZif files.
-
-ZIC_INSTALL= $(ZIC) -d '$(DESTDIR)$(TZDIR)' $(LEAPSECONDS)
-
-# The name of a Posix-compliant 'awk' on your system.
-# mawk 1.3.3 and Solaris 10 /usr/bin/awk do not work.
-# Also, it is better (though not essential) if 'awk' supports UTF-8,
-# and unfortunately mawk and busybox awk do not support UTF-8.
-# Try AWK=gawk or AWK=nawk if your awk has the abovementioned problems.
-AWK= awk
-
-# The full path name of a Posix-compliant shell, preferably one that supports
-# the Korn shell's 'select' statement as an extension.
-# These days, Bash is the most popular.
-# It should be OK to set this to /bin/sh, on platforms where /bin/sh
-# lacks 'select' or doesn't completely conform to Posix, but /bin/bash
-# is typically nicer if it works.
-KSHELL= /bin/bash
-
-# Name of curl <https://curl.haxx.se/>, used for HTML validation.
-CURL= curl
-
-# Name of GNU Privacy Guard <https://gnupg.org/>, used to sign distributions.
-GPG= gpg
-
-# This expensive test requires USE_LTZ.
-# To suppress it, define this macro to be empty.
-CHECK_TIME_T_ALTERNATIVES = check_time_t_alternatives
-
-# SAFE_CHAR is a regular expression that matches a safe character.
-# Some parts of this distribution are limited to safe characters;
-# others can use any UTF-8 character.
-# For now, the safe characters are a safe subset of ASCII.
-# The caller must set the shell variable 'sharp' to the character '#',
-# since Makefile macros cannot contain '#'.
-# TAB_CHAR is a single tab character, in single quotes.
-TAB_CHAR= ' '
-SAFE_CHARSET1= $(TAB_CHAR)' !\"'$$sharp'$$%&'\''()*+,./0123456789:;<=>?@'
-SAFE_CHARSET2= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\^_`'
-SAFE_CHARSET3= 'abcdefghijklmnopqrstuvwxyz{|}~'
-SAFE_CHARSET= $(SAFE_CHARSET1)$(SAFE_CHARSET2)$(SAFE_CHARSET3)
-SAFE_CHAR= '[]'$(SAFE_CHARSET)'-]'
-
-# These characters are Latin-1, and so are likely to be displayable
-# even in editors with limited character sets.
-UNUSUAL_OK_LATIN_1 = «°±»½¾×
-# This IPA symbol is represented in Unicode as the composition of
-# U+0075 and U+032F, and U+032F is not considered alphabetic by some
-# grep implementations that do not grok composition.
-UNUSUAL_OK_IPA = u̯
-# Non-ASCII non-letters that OK_CHAR allows, as these characters are
-# useful in commentary.
-UNUSUAL_OK_CHARSET= $(UNUSUAL_OK_LATIN_1)$(UNUSUAL_OK_IPA)
-
-# Put this in a bracket expression to match spaces.
-s = [:space:]
-
-# OK_CHAR matches any character allowed in the distributed files.
-# This is the same as SAFE_CHAR, except that UNUSUAL_OK_CHARSET and
-# multibyte letters are also allowed so that commentary can contain a
-# few safe symbols and people's names and can quote non-English sources.
-# Other non-letters are limited to ASCII renderings for the
-# convenience of maintainers using XEmacs 21.5.34, which by default
-# mishandles Unicode characters U+0100 and greater.
-OK_CHAR= '[][:alpha:]$(UNUSUAL_OK_CHARSET)'$(SAFE_CHARSET)'-]'
-
-# SAFE_LINE matches a line of safe characters.
-# SAFE_SHARP_LINE is similar, except any OK character can follow '#';
-# this is so that comments can contain non-ASCII characters.
-# OK_LINE matches a line of OK characters.
-SAFE_LINE= '^'$(SAFE_CHAR)'*$$'
-SAFE_SHARP_LINE='^'$(SAFE_CHAR)'*('$$sharp$(OK_CHAR)'*)?$$'
-OK_LINE= '^'$(OK_CHAR)'*$$'
-
-# Flags to give 'tar' when making a distribution.
-# Try to use flags appropriate for GNU tar.
-GNUTARFLAGS= --format=pax --pax-option='delete=atime,delete=ctime' \
- --numeric-owner --owner=0 --group=0 \
- --mode=go+u,go-w --sort=name
-TARFLAGS= `if tar $(GNUTARFLAGS) --version >/dev/null 2>&1; \
- then echo $(GNUTARFLAGS); \
- else :; \
- fi`
-
-# Flags to give 'gzip' when making a distribution.
-GZIPFLAGS= -9n
-
-###############################################################################
-
-#MAKE= make
-
-cc= cc
-CC= $(cc) -DTZDIR='"$(TZDIR)"'
-
-AR= ar
-
-# ':' on typical hosts; 'ranlib' on the ancient hosts that still need ranlib.
-RANLIB= :
-
-TZCOBJS= zic.o
-TZDOBJS= zdump.o localtime.o asctime.o strftime.o
-DATEOBJS= date.o localtime.o strftime.o asctime.o
-LIBSRCS= localtime.c asctime.c difftime.c strftime.c
-LIBOBJS= localtime.o asctime.o difftime.o strftime.o
-HEADERS= tzfile.h private.h
-NONLIBSRCS= zic.c zdump.c
-NEWUCBSRCS= date.c
-SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) \
- tzselect.ksh workman.sh
-MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
- tzfile.5 tzselect.8 zic.8 zdump.8
-MANTXTS= newctime.3.txt newstrftime.3.txt newtzset.3.txt \
- time2posix.3.txt \
- tzfile.5.txt tzselect.8.txt zic.8.txt zdump.8.txt \
- date.1.txt
-COMMON= calendars CONTRIBUTING LICENSE Makefile \
- NEWS README SECURITY theory.html version
-WEB_PAGES= tz-art.html tz-how-to.html tz-link.html
-CHECK_WEB_PAGES=check_theory.html check_tz-art.html \
- check_tz-how-to.html check_tz-link.html
-DOCS= $(MANS) date.1 $(MANTXTS) $(WEB_PAGES)
-PRIMARY_YDATA= africa antarctica asia australasia \
- europe northamerica southamerica
-YDATA= $(PRIMARY_YDATA) etcetera
-NDATA= factory
-TDATA_TO_CHECK= $(YDATA) $(NDATA) backward
-TDATA= $(YDATA) $(NDATA) $(BACKWARD)
-ZONETABLES= zone1970.tab zone.tab
-TABDATA= iso3166.tab $(TZDATA_TEXT) $(ZONETABLES)
-LEAP_DEPS= leapseconds.awk leap-seconds.list
-TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \
- $(PACKRATDATA) $(PACKRATLIST)
-DSTDATA_ZI_DEPS= ziguard.awk $(TDATA) $(PACKRATDATA) $(PACKRATLIST)
-DATA= $(TDATA_TO_CHECK) backzone iso3166.tab leap-seconds.list \
- leapseconds $(ZONETABLES)
-AWK_SCRIPTS= checklinks.awk checktab.awk leapseconds.awk \
- ziguard.awk zishrink.awk
-MISC= $(AWK_SCRIPTS)
-TZS_YEAR= 2050
-TZS_CUTOFF_FLAG= -c $(TZS_YEAR)
-TZS= to$(TZS_YEAR).tzs
-TZS_NEW= to$(TZS_YEAR)new.tzs
-TZS_DEPS= $(YDATA) asctime.c localtime.c \
- private.h tzfile.h zdump.c zic.c
-TZDATA_DIST = $(COMMON) $(DATA) $(MISC)
-# EIGHT_YARDS is just a yard short of the whole ENCHILADA.
-EIGHT_YARDS = $(TZDATA_DIST) $(DOCS) $(SOURCES) tzdata.zi
-ENCHILADA = $(EIGHT_YARDS) $(TZS)
-
-# Consult these files when deciding whether to rebuild the 'version' file.
-# This list is not the same as the output of 'git ls-files', since
-# .gitignore is not distributed.
-VERSION_DEPS= \
- calendars CONTRIBUTING LICENSE Makefile NEWS README SECURITY \
- africa antarctica asctime.c asia australasia \
- backward backzone \
- checklinks.awk checktab.awk \
- date.1 date.c difftime.c \
- etcetera europe factory iso3166.tab \
- leap-seconds.list leapseconds.awk localtime.c \
- newctime.3 newstrftime.3 newtzset.3 northamerica \
- private.h southamerica strftime.c theory.html \
- time2posix.3 tz-art.html tz-how-to.html tz-link.html \
- tzfile.5 tzfile.h tzselect.8 tzselect.ksh \
- workman.sh zdump.8 zdump.c zic.8 zic.c \
- ziguard.awk zishrink.awk \
- zone.tab zone1970.tab
-
-# And for the benefit of csh users on systems that assume the user
-# shell should be used to handle commands in Makefiles. . .
-
-SHELL= /bin/sh
-
-all: tzselect zic zdump libtz.a $(TABDATA) \
- vanguard.zi main.zi rearguard.zi
-
-ALL: all date $(ENCHILADA)
-
-install: all $(DATA) $(REDO) $(MANS)
- mkdir -p '$(DESTDIR)$(BINDIR)' \
- '$(DESTDIR)$(ZDUMPDIR)' '$(DESTDIR)$(ZICDIR)' \
- '$(DESTDIR)$(LIBDIR)' \
- '$(DESTDIR)$(MANDIR)/man3' '$(DESTDIR)$(MANDIR)/man5' \
- '$(DESTDIR)$(MANDIR)/man8'
- $(ZIC_INSTALL) -l $(LOCALTIME) \
- `case '$(POSIXRULES)' in ?*) echo '-p';; esac \
- ` $(POSIXRULES) \
- -t '$(DESTDIR)$(TZDEFAULT)'
- cp -f $(TABDATA) '$(DESTDIR)$(TZDIR)/.'
- cp tzselect '$(DESTDIR)$(BINDIR)/.'
- cp zdump '$(DESTDIR)$(ZDUMPDIR)/.'
- cp zic '$(DESTDIR)$(ZICDIR)/.'
- cp libtz.a '$(DESTDIR)$(LIBDIR)/.'
- $(RANLIB) '$(DESTDIR)$(LIBDIR)/libtz.a'
- cp -f newctime.3 newtzset.3 '$(DESTDIR)$(MANDIR)/man3/.'
- cp -f tzfile.5 '$(DESTDIR)$(MANDIR)/man5/.'
- cp -f tzselect.8 zdump.8 zic.8 '$(DESTDIR)$(MANDIR)/man8/.'
-
-INSTALL: ALL install date.1
- mkdir -p '$(DESTDIR)$(BINDIR)' '$(DESTDIR)$(MANDIR)/man1'
- cp date '$(DESTDIR)$(BINDIR)/.'
- cp -f date.1 '$(DESTDIR)$(MANDIR)/man1/.'
-
-# Calculate version number from git, if available.
-# Otherwise, use $(VERSION) unless it is "unknown" and there is already
-# a 'version' file, in which case reuse the existing 'version' contents
-# and append "-dirty" if the contents do not already end in "-dirty".
-version: $(VERSION_DEPS)
- { (type git) >/dev/null 2>&1 && \
- V=`git describe --match '[0-9][0-9][0-9][0-9][a-z]*' \
- --abbrev=7 --dirty` || \
- if test '$(VERSION)' = unknown && V=`cat $@`; then \
- case $$V in *-dirty);; *) V=$$V-dirty;; esac; \
- else \
- V='$(VERSION)'; \
- fi; } && \
- printf '%s\n' "$$V" >$@.out
- mv $@.out $@
-
-# These files can be tailored by setting BACKWARD, PACKRATDATA, PACKRATLIST.
-vanguard.zi main.zi rearguard.zi: $(DSTDATA_ZI_DEPS)
- $(AWK) \
- -v DATAFORM=`expr $@ : '\(.*\).zi'` \
- -v PACKRATDATA='$(PACKRATDATA)' \
- -v PACKRATLIST='$(PACKRATLIST)' \
- -f ziguard.awk \
- $(TDATA) $(PACKRATDATA) >$@.out
- mv $@.out $@
-# This file has a version comment that attempts to capture any tailoring
-# via BACKWARD, DATAFORM, PACKRATDATA, PACKRATLIST, and REDO.
-tzdata.zi: $(DATAFORM).zi version zishrink.awk
- version=`sed 1q version` && \
- LC_ALL=C $(AWK) \
- -v dataform='$(DATAFORM)' \
- -v deps='$(DSTDATA_ZI_DEPS) zishrink.awk' \
- -v redo='$(REDO)' \
- -v version="$$version" \
- -f zishrink.awk \
- $(DATAFORM).zi >$@.out
- mv $@.out $@
-
-version.h: version
- VERSION=`cat version` && printf '%s\n' \
- 'static char const PKGVERSION[]="($(PACKAGE)) ";' \
- "static char const TZVERSION[]=\"$$VERSION\";" \
- 'static char const REPORT_BUGS_TO[]="$(BUGEMAIL)";' \
- >$@.out
- mv $@.out $@
-
-zdump: $(TZDOBJS)
- $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZDOBJS) $(LDLIBS)
-
-zic: $(TZCOBJS)
- $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZCOBJS) $(LDLIBS)
-
-leapseconds: $(LEAP_DEPS)
- $(AWK) -v EXPIRES_LINE=$(EXPIRES_LINE) \
- -f leapseconds.awk leap-seconds.list >$@.out
- mv $@.out $@
-
-# Arguments to pass to submakes of install_data.
-# They can be overridden by later submake arguments.
-INSTALLARGS = \
- BACKWARD='$(BACKWARD)' \
- DESTDIR='$(DESTDIR)' \
- LEAPSECONDS='$(LEAPSECONDS)' \
- PACKRATDATA='$(PACKRATDATA)' \
- PACKRATLIST='$(PACKRATLIST)' \
- TZDEFAULT='$(TZDEFAULT)' \
- TZDIR='$(TZDIR)' \
- ZIC='$(ZIC)'
-
-INSTALL_DATA_DEPS = zic leapseconds tzdata.zi
-
-# 'make install_data' installs one set of TZif files.
-install_data: $(INSTALL_DATA_DEPS)
- $(ZIC_INSTALL) tzdata.zi
-
-posix_only: $(INSTALL_DATA_DEPS)
- $(MAKE) $(INSTALLARGS) LEAPSECONDS= install_data
-
-right_only: $(INSTALL_DATA_DEPS)
- $(MAKE) $(INSTALLARGS) LEAPSECONDS='-L leapseconds' \
- install_data
-
-# In earlier versions of this makefile, the other two directories were
-# subdirectories of $(TZDIR). However, this led to configuration errors.
-# For example, with posix_right under the earlier scheme,
-# TZ='right/Australia/Adelaide' got you localtime with leap seconds,
-# but gmtime without leap seconds, which led to problems with applications
-# like sendmail that subtract gmtime from localtime.
-# Therefore, the other two directories are now siblings of $(TZDIR).
-# You must replace all of $(TZDIR) to switch from not using leap seconds
-# to using them, or vice versa.
-right_posix: right_only
- rm -fr '$(DESTDIR)$(TZDIR)-leaps'
- ln -s '$(TZDIR_BASENAME)' '$(DESTDIR)$(TZDIR)-leaps' || \
- $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-leaps' right_only
- $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-posix' posix_only
-
-posix_right: posix_only
- rm -fr '$(DESTDIR)$(TZDIR)-posix'
- ln -s '$(TZDIR_BASENAME)' '$(DESTDIR)$(TZDIR)-posix' || \
- $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-posix' posix_only
- $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-leaps' right_only
-
-zones: $(REDO)
-
-# dummy.zd is not a real file; it is mentioned here only so that the
-# top-level 'make' does not have a syntax error.
-ZDS = dummy.zd
-# Rule used only by submakes invoked by the $(TZS_NEW) rule.
-# It is separate so that GNU 'make -j' can run instances in parallel.
-$(ZDS): zdump
- ./zdump -i $(TZS_CUTOFF_FLAG) '$(wd)/'$$(expr $@ : '\(.*\).zd') \
- >$@
-
-TZS_NEW_DEPS = tzdata.zi zdump zic
-$(TZS_NEW): $(TZS_NEW_DEPS)
- rm -fr tzs$(TZS_YEAR).dir
- mkdir tzs$(TZS_YEAR).dir
- $(zic) -d tzs$(TZS_YEAR).dir tzdata.zi
- $(AWK) '/^L/{print "Link\t" $$2 "\t" $$3}' \
- tzdata.zi | LC_ALL=C sort >$@.out
- wd=`pwd` && \
- x=`$(AWK) '/^Z/{print "tzs$(TZS_YEAR).dir/" $$2 ".zd"}' \
- tzdata.zi \
- | LC_ALL=C sort -t . -k 2,2` && \
- set x $$x && \
- shift && \
- ZDS=$$* && \
- $(MAKE) wd="$$wd" TZS_CUTOFF_FLAG="$(TZS_CUTOFF_FLAG)" \
- ZDS="$$ZDS" $$ZDS && \
- sed 's,^TZ=".*\.dir/,TZ=",' $$ZDS >>$@.out
- rm -fr tzs$(TZS_YEAR).dir
- mv $@.out $@
-
-# If $(TZS) exists but 'make check_tzs' fails, a maintainer should inspect the
-# failed output and fix the inconsistency, perhaps by running 'make force_tzs'.
-$(TZS):
- touch $@
-
-force_tzs: $(TZS_NEW)
- cp $(TZS_NEW) $(TZS)
-
-libtz.a: $(LIBOBJS)
- rm -f $@
- $(AR) -rc $@ $(LIBOBJS)
- $(RANLIB) $@
-
-date: $(DATEOBJS)
- $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(DATEOBJS) $(LDLIBS)
-
-tzselect: tzselect.ksh version
- VERSION=`cat version` && sed \
- -e 's|#!/bin/bash|#!$(KSHELL)|g' \
- -e 's|AWK=[^}]*|AWK='\''$(AWK)'\''|g' \
- -e 's|\(PKGVERSION\)=.*|\1='\''($(PACKAGE)) '\''|' \
- -e 's|\(REPORT_BUGS_TO\)=.*|\1=$(BUGEMAIL)|' \
- -e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \
- -e 's|\(TZVERSION\)=.*|\1='"$$VERSION"'|' \
- <$@.ksh >$@.out
- chmod +x $@.out
- mv $@.out $@
-
-check: 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
-
-check_character_set: $(ENCHILADA)
- test ! '$(UTF8_LOCALE)' || \
- ! printf 'A\304\200B\n' | \
- LC_ALL='$(UTF8_LOCALE)' grep -q '^A.B$$' >/dev/null 2>&1 || { \
- LC_ALL='$(UTF8_LOCALE)' && export LC_ALL && \
- sharp='#' && \
- ! grep -Env $(SAFE_LINE) $(MANS) date.1 $(MANTXTS) \
- $(MISC) $(SOURCES) $(WEB_PAGES) \
- CONTRIBUTING LICENSE README SECURITY \
- version tzdata.zi && \
- ! grep -Env $(SAFE_LINE)'|^UNUSUAL_OK_'$(OK_CHAR)'*$$' \
- Makefile && \
- ! grep -Env $(SAFE_SHARP_LINE) $(TDATA_TO_CHECK) backzone \
- leapseconds zone.tab && \
- ! grep -Env $(OK_LINE) $(ENCHILADA); \
- }
- touch $@
-
-check_white_space: $(ENCHILADA)
- patfmt=' \t|[\f\r\v]' && pat=`printf "$$patfmt\\n"` && \
- ! grep -En "$$pat" \
- $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list)
- ! grep -n '[$s]$$' \
- $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list)
- touch $@
-
-PRECEDES_FILE_NAME = ^(Zone|Link[$s]+[^$s]+)[$s]+
-FILE_NAME_COMPONENT_TOO_LONG = $(PRECEDES_FILE_NAME)[^$s]*[^/$s]{15}
-
-check_name_lengths: $(TDATA_TO_CHECK) backzone
- ! grep -En '$(FILE_NAME_COMPONENT_TOO_LONG)' \
- $(TDATA_TO_CHECK) backzone
- touch $@
-
-PRECEDES_STDOFF = ^(Zone[$s]+[^$s]+)?[$s]+
-STDOFF = [-+]?[0-9:.]+
-RULELESS_SAVE = (-|$(STDOFF)[sd]?)
-RULELESS_SLASHED_ABBRS = \
- $(PRECEDES_STDOFF)$(STDOFF)[$s]+$(RULELESS_SAVE)[$s]+[^$s]*/
-
-check_slashed_abbrs: $(TDATA_TO_CHECK)
- ! grep -En '$(RULELESS_SLASHED_ABBRS)' $(TDATA_TO_CHECK)
- touch $@
-
-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++}' \
- 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) \
- -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)
- for tab in $(ZONETABLES); do \
- test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \
- $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \
- || exit; \
- done
- touch $@
-
-check_tzs: $(TZS) $(TZS_NEW)
- if test -s $(TZS); then \
- diff -u $(TZS) $(TZS_NEW); \
- else \
- cp $(TZS_NEW) $(TZS); \
- fi
- touch $@
-
-check_web: $(CHECK_WEB_PAGES)
-check_theory.html: theory.html
-check_tz-art.html: tz-art.html
-check_tz-how-to.html: tz-how-to.html
-check_tz-link.html: tz-link.html
-check_theory.html check_tz-art.html check_tz-how-to.html check_tz-link.html:
- $(CURL) -sS --url https://validator.w3.org/nu/ -F out=gnu \
- -F file=@$$(expr $@ : 'check_\(.*\)') -o $@.out && \
- test ! -s $@.out || { cat $@.out; exit 1; }
- mv $@.out $@
-
-check_ziguard: rearguard.zi vanguard.zi ziguard.awk
- $(AWK) -v DATAFORM=rearguard -f ziguard.awk vanguard.zi | \
- diff -u rearguard.zi -
- $(AWK) -v DATAFORM=vanguard -f ziguard.awk rearguard.zi | \
- diff -u vanguard.zi -
- touch $@
-
-# Check that zishrink.awk does not alter the data, and that ziguard.awk
-# preserves main-format data.
-check_zishrink: check_zishrink_posix check_zishrink_right
-check_zishrink_posix check_zishrink_right: \
- zic leapseconds $(PACKRATDATA) $(PACKRATLIST) \
- $(TDATA) $(DATAFORM).zi tzdata.zi
- rm -fr $@.dir $@-t.dir $@-shrunk.dir
- mkdir $@.dir $@-t.dir $@-shrunk.dir
- case $@ in \
- *_right) leap='-L leapseconds';; \
- *) leap=;; \
- esac && \
- $(ZIC) $$leap -d $@.dir $(DATAFORM).zi && \
- $(ZIC) $$leap -d $@-shrunk.dir tzdata.zi && \
- case $(DATAFORM),$(PACKRATLIST) in \
- main,) \
- $(ZIC) $$leap -d $@-t.dir $(TDATA) && \
- $(AWK) '/^Rule/' $(TDATA) | \
- $(ZIC) $$leap -d $@-t.dir - $(PACKRATDATA) && \
- diff -r $@.dir $@-t.dir;; \
- esac
- diff -r $@.dir $@-shrunk.dir
- rm -fr $@.dir $@-t.dir $@-shrunk.dir
- touch $@
-
-clean_misc:
- rm -fr check_*.dir
- rm -f *.o *.out $(TIME_T_ALTERNATIVES) \
- check_* core typecheck_* \
- date tzselect version.h zdump zic libtz.a
-clean: clean_misc
- rm -fr *.dir tzdb-*/
- rm -f *.zi $(TZS_NEW)
-
-maintainer-clean: clean
- @echo 'This command is intended for maintainers to use; it'
- @echo 'deletes files that may need special tools to rebuild.'
- rm -f leapseconds version $(MANTXTS) $(TZS) *.asc *.tar.*
-
-names:
- @echo $(ENCHILADA)
-
-public: check check_public $(CHECK_TIME_T_ALTERNATIVES) \
- tarballs signatures
-
-date.1.txt: date.1
-newctime.3.txt: newctime.3
-newstrftime.3.txt: newstrftime.3
-newtzset.3.txt: newtzset.3
-time2posix.3.txt: time2posix.3
-tzfile.5.txt: tzfile.5
-tzselect.8.txt: tzselect.8
-zdump.8.txt: zdump.8
-zic.8.txt: zic.8
-
-$(MANTXTS): workman.sh
- LC_ALL=C sh workman.sh `expr $@ : '\(.*\)\.txt$$'` >$@.out
- mv $@.out $@
-
-# Set file timestamps deterministically if possible,
-# so that tarballs containing the timestamps are reproducible.
-#
-# '$(SET_TIMESTAMP_N) N DEST A B C ...' sets the timestamp of the
-# file DEST to the maximum of the timestamps of the files A B C ...,
-# plus N if GNU ls and touch are available.
-SET_TIMESTAMP_N = sh -c '\
- n=$$0 dest=$$1; shift; \
- touch -cmr `ls -t "$$@" | sed 1q` "$$dest" && \
- if test $$n != 0 && \
- lsout=`ls -n --time-style="+%s" "$$dest" 2>/dev/null`; then \
- set x $$lsout && \
- touch -cmd @`expr $$7 + $$n` "$$dest"; \
- else :; fi'
-# If DEST depends on A B C ... in this Makefile, callers should use
-# $(SET_TIMESTAMP_DEP) DEST A B C ..., for the benefit of any
-# downstream 'make' that considers equal timestamps to be out of date.
-# POSIX allows this 'make' behavior, and HP-UX 'make' does it.
-# If all that matters is that the timestamp be reproducible
-# and plausible, use $(SET_TIMESTAMP).
-SET_TIMESTAMP = $(SET_TIMESTAMP_N) 0
-SET_TIMESTAMP_DEP = $(SET_TIMESTAMP_N) 1
-
-# Set the timestamps to those of the git repository, if available,
-# and if the files have not changed since then.
-# This uses GNU 'ls --time-style=+%s', which outputs the seconds count,
-# and GNU 'touch -d@N FILE', where N is the number of seconds since 1970.
-# If git or GNU is absent, don't bother to sync with git timestamps.
-# Also, set the timestamp of each prebuilt file like 'leapseconds'
-# to be the maximum of the files it depends on.
-set-timestamps.out: $(EIGHT_YARDS)
- rm -f $@
- if (type git) >/dev/null 2>&1 && \
- files=`git ls-files $(EIGHT_YARDS)` && \
- touch -md @1 test.out; then \
- rm -f test.out && \
- for file in $$files; do \
- if git diff --quiet $$file; then \
- time=`git log -1 --format='tformat:%ct' $$file` && \
- touch -cmd @$$time $$file; \
- else \
- echo >&2 "$$file: warning: does not match repository"; \
- fi || exit; \
- done; \
- fi
- $(SET_TIMESTAMP_DEP) leapseconds $(LEAP_DEPS)
- for file in `ls $(MANTXTS) | sed 's/\.txt$$//'`; do \
- $(SET_TIMESTAMP_DEP) $$file.txt $$file workman.sh || \
- exit; \
- done
- $(SET_TIMESTAMP_DEP) version $(VERSION_DEPS)
- $(SET_TIMESTAMP_DEP) tzdata.zi $(TZDATA_ZI_DEPS)
- touch $@
-set-tzs-timestamp.out: $(TZS)
- $(SET_TIMESTAMP_DEP) $(TZS) $(TZS_DEPS)
- touch $@
-
-# The zics below ensure that each data file can stand on its own.
-# We also do an all-files run to catch links to links.
-
-check_public: $(VERSION_DEPS)
- rm -fr public.dir
- mkdir public.dir
- ln $(VERSION_DEPS) public.dir
- cd public.dir && $(MAKE) CFLAGS='$(GCC_DEBUG_FLAGS)' ALL
- for i in $(TDATA_TO_CHECK) public.dir/tzdata.zi \
- public.dir/vanguard.zi public.dir/main.zi \
- public.dir/rearguard.zi; \
- do \
- public.dir/zic -v -d public.dir/zoneinfo $$i 2>&1 || exit; \
- done
- public.dir/zic -v -d public.dir/zoneinfo-all $(TDATA_TO_CHECK)
- :
- : Also check 'backzone' syntax.
- rm public.dir/main.zi
- cd public.dir && $(MAKE) PACKRATDATA=backzone main.zi
- public.dir/zic -d public.dir/zoneinfo main.zi
- rm public.dir/main.zi
- cd public.dir && \
- $(MAKE) PACKRATDATA=backzone PACKRATLIST=zone.tab main.zi
- public.dir/zic -d public.dir/zoneinfo main.zi
- :
- rm -fr public.dir
- touch $@
-
-# Check that the code works under various alternative
-# implementations of time_t.
-check_time_t_alternatives: $(TIME_T_ALTERNATIVES)
-$(TIME_T_ALTERNATIVES_TAIL): $(TIME_T_ALTERNATIVES_HEAD)
-$(TIME_T_ALTERNATIVES): $(VERSION_DEPS)
- rm -fr $@.dir
- mkdir $@.dir
- ln $(VERSION_DEPS) $@.dir
- case $@ in \
- int*32_t) range=-2147483648,2147483648;; \
- u*) range=0,4294967296;; \
- *) range=-4294967296,4294967296;; \
- esac && \
- wd=`pwd` && \
- zones=`$(AWK) '/^[^#]/ { print $$3 }' <zone1970.tab` && \
- if test $@ = $(TIME_T_ALTERNATIVES_HEAD); then \
- range_target=; \
- else \
- range_target=to$$range.tzs; \
- fi && \
- (cd $@.dir && \
- $(MAKE) TOPDIR="$$wd/$@.dir" \
- CFLAGS='$(CFLAGS) -Dtime_tz='"'$@'" \
- REDO='$(REDO)' \
- D=$$wd/$@.dir \
- TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
- install $$range_target) && \
- test $@ = $(TIME_T_ALTERNATIVES_HEAD) || { \
- (cd $(TIME_T_ALTERNATIVES_HEAD).dir && \
- $(MAKE) TOPDIR="$$wd/$@.dir" \
- TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
- D=$$wd/$@.dir \
- to$$range.tzs) && \
- diff -u $(TIME_T_ALTERNATIVES_HEAD).dir/to$$range.tzs \
- $@.dir/to$$range.tzs && \
- if diff -q Makefile Makefile 2>/dev/null; then \
- quiet_option='-q'; \
- else \
- quiet_option=''; \
- fi && \
- diff $$quiet_option -r $(TIME_T_ALTERNATIVES_HEAD).dir/etc \
- $@.dir/etc && \
- diff $$quiet_option -r \
- $(TIME_T_ALTERNATIVES_HEAD).dir/usr/share \
- $@.dir/usr/share; \
- }
- touch $@
-
-TRADITIONAL_ASC = \
- tzcode$(VERSION).tar.gz.asc \
- tzdata$(VERSION).tar.gz.asc
-REARGUARD_ASC = \
- tzdata$(VERSION)-rearguard.tar.gz.asc
-ALL_ASC = $(TRADITIONAL_ASC) $(REARGUARD_ASC) \
- tzdb-$(VERSION).tar.lz.asc
-
-tarballs rearguard_tarballs tailored_tarballs traditional_tarballs \
-signatures rearguard_signatures traditional_signatures: \
- version set-timestamps.out rearguard.zi vanguard.zi
- VERSION=`cat version` && \
- $(MAKE) AWK='$(AWK)' VERSION="$$VERSION" $@_version
-
-# These *_version rules are intended for use if VERSION is set by some
-# other means. Ordinarily these rules are used only by the above
-# non-_version rules, which set VERSION on the 'make' command line.
-tarballs_version: traditional_tarballs_version rearguard_tarballs_version \
- tzdb-$(VERSION).tar.lz
-rearguard_tarballs_version: \
- tzdata$(VERSION)-rearguard.tar.gz
-traditional_tarballs_version: \
- tzcode$(VERSION).tar.gz tzdata$(VERSION).tar.gz
-tailored_tarballs_version: \
- tzdata$(VERSION)-tailored.tar.gz
-signatures_version: $(ALL_ASC)
-rearguard_signatures_version: $(REARGUARD_ASC)
-traditional_signatures_version: $(TRADITIONAL_ASC)
-
-tzcode$(VERSION).tar.gz: set-timestamps.out
- LC_ALL=C && export LC_ALL && \
- tar $(TARFLAGS) -cf - \
- $(COMMON) $(DOCS) $(SOURCES) | \
- gzip $(GZIPFLAGS) >$@.out
- mv $@.out $@
-
-tzdata$(VERSION).tar.gz: set-timestamps.out
- LC_ALL=C && export LC_ALL && \
- tar $(TARFLAGS) -cf - $(TZDATA_DIST) | \
- gzip $(GZIPFLAGS) >$@.out
- mv $@.out $@
-
-# Create empty files with a reproducible timestamp.
-CREATE_EMPTY = TZ=UTC0 touch -mt 202010122253.00
-
-# The obsolescent *rearguard* targets and related macros are present
-# for backwards compatibility with tz releases 2018e through 2022a.
-# They should go away eventually. To build rearguard tarballs you
-# can instead use 'make DATAFORM=rearguard tailored_tarballs'.
-tzdata$(VERSION)-rearguard.tar.gz: rearguard.zi set-timestamps.out
- rm -fr $@.dir
- mkdir $@.dir
- ln $(TZDATA_DIST) $@.dir
- cd $@.dir && rm -f $(TDATA) $(PACKRATDATA) version
- for f in $(TDATA) $(PACKRATDATA); do \
- rearf=$@.dir/$$f; \
- $(AWK) -v DATAFORM=rearguard -f ziguard.awk $$f >$$rearf && \
- $(SET_TIMESTAMP_DEP) $$rearf ziguard.awk $$f || exit; \
- done
- sed '1s/$$/-rearguard/' <version >$@.dir/version
- : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
- $(CREATE_EMPTY) $@.dir/pacificnew
- touch -cmr version $@.dir/version
- LC_ALL=C && export LC_ALL && \
- (cd $@.dir && \
- tar $(TARFLAGS) -cf - \
- $(TZDATA_DIST) pacificnew | \
- gzip $(GZIPFLAGS)) >$@.out
- mv $@.out $@
-
-# Create a tailored tarball suitable for TZUpdater and compatible tools.
-# For example, 'make DATAFORM=vanguard tailored_tarballs' makes a tarball
-# useful for testing whether TZUpdater supports vanguard form.
-# The generated tarball is not byte-for-byte equivalent to a hand-tailored
-# traditional tarball, as data entries are put into 'etcetera' even if they
-# came from some other source file. However, the effect should be the same
-# for ordinary use, which reads all the source files.
-tzdata$(VERSION)-tailored.tar.gz: set-timestamps.out
- rm -fr $@.dir
- mkdir $@.dir
- : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
- cd $@.dir && \
- $(CREATE_EMPTY) $(PRIMARY_YDATA) $(NDATA) backward \
- `test $(DATAFORM) = vanguard || echo pacificnew`
- (grep '^#' tzdata.zi && echo && cat $(DATAFORM).zi) \
- >$@.dir/etcetera
- touch -cmr tzdata.zi $@.dir/etcetera
- sed -n \
- -e '/^# *version *\(.*\)/h' \
- -e '/^# *ddeps */H' \
- -e '$$!d' \
- -e 'g' \
- -e 's/^# *version *//' \
- -e 's/\n# *ddeps */-/' \
- -e 's/ /-/g' \
- -e 'p' \
- <tzdata.zi >$@.dir/version
- touch -cmr version $@.dir/version
- links= && \
- for file in $(TZDATA_DIST); do \
- test -f $@.dir/$$file || links="$$links $$file"; \
- done && \
- ln $$links $@.dir
- LC_ALL=C && export LC_ALL && \
- (cd $@.dir && \
- tar $(TARFLAGS) -cf - * | gzip $(GZIPFLAGS)) >$@.out
- mv $@.out $@
-
-tzdb-$(VERSION).tar.lz: set-timestamps.out set-tzs-timestamp.out
- rm -fr tzdb-$(VERSION)
- mkdir tzdb-$(VERSION)
- ln $(ENCHILADA) tzdb-$(VERSION)
- $(SET_TIMESTAMP) tzdb-$(VERSION) tzdb-$(VERSION)/*
- LC_ALL=C && export LC_ALL && \
- tar $(TARFLAGS) -cf - tzdb-$(VERSION) | lzip -9 >$@.out
- mv $@.out $@
-
-tzcode$(VERSION).tar.gz.asc: tzcode$(VERSION).tar.gz
-tzdata$(VERSION).tar.gz.asc: tzdata$(VERSION).tar.gz
-tzdata$(VERSION)-rearguard.tar.gz.asc: tzdata$(VERSION)-rearguard.tar.gz
-tzdb-$(VERSION).tar.lz.asc: tzdb-$(VERSION).tar.lz
-$(ALL_ASC):
- $(GPG) --armor --detach-sign $?
-
-TYPECHECK_CFLAGS = $(CFLAGS) -DTYPECHECK -D__time_t_defined -D_TIME_T
-typecheck: typecheck_long_long typecheck_unsigned
-typecheck_long_long typecheck_unsigned: $(VERSION_DEPS)
- rm -fr $@.dir
- mkdir $@.dir
- ln $(VERSION_DEPS) $@.dir
- cd $@.dir && \
- case $@ in \
- *_long_long) i="long long";; \
- *_unsigned ) i="unsigned" ;; \
- esac && \
- typecheck_cflags='' && \
- $(MAKE) \
- CFLAGS="$(TYPECHECK_CFLAGS) \"-Dtime_t=$$i\"" \
- TOPDIR="`pwd`" \
- install
- $@.dir/zdump -i -c 1970,1971 Europe/Rome
- touch $@
-
-zonenames: tzdata.zi
- @$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi
-
-asctime.o: private.h tzfile.h
-date.o: private.h
-difftime.o: private.h
-localtime.o: private.h tzfile.h
-strftime.o: private.h tzfile.h
-zdump.o: version.h
-zic.o: private.h tzfile.h version.h
-
-.PHONY: ALL INSTALL all
-.PHONY: check check_time_t_alternatives
-.PHONY: check_web check_zishrink
-.PHONY: clean clean_misc dummy.zd force_tzs
-.PHONY: install install_data maintainer-clean names
-.PHONY: posix_only posix_right public
-.PHONY: rearguard_signatures rearguard_signatures_version
-.PHONY: rearguard_tarballs rearguard_tarballs_version
-.PHONY: right_only right_posix signatures signatures_version
-.PHONY: tarballs tarballs_version
-.PHONY: traditional_signatures traditional_signatures_version
-.PHONY: traditional_tarballs traditional_tarballs_version
-.PHONY: tailored_tarballs tailored_tarballs_version
-.PHONY: typecheck
-.PHONY: zonenames zones
-.PHONY: $(ZDS)
Index: tz/create-2022f-version-patch/timezone-new
===================================================================
--- tz/create-2022f-version-patch/timezone-new (revision 384)
+++ tz/create-2022f-version-patch/timezone-new (nonexistent)
Property changes on: tz/create-2022f-version-patch/timezone-new
___________________________________________________________________
Deleted: svn:ignore
## -1,73 +0,0 ##
-
-# install dir
-dist
-
-# Target build dirs
-.a1x-newlib
-.a2x-newlib
-.at91sam7s-newlib
-
-.build-machine
-
-.a1x-glibc
-.a2x-glibc
-.h3-glibc
-.h5-glibc
-.i586-glibc
-.i686-glibc
-.imx6-glibc
-.jz47xx-glibc
-.makefile
-.am335x-glibc
-.omap543x-glibc
-.p5600-glibc
-.power8-glibc
-.power8le-glibc
-.power9-glibc
-.power9le-glibc
-.m1000-glibc
-.riscv64-glibc
-.rk328x-glibc
-.rk33xx-glibc
-.rk339x-glibc
-.s8xx-glibc
-.s9xx-glibc
-.x86_64-glibc
-
-# Hidden files (each file)
-.makefile
-.dist
-.rootfs
-
-# src & hw requires
-.src_requires
-.src_requires_depend
-.requires
-.requires_depend
-
-# Tarballs
-*.gz
-*.bz2
-*.lz
-*.xz
-*.tgz
-*.txz
-
-# Signatures
-*.asc
-*.sig
-*.sign
-*.sha1sum
-
-# Patches
-*.patch
-
-# Descriptions
-*.dsc
-*.txt
-
-# Default linux config files
-*.defconfig
-
-# backup copies
-*~
Index: tz/create-2022f-version-patch/create.patch.sh
===================================================================
--- tz/create-2022f-version-patch/create.patch.sh (revision 384)
+++ tz/create-2022f-version-patch/create.patch.sh (nonexistent)
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-VERSION=2022f
-
-mkdir -p timezone
-tar -C timezone --files-from=file.list -xzvf ../tzcode$VERSION.tar.gz
-mv timezone timezone-orig
-
-cp -rf ./timezone-new ./timezone
-
-diff --unified -Nr timezone-orig timezone > tzcode-$VERSION-version.patch
-
-mv tzcode-$VERSION-version.patch ../patches
-
-rm -rf ./timezone
-rm -rf ./timezone-orig
Property changes on: tz/create-2022f-version-patch/create.patch.sh
___________________________________________________________________
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: tz/create-2022f-version-patch
===================================================================
--- tz/create-2022f-version-patch (revision 384)
+++ tz/create-2022f-version-patch (nonexistent)
Property changes on: tz/create-2022f-version-patch
___________________________________________________________________
Deleted: svn:ignore
## -1,73 +0,0 ##
-
-# install dir
-dist
-
-# Target build dirs
-.a1x-newlib
-.a2x-newlib
-.at91sam7s-newlib
-
-.build-machine
-
-.a1x-glibc
-.a2x-glibc
-.h3-glibc
-.h5-glibc
-.i586-glibc
-.i686-glibc
-.imx6-glibc
-.jz47xx-glibc
-.makefile
-.am335x-glibc
-.omap543x-glibc
-.p5600-glibc
-.power8-glibc
-.power8le-glibc
-.power9-glibc
-.power9le-glibc
-.m1000-glibc
-.riscv64-glibc
-.rk328x-glibc
-.rk33xx-glibc
-.rk339x-glibc
-.s8xx-glibc
-.s9xx-glibc
-.x86_64-glibc
-
-# Hidden files (each file)
-.makefile
-.dist
-.rootfs
-
-# src & hw requires
-.src_requires
-.src_requires_depend
-.requires
-.requires_depend
-
-# Tarballs
-*.gz
-*.bz2
-*.lz
-*.xz
-*.tgz
-*.txz
-
-# Signatures
-*.asc
-*.sig
-*.sign
-*.sha1sum
-
-# Patches
-*.patch
-
-# Descriptions
-*.dsc
-*.txt
-
-# Default linux config files
-*.defconfig
-
-# backup copies
-*~
Index: tz/create-2022f-glibc-2.36-patch/file.list
===================================================================
--- tz/create-2022f-glibc-2.36-patch/file.list (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/file.list (nonexistent)
@@ -1,3 +0,0 @@
-private.h
-zdump.c
-zic.c
Index: tz/create-2022f-glibc-2.36-patch/create.patch.sh
===================================================================
--- tz/create-2022f-glibc-2.36-patch/create.patch.sh (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/create.patch.sh (nonexistent)
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-VERSION=2022f
-GLIBC_VERSION=2.36
-
-mkdir -p glibc-$GLIBC_VERSION/timezone
-tar -C glibc-$GLIBC_VERSION/timezone --files-from=file.list -xzvf ../tzcode$VERSION.tar.gz
-mv glibc-$GLIBC_VERSION glibc-$GLIBC_VERSION-orig
-
-cp -rf ./glibc-$GLIBC_VERSION-new ./glibc-$GLIBC_VERSION
-
-diff --unified -Nr glibc-$GLIBC_VERSION-orig glibc-$GLIBC_VERSION > glibc-$GLIBC_VERSION-tzcode-$VERSION.patch
-
-mv glibc-$GLIBC_VERSION-tzcode-$VERSION.patch ../patches
-
-rm -rf ./glibc-$GLIBC_VERSION
-rm -rf ./glibc-$GLIBC_VERSION-orig
Property changes on: tz/create-2022f-glibc-2.36-patch/create.patch.sh
___________________________________________________________________
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/zdump.c
===================================================================
--- tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/zdump.c (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/zdump.c (nonexistent)
@@ -1,1234 +0,0 @@
-/* Dump time zone data in a textual format. */
-
-/*
-** This file is in the public domain, so clarified as of
-** 2009-05-17 by Arthur David Olson.
-*/
-
-#include "version.h"
-
-#ifndef NETBSD_INSPIRED
-# define NETBSD_INSPIRED 1
-#endif
-
-#include "private.h"
-#include <stdio.h>
-
-#ifndef HAVE_SNPRINTF
-# define HAVE_SNPRINTF (199901 <= __STDC_VERSION__)
-#endif
-
-#ifndef HAVE_LOCALTIME_R
-# define HAVE_LOCALTIME_R 1
-#endif
-
-#ifndef HAVE_LOCALTIME_RZ
-# ifdef TM_ZONE
-# define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
-# else
-# define HAVE_LOCALTIME_RZ 0
-# endif
-#endif
-
-#ifndef HAVE_TZSET
-# define HAVE_TZSET 1
-#endif
-
-#ifndef ZDUMP_LO_YEAR
-# define ZDUMP_LO_YEAR (-500)
-#endif /* !defined ZDUMP_LO_YEAR */
-
-#ifndef ZDUMP_HI_YEAR
-# define ZDUMP_HI_YEAR 2500
-#endif /* !defined ZDUMP_HI_YEAR */
-
-#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
-#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
-#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
- + SECSPERLYEAR * (intmax_t) (100 - 3))
-
-/*
-** True if SECSPER400YEARS is known to be representable as an
-** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
-** even if SECSPER400YEARS is representable, because when that happens
-** the code merely runs a bit more slowly, and this slowness doesn't
-** occur on any practical platform.
-*/
-enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
-
-#if HAVE_GETTEXT
-# include <locale.h> /* for setlocale */
-#endif /* HAVE_GETTEXT */
-
-#if ! HAVE_LOCALTIME_RZ
-# undef timezone_t
-# define timezone_t char **
-#endif
-
-#if !HAVE_POSIX_DECLS
-extern int getopt(int argc, char * const argv[],
- const char * options);
-extern char * optarg;
-extern int optind;
-#endif
-
-/* The minimum and maximum finite time values. */
-enum { atime_shift = CHAR_BIT * sizeof(time_t) - 2 };
-static time_t const absolute_min_time =
- ((time_t) -1 < 0
- ? (- ((time_t) ~ (time_t) 0 < 0)
- - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
- : 0);
-static time_t const absolute_max_time =
- ((time_t) -1 < 0
- ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
- : -1);
-static int longest;
-static char * 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 void dumptime(struct tm const *);
-static time_t hunt(timezone_t, char *, 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;
-
-/* Is C an ASCII digit? */
-static bool
-is_digit(char c)
-{
- return '0' <= c && c <= '9';
-}
-
-/* Is A an alphabetic character in the C locale? */
-static bool
-is_alpha(char a)
-{
- switch (a) {
- default:
- return false;
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
- case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
- case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
- case 'V': case 'W': case 'X': case 'Y': case 'Z':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
- case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
- case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
- case 'v': case 'w': case 'x': case 'y': case 'z':
- return true;
- }
-}
-
-/* Return A + B, exiting if the result would overflow. */
-static size_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;
-}
-
-/* Return a pointer to a newly allocated buffer of size SIZE, exiting
- on failure. SIZE should be nonzero. */
-static void * ATTRIBUTE_MALLOC
-xmalloc(size_t size)
-{
- void *p = malloc(size);
- if (!p) {
- fprintf(stderr, _("%s: Memory exhausted\n"), progname);
- exit(EXIT_FAILURE);
- }
- return p;
-}
-
-#if ! HAVE_TZSET
-# undef tzset
-# define tzset zdump_tzset
-static void tzset(void) { }
-#endif
-
-/* Assume gmtime_r works if localtime_r does.
- A replacement localtime_r is defined below if needed. */
-#if ! HAVE_LOCALTIME_R
-
-# undef gmtime_r
-# define gmtime_r zdump_gmtime_r
-
-static struct tm *
-gmtime_r(time_t *tp, struct tm *tmp)
-{
- struct tm *r = gmtime(tp);
- if (r) {
- *tmp = *r;
- r = tmp;
- }
- return r;
-}
-
-#endif
-
-/* Platforms with TM_ZONE don't need tzname, so they can use the
- faster localtime_rz or localtime_r if available. */
-
-#if defined TM_ZONE && HAVE_LOCALTIME_RZ
-# define USE_LOCALTIME_RZ true
-#else
-# define USE_LOCALTIME_RZ false
-#endif
-
-#if ! USE_LOCALTIME_RZ
-
-# if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
-# undef localtime_r
-# define localtime_r zdump_localtime_r
-static struct tm *
-localtime_r(time_t *tp, struct tm *tmp)
-{
- struct tm *r = localtime(tp);
- if (r) {
- *tmp = *r;
- r = tmp;
- }
- return r;
-}
-# endif
-
-# undef localtime_rz
-# define localtime_rz zdump_localtime_rz
-static struct tm *
-localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp)
-{
- return localtime_r(tp, tmp);
-}
-
-# ifdef TYPECHECK
-# undef mktime_z
-# define mktime_z zdump_mktime_z
-static time_t
-mktime_z(timezone_t tz, struct tm *tmp)
-{
- return mktime(tmp);
-}
-# endif
-
-# undef tzalloc
-# undef tzfree
-# define tzalloc zdump_tzalloc
-# define tzfree zdump_tzfree
-
-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;
- 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;
- 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);
- }
- memcpy(env[0] + TZeqlen, val, valsize);
- initial_environ = environ;
- environ = env;
- tzset();
- free(freeable);
- return initial_environ;
-# endif
-}
-
-static void
-tzfree(timezone_t initial_environ)
-{
-# if !HAVE_SETENV
- environ = initial_environ;
- tzset();
-# endif
-}
-#endif /* ! USE_LOCALTIME_RZ */
-
-/* A UT time zone, and its initializer. */
-static timezone_t gmtz;
-static void
-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 "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT"
- fails, fall back on "GMT0" which might be similar due to the
- "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
- would not work on platforms that omit the "backward" file. */
- gmtz = tzalloc("GMT");
- if (!gmtz) {
- static char const gmt0[] = "GMT0";
- gmtz = tzalloc(gmt0);
- if (!gmtz) {
- perror(gmt0);
- exit(EXIT_FAILURE);
- }
- }
- }
-}
-
-/* Convert *TP to UT, storing the broken-down time into *TMP.
- Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP),
- except typically faster if USE_LOCALTIME_RZ. */
-static struct tm *
-my_gmtime_r(time_t *tp, struct tm *tmp)
-{
- return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
-}
-
-#ifndef TYPECHECK
-# define my_localtime_rz localtime_rz
-#else /* !defined TYPECHECK */
-
-static struct tm *
-my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
-{
- tmp = localtime_rz(tz, tp, tmp);
- if (tmp) {
- struct tm tm;
- register time_t t;
-
- tm = *tmp;
- t = mktime_z(tz, &tm);
- if (t != *tp) {
- fflush(stdout);
- fprintf(stderr, "\n%s: ", progname);
- fprintf(stderr, tformat(), *tp);
- fprintf(stderr, " ->");
- fprintf(stderr, " year=%d", tmp->tm_year);
- fprintf(stderr, " mon=%d", tmp->tm_mon);
- fprintf(stderr, " mday=%d", tmp->tm_mday);
- fprintf(stderr, " hour=%d", tmp->tm_hour);
- fprintf(stderr, " min=%d", tmp->tm_min);
- fprintf(stderr, " sec=%d", tmp->tm_sec);
- fprintf(stderr, " isdst=%d", tmp->tm_isdst);
- fprintf(stderr, " -> ");
- fprintf(stderr, tformat(), t);
- fprintf(stderr, "\n");
- errout = true;
- }
- }
- return tmp;
-}
-#endif /* !defined TYPECHECK */
-
-static void
-abbrok(const char *const abbrp, const char *const zone)
-{
- register const char * cp;
- register const char * wp;
-
- if (warned)
- return;
- cp = abbrp;
- while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
- ++cp;
- if (*cp)
- wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
- else if (cp - abbrp < 3)
- wp = _("has fewer than 3 characters");
- else if (cp - abbrp > 6)
- wp = _("has more than 6 characters");
- else
- return;
- fflush(stdout);
- fprintf(stderr,
- _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
- progname, zone, abbrp, wp);
- warned = errout = true;
-}
-
-/* Return a time zone abbreviation. If the abbreviation needs to be
- saved, use *BUF (of size *BUFALLOC) to save it, and return the
- abbreviation in the possibly-reallocated *BUF. Otherwise, just
- 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)
-{
- char const *ab = abbr(tmp);
- if (HAVE_LOCALTIME_RZ)
- return ab;
- else {
- size_t ablen = strlen(ab);
- if (*bufalloc <= ablen) {
- free(*buf);
-
- /* Make the new buffer at least twice as long as the old,
- to avoid O(N**2) behavior on repeated calls. */
- *bufalloc = sumsize(*bufalloc, ablen + 1);
-
- *buf = xmalloc(*bufalloc);
- }
- return strcpy(*buf, ab);
- }
-}
-
-static void
-close_file(FILE *stream)
-{
- char const *e = (ferror(stream) ? _("I/O error")
- : fclose(stream) != 0 ? strerror(errno) : NULL);
- if (e) {
- fprintf(stderr, "%s: %s\n", progname, e);
- exit(EXIT_FAILURE);
- }
-}
-
-static void
-usage(FILE * const stream, const int status)
-{
- fprintf(stream,
-_("%s: usage: %s OPTIONS TIMEZONE ...\n"
- "Options include:\n"
- " -c [L,]U Start at year L (default -500), end before year U (default 2500)\n"
- " -t [L,]U Start at time L, end before time U (in seconds since 1970)\n"
- " -i List transitions briefly (format is experimental)\n" \
- " -v List transitions verbosely\n"
- " -V List transitions a bit less verbosely\n"
- " --help Output this help\n"
- " --version Output version info\n"
- "\n"
- "Report bugs to %s.\n"),
- progname, progname, REPORT_BUGS_TO);
- if (status == EXIT_SUCCESS)
- close_file(stream);
- exit(status);
-}
-
-int
-main(int argc, char *argv[])
-{
- /* These are static so that they're initially zero. */
- static char * abbrev;
- static size_t abbrevsize;
-
- register int i;
- register bool vflag;
- register bool Vflag;
- register char * cutarg;
- register char * cuttimes;
- register time_t cutlotime;
- register time_t cuthitime;
- time_t now;
- bool iflag = false;
-
- cutlotime = absolute_min_time;
- cuthitime = absolute_max_time;
-#if HAVE_GETTEXT
- setlocale(LC_ALL, "");
-# ifdef TZ_DOMAINDIR
- bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-# endif /* defined TEXTDOMAINDIR */
- textdomain(TZ_DOMAIN);
-#endif /* HAVE_GETTEXT */
- progname = argv[0];
- for (i = 1; i < argc; ++i)
- if (strcmp(argv[i], "--version") == 0) {
- printf("zdump %s%s\n", PKGVERSION, TZVERSION);
- return EXIT_SUCCESS;
- } else if (strcmp(argv[i], "--help") == 0) {
- usage(stdout, EXIT_SUCCESS);
- }
- vflag = Vflag = false;
- cutarg = cuttimes = NULL;
- for (;;)
- switch (getopt(argc, argv, "c:it:vV")) {
- case 'c': cutarg = optarg; break;
- case 't': cuttimes = optarg; break;
- case 'i': iflag = true; break;
- case 'v': vflag = true; break;
- case 'V': Vflag = true; break;
- case -1:
- if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
- goto arg_processing_done;
- /* Fall through. */
- default:
- usage(stderr, EXIT_FAILURE);
- }
- arg_processing_done:;
-
- if (iflag | vflag | Vflag) {
- intmax_t lo;
- intmax_t hi;
- char *loend, *hiend;
- register intmax_t cutloyear = ZDUMP_LO_YEAR;
- register intmax_t cuthiyear = ZDUMP_HI_YEAR;
- if (cutarg != NULL) {
- lo = strtoimax(cutarg, &loend, 10);
- if (cutarg != loend && !*loend) {
- hi = lo;
- cuthiyear = hi;
- } else if (cutarg != loend && *loend == ','
- && (hi = strtoimax(loend + 1, &hiend, 10),
- loend + 1 != hiend && !*hiend)) {
- cutloyear = lo;
- cuthiyear = hi;
- } else {
- fprintf(stderr, _("%s: wild -c argument %s\n"),
- progname, cutarg);
- return EXIT_FAILURE;
- }
- }
- if (cutarg != NULL || cuttimes == NULL) {
- cutlotime = yeartot(cutloyear);
- cuthitime = yeartot(cuthiyear);
- }
- if (cuttimes != NULL) {
- lo = strtoimax(cuttimes, &loend, 10);
- if (cuttimes != loend && !*loend) {
- hi = lo;
- if (hi < cuthitime) {
- if (hi < absolute_min_time + 1)
- hi = absolute_min_time + 1;
- cuthitime = hi;
- }
- } else if (cuttimes != loend && *loend == ','
- && (hi = strtoimax(loend + 1, &hiend, 10),
- loend + 1 != hiend && !*hiend)) {
- if (cutlotime < lo) {
- if (absolute_max_time < lo)
- lo = absolute_max_time;
- cutlotime = lo;
- }
- if (hi < cuthitime) {
- if (hi < absolute_min_time + 1)
- hi = absolute_min_time + 1;
- cuthitime = hi;
- }
- } else {
- fprintf(stderr,
- _("%s: wild -t argument %s\n"),
- progname, cuttimes);
- return EXIT_FAILURE;
- }
- }
- }
- gmtzinit();
- if (iflag | vflag | Vflag)
- now = 0;
- else {
- now = time(NULL);
- now |= !now;
- }
- longest = 0;
- for (i = optind; i < argc; i++) {
- size_t arglen = strlen(argv[i]);
- if (longest < arglen)
- longest = min(arglen, INT_MAX);
- }
-
- for (i = optind; i < argc; ++i) {
- timezone_t tz = tzalloc(argv[i]);
- char const *ab = (char const *)0;
- time_t t;
- struct tm tm, newtm;
- bool tm_ok;
- if (!tz) {
- perror(argv[i]);
- return EXIT_FAILURE;
- }
- if (now) {
- show(tz, argv[i], now, false);
- tzfree(tz);
- continue;
- }
- warned = false;
- t = absolute_min_time;
- if (! (iflag | Vflag)) {
- show(tz, argv[i], t, true);
- if (my_localtime_rz(tz, &t, &tm) == NULL
- && t < cutlotime) {
- time_t newt = cutlotime;
- if (my_localtime_rz(tz, &newt, &newtm) != NULL)
- showextrema(tz, argv[i], t, NULL, newt);
- }
- }
- if (t + 1 < cutlotime)
- t = cutlotime - 1;
- tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
- if (tm_ok) {
- ab = saveabbr(&abbrev, &abbrevsize, &tm);
- if (iflag) {
- showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
- showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
- }
- } else
- ab = NULL;
- while (t < cuthitime - 1) {
- time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
- && t + SECSPERDAY / 2 < cuthitime - 1)
- ? t + SECSPERDAY / 2
- : cuthitime - 1);
- struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
- bool newtm_ok = newtmp != NULL;
- if (tm_ok != newtm_ok
- || (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);
- newtmp = localtime_rz(tz, &newt, &newtm);
- newtm_ok = newtmp != NULL;
- if (iflag)
- showtrans("%Y-%m-%d\t%L\t%Q", newtmp, newt,
- newtm_ok ? abbr(&newtm) : NULL, argv[i]);
- else {
- show(tz, argv[i], newt - 1, true);
- show(tz, argv[i], newt, true);
- }
- }
- t = newt;
- tm_ok = newtm_ok;
- if (newtm_ok) {
- ab = saveabbr(&abbrev, &abbrevsize, &newtm);
- tm = newtm;
- }
- }
- if (! (iflag | Vflag)) {
- time_t newt = absolute_max_time;
- t = cuthitime;
- if (t < newt) {
- struct tm *tmp = my_localtime_rz(tz, &t, &tm);
- if (tmp != NULL
- && my_localtime_rz(tz, &newt, &newtm) == NULL)
- showextrema(tz, argv[i], t, tmp, newt);
- }
- show(tz, argv[i], absolute_max_time, true);
- }
- tzfree(tz);
- }
- close_file(stdout);
- if (errout && (ferror(stderr) || fclose(stderr) != 0))
- return EXIT_FAILURE;
- return EXIT_SUCCESS;
-}
-
-static time_t
-yeartot(intmax_t y)
-{
- register intmax_t myy, seconds, years;
- register time_t t;
-
- myy = EPOCH_YEAR;
- t = 0;
- while (myy < y) {
- if (SECSPER400YEARS_FITS && 400 <= y - myy) {
- intmax_t diff400 = (y - myy) / 400;
- if (INTMAX_MAX / SECSPER400YEARS < diff400)
- return absolute_max_time;
- seconds = diff400 * SECSPER400YEARS;
- years = diff400 * 400;
- } else {
- seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
- years = 1;
- }
- myy += years;
- if (t > absolute_max_time - seconds)
- return absolute_max_time;
- t += seconds;
- }
- while (y < myy) {
- if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
- intmax_t diff400 = (myy - y) / 400;
- if (INTMAX_MAX / SECSPER400YEARS < diff400)
- return absolute_min_time;
- seconds = diff400 * SECSPER400YEARS;
- years = diff400 * 400;
- } else {
- seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
- years = 1;
- }
- myy -= years;
- if (t < absolute_min_time + seconds)
- return absolute_min_time;
- t -= seconds;
- }
- return t;
-}
-
-/* Search for a discontinuity in timezone TZ with name NAME, 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
- transition but fails on the other side. Return the timestamp just
- 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)
-{
- static char * loab;
- static size_t loabsize;
- struct tm lotm;
- struct tm tm;
-
- /* Convert LOT into a broken-down time here, even though our
- caller already did that. On platforms without TM_ZONE,
- tzname may have been altered since our caller broke down
- LOT, and tzname needs to be changed back. */
- bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
- bool tm_ok;
- char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL;
-
- for ( ; ; ) {
- /* T = average of LOT and HIT, rounding down.
- Avoid overflow, even on oddball C89 platforms
- where / rounds down and TIME_T_MIN == -TIME_T_MAX
- so lot / 2 + hit / 2 might overflow. */
- time_t t = (lot / 2
- - ((lot % 2 + hit % 2) < 0)
- + ((lot % 2 + hit % 2) == 2)
- + hit / 2);
- if (t == lot)
- break;
- tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
- if (lotm_ok == tm_ok
- && (only_ok
- || (ab && tm.tm_isdst == lotm.tm_isdst
- && delta(&tm, &lotm) == t - lot
- && strcmp(abbr(&tm), ab) == 0))) {
- lot = t;
- if (tm_ok)
- lotm = tm;
- } else hit = t;
- }
- return hit;
-}
-
-/*
-** Thanks to Paul Eggert for logic used in delta_nonneg.
-*/
-
-static intmax_t
-delta_nonneg(struct tm *newp, struct tm *oldp)
-{
- intmax_t oldy = oldp->tm_year;
- int cycles = (newp->tm_year - oldy) / YEARSPERREPEAT;
- intmax_t sec = SECSPERREPEAT, result = cycles * sec;
- int tmy = oldp->tm_year + cycles * YEARSPERREPEAT;
- for ( ; tmy < newp->tm_year; ++tmy)
- result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
- result += newp->tm_yday - oldp->tm_yday;
- result *= HOURSPERDAY;
- result += newp->tm_hour - oldp->tm_hour;
- result *= MINSPERHOUR;
- result += newp->tm_min - oldp->tm_min;
- result *= SECSPERMIN;
- result += newp->tm_sec - oldp->tm_sec;
- return result;
-}
-
-static intmax_t
-delta(struct tm *newp, struct tm *oldp)
-{
- return (newp->tm_year < oldp->tm_year
- ? -delta_nonneg(oldp, newp)
- : delta_nonneg(newp, oldp));
-}
-
-#ifndef TM_GMTOFF
-/* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
- Assume A and B differ by at most one year. */
-static int
-adjusted_yday(struct tm const *a, struct tm const *b)
-{
- int yday = a->tm_yday;
- if (b->tm_year < a->tm_year)
- yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
- return yday;
-}
-#endif
-
-/* If A is the broken-down local time and B the broken-down UT for
- the same instant, return A's UT offset in seconds, where positive
- offsets are east of Greenwich. On failure, return LONG_MIN.
-
- If T is nonnull, *T is the timestamp that corresponds to A; call
- 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)
-{
-#ifdef TM_GMTOFF
- return a->TM_GMTOFF;
-#else
- struct tm tm;
- if (t)
- b = my_gmtime_r(t, &tm);
- if (! b)
- return LONG_MIN;
- else {
- int ayday = adjusted_yday(a, b);
- int byday = adjusted_yday(b, a);
- int days = ayday - byday;
- long hours = a->tm_hour - b->tm_hour + 24 * days;
- long minutes = a->tm_min - b->tm_min + 60 * hours;
- long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
- return seconds;
- }
-#endif
-}
-
-static void
-show(timezone_t tz, char *zone, time_t t, bool v)
-{
- register struct tm * tmp;
- register struct tm * gmtmp;
- struct tm tm, gmtm;
-
- printf("%-*s ", longest, zone);
- if (v) {
- gmtmp = my_gmtime_r(&t, &gmtm);
- if (gmtmp == NULL) {
- printf(tformat(), t);
- printf(_(" (gmtime failed)"));
- } else {
- dumptime(gmtmp);
- printf(" UT");
- }
- printf(" = ");
- }
- tmp = my_localtime_rz(tz, &t, &tm);
- if (tmp == NULL) {
- printf(tformat(), t);
- printf(_(" (localtime failed)"));
- } else {
- dumptime(tmp);
- if (*abbr(tmp) != '\0')
- printf(" %s", abbr(tmp));
- if (v) {
- long off = gmtoff(tmp, NULL, gmtmp);
- printf(" isdst=%d", tmp->tm_isdst);
- if (off != LONG_MIN)
- printf(" gmtoff=%ld", off);
- }
- }
- printf("\n");
- if (tmp != NULL && *abbr(tmp) != '\0')
- abbrok(abbr(tmp), zone);
-}
-
-/* Show timestamps just before and just after a transition between
- defined and undefined (or vice versa) in either localtime or
- gmtime. These transitions are for timezone TZ with name ZONE, in
- the range from LO (with broken-down time LOTMP if that is nonnull)
- through HI. LO and HI disagree on definedness. */
-
-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);
- bool old = false;
- hi = (SECSPERDAY < hi - boundary
- ? boundary + SECSPERDAY
- : hi + (hi < TIME_T_MAX));
- if (SECSPERDAY < boundary - lo) {
- lo = boundary - SECSPERDAY;
- lotmp = my_localtime_rz(tz, &lo, &localtm[old]);
- }
- if (lotmp)
- localtm[old] = *lotmp;
- else
- localtm[old].tm_sec = -1;
- if (! my_gmtime_r(&lo, &gmtm[old]))
- gmtm[old].tm_sec = -1;
-
- /* Search sequentially for definedness transitions. Although this
- could be sped up by refining 'hunt' to search for either
- localtime or gmtime definedness transitions, it hardly seems
- worth the trouble. */
- for (t = lo + 1; t < hi; t++) {
- bool new = !old;
- if (! my_localtime_rz(tz, &t, &localtm[new]))
- localtm[new].tm_sec = -1;
- if (! my_gmtime_r(&t, &gmtm[new]))
- gmtm[new].tm_sec = -1;
- if (((localtm[old].tm_sec < 0) != (localtm[new].tm_sec < 0))
- | ((gmtm[old].tm_sec < 0) != (gmtm[new].tm_sec < 0))) {
- show(tz, zone, t - 1, true);
- show(tz, zone, t, true);
- }
- old = new;
- }
-}
-
-#if HAVE_SNPRINTF
-# define my_snprintf snprintf
-#else
-# include <stdarg.h>
-
-/* A substitute for snprintf that is good enough for zdump. */
-static int ATTRIBUTE_FORMAT((printf, 3, 4))
-my_snprintf(char *s, size_t size, char const *format, ...)
-{
- int n;
- va_list args;
- char const *arg;
- size_t arglen, slen;
- char buf[1024];
- va_start(args, format);
- if (strcmp(format, "%s") == 0) {
- arg = va_arg(args, char const *);
- arglen = strlen(arg);
- } else {
- n = vsprintf(buf, format, args);
- if (n < 0) {
- va_end(args);
- return n;
- }
- arg = buf;
- arglen = n;
- }
- slen = arglen < size ? arglen : size - 1;
- memcpy(s, arg, slen);
- s[slen] = '\0';
- n = arglen <= INT_MAX ? arglen : -1;
- va_end(args);
- return n;
-}
-#endif
-
-/* Store into BUF, of size SIZE, a formatted local time taken from *TM.
- Use ISO 8601 format +HH:MM:SS. Omit :SS if SS is zero, and omit
- :MM too if MM is also zero.
-
- Return the length of the resulting string. If the string does not
- 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)
-{
- int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
- return (ss
- ? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
- : mm
- ? my_snprintf(buf, size, "%02d:%02d", hh, mm)
- : my_snprintf(buf, size, "%02d", hh));
-}
-
-/* Store into BUF, of size SIZE, a formatted UT offset for the
- localtime *TM corresponding to time T. Use ISO 8601 format
- +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
- format -00 for unknown UT offsets. If the hour needs more than
- two digits to represent, extend the length of HH as needed.
- Otherwise, omit SS if SS is zero, and omit MM too if MM is also
- zero.
-
- Return the length of the resulting string, or -1 if the result is
- not representable as a string. If the string does not fit, return
- 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)
-{
- long off = gmtoff(tm, &t, NULL);
- char sign = ((off < 0
- || (off == 0
- && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0)))
- ? '-' : '+');
- long hh;
- int mm, ss;
- if (off < 0)
- {
- if (off == LONG_MIN)
- return -1;
- off = -off;
- }
- ss = off % 60;
- mm = off / 60 % 60;
- hh = off / 60 / 60;
- return (ss || 100 <= hh
- ? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
- : mm
- ? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
- : my_snprintf(buf, size, "%c%02ld", sign, hh));
-}
-
-/* Store into BUF (of size SIZE) a quoted string representation of P.
- 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)
-{
- char *b = buf;
- size_t s = size;
- if (!s)
- return size;
- *b++ = '"', s--;
- for (;;) {
- char c = *p++;
- if (s <= 1)
- return size;
- switch (c) {
- default: *b++ = c, s--; continue;
- case '\0': *b++ = '"', s--; return size - s;
- case '"': case '\\': break;
- case ' ': c = 's'; break;
- case '\f': c = 'f'; break;
- case '\n': c = 'n'; break;
- case '\r': c = 'r'; break;
- case '\t': c = 't'; break;
- case '\v': c = 'v'; break;
- }
- *b++ = '\\', *b++ = c, s -= 2;
- }
-}
-
-/* Store into BUF (of size SIZE) a timestamp formatted by TIME_FMT.
- TM is the broken-down time, T the seconds count, AB the time zone
- abbreviation, and ZONE_NAME the zone name. Return true if
- successful, false if the output would require more than SIZE bytes.
- TIME_FMT uses the same format that strftime uses, with these
- additions:
-
- %f zone name
- %L local time as per format_local_time
- %Q like "U\t%Z\tD" where U is the UT offset as for format_utc_offset
- and D is the isdst flag; except omit D if it is zero, omit %Z if
- it equals U, quote and escape %Z if it contains nonalphabetics,
- and omit any trailing tabs. */
-
-static bool
-istrftime(char *buf, size_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;
- char const *f = time_fmt, *p;
-
- for (p = f; ; p++)
- if (*p == '%' && p[1] == '%')
- p++;
- 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;
- char fbuf[100];
- bool oversized = sizeof fbuf <= f_prefix_copy_size;
- char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
- memcpy(f_prefix_copy, f, f_prefix_len);
- strcpy(f_prefix_copy + f_prefix_len, "X");
- formatted_len = strftime(b, s, f_prefix_copy, tm);
- if (oversized)
- free(f_prefix_copy);
- if (formatted_len == 0)
- return false;
- formatted_len--;
- b += formatted_len, s -= formatted_len;
- if (!*p++)
- break;
- switch (*p) {
- case 'f':
- formatted_len = format_quoted_string(b, s, zone_name);
- break;
- case 'L':
- formatted_len = format_local_time(b, s, tm);
- break;
- case 'Q':
- {
- bool show_abbr;
- int offlen = format_utc_offset(b, s, tm, t);
- if (! (0 <= offlen && offlen < s))
- return false;
- show_abbr = strcmp(b, ab) != 0;
- b += offlen, s -= offlen;
- if (show_abbr) {
- char const *abp;
- size_t len;
- if (s <= 1)
- return false;
- *b++ = '\t', s--;
- for (abp = ab; is_alpha(*abp); abp++)
- continue;
- len = (!*abp && *ab
- ? my_snprintf(b, s, "%s", ab)
- : format_quoted_string(b, s, ab));
- if (s <= len)
- return false;
- b += len, s -= len;
- }
- formatted_len
- = (tm->tm_isdst
- ? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
- : 0);
- }
- break;
- }
- if (s <= formatted_len)
- return false;
- b += formatted_len, s -= formatted_len;
- f = p + 1;
- }
- *b = '\0';
- return true;
-}
-
-/* Show a time transition. */
-static void
-showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
- char const *zone_name)
-{
- if (!tm) {
- printf(tformat(), t);
- putchar('\n');
- } else {
- char stackbuf[1000];
- size_t size = sizeof stackbuf;
- char *buf = stackbuf;
- char *bufalloc = NULL;
- while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
- size = sumsize(size, size);
- free(bufalloc);
- buf = bufalloc = xmalloc(size);
- }
- puts(buf);
- free(bufalloc);
- }
-}
-
-static char const *
-abbr(struct tm const *tmp)
-{
-#ifdef TM_ZONE
- return tmp->TM_ZONE;
-#else
-# if HAVE_TZNAME
- if (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst])
- return tzname[0 < tmp->tm_isdst];
-# endif
- return "";
-#endif
-}
-
-/*
-** The code below can fail on certain theoretical systems;
-** it works on all known real-world systems as of 2022-01-25.
-*/
-
-static const char *
-tformat(void)
-{
-#if HAVE_GENERIC
- /* C11-style _Generic is more likely to return the correct
- format when distinct types have the same size. */
- char const *fmt =
- _Generic(+ (time_t) 0,
- int: "%d", long: "%ld", long long: "%lld",
- unsigned: "%u", unsigned long: "%lu",
- unsigned long long: "%llu",
- default: NULL);
- if (fmt)
- return fmt;
- fmt = _Generic((time_t) 0,
- intmax_t: "%"PRIdMAX, uintmax_t: "%"PRIuMAX,
- default: NULL);
- if (fmt)
- return fmt;
-#endif
- if (0 > (time_t) -1) { /* signed */
- if (sizeof(time_t) == sizeof(intmax_t))
- return "%"PRIdMAX;
- if (sizeof(time_t) > sizeof(long))
- return "%lld";
- if (sizeof(time_t) > sizeof(int))
- return "%ld";
- return "%d";
- }
-#ifdef PRIuMAX
- if (sizeof(time_t) == sizeof(uintmax_t))
- return "%"PRIuMAX;
-#endif
- if (sizeof(time_t) > sizeof(unsigned long))
- return "%llu";
- if (sizeof(time_t) > sizeof(unsigned int))
- return "%lu";
- return "%u";
-}
-
-static void
-dumptime(register const struct tm *timeptr)
-{
- static const char wday_name[][4] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
- };
- static const char mon_name[][4] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
- register int lead;
- register int trail;
- int DIVISOR = 10;
-
- /*
- ** The packaged localtime_rz and gmtime_r never put out-of-range
- ** values in tm_wday or tm_mon, but since this code might be compiled
- ** with other (perhaps experimental) versions, paranoia is in order.
- */
- printf("%s %s%3d %.2d:%.2d:%.2d ",
- ((0 <= timeptr->tm_wday
- && timeptr->tm_wday < sizeof wday_name / sizeof wday_name[0])
- ? wday_name[timeptr->tm_wday] : "???"),
- ((0 <= timeptr->tm_mon
- && timeptr->tm_mon < sizeof mon_name / sizeof mon_name[0])
- ? mon_name[timeptr->tm_mon] : "???"),
- timeptr->tm_mday, timeptr->tm_hour,
- timeptr->tm_min, timeptr->tm_sec);
- trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
- lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
- trail / DIVISOR;
- trail %= DIVISOR;
- if (trail < 0 && lead > 0) {
- trail += DIVISOR;
- --lead;
- } else if (lead < 0 && trail > 0) {
- trail -= DIVISOR;
- ++lead;
- }
- if (lead == 0)
- printf("%d", trail);
- else printf("%d%d", lead, ((trail < 0) ? -trail : trail));
-}
Index: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/zic.c
===================================================================
--- tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/zic.c (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/zic.c (nonexistent)
@@ -1,3881 +0,0 @@
-/* Compile .zi time zone data into TZif binary files. */
-
-/*
-** This file is in the public domain, so clarified as of
-** 2006-07-17 by Arthur David Olson.
-*/
-
-/* Use the system 'time' function, instead of any private replacement.
- This avoids creating an unnecessary dependency on localtime.c. */
-#undef EPOCH_LOCAL
-#undef EPOCH_OFFSET
-#undef RESERVE_STD_EXT_IDS
-#undef time_tz
-
-#include "version.h"
-#include "private.h"
-#include "tzfile.h"
-
-#include <fcntl.h>
-#include <locale.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-typedef int_fast64_t zic_t;
-static zic_t const
- ZIC_MIN = INT_FAST64_MIN,
- ZIC_MAX = INT_FAST64_MAX,
- ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
- ZIC32_MAX = 0x7fffffff;
-#define SCNdZIC SCNdFAST64
-
-#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
-# define ZIC_MAX_ABBR_LEN_WO_WARN 6
-#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
-
-#ifdef HAVE_DIRECT_H
-# include <direct.h>
-# include <io.h>
-# undef mkdir
-# 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
-#ifdef S_IRUSR
-# define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
-#else
-# 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)
-#elif __STDC_VERSION__ < 202311
-# include <stdalign.h>
-#endif
-
-/* The maximum length of a text line, including the trailing newline. */
-#ifndef _POSIX2_LINE_MAX
-# define _POSIX2_LINE_MAX 2048
-#endif
-
-/* The type for line numbers. Use PRIdMAX to format them; formerly
- there was also "#define PRIdLINENO PRIdMAX" and formats used
- PRIdLINENO, but xgettext cannot grok that. */
-typedef intmax_t lineno;
-
-struct rule {
- int r_filenum;
- lineno r_linenum;
- const char * r_name;
-
- zic_t r_loyear; /* for example, 1986 */
- zic_t r_hiyear; /* for example, 1986 */
- bool r_lowasnum;
- bool r_hiwasnum;
-
- int r_month; /* 0..11 */
-
- int r_dycode; /* see below */
- int r_dayofmonth;
- int r_wday;
-
- zic_t r_tod; /* time from midnight */
- bool r_todisstd; /* is r_tod standard time? */
- bool r_todisut; /* is r_tod UT? */
- bool r_isdst; /* is this daylight saving time? */
- zic_t r_save; /* offset from standard time */
- const char * r_abbrvar; /* variable part of abbreviation */
-
- bool r_todo; /* a rule to do (used in outzone) */
- zic_t r_temp; /* used in outzone */
-};
-
-/*
-** r_dycode r_dayofmonth r_wday
-*/
-enum {
- DC_DOM, /* 1..31 */ /* unused */
- DC_DOWGEQ, /* 1..31 */ /* 0..6 (Sun..Sat) */
- DC_DOWLEQ /* 1..31 */ /* 0..6 (Sun..Sat) */
-};
-
-struct zone {
- int z_filenum;
- lineno z_linenum;
-
- const char * z_name;
- zic_t z_stdoff;
- char * z_rule;
- const char * z_format;
- char z_format_specifier;
-
- bool z_isdst;
- zic_t z_save;
-
- struct rule * z_rules;
- ptrdiff_t z_nrules;
-
- struct rule z_untilrule;
- zic_t z_untiltime;
-};
-
-#if !HAVE_POSIX_DECLS
-extern int getopt(int argc, char * const argv[],
- const char * options);
-extern int link(const char * target, const char * linkname);
-extern char * optarg;
-extern int optind;
-#endif
-
-#if ! HAVE_SYMLINK
-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
-# 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);
-static int addtype(zic_t, char const *, bool, bool, bool);
-static void leapadd(zic_t, int, int);
-static void adjleap(void);
-static void associate(void);
-static void dolink(const char *, const char *, bool);
-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(int, char const *);
-static void inleap(char ** fields, int nfields);
-static void inlink(char ** fields, int nfields);
-static void inrule(char ** fields, int nfields);
-static bool inzcont(char ** fields, int nfields);
-static bool inzone(char ** fields, int nfields);
-static bool inzsub(char **, int, bool);
-static bool itssymlink(char const *);
-static bool is_alpha(char a);
-static char lowerit(char);
-static void mkdirs(char const *, bool);
-static void newabbr(const char * abbr);
-static zic_t oadd(zic_t t1, zic_t t2);
-static void outzone(const struct zone * zp, ptrdiff_t ntzones);
-static zic_t rpytime(const struct rule * rp, zic_t wantedy);
-static bool rulesub(struct rule * rp,
- const char * loyearp, const char * hiyearp,
- const char * typep, const char * monthp,
- const char * dayp, const char * timep);
-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 };
-
-static int charcnt;
-static bool errors;
-static bool warnings;
-static int filenum;
-static int leapcnt;
-static bool leapseen;
-static zic_t leapminyear;
-static zic_t leapmaxyear;
-static lineno linenum;
-static int max_abbrvar_len = PERCENT_Z_LEN_BOUND;
-static int max_format_len;
-static zic_t max_year;
-static zic_t min_year;
-static bool noise;
-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;
-static int unspecifiedtype;
-
-/*
-** Line codes.
-*/
-
-enum {
- LC_RULE,
- LC_ZONE,
- LC_LINK,
- LC_LEAP,
- LC_EXPIRES
-};
-
-/*
-** Which fields are which on a Zone line.
-*/
-
-enum {
- ZF_NAME = 1,
- ZF_STDOFF,
- ZF_RULE,
- ZF_FORMAT,
- ZF_TILYEAR,
- ZF_TILMONTH,
- ZF_TILDAY,
- ZF_TILTIME,
- ZONE_MAXFIELDS,
- ZONE_MINFIELDS = ZF_TILYEAR
-};
-
-/*
-** Which fields are which on a Zone continuation line.
-*/
-
-enum {
- ZFC_STDOFF,
- ZFC_RULE,
- ZFC_FORMAT,
- ZFC_TILYEAR,
- ZFC_TILMONTH,
- ZFC_TILDAY,
- ZFC_TILTIME,
- ZONEC_MAXFIELDS,
- ZONEC_MINFIELDS = ZFC_TILYEAR
-};
-
-/*
-** Which files are which on a Rule line.
-*/
-
-enum {
- RF_NAME = 1,
- RF_LOYEAR,
- RF_HIYEAR,
- RF_COMMAND,
- RF_MONTH,
- RF_DAY,
- RF_TOD,
- RF_SAVE,
- RF_ABBRVAR,
- RULE_FIELDS
-};
-
-/*
-** Which fields are which on a Link line.
-*/
-
-enum {
- LF_TARGET = 1,
- LF_LINKNAME,
- LINK_FIELDS
-};
-
-/*
-** Which fields are which on a Leap line.
-*/
-
-enum {
- LP_YEAR = 1,
- LP_MONTH,
- LP_DAY,
- LP_TIME,
- LP_CORR,
- LP_ROLL,
- LEAP_FIELDS,
-
- /* Expires lines are like Leap lines, except without CORR and ROLL fields. */
- EXPIRES_FIELDS = LP_TIME + 1
-};
-
-/* The maximum number of fields on any of the above lines.
- (The "+"s pacify gcc -Wenum-compare.) */
-enum {
- MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
- max(+LEAP_FIELDS, +EXPIRES_FIELDS))
-};
-
-/*
-** Year synonyms.
-*/
-
-enum {
- YR_MINIMUM,
- YR_MAXIMUM,
- YR_ONLY
-};
-
-static struct rule * rules;
-static ptrdiff_t nrules; /* number of rules */
-static ptrdiff_t nrules_alloc;
-
-static struct zone * zones;
-static ptrdiff_t nzones; /* number of zones */
-static ptrdiff_t nzones_alloc;
-
-struct link {
- int l_filenum;
- lineno l_linenum;
- const char * l_target;
- const char * l_linkname;
-};
-
-static struct link * links;
-static ptrdiff_t nlinks;
-static ptrdiff_t nlinks_alloc;
-
-struct lookup {
- const char * l_word;
- const int l_value;
-};
-
-static struct lookup const * byword(const char * string,
- const struct lookup * lp);
-
-static struct lookup const zi_line_codes[] = {
- { "Rule", LC_RULE },
- { "Zone", LC_ZONE },
- { "Link", LC_LINK },
- { NULL, 0 }
-};
-static struct lookup const leap_line_codes[] = {
- { "Leap", LC_LEAP },
- { "Expires", LC_EXPIRES },
- { NULL, 0}
-};
-
-static struct lookup const mon_names[] = {
- { "January", TM_JANUARY },
- { "February", TM_FEBRUARY },
- { "March", TM_MARCH },
- { "April", TM_APRIL },
- { "May", TM_MAY },
- { "June", TM_JUNE },
- { "July", TM_JULY },
- { "August", TM_AUGUST },
- { "September", TM_SEPTEMBER },
- { "October", TM_OCTOBER },
- { "November", TM_NOVEMBER },
- { "December", TM_DECEMBER },
- { NULL, 0 }
-};
-
-static struct lookup const wday_names[] = {
- { "Sunday", TM_SUNDAY },
- { "Monday", TM_MONDAY },
- { "Tuesday", TM_TUESDAY },
- { "Wednesday", TM_WEDNESDAY },
- { "Thursday", TM_THURSDAY },
- { "Friday", TM_FRIDAY },
- { "Saturday", TM_SATURDAY },
- { NULL, 0 }
-};
-
-static struct lookup const lasts[] = {
- { "last-Sunday", TM_SUNDAY },
- { "last-Monday", TM_MONDAY },
- { "last-Tuesday", TM_TUESDAY },
- { "last-Wednesday", TM_WEDNESDAY },
- { "last-Thursday", TM_THURSDAY },
- { "last-Friday", TM_FRIDAY },
- { "last-Saturday", TM_SATURDAY },
- { NULL, 0 }
-};
-
-static struct lookup const begin_years[] = {
- { "minimum", YR_MINIMUM },
- { "maximum", YR_MAXIMUM },
- { NULL, 0 }
-};
-
-static struct lookup const end_years[] = {
- { "minimum", YR_MINIMUM },
- { "maximum", YR_MAXIMUM },
- { "only", YR_ONLY },
- { NULL, 0 }
-};
-
-static struct lookup const leap_types[] = {
- { "Rolling", true },
- { "Stationary", false },
- { NULL, 0 }
-};
-
-static const int len_months[2][MONSPERYEAR] = {
- { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
-};
-
-static const int len_years[2] = {
- DAYSPERNYEAR, DAYSPERLYEAR
-};
-
-static struct attype {
- zic_t at;
- bool dontmerge;
- unsigned char type;
-} * attypes;
-static zic_t utoffs[TZ_MAX_TYPES];
-static char isdsts[TZ_MAX_TYPES];
-static unsigned char desigidx[TZ_MAX_TYPES];
-static bool ttisstds[TZ_MAX_TYPES];
-static bool ttisuts[TZ_MAX_TYPES];
-static char chars[TZ_MAX_CHARS];
-static zic_t trans[TZ_MAX_LEAPS];
-static zic_t corr[TZ_MAX_LEAPS];
-static char roll[TZ_MAX_LEAPS];
-
-/*
-** Memory allocation.
-*/
-
-static _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)
-{
- if (SIZE_MAX / itemsize < nitems)
- memory_exhausted(_("size overflow"));
- return nitems * itemsize;
-}
-
-static ATTRIBUTE_PURE size_t
-align_to(size_t size, size_t alignment)
-{
- size_t aligned_size = size + alignment - 1;
- aligned_size -= aligned_size % alignment;
- if (aligned_size < size)
- memory_exhausted(_("alignment overflow"));
- return aligned_size;
-}
-
-#if !HAVE_STRDUP
-static char *
-strdup(char const *str)
-{
- char *result = malloc(strlen(str) + 1);
- return result ? strcpy(result, str) : result;
-}
-#endif
-
-static void *
-memcheck(void *ptr)
-{
- if (ptr == NULL)
- memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM));
- return ptr;
-}
-
-static void * ATTRIBUTE_MALLOC
-emalloc(size_t size)
-{
- return memcheck(malloc(size));
-}
-
-static void *
-erealloc(void *ptr, size_t size)
-{
- return memcheck(realloc(ptr, size));
-}
-
-static char * ATTRIBUTE_MALLOC
-ecpyalloc(char const *str)
-{
- return memcheck(strdup(str));
-}
-
-static void *
-growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
-{
- if (nitems < *nitems_alloc)
- return ptr;
- else {
- ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX);
- if ((amax - 1) / 3 * 2 < *nitems_alloc)
- memory_exhausted(_("integer overflow"));
- *nitems_alloc += (*nitems_alloc >> 1) + 1;
- return erealloc(ptr, size_product(*nitems_alloc, itemsize));
- }
-}
-
-/*
-** Error handling.
-*/
-
-/* 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)
-{
- 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;
- rfilenum = rfnum;
- rlinenum = rnum;
-}
-
-static void
-eat(int fnum, lineno num)
-{
- eats(fnum, num, 0, -1);
-}
-
-static void ATTRIBUTE_FORMAT((printf, 1, 0))
-verror(const char *const string, va_list args)
-{
- /*
- ** Match the format of "cc" to allow sh users to
- ** zic ... 2>&1 | error -t "*" -v
- ** on BSD systems.
- */
- if (filenum)
- fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
- filename(filenum), linenum);
- vfprintf(stderr, string, args);
- if (rfilenum)
- fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
- filename(rfilenum), rlinenum);
- fprintf(stderr, "\n");
-}
-
-static void ATTRIBUTE_FORMAT((printf, 1, 2))
-error(const char *const string, ...)
-{
- va_list args;
- va_start(args, string);
- verror(string, args);
- va_end(args);
- errors = true;
-}
-
-static void ATTRIBUTE_FORMAT((printf, 1, 2))
-warning(const char *const string, ...)
-{
- va_list args;
- fprintf(stderr, _("warning: "));
- va_start(args, string);
- verror(string, args);
- va_end(args);
- warnings = true;
-}
-
-/* Close STREAM. If it had an I/O error, report it against DIR/NAME,
- remove TEMPNAME if nonnull, and then exit. */
-static void
-close_file(FILE *stream, char const *dir, char const *name,
- char const *tempname)
-{
- char const *e = (ferror(stream) ? _("I/O error")
- : fclose(stream) != 0 ? strerror(errno) : NULL);
- if (e) {
- fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
- dir ? dir : "", dir ? "/" : "",
- name ? name : "", name ? ": " : "",
- e);
- if (tempname)
- remove(tempname);
- exit(EXIT_FAILURE);
- }
-}
-
-static _Noreturn void
-usage(FILE *stream, int status)
-{
- fprintf(stream,
- _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
- "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
- " [ -L leapseconds ] \\\n"
- "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
- "\t[ -t localtime-link ] \\\n"
- "\t[ filename ... ]\n\n"
- "Report bugs to %s.\n"),
- progname, progname, REPORT_BUGS_TO);
- if (status == EXIT_SUCCESS)
- close_file(stream, NULL, NULL, NULL);
- exit(status);
-}
-
-/* Change the working directory to DIR, possibly creating DIR and its
- ancestors. After this is done, all files are accessed with names
- relative to DIR. */
-static void
-change_directory(char const *dir)
-{
- if (chdir(dir) != 0) {
- int chdir_errno = errno;
- if (chdir_errno == ENOENT) {
- mkdirs(dir, false);
- chdir_errno = chdir(dir) == 0 ? 0 : errno;
- }
- if (chdir_errno != 0) {
- fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
- progname, dir, strerror(chdir_errno));
- exit(EXIT_FAILURE);
- }
- }
-}
-
-/* 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. */
-
-static sig_atomic_t got_signal;
-
-static void
-signal_handler(int sig)
-{
-#ifndef SA_SIGINFO
- signal(sig, signal_handler);
-#endif
- got_signal = sig;
-}
-
-/* Arrange for SIGINT etc. to be caught by the handler. */
-static void
-catch_signals(void)
-{
- static int const signals[] = {
-#ifdef SIGHUP
- SIGHUP,
-#endif
- SIGINT,
-#ifdef SIGPIPE
- SIGPIPE,
-#endif
- SIGTERM
- };
- int i;
- for (i = 0; i < sizeof signals / sizeof signals[0]; i++) {
-#ifdef SA_SIGINFO
- struct sigaction act0, act;
- act.sa_handler = signal_handler;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- if (sigaction(signals[i], &act, &act0) == 0
- && ! (act0.sa_flags & SA_SIGINFO) && act0.sa_handler == SIG_IGN) {
- sigaction(signals[i], &act0, NULL);
- got_signal = 0;
- }
-#else
- if (signal(signals[i], signal_handler) == SIG_IGN) {
- signal(signals[i], SIG_IGN);
- got_signal = 0;
- }
-#endif
- }
-}
-
-/* If a signal has arrived, terminate zic with appropriate status. */
-static void
-check_for_signal(void)
-{
- int sig = got_signal;
- if (sig) {
- signal(sig, SIG_DFL);
- raise(sig);
- abort(); /* A bug in 'raise'. */
- }
-}
-
-enum { TIME_T_BITS_IN_FILE = 64 };
-
-/* The minimum and maximum values representable in a TZif file. */
-static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
-static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
-
-/* The minimum, and one less than the maximum, values specified by
- the -r option. These default to MIN_TIME and MAX_TIME. */
-static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
-static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
-
-/* The time specified by the -R option, defaulting to MIN_TIME. */
-static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
-
-/* The time specified by an Expires line, or negative if no such line. */
-static zic_t leapexpires = -1;
-
-/* Set the time range of the output to TIMERANGE.
- Return true if successful. */
-static bool
-timerange_option(char *timerange)
-{
- intmax_t lo = min_time, hi = max_time;
- char *lo_end = timerange, *hi_end;
- if (*timerange == '@') {
- errno = 0;
- lo = strtoimax(timerange + 1, &lo_end, 10);
- if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
- return false;
- }
- hi_end = lo_end;
- if (lo_end[0] == '/' && lo_end[1] == '@') {
- errno = 0;
- hi = strtoimax(lo_end + 2, &hi_end, 10);
- if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
- return false;
- hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
- }
- if (*hi_end || hi < lo || max_time < lo || hi < min_time)
- return false;
- lo_time = max(lo, min_time);
- hi_time = min(hi, max_time);
- return true;
-}
-
-/* Generate redundant time stamps up to OPT. Return true if successful. */
-static bool
-redundant_time_option(char *opt)
-{
- if (*opt == '@') {
- intmax_t redundant;
- char *opt_end;
- redundant = strtoimax(opt + 1, &opt_end, 10);
- if (opt_end != opt + 1 && !*opt_end) {
- redundant_time = max(redundant_time, redundant);
- return true;
- }
- }
- return false;
-}
-
-static const char * psxrules;
-static const char * lcltime;
-static const char * directory;
-static const char * tzdefault;
-
-/* -1 if the TZif output file should be slim, 0 if default, 1 if the
- output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT
- determines the default. */
-static int bloat;
-
-static bool
-want_bloat(void)
-{
- return 0 <= bloat;
-}
-
-#ifndef ZIC_BLOAT_DEFAULT
-# define ZIC_BLOAT_DEFAULT "slim"
-#endif
-
-int
-main(int argc, char **argv)
-{
- register int c, k;
- register ptrdiff_t i, j;
- bool timerange_given = false;
-
-#ifdef S_IWGRP
- umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
-#endif
-#if HAVE_GETTEXT
- setlocale(LC_ALL, "");
-# ifdef TZ_DOMAINDIR
- bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-# 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,
- _("wild compilation-time specification of zic_t"));
- return EXIT_FAILURE;
- }
- for (k = 1; k < argc; k++)
- if (strcmp(argv[k], "--version") == 0) {
- printf("zic %s%s\n", PKGVERSION, TZVERSION);
- close_file(stdout, NULL, NULL, NULL);
- return EXIT_SUCCESS;
- } else if (strcmp(argv[k], "--help") == 0) {
- usage(stdout, EXIT_SUCCESS);
- }
- while ((c = getopt(argc, argv, "b:d:l:L:p:r:R:st:vy:")) != EOF
- && c != -1)
- switch (c) {
- default:
- usage(stderr, EXIT_FAILURE);
- case 'b':
- if (strcmp(optarg, "slim") == 0) {
- if (0 < bloat)
- error(_("incompatible -b options"));
- bloat = -1;
- } else if (strcmp(optarg, "fat") == 0) {
- if (bloat < 0)
- error(_("incompatible -b options"));
- bloat = 1;
- } else
- error(_("invalid option: -b '%s'"), optarg);
- break;
- case 'd':
- if (directory == NULL)
- directory = optarg;
- else {
- fprintf(stderr,
-_("%s: More than one -d option specified\n"),
- progname);
- return EXIT_FAILURE;
- }
- break;
- case 'l':
- if (lcltime == NULL)
- lcltime = optarg;
- else {
- fprintf(stderr,
-_("%s: More than one -l option specified\n"),
- progname);
- return EXIT_FAILURE;
- }
- break;
- case 'p':
- if (psxrules == NULL)
- psxrules = optarg;
- else {
- fprintf(stderr,
-_("%s: More than one -p option specified\n"),
- progname);
- return EXIT_FAILURE;
- }
- break;
- case 't':
- if (tzdefault != NULL) {
- fprintf(stderr,
- _("%s: More than one -t option"
- " specified\n"),
- progname);
- return EXIT_FAILURE;
- }
- tzdefault = optarg;
- break;
- case 'y':
- warning(_("-y ignored"));
- break;
- case 'L':
- if (leapsec == NULL)
- leapsec = optarg;
- else {
- fprintf(stderr,
-_("%s: More than one -L option specified\n"),
- progname);
- return EXIT_FAILURE;
- }
- break;
- case 'v':
- noise = true;
- break;
- case 'r':
- if (timerange_given) {
- fprintf(stderr,
-_("%s: More than one -r option specified\n"),
- progname);
- return EXIT_FAILURE;
- }
- if (! timerange_option(optarg)) {
- fprintf(stderr,
-_("%s: invalid time range: %s\n"),
- progname, optarg);
- return EXIT_FAILURE;
- }
- timerange_given = true;
- break;
- case 'R':
- if (! redundant_time_option(optarg)) {
- fprintf(stderr, _("%s: invalid time: %s\n"),
- progname, optarg);
- return EXIT_FAILURE;
- }
- break;
- case 's':
- warning(_("-s ignored"));
- break;
- }
- if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
- usage(stderr, EXIT_FAILURE); /* usage message by request */
- if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
- fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
- return EXIT_FAILURE;
- }
- if (bloat == 0) {
- static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
- if (strcmp(bloat_default, "slim") == 0)
- bloat = -1;
- else if (strcmp(bloat_default, "fat") == 0)
- bloat = 1;
- else
- abort(); /* Configuration error. */
- }
- if (directory == NULL)
- directory = TZDIR;
- if (tzdefault == NULL)
- tzdefault = TZDEFAULT;
-
- if (optind < argc && leapsec != NULL) {
- infile(LEAPSEC_FILENUM, leapsec);
- adjleap();
- }
-
- for (k = optind; k < argc; k++)
- infile(k, argv[k]);
- if (errors)
- return EXIT_FAILURE;
- associate();
- change_directory(directory);
- catch_signals();
- for (i = 0; i < nzones; i = j) {
- /*
- ** Find the next non-continuation zone entry.
- */
- for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
- continue;
- outzone(&zones[i], j - i);
- }
- make_links();
- if (lcltime != NULL) {
- eat(COMMAND_LINE_FILENUM, 1);
- dolink(lcltime, tzdefault, true);
- }
- if (psxrules != NULL) {
- eat(COMMAND_LINE_FILENUM, 1);
- dolink(psxrules, TZDEFRULES, true);
- }
- if (warnings && (ferror(stderr) || fclose(stderr) != 0))
- return EXIT_FAILURE;
- return errors ? EXIT_FAILURE : EXIT_SUCCESS;
-}
-
-static bool
-componentcheck(char const *name, char const *component,
- char const *component_end)
-{
- enum { component_len_max = 14 };
- ptrdiff_t component_len = component_end - component;
- if (component_len == 0) {
- if (!*name)
- error(_("empty file name"));
- else
- error(_(component == name
- ? "file name '%s' begins with '/'"
- : *component_end
- ? "file name '%s' contains '//'"
- : "file name '%s' ends with '/'"),
- name);
- return false;
- }
- if (0 < component_len && component_len <= 2
- && component[0] == '.' && component_end[-1] == '.') {
- int len = component_len;
- error(_("file name '%s' contains '%.*s' component"),
- name, len, component);
- return false;
- }
- if (noise) {
- if (0 < component_len && component[0] == '-')
- warning(_("file name '%s' component contains leading '-'"),
- name);
- if (component_len_max < component_len)
- warning(_("file name '%s' contains overlength component"
- " '%.*s...'"),
- name, component_len_max, component);
- }
- return true;
-}
-
-static bool
-namecheck(const char *name)
-{
- register char const *cp;
-
- /* Benign characters in a portable file name. */
- static char const benign[] =
- "-/_"
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
- /* Non-control chars in the POSIX portable character set,
- excluding the benign characters. */
- static char const printable_and_not_benign[] =
- " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
-
- register char const *component = name;
- for (cp = name; *cp; cp++) {
- unsigned char c = *cp;
- if (noise && !strchr(benign, c)) {
- warning((strchr(printable_and_not_benign, c)
- ? _("file name '%s' contains byte '%c'")
- : _("file name '%s' contains byte '\\%o'")),
- name, c);
- }
- if (c == '/') {
- if (!componentcheck(name, component, cp))
- return false;
- component = cp + 1;
- }
- }
- 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
- and equal to *NAME; otherwise, allocate a new name and put its
- address into both *NAMEALLOC and *NAME. */
-static void
-random_dirent(char const **name, char **namealloc)
-{
- char const *src = *name;
- char *dst = *namealloc;
- static char const prefix[] = ".zic";
- static char const alphabet[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- enum { prefixlen = sizeof prefix - 1, alphabetlen = sizeof alphabet - 1 };
- int suffixlen = 6;
- char const *lastslash = strrchr(src, '/');
- ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0;
- 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);
- memcpy(dst, src, dirlen);
- memcpy(dst + dirlen, prefix, prefixlen);
- dst[dirlen + prefixlen + suffixlen] = '\0';
- *name = *namealloc = dst;
- }
-
- do
- r = get_rand_u64();
- while (unfair_min <= r);
-
- for (i = 0; i < suffixlen; i++) {
- dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
- r /= alphabetlen;
- }
-}
-
-/* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
- name of the temporary file that will eventually be renamed to
- *OUTNAME. Assign the temporary file's name to both *OUTNAME and
- *TEMPNAME. If *TEMPNAME is null, allocate the name of any such
- temporary file; otherwise, reuse *TEMPNAME's storage, which is
- already set up and only needs its trailing suffix updated. */
-static FILE *
-open_outfile(char const **outname, char **tempname)
-{
-#if __STDC_VERSION__ < 201112
- static char const fopen_mode[] = "wb";
-#else
- static char const fopen_mode[] = "wbx";
-#endif
-
- FILE *fp;
- bool dirs_made = false;
- if (!*tempname)
- random_dirent(outname, tempname);
-
- while (! (fp = fopen(*outname, fopen_mode))) {
- int fopen_errno = errno;
- if (fopen_errno == ENOENT && !dirs_made) {
- mkdirs(*outname, true);
- dirs_made = true;
- } else if (fopen_errno == EEXIST)
- random_dirent(outname, tempname);
- else {
- fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
- progname, directory, *outname, strerror(fopen_errno));
- exit(EXIT_FAILURE);
- }
- }
-
- return fp;
-}
-
-/* If TEMPNAME, the result is in the temporary file TEMPNAME even
- though the user wanted it in NAME, so rename TEMPNAME to NAME.
- Report an error and exit if there is trouble. Also, free TEMPNAME. */
-static void
-rename_dest(char *tempname, char const *name)
-{
- if (tempname) {
- if (rename(tempname, name) != 0) {
- int rename_errno = errno;
- remove(tempname);
- fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
- progname, directory, name, strerror(rename_errno));
- exit(EXIT_FAILURE);
- }
- free(tempname);
- }
-}
-
-/* Create symlink contents suitable for symlinking FROM to TO, as a
- freshly allocated string. FROM should be a relative file name, and
- is relative to the global variable DIRECTORY. TO can be either
- relative or absolute. */
-static char *
-relname(char const *target, char const *linkname)
-{
- size_t i, taillen, dotdotetcsize;
- size_t dir_len = 0, dotdots = 0, linksize = 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;
- f = result = emalloc(linksize);
- strcpy(result, directory);
- result[len] = '/';
- strcpy(result + len + needslash, target);
- }
- for (i = 0; f[i] && f[i] == linkname[i]; i++)
- if (f[i] == '/')
- dir_len = i + 1;
- for (; linkname[i]; i++)
- dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
- taillen = strlen(f + dir_len);
- dotdotetcsize = 3 * dotdots + taillen + 1;
- if (dotdotetcsize <= linksize) {
- if (!result)
- result = emalloc(dotdotetcsize);
- for (i = 0; i < dotdots; i++)
- memcpy(result + 3 * i, "../", 3);
- memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
- }
- return result;
-}
-
-static void
-dolink(char const *target, char const *linkname, bool staysymlink)
-{
- bool linkdirs_made = false;
- int link_errno;
- char *tempname = NULL;
- char const *outname = linkname;
-
- check_for_signal();
-
- if (strcmp(target, "-") == 0) {
- if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR)
- return;
- else {
- char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
- progname, directory, linkname, e);
- exit(EXIT_FAILURE);
- }
- }
-
- while (true) {
- if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW)
- == 0) {
- link_errno = 0;
- break;
- }
- link_errno = errno;
- if (link_errno == EXDEV || link_errno == ENOTSUP)
- break;
-
- if (link_errno == EEXIST) {
- staysymlink &= !tempname;
- random_dirent(&outname, &tempname);
- if (staysymlink && itssymlink(linkname))
- break;
- } else if (link_errno == ENOENT && !linkdirs_made) {
- mkdirs(linkname, true);
- linkdirs_made = true;
- } else {
- fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
- progname, directory, target, directory, outname,
- strerror(link_errno));
- exit(EXIT_FAILURE);
- }
- }
- if (link_errno != 0) {
- bool absolute = *target == '/';
- char *linkalloc = absolute ? NULL : relname(target, linkname);
- char const *contents = absolute ? target : linkalloc;
- int symlink_errno;
-
- while (true) {
- if (symlink(contents, outname) == 0) {
- symlink_errno = 0;
- break;
- }
- symlink_errno = errno;
- if (symlink_errno == EEXIST)
- random_dirent(&outname, &tempname);
- else if (symlink_errno == ENOENT && !linkdirs_made) {
- mkdirs(linkname, true);
- linkdirs_made = true;
- } else
- break;
- }
- free(linkalloc);
- if (symlink_errno == 0) {
- if (link_errno != ENOTSUP && link_errno != EEXIST)
- warning(_("symbolic link used because hard link failed: %s"),
- strerror(link_errno));
- } else {
- FILE *fp, *tp;
- int c;
- fp = fopen(target, "rb");
- if (!fp) {
- char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
- progname, directory, target, e);
- exit(EXIT_FAILURE);
- }
- tp = open_outfile(&outname, &tempname);
- while ((c = getc(fp)) != EOF)
- putc(c, tp);
- close_file(tp, directory, linkname, tempname);
- close_file(fp, directory, target, NULL);
- if (link_errno != ENOTSUP)
- warning(_("copy used because hard link failed: %s"),
- strerror(link_errno));
- else if (symlink_errno != ENOTSUP)
- warning(_("copy used because symbolic link failed: %s"),
- strerror(symlink_errno));
- }
- }
- rename_dest(tempname, linkname);
-}
-
-/* Return true if NAME is a symbolic link. */
-static bool
-itssymlink(char const *name)
-{
- char c;
- return 0 <= readlink(name, &c, 1);
-}
-
-/*
-** Associate sets of rules with zones.
-*/
-
-/*
-** Sort by rule name.
-*/
-
-static int
-rcomp(const void *cp1, const void *cp2)
-{
- struct rule const *r1 = cp1, *r2 = cp2;
- return strcmp(r1->r_name, r2->r_name);
-}
-
-static void
-associate(void)
-{
- register struct zone * zp;
- register struct rule * rp;
- register ptrdiff_t i, j, base, out;
-
- 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 (rules[i].r_filenum == rules[i + 1].r_filenum)
- continue;
- eat(rules[i].r_filenum, rules[i].r_linenum);
- warning(_("same rule name in multiple files"));
- 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 (rules[i].r_filenum == rules[j].r_filenum)
- continue;
- if (rules[i + 1].r_filenum
- == rules[j].r_filenum)
- continue;
- break;
- }
- i = j - 1;
- }
- }
- for (i = 0; i < nzones; ++i) {
- zp = &zones[i];
- zp->z_rules = NULL;
- zp->z_nrules = 0;
- }
- for (base = 0; base < nrules; base = out) {
- rp = &rules[base];
- for (out = base + 1; out < nrules; ++out)
- if (strcmp(rp->r_name, rules[out].r_name) != 0)
- break;
- for (i = 0; i < nzones; ++i) {
- zp = &zones[i];
- if (strcmp(zp->z_rule, rp->r_name) != 0)
- continue;
- zp->z_rules = rp;
- zp->z_nrules = out - base;
- }
- }
- for (i = 0; i < nzones; ++i) {
- zp = &zones[i];
- if (zp->z_nrules == 0) {
- /*
- ** Maybe we have a local standard time offset.
- */
- 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,
- ** a '%s' in the format is a bad thing.
- */
- if (zp->z_format_specifier == 's')
- error("%s", _("%s in ruleless zone"));
- }
- }
- if (errors)
- exit(EXIT_FAILURE);
-}
-
-/* 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.
- On error, report the error and exit. */
-static ptrdiff_t
-inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
-{
- ptrdiff_t linelen = 0, ch;
- while ((ch = getc(fp)) != '\n') {
- if (ch < 0) {
- if (ferror(fp)) {
- error(_("input error"));
- exit(EXIT_FAILURE);
- }
- if (linelen == 0)
- return -1;
- error(_("unterminated line"));
- exit(EXIT_FAILURE);
- }
- if (!ch) {
- error(_("NUL input byte"));
- exit(EXIT_FAILURE);
- }
- buf[linelen++] = ch;
- if (linelen == bufsize) {
- error(_("line too long"));
- exit(EXIT_FAILURE);
- }
- }
- buf[linelen] = '\0';
- return linelen;
-}
-
-static void
-infile(int fnum, char const *name)
-{
- register FILE * fp;
- register const struct lookup * lp;
- register bool wantcont;
- register lineno num;
-
- if (strcmp(name, "-") == 0) {
- fp = stdin;
- } else if ((fp = fopen(name, "r")) == NULL) {
- const char *e = strerror(errno);
-
- fprintf(stderr, _("%s: Can't open %s: %s\n"),
- progname, name, e);
- exit(EXIT_FAILURE);
- }
- wantcont = false;
- for (num = 1; ; ++num) {
- ptrdiff_t linelen;
- char buf[_POSIX2_LINE_MAX];
- int nfields;
- char *fields[MAX_FIELDS];
- eat(fnum, num);
- linelen = inputline(fp, buf, sizeof buf);
- if (linelen < 0)
- break;
- nfields = getfields(buf, fields,
- sizeof fields / sizeof *fields);
- if (nfields == 0) {
- /* nothing to do */
- } else if (wantcont) {
- wantcont = inzcont(fields, nfields);
- } else {
- struct lookup const *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"));
- else switch (lp->l_value) {
- case LC_RULE:
- inrule(fields, nfields);
- wantcont = false;
- break;
- case LC_ZONE:
- wantcont = inzone(fields, nfields);
- break;
- case LC_LINK:
- inlink(fields, nfields);
- wantcont = false;
- break;
- case LC_LEAP:
- inleap(fields, nfields);
- wantcont = false;
- break;
- case LC_EXPIRES:
- inexpires(fields, nfields);
- wantcont = false;
- break;
- default: unreachable();
- }
- }
- }
- close_file(fp, NULL, filename(fnum), NULL);
- if (wantcont)
- error(_("expected continuation line not found"));
-}
-
-/*
-** Convert a string of one of the forms
-** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
-** into a number of seconds.
-** A null string maps to zero.
-** Call error with errstring and return zero on errors.
-*/
-
-static zic_t
-gethms(char const *string, char const *errstring)
-{
- zic_t hh;
- int sign, mm = 0, ss = 0;
- char hhx, mmx, ssx, xr = '0', xs;
- int tenths = 0;
- bool ok = true;
-
- if (string == NULL || *string == '\0')
- return 0;
- if (*string == '-') {
- sign = -1;
- ++string;
- } else sign = 1;
- switch (sscanf(string,
- "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
- &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) {
- default: ok = false; break;
- case 8:
- ok = '0' <= xr && xr <= '9';
- /* 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 */
- case 1: break;
- }
- if (!ok) {
- error("%s", errstring);
- return 0;
- }
- if (hh < 0 ||
- mm < 0 || mm >= MINSPERHOUR ||
- ss < 0 || ss > SECSPERMIN) {
- error("%s", errstring);
- return 0;
- }
- if (ZIC_MAX / SECSPERHOUR < hh) {
- error(_("time overflow"));
- return 0;
- }
- ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
- if (noise && (hh > HOURSPERDAY ||
- (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
-warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
- return oadd(sign * hh * SECSPERHOUR,
- sign * (mm * SECSPERMIN + ss));
-}
-
-static zic_t
-getsave(char *field, bool *isdst)
-{
- int dst = -1;
- zic_t save;
- size_t fieldlen = strlen(field);
- if (fieldlen != 0) {
- char *ep = field + fieldlen - 1;
- switch (*ep) {
- case 'd': dst = 1; *ep = '\0'; break;
- case 's': dst = 0; *ep = '\0'; break;
- }
- }
- save = gethms(field, _("invalid saved time"));
- *isdst = dst < 0 ? save != 0 : dst;
- return save;
-}
-
-static void
-inrule(char **fields, int nfields)
-{
- struct rule r;
-
- if (nfields != RULE_FIELDS) {
- error(_("wrong number of fields on Rule line"));
- return;
- }
- switch (*fields[RF_NAME]) {
- case '\0':
- case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
- case '+': case '-':
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
- return;
- }
- 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],
- 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]);
- if (max_abbrvar_len < strlen(r.r_abbrvar))
- max_abbrvar_len = strlen(r.r_abbrvar);
- rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
- rules[nrules++] = r;
-}
-
-static bool
-inzone(char **fields, int nfields)
-{
- register ptrdiff_t i;
-
- if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
- error(_("wrong number of fields on Zone line"));
- return false;
- }
- if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
- error(
-_("\"Zone %s\" line and -l option are mutually exclusive"),
- tzdefault);
- return false;
- }
- if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
- error(
-_("\"Zone %s\" line and -p option are mutually exclusive"),
- TZDEFRULES);
- return false;
- }
- for (i = 0; i < nzones; ++i)
- if (zones[i].z_name != NULL &&
- strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
- error(_("duplicate zone name %s"
- " (file \"%s\", line %"PRIdMAX")"),
- fields[ZF_NAME],
- filename(zones[i].z_filenum),
- zones[i].z_linenum);
- return false;
- }
- return inzsub(fields, nfields, false);
-}
-
-static bool
-inzcont(char **fields, int nfields)
-{
- if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
- error(_("wrong number of fields on Zone continuation line"));
- return false;
- }
- return inzsub(fields, nfields, true);
-}
-
-static bool
-inzsub(char **fields, int nfields, bool iscont)
-{
- register char * cp;
- char * cp1;
- struct zone z;
- size_t format_len;
- register int i_stdoff, i_rule, i_format;
- register int i_untilyear, i_untilmonth;
- register int i_untilday, i_untiltime;
- register bool hasuntil;
-
- if (iscont) {
- i_stdoff = ZFC_STDOFF;
- i_rule = ZFC_RULE;
- i_format = ZFC_FORMAT;
- i_untilyear = ZFC_TILYEAR;
- i_untilmonth = ZFC_TILMONTH;
- i_untilday = ZFC_TILDAY;
- i_untiltime = ZFC_TILTIME;
- } else if (!namecheck(fields[ZF_NAME]))
- return false;
- else {
- i_stdoff = ZF_STDOFF;
- i_rule = ZF_RULE;
- i_format = ZF_FORMAT;
- i_untilyear = ZF_TILYEAR;
- i_untilmonth = ZF_TILMONTH;
- i_untilday = ZF_TILDAY;
- i_untiltime = ZF_TILTIME;
- }
- 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) {
- if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
- || strchr(fields[i_format], '/')) {
- error(_("invalid abbreviation format"));
- return false;
- }
- }
- z.z_format_specifier = cp ? *cp : '\0';
- format_len = strlen(fields[i_format]);
- if (max_format_len < format_len)
- max_format_len = format_len;
- hasuntil = nfields > i_untilyear;
- if (hasuntil) {
- z.z_untilrule.r_filenum = filenum;
- z.z_untilrule.r_linenum = linenum;
- if (!rulesub(
- &z.z_untilrule,
- fields[i_untilyear],
- "only",
- "",
- (nfields > i_untilmonth) ?
- fields[i_untilmonth] : "Jan",
- (nfields > i_untilday) ? fields[i_untilday] : "1",
- (nfields > i_untiltime) ? fields[i_untiltime] : "0"))
- return false;
- z.z_untiltime = rpytime(&z.z_untilrule,
- z.z_untilrule.r_loyear);
- if (iscont && nzones > 0 &&
- z.z_untiltime > min_time &&
- z.z_untiltime < max_time &&
- zones[nzones - 1].z_untiltime > min_time &&
- zones[nzones - 1].z_untiltime < max_time &&
- zones[nzones - 1].z_untiltime >= z.z_untiltime) {
- error(_(
-"Zone continuation line end time is not after end time of previous line"
- ));
- 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]);
- if (z.z_format_specifier == 'z') {
- cp1[cp - fields[i_format]] = 's';
- if (noise)
- warning(_("format '%s' not handled by pre-2015 versions of zic"),
- fields[i_format]);
- }
- zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
- zones[nzones++] = z;
- /*
- ** If there was an UNTIL field on this line,
- ** there's more information about the zone on the next line.
- */
- return hasuntil;
-}
-
-static zic_t
-getleapdatetime(char **fields, int nfields, bool expire_line)
-{
- register const char * cp;
- register const struct lookup * lp;
- register zic_t i, j;
- zic_t year;
- int month, day;
- zic_t dayoff, tod;
- zic_t t;
- char xs;
-
- dayoff = 0;
- cp = fields[LP_YEAR];
- if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) {
- /*
- ** Leapin' Lizards!
- */
- error(_("invalid leaping year"));
- return -1;
- }
- if (!expire_line) {
- if (!leapseen || leapmaxyear < year)
- leapmaxyear = year;
- if (!leapseen || leapminyear > year)
- leapminyear = year;
- leapseen = true;
- }
- j = EPOCH_YEAR;
- while (j != year) {
- if (year > j) {
- i = len_years[isleap(j)];
- ++j;
- } else {
- --j;
- i = -len_years[isleap(j)];
- }
- dayoff = oadd(dayoff, i);
- }
- if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
- error(_("invalid month name"));
- return -1;
- }
- month = lp->l_value;
- j = TM_JANUARY;
- while (j != month) {
- i = len_months[isleap(year)][j];
- dayoff = oadd(dayoff, i);
- ++j;
- }
- cp = fields[LP_DAY];
- if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
- day <= 0 || day > len_months[isleap(year)][month]) {
- error(_("invalid day of month"));
- return -1;
- }
- dayoff = oadd(dayoff, day - 1);
- if (dayoff < min_time / SECSPERDAY) {
- error(_("time too small"));
- return -1;
- }
- if (dayoff > max_time / SECSPERDAY) {
- error(_("time too large"));
- return -1;
- }
- t = dayoff * SECSPERDAY;
- tod = gethms(fields[LP_TIME], _("invalid time of day"));
- t = tadd(t, tod);
- if (t < 0)
- error(_("leap second precedes Epoch"));
- return t;
-}
-
-static void
-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);
- if (0 <= t) {
- struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
- if (!lp)
- error(_("invalid Rolling/Stationary field on Leap line"));
- else {
- int correction = 0;
- if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
- correction = -1;
- else if (strcmp(fields[LP_CORR], "+") == 0)
- correction = 1;
- else
- error(_("invalid CORRECTION field on Leap line"));
- if (correction)
- leapadd(t, correction, lp->l_value);
- }
- }
- }
-}
-
-static void
-inexpires(char **fields, int nfields)
-{
- if (nfields != EXPIRES_FIELDS)
- error(_("wrong number of fields on Expires line"));
- else if (0 <= leapexpires)
- error(_("multiple Expires lines"));
- else
- leapexpires = getleapdatetime(fields, nfields, true);
-}
-
-static void
-inlink(char **fields, int nfields)
-{
- struct link l;
-
- if (nfields != LINK_FIELDS) {
- error(_("wrong number of fields on Link line"));
- return;
- }
- if (*fields[LF_TARGET] == '\0') {
- error(_("blank TARGET field on Link line"));
- return;
- }
- if (! namecheck(fields[LF_LINKNAME]))
- return;
- l.l_filenum = filenum;
- l.l_linenum = linenum;
- l.l_target = ecpyalloc(fields[LF_TARGET]);
- l.l_linkname = ecpyalloc(fields[LF_LINKNAME]);
- links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
- links[nlinks++] = l;
-}
-
-static bool
-rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
- const char *typep, const char *monthp, const char *dayp,
- const char *timep)
-{
- register const struct lookup * lp;
- register const char * cp;
- register char * dp;
- register char * ep;
- char xs;
-
- if ((lp = byword(monthp, mon_names)) == NULL) {
- error(_("invalid month name"));
- return false;
- }
- rp->r_month = lp->l_value;
- rp->r_todisstd = false;
- rp->r_todisut = false;
- dp = ecpyalloc(timep);
- if (*dp != '\0') {
- ep = dp + strlen(dp) - 1;
- switch (lowerit(*ep)) {
- case 's': /* Standard */
- rp->r_todisstd = true;
- rp->r_todisut = false;
- *ep = '\0';
- break;
- case 'w': /* Wall */
- rp->r_todisstd = false;
- rp->r_todisut = false;
- *ep = '\0';
- break;
- case 'g': /* Greenwich */
- case 'u': /* Universal */
- case 'z': /* Zulu */
- rp->r_todisstd = true;
- rp->r_todisut = true;
- *ep = '\0';
- break;
- }
- }
- rp->r_tod = gethms(dp, _("invalid time of day"));
- free(dp);
- /*
- ** Year work.
- */
- cp = loyearp;
- lp = byword(cp, begin_years);
- rp->r_lowasnum = lp == NULL;
- if (!rp->r_lowasnum) switch (lp->l_value) {
- case YR_MINIMUM:
- rp->r_loyear = ZIC_MIN;
- break;
- case YR_MAXIMUM:
- rp->r_loyear = ZIC_MAX;
- break;
- default: unreachable();
- } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
- error(_("invalid starting year"));
- return false;
- }
- cp = hiyearp;
- lp = byword(cp, end_years);
- rp->r_hiwasnum = lp == NULL;
- if (!rp->r_hiwasnum) switch (lp->l_value) {
- case YR_MINIMUM:
- rp->r_hiyear = ZIC_MIN;
- break;
- case YR_MAXIMUM:
- rp->r_hiyear = ZIC_MAX;
- break;
- case YR_ONLY:
- rp->r_hiyear = rp->r_loyear;
- break;
- default: unreachable();
- } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
- error(_("invalid ending year"));
- return false;
- }
- if (rp->r_loyear > rp->r_hiyear) {
- error(_("starting year greater than ending year"));
- return false;
- }
- if (*typep != '\0') {
- error(_("year type \"%s\" is unsupported; use \"-\" instead"),
- typep);
- return false;
- }
- /*
- ** Day work.
- ** Accept things such as:
- ** 1
- ** lastSunday
- ** last-Sunday (undocumented; warn about this)
- ** Sun<=20
- ** Sun>=7
- */
- dp = ecpyalloc(dayp);
- if ((lp = byword(dp, lasts)) != NULL) {
- rp->r_dycode = DC_DOWLEQ;
- rp->r_wday = lp->l_value;
- rp->r_dayofmonth = len_months[1][rp->r_month];
- } else {
- if ((ep = strchr(dp, '<')) != 0)
- rp->r_dycode = DC_DOWLEQ;
- else if ((ep = strchr(dp, '>')) != 0)
- rp->r_dycode = DC_DOWGEQ;
- else {
- ep = dp;
- rp->r_dycode = DC_DOM;
- }
- if (rp->r_dycode != DC_DOM) {
- *ep++ = 0;
- if (*ep++ != '=') {
- error(_("invalid day of month"));
- free(dp);
- return false;
- }
- if ((lp = byword(dp, wday_names)) == NULL) {
- error(_("invalid weekday name"));
- free(dp);
- return false;
- }
- rp->r_wday = lp->l_value;
- }
- if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
- rp->r_dayofmonth <= 0 ||
- (rp->r_dayofmonth > len_months[1][rp->r_month])) {
- error(_("invalid day of month"));
- free(dp);
- return false;
- }
- }
- free(dp);
- return true;
-}
-
-static void
-convert(uint_fast32_t val, char *buf)
-{
- register int i;
- register int shift;
- unsigned char *const b = (unsigned char *) buf;
-
- for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
- b[i] = (val >> shift) & 0xff;
-}
-
-static void
-convert64(uint_fast64_t val, char *buf)
-{
- register int i;
- register int shift;
- unsigned char *const b = (unsigned char *) buf;
-
- for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
- b[i] = (val >> shift) & 0xff;
-}
-
-static void
-puttzcode(const int_fast32_t val, FILE *const fp)
-{
- char buf[4];
-
- convert(val, buf);
- fwrite(buf, sizeof buf, 1, fp);
-}
-
-static void
-puttzcodepass(zic_t val, FILE *fp, int pass)
-{
- if (pass == 1)
- puttzcode(val, fp);
- else {
- char buf[8];
-
- convert64(val, buf);
- fwrite(buf, sizeof buf, 1, fp);
- }
-}
-
-static int
-atcomp(const void *avp, const void *bvp)
-{
- struct attype const *ap = avp, *bp = bvp;
- zic_t a = ap->at, b = bp->at;
- return a < b ? -1 : a > b;
-}
-
-struct timerange {
- int defaulttype;
- ptrdiff_t base, count;
- int leapbase, leapcount;
- bool leapexpiry;
-};
-
-static struct timerange
-limitrange(struct timerange r, zic_t lo, zic_t hi,
- zic_t const *ats, unsigned char const *types)
-{
- /* Omit ordinary transitions < LO. */
- while (0 < r.count && ats[r.base] < lo) {
- r.defaulttype = types[r.base];
- r.count--;
- r.base++;
- }
-
- /* Omit as many initial leap seconds as possible, such that the
- first leap second in the truncated list is <= LO, and is a
- positive leap second if and only if it has a positive correction.
- This supports common TZif readers that assume that the first leap
- second is positive if and only if its correction is positive. */
- while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) {
- r.leapcount--;
- r.leapbase++;
- }
- while (0 < r.leapbase
- && ((corr[r.leapbase - 1] < corr[r.leapbase])
- != (0 < corr[r.leapbase]))) {
- r.leapcount++;
- r.leapbase--;
- }
-
-
- /* Omit ordinary and leap second transitions greater than HI + 1. */
- if (hi < max_time) {
- while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
- r.count--;
- while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
- r.leapcount--;
- }
-
- /* Determine whether to append an expiration to the leap second table. */
- r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
-
- return r;
-}
-
-static void
-writezone(const char *const name, const char *const string, char version,
- int defaulttype)
-{
- register FILE * fp;
- register ptrdiff_t i, j;
- register int pass;
- 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(timecnt, sizeof *ats + 1),
- alignof(zic_t)));
- void *typesptr = ats + timecnt;
- unsigned char *types = typesptr;
- struct timerange rangeall = {0}, range32, range64;
-
- /*
- ** Sort.
- */
- if (timecnt > 1)
- qsort(attypes, timecnt, sizeof *attypes, atcomp);
- /*
- ** Optimize.
- */
- {
- ptrdiff_t fromi, toi;
-
- toi = 0;
- fromi = 0;
- for ( ; fromi < timecnt; ++fromi) {
- if (toi != 0
- && ((attypes[fromi].at
- + utoffs[attypes[toi - 1].type])
- <= (attypes[toi - 1].at
- + utoffs[toi == 1 ? 0
- : attypes[toi - 2].type]))) {
- attypes[toi - 1].type =
- attypes[fromi].type;
- continue;
- }
- if (toi == 0
- || attypes[fromi].dontmerge
- || (utoffs[attypes[toi - 1].type]
- != utoffs[attypes[fromi].type])
- || (isdsts[attypes[toi - 1].type]
- != isdsts[attypes[fromi].type])
- || (desigidx[attypes[toi - 1].type]
- != desigidx[attypes[fromi].type]))
- attypes[toi++] = attypes[fromi];
- }
- timecnt = toi;
- }
-
- if (noise && timecnt > 1200) {
- if (timecnt > TZ_MAX_TIMES)
- warning(_("reference clients mishandle"
- " more than %d transition times"),
- TZ_MAX_TIMES);
- else
- warning(_("pre-2014 clients may mishandle"
- " more than 1200 transition times"));
- }
- /*
- ** Transfer.
- */
- for (i = 0; i < timecnt; ++i) {
- ats[i] = attypes[i].at;
- types[i] = attypes[i].type;
- }
-
- /*
- ** Correct for leap seconds.
- */
- for (i = 0; i < timecnt; ++i) {
- j = leapcnt;
- while (--j >= 0)
- if (ats[i] > trans[j] - corr[j]) {
- ats[i] = tadd(ats[i], corr[j]);
- break;
- }
- }
-
- rangeall.defaulttype = defaulttype;
- rangeall.count = timecnt;
- rangeall.leapcount = leapcnt;
- range64 = limitrange(rangeall, lo_time,
- max(hi_time,
- redundant_time - (ZIC_MIN < redundant_time)),
- 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
- leap second transition is not to a +1 or -1 correction. */
- for (pass = 1; pass <= 2; pass++) {
- struct timerange const *r = pass == 1 ? &range32 : &range64;
- if (pass == 1 && !want_bloat())
- continue;
- if (r->leapexpiry) {
- if (noise)
- warning(_("%s: pre-2021b clients may mishandle"
- " leap second expiry"),
- name);
- version = '4';
- }
- if (0 < r->leapcount
- && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) {
- if (noise)
- warning(_("%s: pre-2021b clients may mishandle"
- " leap second table truncation"),
- name);
- version = '4';
- }
- if (version == '4')
- break;
- }
-
- fp = open_outfile(&outname, &tempname);
-
- for (pass = 1; pass <= 2; ++pass) {
- register ptrdiff_t thistimei, thistimecnt, thistimelim;
- register int thisleapi, thisleapcnt, thisleaplim;
- struct tzhead tzh;
- int pretranstype = -1, thisdefaulttype;
- bool locut, hicut, thisleapexpiry;
- zic_t lo, thismin, thismax;
- int old0;
- char omittype[TZ_MAX_TYPES];
- int typemap[TZ_MAX_TYPES];
- int thistypecnt, stdcnt, utcnt;
- char thischars[TZ_MAX_CHARS];
- int thischarcnt;
- bool toomanytimes;
- int indmap[TZ_MAX_CHARS];
-
- if (pass == 1) {
- thisdefaulttype = range32.defaulttype;
- thistimei = range32.base;
- thistimecnt = range32.count;
- toomanytimes = thistimecnt >> 31 >> 1 != 0;
- thisleapi = range32.leapbase;
- thisleapcnt = range32.leapcount;
- thisleapexpiry = range32.leapexpiry;
- thismin = ZIC32_MIN;
- thismax = ZIC32_MAX;
- } else {
- thisdefaulttype = range64.defaulttype;
- thistimei = range64.base;
- thistimecnt = range64.count;
- toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
- thisleapi = range64.leapbase;
- thisleapcnt = range64.leapcount;
- thisleapexpiry = range64.leapexpiry;
- thismin = min_time;
- thismax = max_time;
- }
- if (toomanytimes)
- error(_("too many transition times"));
-
- locut = thismin < lo_time && lo_time <= thismax;
- hicut = thismin <= hi_time && hi_time < thismax;
- thistimelim = thistimei + thistimecnt;
- memset(omittype, true, typecnt);
-
- /* Determine whether to output a transition before the first
- transition in range. This is needed when the output is
- truncated at the start, and is also useful when catering to
- buggy 32-bit clients that do not use time type 0 for
- timestamps before the first transition. */
- if ((locut || (pass == 1 && thistimei))
- && ! (thistimecnt && ats[thistimei] == lo_time)) {
- pretranstype = thisdefaulttype;
- omittype[pretranstype] = false;
- }
-
- /* Arguably the default time type in the 32-bit data
- should be range32.defaulttype, which is suited for
- 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
- to obsolete readers where the traditional type might
- be more appropriate even if it's "wrong". So, use
- the historical zic value, unless -r specifies a low
- cutoff that excludes some 32-bit timestamps. */
- if (pass == 1 && lo_time <= thismin)
- thisdefaulttype = range64.defaulttype;
-
- if (locut)
- thisdefaulttype = unspecifiedtype;
- omittype[thisdefaulttype] = false;
- for (i = thistimei; i < thistimelim; i++)
- omittype[types[i]] = false;
- if (hicut)
- omittype[unspecifiedtype] = false;
-
- /* Reorder types to make THISDEFAULTTYPE type 0.
- Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that
- THISDEFAULTTYPE appears as type 0 in the output instead
- of OLD0. TYPEMAP also omits unused types. */
- old0 = strlen(omittype);
-
-#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
- /*
- ** For some pre-2011 systems: if the last-to-be-written
- ** standard (or daylight) type has an offset different from the
- ** most recently used offset,
- ** append an (unused) copy of the most recently used type
- ** (to help get global "altzone" and "timezone" variables
- ** set correctly).
- */
- if (want_bloat()) {
- register int mrudst, mrustd, hidst, histd, type;
-
- hidst = histd = mrudst = mrustd = -1;
- if (0 <= pretranstype) {
- if (isdsts[pretranstype])
- mrudst = pretranstype;
- else
- mrustd = pretranstype;
- }
- for (i = thistimei; i < thistimelim; i++)
- if (isdsts[types[i]])
- mrudst = types[i];
- else mrustd = types[i];
- for (i = old0; i < typecnt; i++) {
- int h = (i == old0 ? thisdefaulttype
- : i == thisdefaulttype ? old0 : i);
- if (!omittype[h]) {
- if (isdsts[h])
- hidst = i;
- else
- histd = i;
- }
- }
- if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
- utoffs[hidst] != utoffs[mrudst]) {
- isdsts[mrudst] = -1;
- type = addtype(utoffs[mrudst],
- &chars[desigidx[mrudst]],
- true,
- ttisstds[mrudst],
- ttisuts[mrudst]);
- isdsts[mrudst] = 1;
- omittype[type] = false;
- }
- if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
- utoffs[histd] != utoffs[mrustd]) {
- isdsts[mrustd] = -1;
- type = addtype(utoffs[mrustd],
- &chars[desigidx[mrustd]],
- false,
- ttisstds[mrustd],
- ttisuts[mrustd]);
- isdsts[mrustd] = 0;
- omittype[type] = false;
- }
- }
-#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
- thistypecnt = 0;
- for (i = old0; i < typecnt; i++)
- if (!omittype[i])
- typemap[i == old0 ? thisdefaulttype
- : i == thisdefaulttype ? old0 : i]
- = thistypecnt++;
-
- for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
- indmap[i] = -1;
- thischarcnt = stdcnt = utcnt = 0;
- for (i = old0; i < typecnt; i++) {
- register char * thisabbr;
-
- if (omittype[i])
- continue;
- if (ttisstds[i])
- stdcnt = thistypecnt;
- if (ttisuts[i])
- utcnt = thistypecnt;
- if (indmap[desigidx[i]] >= 0)
- continue;
- thisabbr = &chars[desigidx[i]];
- for (j = 0; j < thischarcnt; ++j)
- if (strcmp(&thischars[j], thisabbr) == 0)
- break;
- if (j == thischarcnt) {
- strcpy(&thischars[thischarcnt], thisabbr);
- thischarcnt += strlen(thisabbr) + 1;
- }
- indmap[desigidx[i]] = j;
- }
- if (pass == 1 && !want_bloat()) {
- hicut = thisleapexpiry = false;
- pretranstype = -1;
- thistimecnt = thisleapcnt = 0;
- thistypecnt = thischarcnt = 1;
- }
-#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
- memset(&tzh, 0, sizeof tzh);
- memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
- tzh.tzh_version[0] = version;
- convert(utcnt, tzh.tzh_ttisutcnt);
- convert(stdcnt, tzh.tzh_ttisstdcnt);
- convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
- convert((0 <= pretranstype) + thistimecnt + hicut,
- tzh.tzh_timecnt);
- convert(thistypecnt, tzh.tzh_typecnt);
- convert(thischarcnt, tzh.tzh_charcnt);
- DO(tzh_magic);
- DO(tzh_version);
- DO(tzh_reserved);
- DO(tzh_ttisutcnt);
- DO(tzh_ttisstdcnt);
- DO(tzh_leapcnt);
- DO(tzh_timecnt);
- DO(tzh_typecnt);
- DO(tzh_charcnt);
-#undef DO
- if (pass == 1 && !want_bloat()) {
- /* Output a minimal data block with just one time type. */
- puttzcode(0, fp); /* utoff */
- putc(0, fp); /* dst */
- putc(0, fp); /* index of abbreviation */
- putc(0, fp); /* empty-string abbreviation */
- continue;
- }
-
- /* 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 < ZIC32_MIN ? ZIC32_MIN : lo_time;
-
- if (0 <= pretranstype)
- puttzcodepass(lo, fp, pass);
- for (i = thistimei; i < thistimelim; ++i) {
- puttzcodepass(ats[i], fp, pass);
- }
- if (hicut)
- puttzcodepass(hi_time + 1, fp, pass);
- if (0 <= pretranstype)
- putc(typemap[pretranstype], fp);
- for (i = thistimei; i < thistimelim; i++)
- putc(typemap[types[i]], fp);
- if (hicut)
- putc(typemap[unspecifiedtype], fp);
-
- for (i = old0; i < typecnt; i++) {
- int h = (i == old0 ? thisdefaulttype
- : i == thisdefaulttype ? old0 : i);
- if (!omittype[h]) {
- puttzcode(utoffs[h], fp);
- putc(isdsts[h], fp);
- putc(indmap[desigidx[h]], fp);
- }
- }
- if (thischarcnt != 0)
- fwrite(thischars, sizeof thischars[0],
- thischarcnt, fp);
- thisleaplim = thisleapi + thisleapcnt;
- for (i = thisleapi; i < thisleaplim; ++i) {
- register zic_t todo;
-
- if (roll[i]) {
- if (timecnt == 0 || trans[i] < ats[0]) {
- j = 0;
- while (isdsts[j])
- if (++j >= typecnt) {
- j = 0;
- break;
- }
- } else {
- j = 1;
- while (j < timecnt &&
- trans[i] >= ats[j])
- ++j;
- j = types[j - 1];
- }
- todo = tadd(trans[i], -utoffs[j]);
- } else todo = trans[i];
- puttzcodepass(todo, fp, pass);
- puttzcode(corr[i], fp);
- }
- if (thisleapexpiry) {
- /* Append a no-op leap correction indicating when the leap
- second table expires. Although this does not conform to
- Internet RFC 8536, most clients seem to accept this and
- the plan is to amend the RFC to allow this in version 4
- TZif files. */
- puttzcodepass(leapexpires, fp, pass);
- puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp);
- }
- if (stdcnt != 0)
- for (i = old0; i < typecnt; i++)
- if (!omittype[i])
- putc(ttisstds[i], fp);
- if (utcnt != 0)
- for (i = old0; i < typecnt; i++)
- if (!omittype[i])
- putc(ttisuts[i], fp);
- }
- fprintf(fp, "\n%s\n", string);
- close_file(fp, directory, name, tempname);
- rename_dest(tempname, name);
- free(ats);
-}
-
-static char const *
-abbroffset(char *buf, zic_t offset)
-{
- char sign = '+';
- int seconds, minutes;
-
- if (offset < 0) {
- offset = -offset;
- sign = '-';
- }
-
- seconds = offset % SECSPERMIN;
- offset /= SECSPERMIN;
- minutes = offset % MINSPERHOUR;
- offset /= MINSPERHOUR;
- if (100 <= offset) {
- error(_("%%z UT offset magnitude exceeds 99:59:59"));
- return "%z";
- } else {
- char *p = buf;
- *p++ = sign;
- *p++ = '0' + offset / 10;
- *p++ = '0' + offset % 10;
- if (minutes | seconds) {
- *p++ = '0' + minutes / 10;
- *p++ = '0' + minutes % 10;
- if (seconds) {
- *p++ = '0' + seconds / 10;
- *p++ = '0' + seconds % 10;
- }
- }
- *p = '\0';
- return buf;
- }
-}
-
-static char const disable_percent_s[] = "";
-
-static size_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;
- char const *format = zp->z_format;
-
- slashp = strchr(format, '/');
- if (slashp == NULL) {
- char letterbuf[PERCENT_Z_LEN_BOUND + 1];
- if (zp->z_format_specifier == 'z')
- letters = abbroffset(letterbuf, zp->z_stdoff + save);
- else if (!letters)
- letters = "%s";
- else if (letters == disable_percent_s)
- return 0;
- sprintf(abbr, format, letters);
- } else if (isdst) {
- strcpy(abbr, slashp + 1);
- } else {
- memcpy(abbr, format, slashp - format);
- abbr[slashp - format] = '\0';
- }
- len = strlen(abbr);
- if (!doquotes)
- return len;
- for (cp = abbr; is_alpha(*cp); cp++)
- continue;
- if (len > 0 && *cp == '\0')
- return len;
- abbr[len + 2] = '\0';
- abbr[len + 1] = '>';
- memmove(abbr + 1, abbr, len);
- abbr[0] = '<';
- return len + 2;
-}
-
-static void
-updateminmax(const zic_t x)
-{
- if (min_year > x)
- min_year = x;
- if (max_year < x)
- max_year = x;
-}
-
-static int
-stringoffset(char *result, zic_t offset)
-{
- register int hours;
- register int minutes;
- register int seconds;
- bool negative = offset < 0;
- int len = negative;
-
- if (negative) {
- offset = -offset;
- result[0] = '-';
- }
- seconds = offset % SECSPERMIN;
- offset /= SECSPERMIN;
- minutes = offset % MINSPERHOUR;
- offset /= MINSPERHOUR;
- hours = offset;
- if (hours >= HOURSPERDAY * DAYSPERWEEK) {
- result[0] = '\0';
- return 0;
- }
- len += sprintf(result + len, "%d", hours);
- if (minutes != 0 || seconds != 0) {
- len += sprintf(result + len, ":%02d", minutes);
- if (seconds != 0)
- len += sprintf(result + len, ":%02d", seconds);
- }
- return len;
-}
-
-static int
-stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff)
-{
- register zic_t tod = rp->r_tod;
- register int compat = 0;
-
- if (rp->r_dycode == DC_DOM) {
- register int month, total;
-
- if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
- return -1;
- total = 0;
- for (month = 0; month < rp->r_month; ++month)
- total += len_months[0][month];
- /* Omit the "J" in Jan and Feb, as that's shorter. */
- if (rp->r_month <= 1)
- result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
- else
- result += sprintf(result, "J%d", total + rp->r_dayofmonth);
- } else {
- register int week;
- register int wday = rp->r_wday;
- register int wdayoff;
-
- if (rp->r_dycode == DC_DOWGEQ) {
- wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
- if (wdayoff)
- compat = 2013;
- wday -= wdayoff;
- tod += wdayoff * SECSPERDAY;
- week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
- } else if (rp->r_dycode == DC_DOWLEQ) {
- if (rp->r_dayofmonth == len_months[1][rp->r_month])
- week = 5;
- else {
- wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
- if (wdayoff)
- compat = 2013;
- wday -= wdayoff;
- tod += wdayoff * SECSPERDAY;
- week = rp->r_dayofmonth / DAYSPERWEEK;
- }
- } else return -1; /* "cannot happen" */
- if (wday < 0)
- wday += DAYSPERWEEK;
- result += sprintf(result, "M%d.%d.%d",
- rp->r_month + 1, week, wday);
- }
- if (rp->r_todisut)
- tod += stdoff;
- if (rp->r_todisstd && !rp->r_isdst)
- tod += save;
- if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
- *result++ = '/';
- if (! stringoffset(result, tod))
- return -1;
- if (tod < 0) {
- if (compat < 2013)
- compat = 2013;
- } else if (SECSPERDAY <= tod) {
- if (compat < 1994)
- compat = 1994;
- }
- }
- return compat;
-}
-
-static int
-rule_cmp(struct rule const *a, struct rule const *b)
-{
- if (!a)
- return -!!b;
- if (!b)
- return 1;
- if (a->r_hiyear != b->r_hiyear)
- return a->r_hiyear < b->r_hiyear ? -1 : 1;
- if (a->r_hiyear == ZIC_MAX)
- return 0;
- if (a->r_month - b->r_month != 0)
- return a->r_month - b->r_month;
- return a->r_dayofmonth - b->r_dayofmonth;
-}
-
-static int
-stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
-{
- register const struct zone * zp;
- register struct rule * rp;
- register struct rule * stdrp;
- register struct rule * dstrp;
- register ptrdiff_t i;
- register int compat = 0;
- register int c;
- size_t len;
- int offsetlen;
- struct rule stdr, dstr;
- int dstcmp;
- struct rule *lastrp[2] = { NULL, NULL };
- struct zone zstr[2];
- struct zone const *stdzp;
- struct zone const *dstzp;
-
- result[0] = '\0';
-
- /* Internet RFC 8536 section 5.1 says to use an empty TZ string if
- future timestamps are truncated. */
- if (hi_time < max_time)
- return -1;
-
- zp = zpfirst + zonecount - 1;
- for (i = 0; i < zp->z_nrules; ++i) {
- struct rule **last;
- int cmp;
- rp = &zp->z_rules[i];
- last = &lastrp[rp->r_isdst];
- cmp = rule_cmp(*last, rp);
- if (cmp < 0)
- *last = rp;
- else if (cmp == 0)
- return -1;
- }
- stdrp = lastrp[false];
- dstrp = lastrp[true];
- dstcmp = zp->z_nrules ? rule_cmp(dstrp, stdrp) : zp->z_isdst ? 1 : -1;
- stdzp = dstzp = zp;
-
- if (dstcmp < 0) {
- /* Standard time all year. */
- dstrp = NULL;
- } else if (0 < dstcmp) {
- /* DST all year. Use an abbreviation like
- "XXX3EDT4,0/0,J365/23" for EDT (-04) all year. */
- zic_t save = dstrp ? dstrp->r_save : zp->z_save;
- if (0 <= save)
- {
- /* Positive DST, the typical case for all-year DST.
- Fake a timezone with negative DST. */
- stdzp = &zstr[0];
- dstzp = &zstr[1];
- zstr[0].z_stdoff = zp->z_stdoff + 2 * save;
- zstr[0].z_format = "XXX"; /* Any 3 letters will do. */
- zstr[0].z_format_specifier = 0;
- zstr[1].z_stdoff = zstr[0].z_stdoff;
- zstr[1].z_format = zp->z_format;
- zstr[1].z_format_specifier = zp->z_format_specifier;
- }
- dstr.r_month = TM_JANUARY;
- dstr.r_dycode = DC_DOM;
- dstr.r_dayofmonth = 1;
- dstr.r_tod = 0;
- dstr.r_todisstd = dstr.r_todisut = false;
- dstr.r_isdst = true;
- dstr.r_save = save < 0 ? save : -save;
- dstr.r_abbrvar = dstrp ? dstrp->r_abbrvar : NULL;
- stdr.r_month = TM_DECEMBER;
- stdr.r_dycode = DC_DOM;
- stdr.r_dayofmonth = 31;
- stdr.r_tod = SECSPERDAY + dstr.r_save;
- stdr.r_todisstd = stdr.r_todisut = false;
- stdr.r_isdst = false;
- stdr.r_save = 0;
- stdr.r_abbrvar = save < 0 && stdrp ? stdrp->r_abbrvar : NULL;
- dstrp = &dstr;
- stdrp = &stdr;
- }
- len = doabbr(result, stdzp, stdrp ? stdrp->r_abbrvar : NULL,
- false, 0, true);
- offsetlen = stringoffset(result + len, - stdzp->z_stdoff);
- if (! offsetlen) {
- result[0] = '\0';
- return -1;
- }
- len += offsetlen;
- if (dstrp == NULL)
- return compat;
- len += doabbr(result + len, dstzp, dstrp->r_abbrvar,
- dstrp->r_isdst, dstrp->r_save, true);
- if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) {
- offsetlen = stringoffset(result + len,
- - (dstzp->z_stdoff + dstrp->r_save));
- if (! offsetlen) {
- result[0] = '\0';
- return -1;
- }
- len += offsetlen;
- }
- result[len++] = ',';
- c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff);
- if (c < 0) {
- result[0] = '\0';
- return -1;
- }
- if (compat < c)
- compat = c;
- len += strlen(result + len);
- result[len++] = ',';
- c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff);
- if (c < 0) {
- result[0] = '\0';
- return -1;
- }
- if (compat < c)
- compat = c;
- return compat;
-}
-
-static void
-outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
-{
- register ptrdiff_t i, j;
- register zic_t starttime = 0, untiltime = 0;
- register bool startttisstd;
- register bool startttisut;
- register char * startbuf;
- register char * ab;
- register char * envvar;
- register int max_abbr_len;
- register int max_envvar_len;
- register bool prodstic; /* all rules are min to max */
- register int compat;
- register bool do_extend;
- register char version;
- ptrdiff_t lastatmax = -1;
- zic_t max_year0;
- int defaulttype = -1;
-
- check_for_signal();
-
- 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);
- INITIALIZE(untiltime);
- INITIALIZE(starttime);
- /*
- ** Now. . .finally. . .generate some useful data!
- */
- timecnt = 0;
- typecnt = 0;
- charcnt = 0;
- prodstic = zonecount == 1;
- /*
- ** Thanks to Earl Chew
- ** for noting the need to unconditionally initialize startttisstd.
- */
- startttisstd = false;
- startttisut = false;
- min_year = max_year = EPOCH_YEAR;
- if (leapseen) {
- updateminmax(leapminyear);
- updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
- }
- for (i = 0; i < zonecount; ++i) {
- struct zone const *zp = &zpfirst[i];
- if (i < zonecount - 1)
- updateminmax(zp->z_untilrule.r_loyear);
- for (j = 0; j < zp->z_nrules; ++j) {
- struct rule *rp = &zp->z_rules[j];
- if (rp->r_lowasnum)
- updateminmax(rp->r_loyear);
- if (rp->r_hiwasnum)
- updateminmax(rp->r_hiyear);
- if (rp->r_lowasnum || rp->r_hiwasnum)
- prodstic = false;
- }
- }
- /*
- ** Generate lots of data if a rule can't cover all future times.
- */
- compat = stringzone(envvar, zpfirst, zonecount);
- version = compat < 2013 ? '2' : '3';
- do_extend = compat < 0;
- if (noise) {
- if (!*envvar)
- warning("%s %s",
- _("no POSIX environment variable for zone"),
- zpfirst->z_name);
- else if (compat != 0) {
- /* Circa-COMPAT clients, and earlier clients, might
- not work for this zone when given dates before
- 1970 or after 2038. */
- warning(_("%s: pre-%d clients may mishandle"
- " distant timestamps"),
- zpfirst->z_name, compat);
- }
- }
- if (do_extend) {
- /*
- ** Search through a couple of extra years past the obvious
- ** 400, to avoid edge cases. For example, suppose a non-POSIX
- ** rule applies from 2012 onwards and has transitions in March
- ** and September, plus some one-off transitions in November
- ** 2013. If zic looked only at the last 400 years, it would
- ** set max_year=2413, with the intent that the 400 years 2014
- ** through 2413 will be repeated. The last transition listed
- ** in the tzfile would be in 2413-09, less than 400 years
- ** after the last one-off transition in 2013-11. Two years
- ** might be overkill, but with the kind of edge cases
- ** available we're not sure that one year would suffice.
- */
- enum { years_of_observations = YEARSPERREPEAT + 2 };
-
- if (min_year >= ZIC_MIN + years_of_observations)
- min_year -= years_of_observations;
- else min_year = ZIC_MIN;
- if (max_year <= ZIC_MAX - years_of_observations)
- max_year += years_of_observations;
- else max_year = ZIC_MAX;
- /*
- ** Regardless of any of the above,
- ** for a "proDSTic" zone which specifies that its rules
- ** always have and always will be in effect,
- ** we only need one cycle to define the zone.
- */
- if (prodstic) {
- min_year = 1900;
- max_year = min_year + years_of_observations;
- }
- }
- max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
- + EPOCH_YEAR + 1));
- max_year0 = max_year;
- if (want_bloat()) {
- /* For the benefit of older systems,
- generate data from 1900 through 2038. */
- if (min_year > 1900)
- min_year = 1900;
- if (max_year < 2038)
- max_year = 2038;
- }
-
- if (min_time < lo_time || hi_time < max_time)
- unspecifiedtype = addtype(0, "-00", false, false, false);
-
- for (i = 0; i < zonecount; ++i) {
- struct rule *prevrp = NULL;
- /*
- ** A guess that may well be corrected later.
- */
- zic_t save = 0;
- struct zone const *zp = &zpfirst[i];
- bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
- bool useuntil = i < (zonecount - 1);
- zic_t stdoff = zp->z_stdoff;
- zic_t startoff = stdoff;
- zic_t prevktime;
- INITIALIZE(prevktime);
- if (useuntil && zp->z_untiltime <= min_time)
- continue;
- eat(zp->z_filenum, zp->z_linenum);
- *startbuf = '\0';
- if (zp->z_nrules == 0) {
- int type;
- save = zp->z_save;
- doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
- type = addtype(oadd(zp->z_stdoff, save),
- startbuf, zp->z_isdst, startttisstd,
- startttisut);
- if (usestart) {
- addtt(starttime, type);
- usestart = false;
- } else
- defaulttype = type;
- } else {
- zic_t year;
- for (year = min_year; year <= max_year; ++year) {
- if (useuntil && year > zp->z_untilrule.r_hiyear)
- break;
- /*
- ** Mark which rules to do in the current year.
- ** For those to do, calculate rpytime(rp, year);
- ** The former TYPE field was also considered here.
- */
- for (j = 0; j < zp->z_nrules; ++j) {
- zic_t one = 1;
- zic_t y2038_boundary = one << 31;
- struct rule *rp = &zp->z_rules[j];
- 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) {
- rp->r_temp = rpytime(rp, year);
- rp->r_todo
- = (rp->r_temp < y2038_boundary
- || year <= max_year0);
- }
- }
- for ( ; ; ) {
- register ptrdiff_t k;
- register zic_t jtime = 0, ktime = 0;
- register zic_t offset;
- struct rule *rp;
- int type;
-
- INITIALIZE(ktime);
- if (useuntil) {
- /*
- ** Turn untiltime into UT
- ** assuming the current stdoff and
- ** save values.
- */
- untiltime = zp->z_untiltime;
- if (!zp->z_untilrule.r_todisut)
- untiltime = tadd(untiltime,
- -stdoff);
- if (!zp->z_untilrule.r_todisstd)
- untiltime = tadd(untiltime,
- -save);
- }
- /*
- ** Find the rule (of those to do, if any)
- ** that takes effect earliest in the year.
- */
- k = -1;
- for (j = 0; j < zp->z_nrules; ++j) {
- struct rule *r = &zp->z_rules[j];
- if (!r->r_todo)
- continue;
- 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);
- jtime = r->r_temp;
- if (jtime == min_time ||
- jtime == max_time)
- continue;
- jtime = tadd(jtime, -offset);
- if (k < 0 || jtime < ktime) {
- k = j;
- ktime = jtime;
- } else if (jtime == ktime) {
- char const *dup_rules_msg =
- _("two rules for same instant");
- 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_filenum, zp->z_linenum,
- r->r_filenum, r->r_linenum);
- error("%s", dup_rules_msg);
- }
- }
- if (k < 0)
- break; /* go on to next year */
- rp = &zp->z_rules[k];
- rp->r_todo = false;
- if (useuntil && ktime >= untiltime) {
- if (!*startbuf
- && (oadd(zp->z_stdoff, rp->r_save)
- == startoff))
- doabbr(startbuf, zp, rp->r_abbrvar,
- rp->r_isdst, rp->r_save,
- false);
- break;
- }
- save = rp->r_save;
- if (usestart && ktime == starttime)
- usestart = false;
- if (usestart) {
- if (ktime < starttime) {
- startoff = oadd(zp->z_stdoff,
- save);
- doabbr(startbuf, zp,
- rp->r_abbrvar,
- rp->r_isdst,
- rp->r_save,
- false);
- continue;
- }
- if (*startbuf == '\0'
- && startoff == oadd(zp->z_stdoff,
- save)) {
- doabbr(startbuf,
- zp,
- rp->r_abbrvar,
- rp->r_isdst,
- rp->r_save,
- false);
- }
- }
- 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);
- if (!want_bloat() && !useuntil && !do_extend
- && prevrp && lo_time <= prevktime
- && redundant_time <= ktime
- && rp->r_hiyear == ZIC_MAX
- && prevrp->r_hiyear == ZIC_MAX)
- break;
- type = addtype(offset, ab, rp->r_isdst,
- rp->r_todisstd, rp->r_todisut);
- if (defaulttype < 0 && !rp->r_isdst)
- defaulttype = type;
- if (rp->r_hiyear == ZIC_MAX
- && ! (0 <= lastatmax
- && ktime < attypes[lastatmax].at))
- lastatmax = timecnt;
- addtt(ktime, type);
- prevrp = rp;
- prevktime = ktime;
- }
- }
- }
- if (usestart) {
- bool isdst = startoff != zp->z_stdoff;
- if (*startbuf == '\0' && zp->z_format)
- doabbr(startbuf, zp, disable_percent_s,
- isdst, save, false);
- eat(zp->z_filenum, zp->z_linenum);
- if (*startbuf == '\0')
-error(_("can't determine time zone abbreviation to use just after until time"));
- else {
- int type = addtype(startoff, startbuf, isdst,
- startttisstd, startttisut);
- if (defaulttype < 0 && !isdst)
- defaulttype = type;
- addtt(starttime, type);
- }
- }
- /*
- ** Now we may get to set starttime for the next zone line.
- */
- if (useuntil) {
- startttisstd = zp->z_untilrule.r_todisstd;
- startttisut = zp->z_untilrule.r_todisut;
- starttime = zp->z_untiltime;
- if (!startttisstd)
- starttime = tadd(starttime, -save);
- if (!startttisut)
- starttime = tadd(starttime, -stdoff);
- }
- }
- if (defaulttype < 0)
- defaulttype = 0;
- if (0 <= lastatmax)
- attypes[lastatmax].dontmerge = true;
- if (do_extend) {
- /*
- ** If we're extending the explicitly listed observations
- ** for 400 years because we can't fill the POSIX-TZ field,
- ** check whether we actually ended up explicitly listing
- ** observations through that period. If there aren't any
- ** near the end of the 400-year period, add a redundant
- ** one at the end of the final year, to make it clear
- ** that we are claiming to have definite knowledge of
- ** the lack of transitions up to that point.
- */
- struct rule xr;
- struct attype *lastat;
- xr.r_month = TM_JANUARY;
- xr.r_dycode = DC_DOM;
- xr.r_dayofmonth = 1;
- xr.r_tod = 0;
- for (lastat = attypes, i = 1; i < timecnt; i++)
- if (attypes[i].at > lastat->at)
- lastat = &attypes[i];
- if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) {
- addtt(rpytime(&xr, max_year + 1),
- lastat ? lastat->type : defaulttype);
- attypes[timecnt - 1].dontmerge = true;
- }
- }
- writezone(zpfirst->z_name, envvar, version, defaulttype);
- free(startbuf);
- free(ab);
- free(envvar);
-}
-
-static void
-addtt(zic_t starttime, int type)
-{
- attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
- attypes[timecnt].at = starttime;
- attypes[timecnt].dontmerge = false;
- attypes[timecnt].type = type;
- ++timecnt;
-}
-
-static int
-addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
-{
- register int i, j;
-
- if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) {
- error(_("UT offset out of range"));
- exit(EXIT_FAILURE);
- }
- if (!want_bloat())
- ttisstd = ttisut = false;
-
- for (j = 0; j < charcnt; ++j)
- if (strcmp(&chars[j], abbr) == 0)
- break;
- if (j == charcnt)
- newabbr(abbr);
- else {
- /* If there's already an entry, return its index. */
- for (i = 0; i < typecnt; i++)
- if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
- && ttisstd == ttisstds[i] && ttisut == ttisuts[i])
- return i;
- }
- /*
- ** There isn't one; add a new one, unless there are already too
- ** many.
- */
- if (typecnt >= TZ_MAX_TYPES) {
- error(_("too many local time types"));
- exit(EXIT_FAILURE);
- }
- i = typecnt++;
- utoffs[i] = utoff;
- isdsts[i] = isdst;
- ttisstds[i] = ttisstd;
- ttisuts[i] = ttisut;
- desigidx[i] = j;
- return i;
-}
-
-static void
-leapadd(zic_t t, int correction, int rolling)
-{
- register int i;
-
- if (TZ_MAX_LEAPS <= leapcnt) {
- error(_("too many leap seconds"));
- exit(EXIT_FAILURE);
- }
- if (rolling && (lo_time != min_time || hi_time != max_time)) {
- error(_("Rolling leap seconds not supported with -r"));
- exit(EXIT_FAILURE);
- }
- for (i = 0; i < leapcnt; ++i)
- if (t <= trans[i])
- break;
- memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
- memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
- memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
- trans[i] = t;
- corr[i] = correction;
- roll[i] = rolling;
- ++leapcnt;
-}
-
-static void
-adjleap(void)
-{
- register int i;
- register zic_t last = 0;
- register zic_t prevtrans = 0;
-
- /*
- ** propagate leap seconds forward
- */
- for (i = 0; i < leapcnt; ++i) {
- if (trans[i] - prevtrans < 28 * SECSPERDAY) {
- error(_("Leap seconds too close together"));
- exit(EXIT_FAILURE);
- }
- prevtrans = trans[i];
- trans[i] = tadd(trans[i], last);
- last = corr[i] += last;
- }
-
- if (0 <= leapexpires) {
- leapexpires = oadd(leapexpires, last);
- if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) {
- error(_("last Leap time does not precede Expires time"));
- exit(EXIT_FAILURE);
- }
- }
-}
-
-/* Is A a space character in the C locale? */
-static bool
-is_space(char a)
-{
- switch (a) {
- default:
- return false;
- case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
- return true;
- }
-}
-
-/* Is A an alphabetic character in the C locale? */
-static bool
-is_alpha(char a)
-{
- switch (a) {
- default:
- return false;
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
- case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
- case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
- case 'V': case 'W': case 'X': case 'Y': case 'Z':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
- case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
- case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
- case 'v': case 'w': case 'x': case 'y': case 'z':
- return true;
- }
-}
-
-/* If A is an uppercase character in the C locale, return its lowercase
- counterpart. Otherwise, return A. */
-static char
-lowerit(char a)
-{
- switch (a) {
- default: return a;
- case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
- case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
- case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
- case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
- case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
- case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
- case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
- case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
- case 'Y': return 'y'; case 'Z': return 'z';
- }
-}
-
-/* case-insensitive equality */
-static ATTRIBUTE_PURE bool
-ciequal(register const char *ap, register const char *bp)
-{
- while (lowerit(*ap) == lowerit(*bp++))
- if (*ap++ == '\0')
- return true;
- return false;
-}
-
-static ATTRIBUTE_PURE bool
-itsabbr(register const char *abbr, register const char *word)
-{
- if (lowerit(*abbr) != lowerit(*word))
- return false;
- ++word;
- while (*++abbr != '\0')
- do {
- if (*word == '\0')
- return false;
- } while (lowerit(*word++) != lowerit(*abbr));
- return true;
-}
-
-/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
-
-static ATTRIBUTE_PURE bool
-ciprefix(char const *abbr, char const *word)
-{
- do
- if (!*abbr)
- return true;
- while (lowerit(*abbr++) == lowerit(*word++));
-
- return false;
-}
-
-static const struct lookup *
-byword(const char *word, const struct lookup *table)
-{
- register const struct lookup * foundlp;
- register const struct lookup * lp;
-
- if (word == NULL || table == NULL)
- return NULL;
-
- /* If TABLE is LASTS and the word starts with "last" followed
- by a non-'-', skip the "last" and look in WDAY_NAMES instead.
- Warn about any usage of the undocumented prefix "last-". */
- if (table == lasts && ciprefix("last", word) && word[4]) {
- if (word[4] == '-')
- warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
- word, word + 5);
- else {
- word += 4;
- table = wday_names;
- }
- }
-
- /*
- ** Look for exact match.
- */
- for (lp = table; lp->l_word != NULL; ++lp)
- if (ciequal(word, lp->l_word))
- return lp;
- /*
- ** Look for inexact match.
- */
- foundlp = NULL;
- for (lp = table; lp->l_word != NULL; ++lp)
- if (ciprefix(word, lp->l_word)) {
- if (foundlp == NULL)
- foundlp = lp;
- else return NULL; /* multiple inexact matches */
- }
-
- if (foundlp && noise) {
- /* Warn about any backward-compatibility issue with pre-2017c zic. */
- bool pre_2017c_match = false;
- for (lp = table; lp->l_word; lp++)
- if (itsabbr(word, lp->l_word)) {
- if (pre_2017c_match) {
- warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
- break;
- }
- pre_2017c_match = true;
- }
- }
-
- return foundlp;
-}
-
-static int
-getfields(char *cp, char **array, int arrayelts)
-{
- register char * dp;
- register int nsubs;
-
- nsubs = 0;
- for ( ; ; ) {
- char *dstart;
- while (is_space(*cp))
- ++cp;
- if (*cp == '\0' || *cp == '#')
- break;
- dstart = dp = cp;
- do {
- if ((*dp = *cp++) != '"')
- ++dp;
- else while ((*dp = *cp++) != '"')
- if (*dp != '\0')
- ++dp;
- else {
- error(_("Odd number of quotation marks"));
- exit(EXIT_FAILURE);
- }
- } while (*cp && *cp != '#' && !is_space(*cp));
- if (is_space(*cp))
- ++cp;
- *dp = '\0';
- if (nsubs == arrayelts) {
- error(_("Too many input fields"));
- exit(EXIT_FAILURE);
- }
- array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
- }
- return nsubs;
-}
-
-static _Noreturn void
-time_overflow(void)
-{
- error(_("time overflow"));
- exit(EXIT_FAILURE);
-}
-
-static ATTRIBUTE_PURE 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;
-}
-
-static ATTRIBUTE_PURE 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;
-}
-
-/*
-** Given a rule, and a year, compute the date (in seconds since January 1,
-** 1970, 00:00 LOCAL time) in that year that the rule refers to.
-*/
-
-static zic_t
-rpytime(const struct rule *rp, zic_t wantedy)
-{
- register int m, i;
- register zic_t dayoff; /* with a nod to Margaret O. */
- register zic_t t, y;
- int yrem;
-
- if (wantedy == ZIC_MIN)
- return min_time;
- if (wantedy == ZIC_MAX)
- return max_time;
- m = TM_JANUARY;
- y = EPOCH_YEAR;
-
- /* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
- sans overflow. */
- yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
- dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
- + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
- * DAYSPERREPEAT);
- /* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow. */
- wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
-
- while (wantedy != y) {
- i = len_years[isleap(y)];
- dayoff = oadd(dayoff, i);
- y++;
- }
- while (m != rp->r_month) {
- i = len_months[isleap(y)][m];
- dayoff = oadd(dayoff, i);
- ++m;
- }
- i = rp->r_dayofmonth;
- if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
- if (rp->r_dycode == DC_DOWLEQ)
- --i;
- else {
- error(_("use of 2/29 in non leap-year"));
- exit(EXIT_FAILURE);
- }
- }
- --i;
- dayoff = oadd(dayoff, i);
- if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
- /*
- ** Don't trust mod of negative numbers.
- */
- zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK)
- % DAYSPERWEEK);
- while (wday != rp->r_wday)
- if (rp->r_dycode == DC_DOWGEQ) {
- dayoff = oadd(dayoff, 1);
- if (++wday >= DAYSPERWEEK)
- wday = 0;
- ++i;
- } else {
- dayoff = oadd(dayoff, -1);
- if (--wday < 0)
- wday = DAYSPERWEEK - 1;
- --i;
- }
- if (i < 0 || i >= len_months[isleap(y)][m]) {
- if (noise)
- warning(_("rule goes past start/end of month; \
-will not work with pre-2004 versions of zic"));
- }
- }
- if (dayoff < min_time / SECSPERDAY)
- return min_time;
- if (dayoff > max_time / SECSPERDAY)
- return max_time;
- t = (zic_t) dayoff * SECSPERDAY;
- return tadd(t, rp->r_tod);
-}
-
-static void
-newabbr(const char *string)
-{
- register int i;
-
- if (strcmp(string, GRANDPARENTED) != 0) {
- register const char * cp;
- const char * mp;
-
- cp = string;
- mp = NULL;
- while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
- || *cp == '-' || *cp == '+')
- ++cp;
- if (noise && cp - string < 3)
- mp = _("time zone abbreviation has fewer than 3 characters");
- if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
- mp = _("time zone abbreviation has too many characters");
- if (*cp != '\0')
-mp = _("time zone abbreviation differs from POSIX standard");
- if (mp != NULL)
- warning("%s (%s)", mp, string);
- }
- i = strlen(string) + 1;
- if (charcnt + i > TZ_MAX_CHARS) {
- error(_("too many, or too long, time zone abbreviations"));
- exit(EXIT_FAILURE);
- }
- strcpy(&chars[charcnt], string);
- charcnt += i;
-}
-
-/* Ensure that the directories of ARGNAME exist, by making any missing
- ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
- do it for ARGNAME too. Exit with failure if there is trouble.
- Do not consider an existing file to be trouble. */
-static void
-mkdirs(char const *argname, bool ancestors)
-{
- register char * name;
- register char * cp;
-
- cp = name = ecpyalloc(argname);
-
- /* On MS-Windows systems, do not worry about drive letters or
- backslashes, as this should suffice in practice. Time zone
- names do not use drive letters and backslashes. If the -d
- option of zic does not name an already-existing directory,
- it can use slashes to separate the already-existing
- ancestor prefix from the to-be-created subdirectories. */
-
- /* Do not mkdir a root directory, as it must exist. */
- while (*cp == '/')
- cp++;
-
- while (cp && ((cp = strchr(cp, '/')) || !ancestors)) {
- if (cp)
- *cp = '\0';
- /*
- ** Try to create it. It's OK if creation fails because
- ** the directory already exists, perhaps because some
- ** other process just created it. For simplicity do
- ** not check first whether it already exists, as that
- ** is checked anyway if the mkdir fails.
- */
- if (mkdir(name, MKDIR_UMASK) != 0) {
- /* Do not report an error if err == EEXIST, because
- some other process might have made the directory
- in the meantime. Likewise for ENOSYS, because
- Solaris 10 mkdir fails with ENOSYS if the
- directory is an automounted mount point.
- Likewise for EACCES, since mkdir can fail
- with EACCES merely because the parent directory
- is unwritable. Likewise for most other error
- numbers. */
- int err = errno;
- if (err == ELOOP || err == ENAMETOOLONG
- || err == ENOENT || err == ENOTDIR) {
- error(_("%s: Can't create directory %s: %s"),
- progname, name, strerror(err));
- exit(EXIT_FAILURE);
- }
- }
- if (cp)
- *cp++ = '/';
- }
- free(name);
-}
Index: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/private.h
===================================================================
--- tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/private.h (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone/private.h (nonexistent)
@@ -1,840 +0,0 @@
-/* Private header for tzdb code. */
-
-#ifndef PRIVATE_H
-
-#define PRIVATE_H
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*
-** This header is for use ONLY with the time conversion code.
-** There is no guarantee that it will remain unchanged,
-** or that it will remain at all.
-** Do NOT copy it to any system include directory.
-** 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.
-** You can use zdump to help in verifying other implementations.
-** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
-*/
-#ifndef USE_LTZ
-# define USE_LTZ 1
-#endif
-
-/* This string was in the Factory zone through version 2016f. */
-#define GRANDPARENTED "Local time zone must be set--see zic manual page"
-
-/*
-** Defaults for preprocessor symbols.
-** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
-*/
-
-#ifndef HAVE_DECL_ASCTIME_R
-# define HAVE_DECL_ASCTIME_R 1
-#endif
-
-#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 !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 false
-#endif
-
-#ifndef HAVE_INCOMPATIBLE_CTIME_R
-# define HAVE_INCOMPATIBLE_CTIME_R 0
-#endif
-
-#ifndef HAVE_LINK
-# define HAVE_LINK 1
-#endif /* !defined HAVE_LINK */
-
-#ifndef HAVE_MALLOC_ERRNO
-# define HAVE_MALLOC_ERRNO 1
-#endif
-
-#ifndef HAVE_POSIX_DECLS
-# define HAVE_POSIX_DECLS 1
-#endif
-
-#ifndef HAVE_SETENV
-# define HAVE_SETENV 1
-#endif
-
-#ifndef HAVE_STRDUP
-# define HAVE_STRDUP 1
-#endif
-
-#ifndef HAVE_STRTOLL
-# define HAVE_STRTOLL 1
-#endif
-
-#ifndef HAVE_SYMLINK
-# 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 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 true
-#endif
-
-#ifndef NETBSD_INSPIRED
-# define NETBSD_INSPIRED 1
-#endif
-
-#if HAVE_INCOMPATIBLE_CTIME_R
-# define asctime_r _incompatible_asctime_r
-# define ctime_r _incompatible_ctime_r
-#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-
-/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
-#define _GNU_SOURCE 1
-/* Fix asctime_r on Solaris 11. */
-#define _POSIX_PTHREAD_SEMANTICS 1
-/* Enable strtoimax on pre-C99 Solaris 11. */
-#define __EXTENSIONS__ 1
-
-/* 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
-#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
-# define _TIME_BITS 64
-#endif
-
-/*
-** Nested includes
-*/
-
-/* Avoid clashes with NetBSD by renaming NetBSD's declarations.
- If defining the 'timezone' variable, avoid a clash with FreeBSD's
- 'timezone' function by renaming its declaration. */
-#define localtime_rz sys_localtime_rz
-#define mktime_z sys_mktime_z
-#define posix2time_z sys_posix2time_z
-#define time2posix_z sys_time2posix_z
-#if defined USG_COMPAT && USG_COMPAT == 2
-# define timezone sys_timezone
-#endif
-#define timezone_t sys_timezone_t
-#define tzalloc sys_tzalloc
-#define tzfree sys_tzfree
-#include <time.h>
-#undef localtime_rz
-#undef mktime_z
-#undef posix2time_z
-#undef time2posix_z
-#if defined USG_COMPAT && USG_COMPAT == 2
-# undef timezone
-#endif
-#undef timezone_t
-#undef tzalloc
-#undef tzfree
-
-#include <stddef.h>
-#include <string.h>
-#include <limits.h> /* for CHAR_BIT et al. */
-#include <stdlib.h>
-
-#include <errno.h>
-
-#ifndef EINVAL
-# define EINVAL ERANGE
-#endif
-
-#ifndef ELOOP
-# define ELOOP EINVAL
-#endif
-#ifndef ENAMETOOLONG
-# define ENAMETOOLONG EINVAL
-#endif
-#ifndef ENOMEM
-# define ENOMEM EINVAL
-#endif
-#ifndef ENOTSUP
-# define ENOTSUP EINVAL
-#endif
-#ifndef EOVERFLOW
-# define EOVERFLOW EINVAL
-#endif
-
-#if HAVE_GETTEXT
-# include <libintl.h>
-#endif /* HAVE_GETTEXT */
-
-#if HAVE_UNISTD_H
-# include <unistd.h> /* for R_OK, and other POSIX goodness */
-#endif /* HAVE_UNISTD_H */
-
-#ifndef HAVE_STRFTIME_L
-# if _POSIX_VERSION < 200809
-# define HAVE_STRFTIME_L 0
-# else
-# define HAVE_STRFTIME_L 1
-# endif
-#endif
-
-#ifndef USG_COMPAT
-# ifndef _XOPEN_VERSION
-# define USG_COMPAT 0
-# else
-# define USG_COMPAT 1
-# endif
-#endif
-
-#ifndef HAVE_TZNAME
-# if _POSIX_VERSION < 198808 && !USG_COMPAT
-# define HAVE_TZNAME 0
-# else
-# define HAVE_TZNAME 1
-# endif
-#endif
-
-#ifndef ALTZONE
-# if defined __sun || defined _M_XENIX
-# define ALTZONE 1
-# else
-# define ALTZONE 0
-# endif
-#endif
-
-#ifndef R_OK
-# define R_OK 4
-#endif /* !defined R_OK */
-
-/*
-** Define HAVE_STDINT_H's default value here, rather than at the
-** start, since __GLIBC__ and INTMAX_MAX's values depend on
-** 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__ \
- || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
- || __CYGWIN__ || INTMAX_MAX)
-#endif /* !defined HAVE_STDINT_H */
-
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif /* !HAVE_STDINT_H */
-
-#ifndef HAVE_INTTYPES_H
-# define HAVE_INTTYPES_H HAVE_STDINT_H
-#endif
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-
-/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
-#ifdef __LONG_LONG_MAX__
-# ifndef LLONG_MAX
-# define LLONG_MAX __LONG_LONG_MAX__
-# endif
-# ifndef LLONG_MIN
-# define LLONG_MIN (-1 - LLONG_MAX)
-# 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;
-# define INT_FAST64_MIN LONG_MIN
-# define INT_FAST64_MAX LONG_MAX
-# endif
-#endif
-
-#ifndef PRIdFAST64
-# if INT_FAST64_MAX == LLONG_MAX
-# define PRIdFAST64 "lld"
-# else
-# define PRIdFAST64 "ld"
-# endif
-#endif
-
-#ifndef SCNdFAST64
-# define SCNdFAST64 PRIdFAST64
-#endif
-
-#ifndef INT_FAST32_MAX
-# if INT_MAX >> 31 == 0
-typedef long int_fast32_t;
-# define INT_FAST32_MAX LONG_MAX
-# define INT_FAST32_MIN LONG_MIN
-# else
-typedef int int_fast32_t;
-# define INT_FAST32_MAX INT_MAX
-# define INT_FAST32_MIN INT_MIN
-# endif
-#endif
-
-#ifndef INTMAX_MAX
-# ifdef LLONG_MAX
-typedef long long intmax_t;
-# if HAVE_STRTOLL
-# define strtoimax strtoll
-# endif
-# define INTMAX_MAX LLONG_MAX
-# define INTMAX_MIN LLONG_MIN
-# else
-typedef long intmax_t;
-# define INTMAX_MAX LONG_MAX
-# define INTMAX_MIN LONG_MIN
-# endif
-# ifndef strtoimax
-# define strtoimax strtol
-# endif
-#endif
-
-#ifndef PRIdMAX
-# if INTMAX_MAX == LLONG_MAX
-# define PRIdMAX "lld"
-# else
-# define PRIdMAX "ld"
-# endif
-#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;
-# 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;
-# endif
-#endif
-
-#ifndef UINTMAX_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
-typedef unsigned long long uintmax_t;
-# else
-typedef unsigned long uintmax_t;
-# endif
-#endif
-
-#ifndef PRIuMAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
-# define PRIuMAX "llu"
-# else
-# define PRIuMAX "lu"
-# endif
-#endif
-
-#ifndef SIZE_MAX
-# define SIZE_MAX ((size_t) -1)
-#endif
-
-#if 3 <= __GNUC__
-# define ATTRIBUTE_CONST __attribute__((const))
-# define ATTRIBUTE_MALLOC __attribute__((__malloc__))
-# define ATTRIBUTE_PURE __attribute__((__pure__))
-# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
-#else
-# define ATTRIBUTE_CONST /* empty */
-# define ATTRIBUTE_MALLOC /* empty */
-# define ATTRIBUTE_PURE /* empty */
-# define ATTRIBUTE_FORMAT(spec) /* empty */
-#endif
-
-#if !defined _Noreturn && __STDC_VERSION__ < 201112
-# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
-# define _Noreturn __attribute__((__noreturn__))
-# else
-# define _Noreturn
-# endif
-#endif
-
-#if __STDC_VERSION__ < 199901 && !defined restrict
-# define restrict /* empty */
-#endif
-
-/*
-** Workarounds for compilers/systems.
-*/
-
-#ifndef EPOCH_LOCAL
-# define EPOCH_LOCAL 0
-#endif
-#ifndef EPOCH_OFFSET
-# define EPOCH_OFFSET 0
-#endif
-#ifndef RESERVE_STD_EXT_IDS
-# define RESERVE_STD_EXT_IDS 0
-#endif
-
-/* If standard C identifiers with external linkage (e.g., localtime)
- are reserved and are not already being renamed anyway, rename them
- as if compiling with '-Dtime_tz=time_t'. */
-#if !defined time_tz && RESERVE_STD_EXT_IDS && USE_LTZ
-# define time_tz time_t
-#endif
-
-/*
-** Compile with -Dtime_tz=T to build the tz package with a private
-** time_t type equivalent to T rather than the system-supplied time_t.
-** This debugging feature can test unusual design decisions
-** (e.g., time_t wider than 'long', or unsigned time_t) even on
-** typical platforms.
-*/
-#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
-# define TZ_TIME_T 1
-#else
-# define TZ_TIME_T 0
-#endif
-
-#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
-static time_t sys_time(time_t *x) { return time(x); }
-#endif
-
-#if TZ_TIME_T
-
-typedef time_tz tz_time_t;
-
-# undef asctime
-# define asctime tz_asctime
-# undef asctime_r
-# define asctime_r tz_asctime_r
-# undef ctime
-# define ctime tz_ctime
-# undef ctime_r
-# define ctime_r tz_ctime_r
-# undef difftime
-# define difftime tz_difftime
-# undef gmtime
-# define gmtime tz_gmtime
-# undef gmtime_r
-# define gmtime_r tz_gmtime_r
-# undef localtime
-# define localtime tz_localtime
-# undef localtime_r
-# define localtime_r tz_localtime_r
-# undef localtime_rz
-# define localtime_rz tz_localtime_rz
-# undef mktime
-# define mktime tz_mktime
-# undef mktime_z
-# define mktime_z tz_mktime_z
-# undef offtime
-# define offtime tz_offtime
-# undef posix2time
-# define posix2time tz_posix2time
-# undef posix2time_z
-# define posix2time_z tz_posix2time_z
-# undef strftime
-# define strftime tz_strftime
-# undef time
-# define time tz_time
-# undef time2posix
-# define time2posix tz_time2posix
-# undef time2posix_z
-# define time2posix_z tz_time2posix_z
-# undef time_t
-# define time_t tz_time_t
-# undef timegm
-# define timegm tz_timegm
-# undef timelocal
-# define timelocal tz_timelocal
-# undef timeoff
-# define timeoff tz_timeoff
-# undef tzalloc
-# define tzalloc tz_tzalloc
-# undef tzfree
-# define tzfree tz_tzfree
-# undef tzset
-# define tzset tz_tzset
-# if HAVE_STRFTIME_L
-# undef strftime_l
-# define strftime_l tz_strftime_l
-# endif
-# if HAVE_TZNAME
-# undef tzname
-# define tzname tz_tzname
-# endif
-# if USG_COMPAT
-# undef daylight
-# define daylight tz_daylight
-# undef timezone
-# define timezone tz_timezone
-# endif
-# if ALTZONE
-# undef altzone
-# define altzone tz_altzone
-# endif
-
-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;
-size_t strftime(char *restrict, size_t, char const *restrict,
- struct tm const *restrict);
-# if HAVE_STRFTIME_L
-size_t strftime_l(char *restrict, size_t, char const *restrict,
- struct tm const *restrict, locale_t);
-# endif
-struct tm *gmtime(time_t const *);
-struct tm *gmtime_r(time_t const *restrict, struct tm *restrict);
-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 *);
-void tzset(void);
-#endif
-
-#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
-extern char *asctime_r(struct tm const *restrict, char *restrict);
-#endif
-
-#ifndef HAVE_DECL_ENVIRON
-# if defined environ || defined __USE_GNU
-# define HAVE_DECL_ENVIRON 1
-# else
-# define HAVE_DECL_ENVIRON 0
-# endif
-#endif
-
-#if !HAVE_DECL_ENVIRON
-extern char **environ;
-#endif
-
-#if 2 <= HAVE_TZNAME + (TZ_TIME_T || !HAVE_POSIX_DECLS)
-extern char *tzname[];
-#endif
-#if 2 <= USG_COMPAT + (TZ_TIME_T || !HAVE_POSIX_DECLS)
-extern long timezone;
-extern int daylight;
-#endif
-#if 2 <= ALTZONE + (TZ_TIME_T || !HAVE_POSIX_DECLS)
-extern long altzone;
-#endif
-
-/*
-** The STD_INSPIRED functions are similar, but most also need
-** declarations if time_tz is defined.
-*/
-
-#ifdef STD_INSPIRED
-# 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
-# if TZ_TIME_T || !defined timeoff
-time_t timeoff(struct tm *, long);
-# endif
-# if TZ_TIME_T || !defined time2posix
-time_t time2posix(time_t);
-# endif
-# if TZ_TIME_T || !defined posix2time
-time_t posix2time(time_t);
-# endif
-#endif
-
-/* 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 __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
- || (defined __APPLE__ && defined __MACH__))
-# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
-# define TM_GMTOFF tm_gmtoff
-# endif
-# if !defined TM_ZONE && !defined NO_TM_ZONE
-# define TM_ZONE tm_zone
-# endif
-#endif
-
-/*
-** Define functions that are ABI compatible with NetBSD but have
-** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t
-** and labors under the misconception that 'const timezone_t' is a
-** pointer to a constant. This use of 'const' is ineffective, so it
-** is not done here. What we call 'struct state' NetBSD calls
-** 'struct __state', but this is a private name so it doesn't matter.
-*/
-#if NETBSD_INSPIRED
-typedef struct state *timezone_t;
-struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
- struct tm *restrict);
-time_t mktime_z(timezone_t restrict, struct tm *restrict);
-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;
-# endif
-# if TZ_TIME_T || !defined time2posix_z
-time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
-# endif
-# endif
-#endif
-
-/*
-** Finally, some convenience items.
-*/
-
-#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
-#define TYPE_SIGNED(type) (((type) -1) < 0)
-#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
-
-/* Minimum and maximum of two values. Use lower case to avoid
- naming clashes with standard include files. */
-#define max(a, b) ((a) > (b) ? (a) : (b))
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
-/* Max and min values of the integer type T, of which only the bottom
- B bits are used, and where the highest-order used bit is considered
- to be a sign bit if T is signed. */
-#define MAXVAL(t, b) \
- ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
- - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
-#define MINVAL(t, b) \
- ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
-
-/* The extreme time values, assuming no padding. */
-#define TIME_T_MIN_NO_PADDING MINVAL(time_t, TYPE_BIT(time_t))
-#define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t))
-
-/* The extreme time values. These are macros, not constants, so that
- any portability problems occur only when compiling .c files that use
- the macros, which is safer for applications that need only zdump and zic.
- This implementation assumes no padding if time_t is signed and
- either the compiler lacks support for _Generic or time_t is not one
- of the standard signed integer types. */
-#if HAVE_GENERIC
-# define TIME_T_MIN \
- _Generic((time_t) 0, \
- signed char: SCHAR_MIN, short: SHRT_MIN, \
- int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \
- default: TIME_T_MIN_NO_PADDING)
-# define TIME_T_MAX \
- (TYPE_SIGNED(time_t) \
- ? _Generic((time_t) 0, \
- signed char: SCHAR_MAX, short: SHRT_MAX, \
- int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
- default: TIME_T_MAX_NO_PADDING) \
- : (time_t) -1)
-#else
-# define TIME_T_MIN TIME_T_MIN_NO_PADDING
-# define TIME_T_MAX TIME_T_MAX_NO_PADDING
-#endif
-
-/*
-** 302 / 1000 is log10(2.0) rounded up.
-** Subtract one for the sign bit if the type is signed;
-** add one for integer division truncation;
-** add one more for a minus sign if the type is signed.
-*/
-#define INT_STRLEN_MAXIMUM(type) \
- ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
- 1 + TYPE_SIGNED(type))
-
-/*
-** INITIALIZE(x)
-*/
-
-#ifdef GCC_LINT
-# define INITIALIZE(x) ((x) = 0)
-#else
-# define INITIALIZE(x)
-#endif
-
-/* Whether memory access must strictly follow the C standard.
- If 0, it's OK to read uninitialized storage so long as the value is
- not relied upon. Defining it to 0 lets mktime access parts of
- struct tm that might be uninitialized, as a heuristic when the
- standard doesn't say what to return and when tm_gmtoff can help
- mktime likely infer a better value. */
-#ifndef UNINIT_TRAP
-# define UNINIT_TRAP 0
-#endif
-
-#ifdef DEBUG
-# 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
-
-/*
-** For the benefit of GNU folk...
-** '_(MSGID)' uses the current locale's message library string for MSGID.
-** The default is to use gettext if available, and use MSGID otherwise.
-*/
-
-#ifndef _
-#if HAVE_GETTEXT
-#define _(msgid) gettext(msgid)
-#else /* !HAVE_GETTEXT */
-#define _(msgid) msgid
-#endif /* !HAVE_GETTEXT */
-#endif /* !defined _ */
-
-#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
-# define TZ_DOMAIN "tz"
-#endif
-
-#if HAVE_INCOMPATIBLE_CTIME_R
-#undef asctime_r
-#undef ctime_r
-char *asctime_r(struct tm const *, char *);
-char *ctime_r(time_t const *, char *);
-#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-
-/* Handy macros that are independent of tzfile implementation. */
-
-enum {
- SECSPERMIN = 60,
- MINSPERHOUR = 60,
- SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
- HOURSPERDAY = 24,
- DAYSPERWEEK = 7,
- DAYSPERNYEAR = 365,
- DAYSPERLYEAR = DAYSPERNYEAR + 1,
- MONSPERYEAR = 12,
- YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
-};
-
-#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
-
-#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
-#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
-#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
-
-enum {
- TM_SUNDAY,
- TM_MONDAY,
- TM_TUESDAY,
- TM_WEDNESDAY,
- TM_THURSDAY,
- TM_FRIDAY,
- TM_SATURDAY
-};
-
-enum {
- TM_JANUARY,
- TM_FEBRUARY,
- TM_MARCH,
- TM_APRIL,
- TM_MAY,
- TM_JUNE,
- TM_JULY,
- TM_AUGUST,
- TM_SEPTEMBER,
- TM_OCTOBER,
- TM_NOVEMBER,
- TM_DECEMBER
-};
-
-enum {
- TM_YEAR_BASE = 1900,
- TM_WDAY_BASE = TM_MONDAY,
- EPOCH_YEAR = 1970,
- EPOCH_WDAY = TM_THURSDAY
-};
-
-#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
-
-/*
-** Since everything in isleap is modulo 400 (or a factor of 400), we know that
-** isleap(y) == isleap(y % 400)
-** and so
-** isleap(a + b) == isleap((a + b) % 400)
-** or
-** isleap(a + b) == isleap(a % 400 + b % 400)
-** This is true even if % means modulo rather than Fortran remainder
-** (which is allowed by C89 but not by C99 or later).
-** We use this to avoid addition overflow problems.
-*/
-
-#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
-
-#endif /* !defined PRIVATE_H */
Index: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone
===================================================================
--- tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone (nonexistent)
Property changes on: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new/timezone
___________________________________________________________________
Deleted: svn:ignore
## -1,73 +0,0 ##
-
-# install dir
-dist
-
-# Target build dirs
-.a1x-newlib
-.a2x-newlib
-.at91sam7s-newlib
-
-.build-machine
-
-.a1x-glibc
-.a2x-glibc
-.h3-glibc
-.h5-glibc
-.i586-glibc
-.i686-glibc
-.imx6-glibc
-.jz47xx-glibc
-.makefile
-.am335x-glibc
-.omap543x-glibc
-.p5600-glibc
-.power8-glibc
-.power8le-glibc
-.power9-glibc
-.power9le-glibc
-.m1000-glibc
-.riscv64-glibc
-.rk328x-glibc
-.rk33xx-glibc
-.rk339x-glibc
-.s8xx-glibc
-.s9xx-glibc
-.x86_64-glibc
-
-# Hidden files (each file)
-.makefile
-.dist
-.rootfs
-
-# src & hw requires
-.src_requires
-.src_requires_depend
-.requires
-.requires_depend
-
-# Tarballs
-*.gz
-*.bz2
-*.lz
-*.xz
-*.tgz
-*.txz
-
-# Signatures
-*.asc
-*.sig
-*.sign
-*.sha1sum
-
-# Patches
-*.patch
-
-# Descriptions
-*.dsc
-*.txt
-
-# Default linux config files
-*.defconfig
-
-# backup copies
-*~
Index: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new
===================================================================
--- tz/create-2022f-glibc-2.36-patch/glibc-2.36-new (revision 384)
+++ tz/create-2022f-glibc-2.36-patch/glibc-2.36-new (nonexistent)
Property changes on: tz/create-2022f-glibc-2.36-patch/glibc-2.36-new
___________________________________________________________________
Deleted: svn:ignore
## -1,73 +0,0 ##
-
-# install dir
-dist
-
-# Target build dirs
-.a1x-newlib
-.a2x-newlib
-.at91sam7s-newlib
-
-.build-machine
-
-.a1x-glibc
-.a2x-glibc
-.h3-glibc
-.h5-glibc
-.i586-glibc
-.i686-glibc
-.imx6-glibc
-.jz47xx-glibc
-.makefile
-.am335x-glibc
-.omap543x-glibc
-.p5600-glibc
-.power8-glibc
-.power8le-glibc
-.power9-glibc
-.power9le-glibc
-.m1000-glibc
-.riscv64-glibc
-.rk328x-glibc
-.rk33xx-glibc
-.rk339x-glibc
-.s8xx-glibc
-.s9xx-glibc
-.x86_64-glibc
-
-# Hidden files (each file)
-.makefile
-.dist
-.rootfs
-
-# src & hw requires
-.src_requires
-.src_requires_depend
-.requires
-.requires_depend
-
-# Tarballs
-*.gz
-*.bz2
-*.lz
-*.xz
-*.tgz
-*.txz
-
-# Signatures
-*.asc
-*.sig
-*.sign
-*.sha1sum
-
-# Patches
-*.patch
-
-# Descriptions
-*.dsc
-*.txt
-
-# Default linux config files
-*.defconfig
-
-# backup copies
-*~
Index: tz/create-2022f-glibc-2.36-patch
===================================================================
--- tz/create-2022f-glibc-2.36-patch (revision 384)
+++ tz/create-2022f-glibc-2.36-patch (nonexistent)
Property changes on: tz/create-2022f-glibc-2.36-patch
___________________________________________________________________
Deleted: svn:ignore
## -1,73 +0,0 ##
-
-# install dir
-dist
-
-# Target build dirs
-.a1x-newlib
-.a2x-newlib
-.at91sam7s-newlib
-
-.build-machine
-
-.a1x-glibc
-.a2x-glibc
-.h3-glibc
-.h5-glibc
-.i586-glibc
-.i686-glibc
-.imx6-glibc
-.jz47xx-glibc
-.makefile
-.am335x-glibc
-.omap543x-glibc
-.p5600-glibc
-.power8-glibc
-.power8le-glibc
-.power9-glibc
-.power9le-glibc
-.m1000-glibc
-.riscv64-glibc
-.rk328x-glibc
-.rk33xx-glibc
-.rk339x-glibc
-.s8xx-glibc
-.s9xx-glibc
-.x86_64-glibc
-
-# Hidden files (each file)
-.makefile
-.dist
-.rootfs
-
-# src & hw requires
-.src_requires
-.src_requires_depend
-.requires
-.requires_depend
-
-# Tarballs
-*.gz
-*.bz2
-*.lz
-*.xz
-*.tgz
-*.txz
-
-# Signatures
-*.asc
-*.sig
-*.sign
-*.sha1sum
-
-# Patches
-*.patch
-
-# Descriptions
-*.dsc
-*.txt
-
-# Default linux config files
-*.defconfig
-
-# backup copies
-*~
Index: tz/Makefile
===================================================================
--- tz/Makefile (revision 384)
+++ tz/Makefile (revision 385)
@@ -7,7 +7,7 @@
url = $(DOWNLOAD_SERVER)/sources/iana/tz
-versions = 2022f
+versions = 2023c
tarballs = $(sort \
$(shell \
@@ -18,8 +18,8 @@
)
sha1s = $(addsuffix .sha1sum, $(tarballs))
-patches = $(CURDIR)/patches/glibc-2.36-tzcode-2022f.patch
-patches += $(CURDIR)/patches/tzcode-2022f-version.patch
+patches = $(CURDIR)/patches/glibc-2.37-tzcode-2023c.patch
+patches += $(CURDIR)/patches/tzcode-2023c-version.patch
.NOTPARALLEL: $(patches)
@@ -54,8 +54,8 @@
$(patches): $(sha1s)
@echo -e "\n======= Create Patches =======\n" ; \
- ( cd create-2022f-glibc-2.36-patch ; ./create.patch.sh ) ; \
- ( cd create-2022f-version-patch ; ./create.patch.sh ) ; \
+ ( cd create-2023c-glibc-2.37-patch ; ./create.patch.sh ) ; \
+ ( cd create-2023c-version-patch ; ./create.patch.sh ) ; \
echo -e "\n"
download_clean:
Index: tz/create-2023c-glibc-2.37-patch/create.patch.sh
===================================================================
--- tz/create-2023c-glibc-2.37-patch/create.patch.sh (nonexistent)
+++ tz/create-2023c-glibc-2.37-patch/create.patch.sh (revision 385)
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+VERSION=2023c
+GLIBC_VERSION=2.37
+
+mkdir -p glibc-$GLIBC_VERSION/timezone
+tar -C glibc-$GLIBC_VERSION/timezone --files-from=file.list -xzvf ../tzcode$VERSION.tar.gz
+mv glibc-$GLIBC_VERSION glibc-$GLIBC_VERSION-orig
+
+cp -rf ./glibc-$GLIBC_VERSION-new ./glibc-$GLIBC_VERSION
+
+diff --unified -Nr glibc-$GLIBC_VERSION-orig glibc-$GLIBC_VERSION > glibc-$GLIBC_VERSION-tzcode-$VERSION.patch
+
+mv glibc-$GLIBC_VERSION-tzcode-$VERSION.patch ../patches
+
+rm -rf ./glibc-$GLIBC_VERSION
+rm -rf ./glibc-$GLIBC_VERSION-orig
Property changes on: tz/create-2023c-glibc-2.37-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: tz/create-2023c-glibc-2.37-patch/file.list
===================================================================
--- tz/create-2023c-glibc-2.37-patch/file.list (nonexistent)
+++ tz/create-2023c-glibc-2.37-patch/file.list (revision 385)
@@ -0,0 +1,3 @@
+private.h
+zdump.c
+zic.c
Index: tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/private.h
===================================================================
--- tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/private.h (nonexistent)
+++ tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/private.h (revision 385)
@@ -0,0 +1,1008 @@
+/* Private header for tzdb code. */
+
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/* PORT_TO_C89 means the code should work even if the underlying
+ compiler and library support only C89. SUPPORT_C89 means the
+ tzcode library should support C89 callers in addition to the usual
+ support for C99-and-later callers. These macros are obsolescent,
+ and the plan is to remove them along with any code needed only when
+ they are nonzero. */
+#ifndef PORT_TO_C89
+# define PORT_TO_C89 0
+#endif
+#ifndef SUPPORT_C89
+# define SUPPORT_C89 0
+#endif
+
+#ifndef __STDC_VERSION__
+# define __STDC_VERSION__ 0
+#endif
+
+/* Define true, false and bool if they don't work out of the box. */
+#if PORT_TO_C89 && __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#elif __STDC_VERSION__ < 202311
+# include <stdbool.h>
+#endif
+
+#if __STDC_VERSION__ < 202311
+# define static_assert(cond) extern int static_assert_check[(cond) ? 1 : -1]
+#endif
+
+/*
+** zdump has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use zdump to help in verifying other implementations.
+** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
+*/
+#ifndef USE_LTZ
+# define USE_LTZ 1
+#endif
+
+/* This string was in the Factory zone through version 2016f. */
+#define GRANDPARENTED "Local time zone must be set--see zic manual page"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
+*/
+
+#ifndef HAVE_DECL_ASCTIME_R
+# define HAVE_DECL_ASCTIME_R 1
+#endif
+
+#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__ && !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_GETTEXT && defined __has_include
+# if __has_include(<libintl.h>)
+# define HAVE_GETTEXT true
+# endif
+#endif
+#ifndef HAVE_GETTEXT
+# define HAVE_GETTEXT false
+#endif
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+# define HAVE_INCOMPATIBLE_CTIME_R 0
+#endif
+
+#ifndef HAVE_LINK
+# define HAVE_LINK 1
+#endif /* !defined HAVE_LINK */
+
+#ifndef HAVE_MALLOC_ERRNO
+# define HAVE_MALLOC_ERRNO 1
+#endif
+
+#ifndef HAVE_POSIX_DECLS
+# define HAVE_POSIX_DECLS 1
+#endif
+
+#ifndef HAVE_SETENV
+# define HAVE_SETENV 1
+#endif
+
+#ifndef HAVE_STRDUP
+# define HAVE_STRDUP 1
+#endif
+
+#ifndef HAVE_SYMLINK
+# 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 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 true
+#endif
+
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+# define asctime_r _incompatible_asctime_r
+# define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
+#define _GNU_SOURCE 1
+/* Fix asctime_r on Solaris 11. */
+#define _POSIX_PTHREAD_SEMANTICS 1
+/* Enable strtoimax on pre-C99 Solaris 11. */
+#define __EXTENSIONS__ 1
+
+/* 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
+#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
+# define _TIME_BITS 64
+#endif
+
+/*
+** Nested includes
+*/
+
+/* Avoid clashes with NetBSD by renaming NetBSD's declarations.
+ If defining the 'timezone' variable, avoid a clash with FreeBSD's
+ 'timezone' function by renaming its declaration. */
+#define localtime_rz sys_localtime_rz
+#define mktime_z sys_mktime_z
+#define posix2time_z sys_posix2time_z
+#define time2posix_z sys_time2posix_z
+#if defined USG_COMPAT && USG_COMPAT == 2
+# define timezone sys_timezone
+#endif
+#define timezone_t sys_timezone_t
+#define tzalloc sys_tzalloc
+#define tzfree sys_tzfree
+#include <time.h>
+#undef localtime_rz
+#undef mktime_z
+#undef posix2time_z
+#undef time2posix_z
+#if defined USG_COMPAT && USG_COMPAT == 2
+# undef timezone
+#endif
+#undef timezone_t
+#undef tzalloc
+#undef tzfree
+
+#include <stddef.h>
+#include <string.h>
+#if !PORT_TO_C89
+# include <inttypes.h>
+#endif
+#include <limits.h> /* for CHAR_BIT et al. */
+#include <stdlib.h>
+
+#include <errno.h>
+
+#ifndef EINVAL
+# define EINVAL ERANGE
+#endif
+
+#ifndef ELOOP
+# define ELOOP EINVAL
+#endif
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+#ifndef ENOMEM
+# define ENOMEM EINVAL
+#endif
+#ifndef ENOTSUP
+# define ENOTSUP EINVAL
+#endif
+#ifndef EOVERFLOW
+# define EOVERFLOW EINVAL
+#endif
+
+#if HAVE_GETTEXT
+# include <libintl.h>
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_UNISTD_H
+# include <unistd.h> /* for R_OK, and other POSIX goodness */
+#endif /* HAVE_UNISTD_H */
+
+#ifndef HAVE_STRFTIME_L
+# if _POSIX_VERSION < 200809
+# define HAVE_STRFTIME_L 0
+# else
+# define HAVE_STRFTIME_L 1
+# endif
+#endif
+
+#ifndef USG_COMPAT
+# ifndef _XOPEN_VERSION
+# define USG_COMPAT 0
+# else
+# define USG_COMPAT 1
+# endif
+#endif
+
+#ifndef HAVE_TZNAME
+# if _POSIX_VERSION < 198808 && !USG_COMPAT
+# define HAVE_TZNAME 0
+# else
+# define HAVE_TZNAME 1
+# endif
+#endif
+
+#ifndef ALTZONE
+# if defined __sun || defined _M_XENIX
+# define ALTZONE 1
+# else
+# define ALTZONE 0
+# endif
+#endif
+
+#ifndef R_OK
+# define R_OK 4
+#endif /* !defined R_OK */
+
+#if PORT_TO_C89
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__ and INTMAX_MAX's values depend on
+** 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__ \
+ || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
+ || __CYGWIN__ || INTMAX_MAX)
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif /* !HAVE_STDINT_H */
+
+#ifndef HAVE_INTTYPES_H
+# define HAVE_INTTYPES_H HAVE_STDINT_H
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_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
+# 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 == LONG_MAX
+# define PRIdFAST64 "ld"
+# else
+# define PRIdFAST64 "lld"
+# endif
+#endif
+
+#ifndef SCNdFAST64
+# define SCNdFAST64 PRIdFAST64
+#endif
+
+#ifndef INT_FAST32_MAX
+# if INT_MAX >> 31 == 0
+typedef long int_fast32_t;
+# define INT_FAST32_MAX LONG_MAX
+# define INT_FAST32_MIN LONG_MIN
+# else
+typedef int int_fast32_t;
+# define INT_FAST32_MAX INT_MAX
+# define INT_FAST32_MIN INT_MIN
+# endif
+#endif
+
+#ifndef INTMAX_MAX
+# ifdef LLONG_MAX
+typedef long long intmax_t;
+# ifndef HAVE_STRTOLL
+# define HAVE_STRTOLL true
+# endif
+# if HAVE_STRTOLL
+# define strtoimax strtoll
+# endif
+# define INTMAX_MAX LLONG_MAX
+# define INTMAX_MIN LLONG_MIN
+# else
+typedef long intmax_t;
+# define INTMAX_MAX LONG_MAX
+# define INTMAX_MIN LONG_MIN
+# endif
+# ifndef strtoimax
+# define strtoimax strtol
+# endif
+#endif
+
+#ifndef PRIdMAX
+# if INTMAX_MAX == LLONG_MAX
+# define PRIdMAX "lld"
+# else
+# define PRIdMAX "ld"
+# 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 3 <= ULONG_MAX >> 31 >> 31
+typedef unsigned long uint_fast64_t;
+# define UINT_FAST64_MAX ULONG_MAX
+# else
+/* 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
+# ifdef ULLONG_MAX
+typedef unsigned long long uintmax_t;
+# define UINTMAX_MAX ULLONG_MAX
+# else
+typedef unsigned long uintmax_t;
+# define UINTMAX_MAX ULONG_MAX
+# endif
+#endif
+
+#ifndef PRIuMAX
+# ifdef ULLONG_MAX
+# define PRIuMAX "llu"
+# else
+# define PRIuMAX "lu"
+# endif
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#endif /* PORT_TO_C89 */
+
+/* The maximum size of any created object, as a signed integer.
+ Although the C standard does not outright prohibit larger objects,
+ behavior is undefined if the result of pointer subtraction does not
+ fit into ptrdiff_t, and the code assumes in several places that
+ pointer subtraction works. As a practical matter it's OK to not
+ support objects larger than this. */
+#define INDEX_MAX ((ptrdiff_t) min(PTRDIFF_MAX, SIZE_MAX))
+
+/* 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_MALLOC __attribute__((malloc))
+# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
+#else
+# define ATTRIBUTE_MALLOC /* empty */
+# define ATTRIBUTE_FORMAT(spec) /* empty */
+#endif
+
+#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(deprecated)
+# define ATTRIBUTE_DEPRECATED [[deprecated]]
+# endif
+#endif
+#ifndef ATTRIBUTE_DEPRECATED
+# if 3 < __GNUC__ + (2 <= __GNUC_MINOR__)
+# define ATTRIBUTE_DEPRECATED __attribute__((deprecated))
+# else
+# define ATTRIBUTE_DEPRECATED /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(fallthrough)
+# define ATTRIBUTE_FALLTHROUGH [[fallthrough]]
+# endif
+#endif
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if 7 <= __GNUC__
+# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough))
+# else
+# define ATTRIBUTE_FALLTHROUGH ((void) 0)
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(maybe_unused)
+# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
+# endif
+#endif
+#ifndef ATTRIBUTE_MAYBE_UNUSED
+# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused))
+# else
+# define ATTRIBUTE_MAYBE_UNUSED /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(noreturn)
+# define ATTRIBUTE_NORETURN [[noreturn]]
+# endif
+#endif
+#ifndef ATTRIBUTE_NORETURN
+# if 201112 <= __STDC_VERSION__
+# define ATTRIBUTE_NORETURN _Noreturn
+# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
+# define ATTRIBUTE_NORETURN __attribute__((noreturn))
+# else
+# define 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
+
+#if (__STDC_VERSION__ < 199901 && !defined restrict \
+ && (PORT_TO_C89 || defined _MSC_VER))
+# define restrict /* empty */
+#endif
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+#ifndef EPOCH_LOCAL
+# define EPOCH_LOCAL 0
+#endif
+#ifndef EPOCH_OFFSET
+# define EPOCH_OFFSET 0
+#endif
+#ifndef RESERVE_STD_EXT_IDS
+# define RESERVE_STD_EXT_IDS 0
+#endif
+
+/* If standard C identifiers with external linkage (e.g., localtime)
+ are reserved and are not already being renamed anyway, rename them
+ as if compiling with '-Dtime_tz=time_t'. */
+#if !defined time_tz && RESERVE_STD_EXT_IDS && USE_LTZ
+# define time_tz time_t
+#endif
+
+/*
+** Compile with -Dtime_tz=T to build the tz package with a private
+** time_t type equivalent to T rather than the system-supplied time_t.
+** This debugging feature can test unusual design decisions
+** (e.g., time_t wider than 'long', or unsigned time_t) even on
+** typical platforms.
+*/
+#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
+# define TZ_TIME_T 1
+#else
+# define TZ_TIME_T 0
+#endif
+
+#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
+static time_t sys_time(time_t *x) { return time(x); }
+#endif
+
+#if TZ_TIME_T
+
+typedef time_tz tz_time_t;
+
+# undef asctime
+# define asctime tz_asctime
+# undef asctime_r
+# define asctime_r tz_asctime_r
+# undef ctime
+# define ctime tz_ctime
+# undef ctime_r
+# define ctime_r tz_ctime_r
+# undef difftime
+# define difftime tz_difftime
+# undef gmtime
+# define gmtime tz_gmtime
+# undef gmtime_r
+# define gmtime_r tz_gmtime_r
+# undef localtime
+# define localtime tz_localtime
+# undef localtime_r
+# define localtime_r tz_localtime_r
+# undef localtime_rz
+# define localtime_rz tz_localtime_rz
+# undef mktime
+# define mktime tz_mktime
+# undef mktime_z
+# define mktime_z tz_mktime_z
+# undef offtime
+# define offtime tz_offtime
+# undef posix2time
+# define posix2time tz_posix2time
+# undef posix2time_z
+# define posix2time_z tz_posix2time_z
+# undef strftime
+# define strftime tz_strftime
+# undef time
+# define time tz_time
+# undef time2posix
+# define time2posix tz_time2posix
+# undef time2posix_z
+# define time2posix_z tz_time2posix_z
+# undef time_t
+# define time_t tz_time_t
+# undef timegm
+# define timegm tz_timegm
+# undef timelocal
+# define timelocal tz_timelocal
+# undef timeoff
+# define timeoff tz_timeoff
+# undef tzalloc
+# define tzalloc tz_tzalloc
+# undef tzfree
+# define tzfree tz_tzfree
+# undef tzset
+# define tzset tz_tzset
+# if HAVE_STRFTIME_L
+# undef strftime_l
+# define strftime_l tz_strftime_l
+# endif
+# if HAVE_TZNAME
+# undef tzname
+# define tzname tz_tzname
+# endif
+# if USG_COMPAT
+# undef daylight
+# define daylight tz_daylight
+# undef timezone
+# define timezone tz_timezone
+# endif
+# if ALTZONE
+# undef altzone
+# define altzone tz_altzone
+# endif
+
+# if __STDC_VERSION__ < 202311
+# define DEPRECATED_IN_C23 /* empty */
+# else
+# define DEPRECATED_IN_C23 ATTRIBUTE_DEPRECATED
+# endif
+DEPRECATED_IN_C23 char *asctime(struct tm const *);
+char *asctime_r(struct tm const *restrict, char *restrict);
+DEPRECATED_IN_C23 char *ctime(time_t const *);
+char *ctime_r(time_t const *, char *);
+ATTRIBUTE_UNSEQUENCED double difftime(time_t, time_t);
+size_t strftime(char *restrict, size_t, char const *restrict,
+ struct tm const *restrict);
+# if HAVE_STRFTIME_L
+size_t strftime_l(char *restrict, size_t, char const *restrict,
+ struct tm const *restrict, locale_t);
+# endif
+struct tm *gmtime(time_t const *);
+struct tm *gmtime_r(time_t const *restrict, struct tm *restrict);
+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
+
+#ifndef HAVE_DECL_ENVIRON
+# if defined environ || defined __USE_GNU
+# define HAVE_DECL_ENVIRON 1
+# else
+# define HAVE_DECL_ENVIRON 0
+# endif
+#endif
+
+#if !HAVE_DECL_ENVIRON
+extern char **environ;
+#endif
+
+#if 2 <= HAVE_TZNAME + (TZ_TIME_T || !HAVE_POSIX_DECLS)
+extern char *tzname[];
+#endif
+#if 2 <= USG_COMPAT + (TZ_TIME_T || !HAVE_POSIX_DECLS)
+extern long timezone;
+extern int daylight;
+#endif
+#if 2 <= ALTZONE + (TZ_TIME_T || !HAVE_POSIX_DECLS)
+extern long altzone;
+#endif
+
+/*
+** The STD_INSPIRED functions are similar, but most also need
+** declarations if time_tz is defined.
+*/
+
+#ifndef STD_INSPIRED
+# define STD_INSPIRED 0
+#endif
+#if STD_INSPIRED
+# if TZ_TIME_T || !defined offtime
+struct tm *offtime(time_t const *, long);
+# endif
+# if TZ_TIME_T || !defined timelocal
+time_t timelocal(struct tm *);
+# endif
+# if TZ_TIME_T || !defined timeoff
+time_t timeoff(struct tm *, long);
+# endif
+# if TZ_TIME_T || !defined time2posix
+time_t time2posix(time_t);
+# endif
+# if TZ_TIME_T || !defined posix2time
+time_t posix2time(time_t);
+# endif
+#endif
+
+/* 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
+# define TM_GMTOFF tm_gmtoff
+# endif
+# if !defined TM_ZONE && !defined NO_TM_ZONE
+# define TM_ZONE tm_zone
+# endif
+#endif
+
+/*
+** Define functions that are ABI compatible with NetBSD but have
+** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t
+** and labors under the misconception that 'const timezone_t' is a
+** pointer to a constant. This use of 'const' is ineffective, so it
+** is not done here. What we call 'struct state' NetBSD calls
+** 'struct __state', but this is a private name so it doesn't matter.
+*/
+#if NETBSD_INSPIRED
+typedef struct state *timezone_t;
+struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
+ struct tm *restrict);
+time_t mktime_z(timezone_t restrict, struct tm *restrict);
+timezone_t tzalloc(char const *);
+void tzfree(timezone_t);
+# if STD_INSPIRED
+# if TZ_TIME_T || !defined posix2time_z
+ATTRIBUTE_REPRODUCIBLE time_t posix2time_z(timezone_t, time_t);
+# endif
+# if TZ_TIME_T || !defined time2posix_z
+ATTRIBUTE_REPRODUCIBLE time_t time2posix_z(timezone_t, time_t);
+# endif
+# endif
+#endif
+
+/*
+** Finally, some convenience items.
+*/
+
+#define TYPE_BIT(type) (CHAR_BIT * (ptrdiff_t) sizeof(type))
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+
+/* Minimum and maximum of two values. Use lower case to avoid
+ naming clashes with standard include files. */
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+/* Max and min values of the integer type T, of which only the bottom
+ B bits are used, and where the highest-order used bit is considered
+ to be a sign bit if T is signed. */
+#define MAXVAL(t, b) \
+ ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
+ - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b) \
+ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+
+/* The extreme time values, assuming no padding. */
+#define TIME_T_MIN_NO_PADDING MINVAL(time_t, TYPE_BIT(time_t))
+#define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t))
+
+/* The extreme time values. These are macros, not constants, so that
+ any portability problems occur only when compiling .c files that use
+ the macros, which is safer for applications that need only zdump and zic.
+ This implementation assumes no padding if time_t is signed and
+ either the compiler lacks support for _Generic or time_t is not one
+ of the standard signed integer types. */
+#if HAVE__GENERIC
+# define TIME_T_MIN \
+ _Generic((time_t) 0, \
+ signed char: SCHAR_MIN, short: SHRT_MIN, \
+ int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \
+ default: TIME_T_MIN_NO_PADDING)
+# define TIME_T_MAX \
+ (TYPE_SIGNED(time_t) \
+ ? _Generic((time_t) 0, \
+ signed char: SCHAR_MAX, short: SHRT_MAX, \
+ int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
+ default: TIME_T_MAX_NO_PADDING) \
+ : (time_t) -1)
+enum { SIGNED_PADDING_CHECK_NEEDED
+ = _Generic((time_t) 0,
+ signed char: false, short: false,
+ int: false, long: false, long long: false,
+ default: true) };
+#else
+# define TIME_T_MIN TIME_T_MIN_NO_PADDING
+# define TIME_T_MAX TIME_T_MAX_NO_PADDING
+enum { SIGNED_PADDING_CHECK_NEEDED = true };
+#endif
+/* Try to check the padding assumptions. Although TIME_T_MAX and the
+ following check can both have undefined behavior on oddball
+ platforms due to shifts exceeding widths of signed integers, these
+ platforms' compilers are likely to diagnose these issues in integer
+ constant expressions, so it shouldn't hurt to check statically. */
+static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
+ || TIME_T_MAX >> (TYPE_BIT(time_t) - 2) == 1);
+
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+ 1 + TYPE_SIGNED(type))
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifdef GCC_LINT
+# define INITIALIZE(x) ((x) = 0)
+#else
+# define INITIALIZE(x)
+#endif
+
+/* Whether memory access must strictly follow the C standard.
+ If 0, it's OK to read uninitialized storage so long as the value is
+ not relied upon. Defining it to 0 lets mktime access parts of
+ struct tm that might be uninitialized, as a heuristic when the
+ standard doesn't say what to return and when tm_gmtoff can help
+ mktime likely infer a better value. */
+#ifndef UNINIT_TRAP
+# define UNINIT_TRAP 0
+#endif
+
+#ifdef DEBUG
+# 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
+
+/*
+** For the benefit of GNU folk...
+** '_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+#endif /* !defined _ */
+
+#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
+# define TZ_DOMAIN "tz"
+#endif
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r(struct tm const *restrict, char *restrict);
+char *ctime_r(time_t const *, char *);
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/* Handy macros that are independent of tzfile implementation. */
+
+enum {
+ SECSPERMIN = 60,
+ MINSPERHOUR = 60,
+ SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
+ HOURSPERDAY = 24,
+ DAYSPERWEEK = 7,
+ DAYSPERNYEAR = 365,
+ DAYSPERLYEAR = DAYSPERNYEAR + 1,
+ MONSPERYEAR = 12,
+ YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
+};
+
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+
+#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
+#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
+#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
+
+enum {
+ TM_SUNDAY,
+ TM_MONDAY,
+ TM_TUESDAY,
+ TM_WEDNESDAY,
+ TM_THURSDAY,
+ TM_FRIDAY,
+ TM_SATURDAY
+};
+
+enum {
+ TM_JANUARY,
+ TM_FEBRUARY,
+ TM_MARCH,
+ TM_APRIL,
+ TM_MAY,
+ TM_JUNE,
+ TM_JULY,
+ TM_AUGUST,
+ TM_SEPTEMBER,
+ TM_OCTOBER,
+ TM_NOVEMBER,
+ TM_DECEMBER
+};
+
+enum {
+ TM_YEAR_BASE = 1900,
+ TM_WDAY_BASE = TM_MONDAY,
+ EPOCH_YEAR = 1970,
+ EPOCH_WDAY = TM_THURSDAY
+};
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not by C99 or later).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined PRIVATE_H */
Index: tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/zdump.c
===================================================================
--- tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/zdump.c (nonexistent)
+++ tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/zdump.c (revision 385)
@@ -0,0 +1,1267 @@
+/* Dump time zone data in a textual format. */
+
+/*
+** This file is in the public domain, so clarified as of
+** 2009-05-17 by Arthur David Olson.
+*/
+
+#include "version.h"
+
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+
+#include "private.h"
+#include <stdio.h>
+
+#ifndef HAVE_SNPRINTF
+# define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+# define HAVE_LOCALTIME_R 1
+#endif
+
+#ifndef HAVE_LOCALTIME_RZ
+# ifdef TM_ZONE
+# define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
+# else
+# define HAVE_LOCALTIME_RZ 0
+# endif
+#endif
+
+#ifndef HAVE_TZSET
+# define HAVE_TZSET 1
+#endif
+
+#ifndef ZDUMP_LO_YEAR
+# define ZDUMP_LO_YEAR (-500)
+#endif /* !defined ZDUMP_LO_YEAR */
+
+#ifndef ZDUMP_HI_YEAR
+# define ZDUMP_HI_YEAR 2500
+#endif /* !defined ZDUMP_HI_YEAR */
+
+#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
+#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
+#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
+ + SECSPERLYEAR * (intmax_t) (100 - 3))
+
+/*
+** True if SECSPER400YEARS is known to be representable as an
+** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
+** even if SECSPER400YEARS is representable, because when that happens
+** the code merely runs a bit more slowly, and this slowness doesn't
+** occur on any practical platform.
+*/
+enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
+
+#if HAVE_GETTEXT
+# include <locale.h> /* for setlocale */
+#endif /* HAVE_GETTEXT */
+
+#if ! HAVE_LOCALTIME_RZ
+# undef timezone_t
+# define timezone_t char **
+#endif
+
+#if !HAVE_POSIX_DECLS
+extern int getopt(int argc, char * const argv[],
+ const char * options);
+extern char * optarg;
+extern int optind;
+#endif
+
+/* The minimum and maximum finite time values. */
+enum { atime_shift = CHAR_BIT * sizeof(time_t) - 2 };
+static time_t const absolute_min_time =
+ ((time_t) -1 < 0
+ ? (- ((time_t) ~ (time_t) 0 < 0)
+ - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
+ : 0);
+static time_t const absolute_max_time =
+ ((time_t) -1 < 0
+ ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
+ : -1);
+static int longest;
+static char const *progname;
+static bool warned;
+static bool errout;
+
+static char const *abbr(struct tm const *);
+ATTRIBUTE_REPRODUCIBLE static intmax_t delta(struct tm *, struct tm *);
+static void dumptime(struct tm const *);
+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);
+ATTRIBUTE_REPRODUCIBLE static time_t yeartot(intmax_t);
+
+/* Is C an ASCII digit? */
+static bool
+is_digit(char c)
+{
+ return '0' <= c && c <= '9';
+}
+
+/* Is A an alphabetic character in the C locale? */
+static bool
+is_alpha(char a)
+{
+ switch (a) {
+ default:
+ return false;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ return true;
+ }
+}
+
+ATTRIBUTE_NORETURN static 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. A and B are both nonnegative. */
+ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
+sumsize(ptrdiff_t a, ptrdiff_t b)
+{
+#ifdef ckd_add
+ ptrdiff_t sum;
+ if (!ckd_add(&sum, a, b) && sum <= INDEX_MAX)
+ return sum;
+#else
+ if (a <= INDEX_MAX && b <= INDEX_MAX - a)
+ return a + b;
+#endif
+ size_overflow();
+}
+
+/* Return the size of of the string STR, including its trailing NUL.
+ Report an error and exit if this would exceed INDEX_MAX which means
+ pointer subtraction wouldn't work. */
+static ptrdiff_t
+xstrsize(char const *str)
+{
+ size_t len = strlen(str);
+ if (len < INDEX_MAX)
+ return len + 1;
+ size_overflow();
+}
+
+/* Return a pointer to a newly allocated buffer of size SIZE, exiting
+ on failure. SIZE should be positive. */
+ATTRIBUTE_MALLOC static void *
+xmalloc(ptrdiff_t size)
+{
+ void *p = malloc(size);
+ if (!p) {
+ fprintf(stderr, _("%s: Memory exhausted\n"), progname);
+ exit(EXIT_FAILURE);
+ }
+ return p;
+}
+
+#if ! HAVE_TZSET
+# undef tzset
+# define tzset zdump_tzset
+static void tzset(void) { }
+#endif
+
+/* Assume gmtime_r works if localtime_r does.
+ A replacement localtime_r is defined below if needed. */
+#if ! HAVE_LOCALTIME_R
+
+# undef gmtime_r
+# define gmtime_r zdump_gmtime_r
+
+static struct tm *
+gmtime_r(time_t *tp, struct tm *tmp)
+{
+ struct tm *r = gmtime(tp);
+ if (r) {
+ *tmp = *r;
+ r = tmp;
+ }
+ return r;
+}
+
+#endif
+
+/* Platforms with TM_ZONE don't need tzname, so they can use the
+ faster localtime_rz or localtime_r if available. */
+
+#if defined TM_ZONE && HAVE_LOCALTIME_RZ
+# define USE_LOCALTIME_RZ true
+#else
+# define USE_LOCALTIME_RZ false
+#endif
+
+#if ! USE_LOCALTIME_RZ
+
+# if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
+# undef localtime_r
+# define localtime_r zdump_localtime_r
+static struct tm *
+localtime_r(time_t *tp, struct tm *tmp)
+{
+ struct tm *r = localtime(tp);
+ if (r) {
+ *tmp = *r;
+ r = tmp;
+ }
+ return r;
+}
+# endif
+
+# undef localtime_rz
+# define localtime_rz zdump_localtime_rz
+static struct tm *
+localtime_rz(ATTRIBUTE_MAYBE_UNUSED timezone_t rz, time_t *tp, struct tm *tmp)
+{
+ return localtime_r(tp, tmp);
+}
+
+# ifdef TYPECHECK
+# undef mktime_z
+# define mktime_z zdump_mktime_z
+static time_t
+mktime_z(timezone_t tz, struct tm *tmp)
+{
+ return mktime(tmp);
+}
+# endif
+
+# undef tzalloc
+# undef tzfree
+# define tzalloc zdump_tzalloc
+# define tzfree zdump_tzfree
+
+static timezone_t
+tzalloc(char const *val)
+{
+# if HAVE_SETENV
+ if (setenv("TZ", val, 1) != 0) {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: setenv: %s\n"), progname, e);
+ exit(EXIT_FAILURE);
+ }
+ tzset();
+ return &optarg; /* Any valid non-null char ** will do. */
+# else
+ enum { TZeqlen = 3 };
+ static char const TZeq[TZeqlen] = "TZ=";
+ static char **fakeenv;
+ static ptrdiff_t fakeenv0size;
+ void *freeable = NULL;
+ char **env = fakeenv, **initial_environ;
+ ptrdiff_t valsize = xstrsize(val);
+ if (fakeenv0size < valsize) {
+ char **e = environ, **to;
+ ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */
+
+ while (*e++) {
+# ifdef ckd_add
+ if (ckd_add(&initial_nenvptrs, initial_nenvptrs, 1)
+ || INDEX_MAX < initial_nenvptrs)
+ size_overflow();
+# else
+ if (initial_nenvptrs == INDEX_MAX / sizeof *environ)
+ size_overflow();
+ initial_nenvptrs++;
+# endif
+ }
+ 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);
+ }
+ memcpy(env[0] + TZeqlen, val, valsize);
+ initial_environ = environ;
+ environ = env;
+ tzset();
+ free(freeable);
+ return initial_environ;
+# endif
+}
+
+static void
+tzfree(ATTRIBUTE_MAYBE_UNUSED timezone_t initial_environ)
+{
+# if !HAVE_SETENV
+ environ = initial_environ;
+ tzset();
+# endif
+}
+#endif /* ! USE_LOCALTIME_RZ */
+
+/* A UT time zone, and its initializer. */
+static timezone_t gmtz;
+static void
+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 "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT"
+ fails, fall back on "GMT0" which might be similar due to the
+ "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 modern "UTC" which
+ would not work on platforms that omit the "backward" file. */
+ gmtz = tzalloc("GMT");
+ if (!gmtz) {
+ static char const gmt0[] = "GMT0";
+ gmtz = tzalloc(gmt0);
+ if (!gmtz) {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: unknown timezone '%s': %s\n"),
+ progname, gmt0, e);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+}
+
+/* Convert *TP to UT, storing the broken-down time into *TMP.
+ Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP),
+ except typically faster if USE_LOCALTIME_RZ. */
+static struct tm *
+my_gmtime_r(time_t *tp, struct tm *tmp)
+{
+ return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
+}
+
+#ifndef TYPECHECK
+# define my_localtime_rz localtime_rz
+#else /* !defined TYPECHECK */
+
+static struct tm *
+my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
+{
+ tmp = localtime_rz(tz, tp, tmp);
+ if (tmp) {
+ struct tm tm;
+ register time_t t;
+
+ tm = *tmp;
+ t = mktime_z(tz, &tm);
+ if (t != *tp) {
+ fflush(stdout);
+ fprintf(stderr, "\n%s: ", progname);
+ fprintf(stderr, tformat(), *tp);
+ fprintf(stderr, " ->");
+ fprintf(stderr, " year=%d", tmp->tm_year);
+ fprintf(stderr, " mon=%d", tmp->tm_mon);
+ fprintf(stderr, " mday=%d", tmp->tm_mday);
+ fprintf(stderr, " hour=%d", tmp->tm_hour);
+ fprintf(stderr, " min=%d", tmp->tm_min);
+ fprintf(stderr, " sec=%d", tmp->tm_sec);
+ fprintf(stderr, " isdst=%d", tmp->tm_isdst);
+ fprintf(stderr, " -> ");
+ fprintf(stderr, tformat(), t);
+ fprintf(stderr, "\n");
+ errout = true;
+ }
+ }
+ return tmp;
+}
+#endif /* !defined TYPECHECK */
+
+static void
+abbrok(const char *const abbrp, const char *const zone)
+{
+ register const char * cp;
+ register const char * wp;
+
+ if (warned)
+ return;
+ cp = abbrp;
+ while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
+ ++cp;
+ if (*cp)
+ wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
+ else if (cp - abbrp < 3)
+ wp = _("has fewer than 3 characters");
+ else if (cp - abbrp > 6)
+ wp = _("has more than 6 characters");
+ else
+ return;
+ fflush(stdout);
+ fprintf(stderr,
+ _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
+ progname, zone, abbrp, wp);
+ warned = errout = true;
+}
+
+/* Return a time zone abbreviation. If the abbreviation needs to be
+ saved, use *BUF (of size *BUFALLOC) to save it, and return the
+ abbreviation in the possibly reallocated *BUF. Otherwise, just
+ return the abbreviation. Get the abbreviation from TMP.
+ Exit on memory allocation failure. */
+static char const *
+saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp)
+{
+ char const *ab = abbr(tmp);
+ if (HAVE_LOCALTIME_RZ)
+ return ab;
+ else {
+ ptrdiff_t absize = xstrsize(ab);
+ if (*bufalloc < absize) {
+ free(*buf);
+
+ /* Make the new buffer at least twice as long as the old,
+ to avoid O(N**2) behavior on repeated calls. */
+ *bufalloc = sumsize(*bufalloc, absize);
+
+ *buf = xmalloc(*bufalloc);
+ }
+ return strcpy(*buf, ab);
+ }
+}
+
+static void
+close_file(FILE *stream)
+{
+ char const *e = (ferror(stream) ? _("I/O error")
+ : fclose(stream) != 0 ? strerror(errno) : NULL);
+ if (e) {
+ fprintf(stderr, "%s: %s\n", progname, e);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+usage(FILE * const stream, const int status)
+{
+ fprintf(stream,
+_("%s: usage: %s OPTIONS TIMEZONE ...\n"
+ "Options include:\n"
+ " -c [L,]U Start at year L (default -500), end before year U (default 2500)\n"
+ " -t [L,]U Start at time L, end before time U (in seconds since 1970)\n"
+ " -i List transitions briefly (format is experimental)\n" \
+ " -v List transitions verbosely\n"
+ " -V List transitions a bit less verbosely\n"
+ " --help Output this help\n"
+ " --version Output version info\n"
+ "\n"
+ "Report bugs to %s.\n"),
+ progname, progname, REPORT_BUGS_TO);
+ if (status == EXIT_SUCCESS)
+ close_file(stream);
+ exit(status);
+}
+
+int
+main(int argc, char *argv[])
+{
+ /* These are static so that they're initially zero. */
+ static char * abbrev;
+ static ptrdiff_t abbrevsize;
+
+ register int i;
+ register bool vflag;
+ register bool Vflag;
+ register char * cutarg;
+ register char * cuttimes;
+ register time_t cutlotime;
+ register time_t cuthitime;
+ time_t now;
+ bool iflag = false;
+
+ cutlotime = absolute_min_time;
+ cuthitime = absolute_max_time;
+#if HAVE_GETTEXT
+ setlocale(LC_ALL, "");
+# ifdef TZ_DOMAINDIR
+ bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+# endif /* defined TEXTDOMAINDIR */
+ textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT */
+ 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);
+ return EXIT_SUCCESS;
+ } else if (strcmp(argv[i], "--help") == 0) {
+ usage(stdout, EXIT_SUCCESS);
+ }
+ vflag = Vflag = false;
+ cutarg = cuttimes = NULL;
+ for (;;)
+ switch (getopt(argc, argv, "c:it:vV")) {
+ case 'c': cutarg = optarg; break;
+ case 't': cuttimes = optarg; break;
+ case 'i': iflag = true; break;
+ case 'v': vflag = true; break;
+ case 'V': Vflag = true; break;
+ case -1:
+ if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
+ goto arg_processing_done;
+ ATTRIBUTE_FALLTHROUGH;
+ default:
+ usage(stderr, EXIT_FAILURE);
+ }
+ arg_processing_done:;
+
+ if (iflag | vflag | Vflag) {
+ intmax_t lo;
+ intmax_t hi;
+ char *loend, *hiend;
+ register intmax_t cutloyear = ZDUMP_LO_YEAR;
+ register intmax_t cuthiyear = ZDUMP_HI_YEAR;
+ if (cutarg != NULL) {
+ lo = strtoimax(cutarg, &loend, 10);
+ if (cutarg != loend && !*loend) {
+ hi = lo;
+ cuthiyear = hi;
+ } else if (cutarg != loend && *loend == ','
+ && (hi = strtoimax(loend + 1, &hiend, 10),
+ loend + 1 != hiend && !*hiend)) {
+ cutloyear = lo;
+ cuthiyear = hi;
+ } else {
+ fprintf(stderr, _("%s: wild -c argument %s\n"),
+ progname, cutarg);
+ return EXIT_FAILURE;
+ }
+ }
+ if (cutarg != NULL || cuttimes == NULL) {
+ cutlotime = yeartot(cutloyear);
+ cuthitime = yeartot(cuthiyear);
+ }
+ if (cuttimes != NULL) {
+ lo = strtoimax(cuttimes, &loend, 10);
+ if (cuttimes != loend && !*loend) {
+ hi = lo;
+ if (hi < cuthitime) {
+ if (hi < absolute_min_time + 1)
+ hi = absolute_min_time + 1;
+ cuthitime = hi;
+ }
+ } else if (cuttimes != loend && *loend == ','
+ && (hi = strtoimax(loend + 1, &hiend, 10),
+ loend + 1 != hiend && !*hiend)) {
+ if (cutlotime < lo) {
+ if (absolute_max_time < lo)
+ lo = absolute_max_time;
+ cutlotime = lo;
+ }
+ if (hi < cuthitime) {
+ if (hi < absolute_min_time + 1)
+ hi = absolute_min_time + 1;
+ cuthitime = hi;
+ }
+ } else {
+ fprintf(stderr,
+ _("%s: wild -t argument %s\n"),
+ progname, cuttimes);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ gmtzinit();
+ if (iflag | vflag | Vflag)
+ now = 0;
+ else {
+ now = time(NULL);
+ now |= !now;
+ }
+ longest = 0;
+ for (i = optind; i < argc; i++) {
+ size_t arglen = strlen(argv[i]);
+ if (longest < arglen)
+ longest = min(arglen, INT_MAX);
+ }
+
+ for (i = optind; i < argc; ++i) {
+ timezone_t tz = tzalloc(argv[i]);
+ char const *ab = (char const *)0;
+ time_t t;
+ struct tm tm, newtm;
+ bool tm_ok;
+ if (!tz) {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: unknown timezone '%s': %s\n"),
+ progname, argv[1], e);
+ return EXIT_FAILURE;
+ }
+ if (now) {
+ show(tz, argv[i], now, false);
+ tzfree(tz);
+ continue;
+ }
+ warned = false;
+ t = absolute_min_time;
+ if (! (iflag | Vflag)) {
+ show(tz, argv[i], t, true);
+ if (my_localtime_rz(tz, &t, &tm) == NULL
+ && t < cutlotime) {
+ time_t newt = cutlotime;
+ if (my_localtime_rz(tz, &newt, &newtm) != NULL)
+ showextrema(tz, argv[i], t, NULL, newt);
+ }
+ }
+ if (t + 1 < cutlotime)
+ t = cutlotime - 1;
+ tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
+ if (tm_ok) {
+ ab = saveabbr(&abbrev, &abbrevsize, &tm);
+ if (iflag) {
+ showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
+ showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
+ }
+ } else
+ ab = NULL;
+ while (t < cuthitime - 1) {
+ time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
+ && t + SECSPERDAY / 2 < cuthitime - 1)
+ ? t + SECSPERDAY / 2
+ : cuthitime - 1);
+ struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
+ bool newtm_ok = newtmp != NULL;
+ if (tm_ok != newtm_ok
+ || (ab && (delta(&newtm, &tm) != newt - t
+ || newtm.tm_isdst != tm.tm_isdst
+ || strcmp(abbr(&newtm), ab) != 0))) {
+ newt = hunt(tz, t, newt, false);
+ newtmp = localtime_rz(tz, &newt, &newtm);
+ newtm_ok = newtmp != NULL;
+ if (iflag)
+ showtrans("%Y-%m-%d\t%L\t%Q", newtmp, newt,
+ newtm_ok ? abbr(&newtm) : NULL, argv[i]);
+ else {
+ show(tz, argv[i], newt - 1, true);
+ show(tz, argv[i], newt, true);
+ }
+ }
+ t = newt;
+ tm_ok = newtm_ok;
+ if (newtm_ok) {
+ ab = saveabbr(&abbrev, &abbrevsize, &newtm);
+ tm = newtm;
+ }
+ }
+ if (! (iflag | Vflag)) {
+ time_t newt = absolute_max_time;
+ t = cuthitime;
+ if (t < newt) {
+ struct tm *tmp = my_localtime_rz(tz, &t, &tm);
+ if (tmp != NULL
+ && my_localtime_rz(tz, &newt, &newtm) == NULL)
+ showextrema(tz, argv[i], t, tmp, newt);
+ }
+ show(tz, argv[i], absolute_max_time, true);
+ }
+ tzfree(tz);
+ }
+ close_file(stdout);
+ if (errout && (ferror(stderr) || fclose(stderr) != 0))
+ return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+}
+
+static time_t
+yeartot(intmax_t y)
+{
+ register intmax_t myy, seconds, years;
+ register time_t t;
+
+ myy = EPOCH_YEAR;
+ t = 0;
+ while (myy < y) {
+ if (SECSPER400YEARS_FITS && 400 <= y - myy) {
+ intmax_t diff400 = (y - myy) / 400;
+ if (INTMAX_MAX / SECSPER400YEARS < diff400)
+ return absolute_max_time;
+ seconds = diff400 * SECSPER400YEARS;
+ years = diff400 * 400;
+ } else {
+ seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
+ years = 1;
+ }
+ myy += years;
+ if (t > absolute_max_time - seconds)
+ return absolute_max_time;
+ t += seconds;
+ }
+ while (y < myy) {
+ if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
+ intmax_t diff400 = (myy - y) / 400;
+ if (INTMAX_MAX / SECSPER400YEARS < diff400)
+ return absolute_min_time;
+ seconds = diff400 * SECSPER400YEARS;
+ years = diff400 * 400;
+ } else {
+ seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
+ years = 1;
+ }
+ myy -= years;
+ if (t < absolute_min_time + seconds)
+ return absolute_min_time;
+ t -= seconds;
+ }
+ return t;
+}
+
+/* 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
+ transition but fails on the other side. Return the timestamp just
+ before the transition from LOT's settings. */
+
+static time_t
+hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok)
+{
+ static char * loab;
+ static ptrdiff_t loabsize;
+ struct tm lotm;
+ struct tm tm;
+
+ /* Convert LOT into a broken-down time here, even though our
+ caller already did that. On platforms without TM_ZONE,
+ tzname may have been altered since our caller broke down
+ LOT, and tzname needs to be changed back. */
+ bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
+ bool tm_ok;
+ char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL;
+
+ for ( ; ; ) {
+ /* T = average of LOT and HIT, rounding down.
+ Avoid overflow. */
+ int rem_sum = lot % 2 + hit % 2;
+ time_t t = (rem_sum == 2) - (rem_sum < 0) + lot / 2 + hit / 2;
+ if (t == lot)
+ break;
+ tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
+ if (lotm_ok == tm_ok
+ && (only_ok
+ || (ab && tm.tm_isdst == lotm.tm_isdst
+ && delta(&tm, &lotm) == t - lot
+ && strcmp(abbr(&tm), ab) == 0))) {
+ lot = t;
+ if (tm_ok)
+ lotm = tm;
+ } else hit = t;
+ }
+ return hit;
+}
+
+/*
+** Thanks to Paul Eggert for logic used in delta_nonneg.
+*/
+
+static intmax_t
+delta_nonneg(struct tm *newp, struct tm *oldp)
+{
+ intmax_t oldy = oldp->tm_year;
+ int cycles = (newp->tm_year - oldy) / YEARSPERREPEAT;
+ intmax_t sec = SECSPERREPEAT, result = cycles * sec;
+ int tmy = oldp->tm_year + cycles * YEARSPERREPEAT;
+ for ( ; tmy < newp->tm_year; ++tmy)
+ result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
+ result += newp->tm_yday - oldp->tm_yday;
+ result *= HOURSPERDAY;
+ result += newp->tm_hour - oldp->tm_hour;
+ result *= MINSPERHOUR;
+ result += newp->tm_min - oldp->tm_min;
+ result *= SECSPERMIN;
+ result += newp->tm_sec - oldp->tm_sec;
+ return result;
+}
+
+static intmax_t
+delta(struct tm *newp, struct tm *oldp)
+{
+ return (newp->tm_year < oldp->tm_year
+ ? -delta_nonneg(oldp, newp)
+ : delta_nonneg(newp, oldp));
+}
+
+#ifndef TM_GMTOFF
+/* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
+ Assume A and B differ by at most one year. */
+static int
+adjusted_yday(struct tm const *a, struct tm const *b)
+{
+ int yday = a->tm_yday;
+ if (b->tm_year < a->tm_year)
+ yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
+ return yday;
+}
+#endif
+
+/* If A is the broken-down local time and B the broken-down UT for
+ the same instant, return A's UT offset in seconds, where positive
+ offsets are east of Greenwich. On failure, return LONG_MIN.
+
+ If T is nonnull, *T is the timestamp that corresponds to A; call
+ 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, ATTRIBUTE_MAYBE_UNUSED time_t *t,
+ ATTRIBUTE_MAYBE_UNUSED struct tm const *b)
+{
+#ifdef TM_GMTOFF
+ return a->TM_GMTOFF;
+#else
+ struct tm tm;
+ if (t)
+ b = my_gmtime_r(t, &tm);
+ if (! b)
+ return LONG_MIN;
+ else {
+ int ayday = adjusted_yday(a, b);
+ int byday = adjusted_yday(b, a);
+ int days = ayday - byday;
+ long hours = a->tm_hour - b->tm_hour + 24 * days;
+ long minutes = a->tm_min - b->tm_min + 60 * hours;
+ long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
+ return seconds;
+ }
+#endif
+}
+
+static void
+show(timezone_t tz, char *zone, time_t t, bool v)
+{
+ register struct tm * tmp;
+ register struct tm * gmtmp;
+ struct tm tm, gmtm;
+
+ printf("%-*s ", longest, zone);
+ if (v) {
+ gmtmp = my_gmtime_r(&t, &gmtm);
+ if (gmtmp == NULL) {
+ printf(tformat(), t);
+ printf(_(" (gmtime failed)"));
+ } else {
+ dumptime(gmtmp);
+ printf(" UT");
+ }
+ printf(" = ");
+ }
+ tmp = my_localtime_rz(tz, &t, &tm);
+ if (tmp == NULL) {
+ printf(tformat(), t);
+ printf(_(" (localtime failed)"));
+ } else {
+ dumptime(tmp);
+ if (*abbr(tmp) != '\0')
+ printf(" %s", abbr(tmp));
+ if (v) {
+ long off = gmtoff(tmp, NULL, gmtmp);
+ printf(" isdst=%d", tmp->tm_isdst);
+ if (off != LONG_MIN)
+ printf(" gmtoff=%ld", off);
+ }
+ }
+ printf("\n");
+ if (tmp != NULL && *abbr(tmp) != '\0')
+ abbrok(abbr(tmp), zone);
+}
+
+/* Show timestamps just before and just after a transition between
+ defined and undefined (or vice versa) in either localtime or
+ gmtime. These transitions are for timezone TZ with name ZONE, in
+ the range from LO (with broken-down time LOTMP if that is nonnull)
+ through HI. LO and HI disagree on definedness. */
+
+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, lo, hi, true);
+ bool old = false;
+ hi = (SECSPERDAY < hi - boundary
+ ? boundary + SECSPERDAY
+ : hi + (hi < TIME_T_MAX));
+ if (SECSPERDAY < boundary - lo) {
+ lo = boundary - SECSPERDAY;
+ lotmp = my_localtime_rz(tz, &lo, &localtm[old]);
+ }
+ if (lotmp)
+ localtm[old] = *lotmp;
+ else
+ localtm[old].tm_sec = -1;
+ if (! my_gmtime_r(&lo, &gmtm[old]))
+ gmtm[old].tm_sec = -1;
+
+ /* Search sequentially for definedness transitions. Although this
+ could be sped up by refining 'hunt' to search for either
+ localtime or gmtime definedness transitions, it hardly seems
+ worth the trouble. */
+ for (t = lo + 1; t < hi; t++) {
+ bool new = !old;
+ if (! my_localtime_rz(tz, &t, &localtm[new]))
+ localtm[new].tm_sec = -1;
+ if (! my_gmtime_r(&t, &gmtm[new]))
+ gmtm[new].tm_sec = -1;
+ if (((localtm[old].tm_sec < 0) != (localtm[new].tm_sec < 0))
+ | ((gmtm[old].tm_sec < 0) != (gmtm[new].tm_sec < 0))) {
+ show(tz, zone, t - 1, true);
+ show(tz, zone, t, true);
+ }
+ old = new;
+ }
+}
+
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+
+/* A substitute for snprintf that is good enough for zdump. */
+ATTRIBUTE_FORMAT((printf, 3, 4)) static int
+my_snprintf(char *s, size_t size, char const *format, ...)
+{
+ int n;
+ va_list args;
+ char const *arg;
+ size_t arglen, slen;
+ char buf[1024];
+ va_start(args, format);
+ if (strcmp(format, "%s") == 0) {
+ arg = va_arg(args, char const *);
+ arglen = strlen(arg);
+ } else {
+ n = vsprintf(buf, format, args);
+ if (n < 0) {
+ va_end(args);
+ return n;
+ }
+ arg = buf;
+ arglen = n;
+ }
+ slen = arglen < size ? arglen : size - 1;
+ memcpy(s, arg, slen);
+ s[slen] = '\0';
+ n = arglen <= INT_MAX ? arglen : -1;
+ va_end(args);
+ return n;
+}
+#endif
+
+/* Store into BUF, of size SIZE, a formatted local time taken from *TM.
+ Use ISO 8601 format +HH:MM:SS. Omit :SS if SS is zero, and omit
+ :MM too if MM is also zero.
+
+ Return the length of the resulting string. If the string does not
+ 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, ptrdiff_t size, struct tm const *tm)
+{
+ int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
+ return (ss
+ ? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
+ : mm
+ ? my_snprintf(buf, size, "%02d:%02d", hh, mm)
+ : my_snprintf(buf, size, "%02d", hh));
+}
+
+/* Store into BUF, of size SIZE, a formatted UT offset for the
+ localtime *TM corresponding to time T. Use ISO 8601 format
+ +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
+ format -00 for unknown UT offsets. If the hour needs more than
+ two digits to represent, extend the length of HH as needed.
+ Otherwise, omit SS if SS is zero, and omit MM too if MM is also
+ zero.
+
+ Return the length of the resulting string, or -1 if the result is
+ not representable as a string. If the string does not fit, return
+ 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, ptrdiff_t size, struct tm const *tm, time_t t)
+{
+ long off = gmtoff(tm, &t, NULL);
+ char sign = ((off < 0
+ || (off == 0
+ && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0)))
+ ? '-' : '+');
+ long hh;
+ int mm, ss;
+ if (off < 0)
+ {
+ if (off == LONG_MIN)
+ return -1;
+ off = -off;
+ }
+ ss = off % 60;
+ mm = off / 60 % 60;
+ hh = off / 60 / 60;
+ return (ss || 100 <= hh
+ ? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
+ : mm
+ ? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
+ : my_snprintf(buf, size, "%c%02ld", sign, hh));
+}
+
+/* Store into BUF (of size SIZE) a quoted string representation of P.
+ 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 ptrdiff_t
+format_quoted_string(char *buf, ptrdiff_t size, char const *p)
+{
+ char *b = buf;
+ ptrdiff_t s = size;
+ if (!s)
+ return size;
+ *b++ = '"', s--;
+ for (;;) {
+ char c = *p++;
+ if (s <= 1)
+ return size;
+ switch (c) {
+ default: *b++ = c, s--; continue;
+ case '\0': *b++ = '"', s--; return size - s;
+ case '"': case '\\': break;
+ case ' ': c = 's'; break;
+ case '\f': c = 'f'; break;
+ case '\n': c = 'n'; break;
+ case '\r': c = 'r'; break;
+ case '\t': c = 't'; break;
+ case '\v': c = 'v'; break;
+ }
+ *b++ = '\\', *b++ = c, s -= 2;
+ }
+}
+
+/* Store into BUF (of size SIZE) a timestamp formatted by TIME_FMT.
+ TM is the broken-down time, T the seconds count, AB the time zone
+ abbreviation, and ZONE_NAME the zone name. Return true if
+ successful, false if the output would require more than SIZE bytes.
+ TIME_FMT uses the same format that strftime uses, with these
+ additions:
+
+ %f zone name
+ %L local time as per format_local_time
+ %Q like "U\t%Z\tD" where U is the UT offset as for format_utc_offset
+ and D is the isdst flag; except omit D if it is zero, omit %Z if
+ it equals U, quote and escape %Z if it contains nonalphabetics,
+ and omit any trailing tabs. */
+
+static bool
+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;
+ ptrdiff_t s = size;
+ char const *f = time_fmt, *p;
+
+ for (p = f; ; p++)
+ if (*p == '%' && p[1] == '%')
+ p++;
+ else if (!*p
+ || (*p == '%'
+ && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
+ 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;
+ memcpy(f_prefix_copy, f, f_prefix_len);
+ strcpy(f_prefix_copy + f_prefix_len, "X");
+ formatted_len = strftime(b, s, f_prefix_copy, tm);
+ if (oversized)
+ free(f_prefix_copy);
+ if (formatted_len == 0)
+ return false;
+ formatted_len--;
+ b += formatted_len, s -= formatted_len;
+ if (!*p++)
+ break;
+ switch (*p) {
+ case 'f':
+ formatted_len = format_quoted_string(b, s, zone_name);
+ break;
+ case 'L':
+ formatted_len = format_local_time(b, s, tm);
+ break;
+ case 'Q':
+ {
+ bool show_abbr;
+ int offlen = format_utc_offset(b, s, tm, t);
+ if (! (0 <= offlen && offlen < s))
+ return false;
+ show_abbr = strcmp(b, ab) != 0;
+ b += offlen, s -= offlen;
+ if (show_abbr) {
+ char const *abp;
+ ptrdiff_t len;
+ if (s <= 1)
+ return false;
+ *b++ = '\t', s--;
+ for (abp = ab; is_alpha(*abp); abp++)
+ continue;
+ len = (!*abp && *ab
+ ? my_snprintf(b, s, "%s", ab)
+ : format_quoted_string(b, s, ab));
+ if (s <= len)
+ return false;
+ b += len, s -= len;
+ }
+ formatted_len
+ = (tm->tm_isdst
+ ? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
+ : 0);
+ }
+ break;
+ }
+ if (s <= formatted_len)
+ return false;
+ b += formatted_len, s -= formatted_len;
+ f = p + 1;
+ }
+ *b = '\0';
+ return true;
+}
+
+/* Show a time transition. */
+static void
+showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
+ char const *zone_name)
+{
+ if (!tm) {
+ printf(tformat(), t);
+ putchar('\n');
+ } else {
+ char stackbuf[1000];
+ ptrdiff_t size = sizeof stackbuf;
+ char *buf = stackbuf;
+ char *bufalloc = NULL;
+ while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
+ size = sumsize(size, size);
+ free(bufalloc);
+ buf = bufalloc = xmalloc(size);
+ }
+ puts(buf);
+ free(bufalloc);
+ }
+}
+
+static char const *
+abbr(struct tm const *tmp)
+{
+#ifdef TM_ZONE
+ return tmp->TM_ZONE;
+#else
+# if HAVE_TZNAME
+ if (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst])
+ return tzname[0 < tmp->tm_isdst];
+# endif
+ return "";
+#endif
+}
+
+/*
+** The code below can fail on certain theoretical systems;
+** it works on all known real-world systems as of 2022-01-25.
+*/
+
+static const char *
+tformat(void)
+{
+#if HAVE__GENERIC
+ /* C11-style _Generic is more likely to return the correct
+ format when distinct types have the same size. */
+ char const *fmt =
+ _Generic(+ (time_t) 0,
+ int: "%d", long: "%ld", long long: "%lld",
+ unsigned: "%u", unsigned long: "%lu",
+ unsigned long long: "%llu",
+ default: NULL);
+ if (fmt)
+ return fmt;
+ fmt = _Generic((time_t) 0,
+ intmax_t: "%"PRIdMAX, uintmax_t: "%"PRIuMAX,
+ default: NULL);
+ if (fmt)
+ return fmt;
+#endif
+ if (0 > (time_t) -1) { /* signed */
+ if (sizeof(time_t) == sizeof(intmax_t))
+ return "%"PRIdMAX;
+ if (sizeof(time_t) > sizeof(long))
+ return "%lld";
+ if (sizeof(time_t) > sizeof(int))
+ return "%ld";
+ return "%d";
+ }
+#ifdef PRIuMAX
+ if (sizeof(time_t) == sizeof(uintmax_t))
+ return "%"PRIuMAX;
+#endif
+ if (sizeof(time_t) > sizeof(unsigned long))
+ return "%llu";
+ if (sizeof(time_t) > sizeof(unsigned int))
+ return "%lu";
+ return "%u";
+}
+
+static void
+dumptime(register const struct tm *timeptr)
+{
+ static const char wday_name[][4] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+ static const char mon_name[][4] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ register int lead;
+ register int trail;
+ int DIVISOR = 10;
+
+ /*
+ ** The packaged localtime_rz and gmtime_r never put out-of-range
+ ** values in tm_wday or tm_mon, but since this code might be compiled
+ ** with other (perhaps experimental) versions, paranoia is in order.
+ */
+ printf("%s %s%3d %.2d:%.2d:%.2d ",
+ ((0 <= timeptr->tm_wday
+ && timeptr->tm_wday < sizeof wday_name / sizeof wday_name[0])
+ ? wday_name[timeptr->tm_wday] : "???"),
+ ((0 <= timeptr->tm_mon
+ && timeptr->tm_mon < sizeof mon_name / sizeof mon_name[0])
+ ? mon_name[timeptr->tm_mon] : "???"),
+ timeptr->tm_mday, timeptr->tm_hour,
+ timeptr->tm_min, timeptr->tm_sec);
+ trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
+ lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
+ trail / DIVISOR;
+ trail %= DIVISOR;
+ if (trail < 0 && lead > 0) {
+ trail += DIVISOR;
+ --lead;
+ } else if (lead < 0 && trail > 0) {
+ trail -= DIVISOR;
+ ++lead;
+ }
+ if (lead == 0)
+ printf("%d", trail);
+ else printf("%d%d", lead, ((trail < 0) ? -trail : trail));
+}
Index: tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/zic.c
===================================================================
--- tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/zic.c (nonexistent)
+++ tz/create-2023c-glibc-2.37-patch/glibc-2.37-new/timezone/zic.c (revision 385)
@@ -0,0 +1,3931 @@
+/* Compile .zi time zone data into TZif binary files. */
+
+/*
+** This file is in the public domain, so clarified as of
+** 2006-07-17 by Arthur David Olson.
+*/
+
+/* Use the system 'time' function, instead of any private replacement.
+ This avoids creating an unnecessary dependency on localtime.c. */
+#undef EPOCH_LOCAL
+#undef EPOCH_OFFSET
+#undef RESERVE_STD_EXT_IDS
+#undef time_tz
+
+#include "version.h"
+#include "private.h"
+#include "tzfile.h"
+
+#include <fcntl.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+typedef int_fast64_t zic_t;
+static zic_t const
+ ZIC_MIN = INT_FAST64_MIN,
+ ZIC_MAX = INT_FAST64_MAX,
+ ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
+ ZIC32_MAX = 0x7fffffff;
+#define SCNdZIC SCNdFAST64
+
+#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
+# 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>
+# undef mkdir
+# define mkdir(name, mode) _mkdir(name)
+#endif
+
+#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
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef S_IRUSR
+# define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+#else
+# define MKDIR_UMASK 0755
+#endif
+
+/* The minimum alignment of a type, for pre-C23 platforms.
+ The __SUNPRO_C test is because Oracle Developer Studio 12.6 lacks
+ <stdalign.h> even though __STDC_VERSION__ == 201112. */
+#if __STDC_VERSION__ < 201112 || defined __SUNPRO_C
+# 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. */
+#ifndef _POSIX2_LINE_MAX
+# define _POSIX2_LINE_MAX 2048
+#endif
+
+/* The type for line numbers. Use PRIdMAX to format them; formerly
+ there was also "#define PRIdLINENO PRIdMAX" and formats used
+ PRIdLINENO, but xgettext cannot grok that. */
+typedef intmax_t lineno;
+
+struct rule {
+ int r_filenum;
+ lineno r_linenum;
+ const char * r_name;
+
+ zic_t r_loyear; /* for example, 1986 */
+ zic_t r_hiyear; /* for example, 1986 */
+ bool r_lowasnum;
+ bool r_hiwasnum;
+
+ int r_month; /* 0..11 */
+
+ int r_dycode; /* see below */
+ int r_dayofmonth;
+ int r_wday;
+
+ zic_t r_tod; /* time from midnight */
+ bool r_todisstd; /* is r_tod standard time? */
+ bool r_todisut; /* is r_tod UT? */
+ bool r_isdst; /* is this daylight saving time? */
+ zic_t r_save; /* offset from standard time */
+ const char * r_abbrvar; /* variable part of abbreviation */
+
+ bool r_todo; /* a rule to do (used in outzone) */
+ zic_t r_temp; /* used in outzone */
+};
+
+/*
+** r_dycode r_dayofmonth r_wday
+*/
+enum {
+ DC_DOM, /* 1..31 */ /* unused */
+ DC_DOWGEQ, /* 1..31 */ /* 0..6 (Sun..Sat) */
+ DC_DOWLEQ /* 1..31 */ /* 0..6 (Sun..Sat) */
+};
+
+struct zone {
+ int z_filenum;
+ lineno z_linenum;
+
+ const char * z_name;
+ zic_t z_stdoff;
+ char * z_rule;
+ const char * z_format;
+ char z_format_specifier;
+
+ bool z_isdst;
+ zic_t z_save;
+
+ struct rule * z_rules;
+ ptrdiff_t z_nrules;
+
+ struct rule z_untilrule;
+ zic_t z_untiltime;
+};
+
+#if !HAVE_POSIX_DECLS
+extern int getopt(int argc, char * const argv[],
+ const char * options);
+extern int link(const char * target, const char * linkname);
+extern char * optarg;
+extern int optind;
+#endif
+
+#if ! HAVE_SYMLINK
+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
+# 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);
+static int addtype(zic_t, char const *, bool, bool, bool);
+static void leapadd(zic_t, int, int);
+static void adjleap(void);
+static void associate(void);
+static void dolink(const char *, const char *, bool);
+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(int, char const *);
+static void inleap(char ** fields, int nfields);
+static void inlink(char ** fields, int nfields);
+static void inrule(char ** fields, int nfields);
+static bool inzcont(char ** fields, int nfields);
+static bool inzone(char ** fields, int nfields);
+static bool inzsub(char **, int, bool);
+static bool itssymlink(char const *);
+static bool is_alpha(char a);
+static char lowerit(char);
+static void mkdirs(char const *, bool);
+static void newabbr(const char * abbr);
+static zic_t oadd(zic_t t1, zic_t t2);
+static void outzone(const struct zone * zp, ptrdiff_t ntzones);
+static zic_t rpytime(const struct rule * rp, zic_t wantedy);
+static bool rulesub(struct rule * rp,
+ const char * loyearp, const char * hiyearp,
+ const char * typep, const char * monthp,
+ const char * dayp, const char * timep);
+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 };
+
+static int charcnt;
+static bool errors;
+static bool warnings;
+static int filenum;
+static int leapcnt;
+static bool leapseen;
+static zic_t leapminyear;
+static zic_t leapmaxyear;
+static lineno linenum;
+static int max_abbrvar_len = PERCENT_Z_LEN_BOUND;
+static int max_format_len;
+static zic_t max_year;
+static zic_t min_year;
+static bool noise;
+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;
+static int unspecifiedtype;
+
+/*
+** Line codes.
+*/
+
+enum {
+ LC_RULE,
+ LC_ZONE,
+ LC_LINK,
+ LC_LEAP,
+ LC_EXPIRES
+};
+
+/*
+** Which fields are which on a Zone line.
+*/
+
+enum {
+ ZF_NAME = 1,
+ ZF_STDOFF,
+ ZF_RULE,
+ ZF_FORMAT,
+ ZF_TILYEAR,
+ ZF_TILMONTH,
+ ZF_TILDAY,
+ ZF_TILTIME,
+ ZONE_MAXFIELDS,
+ ZONE_MINFIELDS = ZF_TILYEAR
+};
+
+/*
+** Which fields are which on a Zone continuation line.
+*/
+
+enum {
+ ZFC_STDOFF,
+ ZFC_RULE,
+ ZFC_FORMAT,
+ ZFC_TILYEAR,
+ ZFC_TILMONTH,
+ ZFC_TILDAY,
+ ZFC_TILTIME,
+ ZONEC_MAXFIELDS,
+ ZONEC_MINFIELDS = ZFC_TILYEAR
+};
+
+/*
+** Which files are which on a Rule line.
+*/
+
+enum {
+ RF_NAME = 1,
+ RF_LOYEAR,
+ RF_HIYEAR,
+ RF_COMMAND,
+ RF_MONTH,
+ RF_DAY,
+ RF_TOD,
+ RF_SAVE,
+ RF_ABBRVAR,
+ RULE_FIELDS
+};
+
+/*
+** Which fields are which on a Link line.
+*/
+
+enum {
+ LF_TARGET = 1,
+ LF_LINKNAME,
+ LINK_FIELDS
+};
+
+/*
+** Which fields are which on a Leap line.
+*/
+
+enum {
+ LP_YEAR = 1,
+ LP_MONTH,
+ LP_DAY,
+ LP_TIME,
+ LP_CORR,
+ LP_ROLL,
+ LEAP_FIELDS,
+
+ /* Expires lines are like Leap lines, except without CORR and ROLL fields. */
+ EXPIRES_FIELDS = LP_TIME + 1
+};
+
+/* The maximum number of fields on any of the above lines.
+ (The "+"s pacify gcc -Wenum-compare.) */
+enum {
+ MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
+ max(+LEAP_FIELDS, +EXPIRES_FIELDS))
+};
+
+/*
+** Year synonyms.
+*/
+
+enum {
+ YR_MINIMUM,
+ YR_MAXIMUM,
+ YR_ONLY
+};
+
+static struct rule * rules;
+static ptrdiff_t nrules; /* number of rules */
+static ptrdiff_t nrules_alloc;
+
+static struct zone * zones;
+static ptrdiff_t nzones; /* number of zones */
+static ptrdiff_t nzones_alloc;
+
+struct link {
+ int l_filenum;
+ lineno l_linenum;
+ const char * l_target;
+ const char * l_linkname;
+};
+
+static struct link * links;
+static ptrdiff_t nlinks;
+static ptrdiff_t nlinks_alloc;
+
+struct lookup {
+ const char * l_word;
+ const int l_value;
+};
+
+static struct lookup const * byword(const char * string,
+ const struct lookup * lp);
+
+static struct lookup const zi_line_codes[] = {
+ { "Rule", LC_RULE },
+ { "Zone", LC_ZONE },
+ { "Link", LC_LINK },
+ { NULL, 0 }
+};
+static struct lookup const leap_line_codes[] = {
+ { "Leap", LC_LEAP },
+ { "Expires", LC_EXPIRES },
+ { NULL, 0}
+};
+
+static struct lookup const mon_names[] = {
+ { "January", TM_JANUARY },
+ { "February", TM_FEBRUARY },
+ { "March", TM_MARCH },
+ { "April", TM_APRIL },
+ { "May", TM_MAY },
+ { "June", TM_JUNE },
+ { "July", TM_JULY },
+ { "August", TM_AUGUST },
+ { "September", TM_SEPTEMBER },
+ { "October", TM_OCTOBER },
+ { "November", TM_NOVEMBER },
+ { "December", TM_DECEMBER },
+ { NULL, 0 }
+};
+
+static struct lookup const wday_names[] = {
+ { "Sunday", TM_SUNDAY },
+ { "Monday", TM_MONDAY },
+ { "Tuesday", TM_TUESDAY },
+ { "Wednesday", TM_WEDNESDAY },
+ { "Thursday", TM_THURSDAY },
+ { "Friday", TM_FRIDAY },
+ { "Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const lasts[] = {
+ { "last-Sunday", TM_SUNDAY },
+ { "last-Monday", TM_MONDAY },
+ { "last-Tuesday", TM_TUESDAY },
+ { "last-Wednesday", TM_WEDNESDAY },
+ { "last-Thursday", TM_THURSDAY },
+ { "last-Friday", TM_FRIDAY },
+ { "last-Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const begin_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { NULL, 0 }
+};
+
+static struct lookup const end_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { "only", YR_ONLY },
+ { NULL, 0 }
+};
+
+static struct lookup const leap_types[] = {
+ { "Rolling", true },
+ { "Stationary", false },
+ { NULL, 0 }
+};
+
+static const int len_months[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int len_years[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+static struct attype {
+ zic_t at;
+ bool dontmerge;
+ unsigned char type;
+} * attypes;
+static zic_t utoffs[TZ_MAX_TYPES];
+static char isdsts[TZ_MAX_TYPES];
+static unsigned char desigidx[TZ_MAX_TYPES];
+static bool ttisstds[TZ_MAX_TYPES];
+static bool ttisuts[TZ_MAX_TYPES];
+static char chars[TZ_MAX_CHARS];
+static zic_t trans[TZ_MAX_LEAPS];
+static zic_t corr[TZ_MAX_LEAPS];
+static char roll[TZ_MAX_LEAPS];
+
+/*
+** Memory allocation.
+*/
+
+ATTRIBUTE_NORETURN static void
+memory_exhausted(const char *msg)
+{
+ fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
+ exit(EXIT_FAILURE);
+}
+
+ATTRIBUTE_NORETURN static void
+size_overflow(void)
+{
+ memory_exhausted(_("size overflow"));
+}
+
+ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
+size_sum(size_t a, size_t b)
+{
+#ifdef ckd_add
+ ptrdiff_t sum;
+ if (!ckd_add(&sum, a, b) && sum <= INDEX_MAX)
+ return sum;
+#else
+ if (a <= INDEX_MAX && b <= INDEX_MAX - a)
+ return a + b;
+#endif
+ size_overflow();
+}
+
+ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
+size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
+{
+#ifdef ckd_mul
+ ptrdiff_t product;
+ if (!ckd_mul(&product, nitems, itemsize) && product <= INDEX_MAX)
+ return product;
+#else
+ ptrdiff_t nitems_max = INDEX_MAX / itemsize;
+ if (nitems <= nitems_max)
+ return nitems * itemsize;
+#endif
+ size_overflow();
+}
+
+ATTRIBUTE_REPRODUCIBLE static 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
+static char *
+strdup(char const *str)
+{
+ char *result = malloc(strlen(str) + 1);
+ return result ? strcpy(result, str) : result;
+}
+#endif
+
+static void *
+memcheck(void *ptr)
+{
+ if (ptr == NULL)
+ memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM));
+ return ptr;
+}
+
+ATTRIBUTE_MALLOC static void *
+emalloc(size_t size)
+{
+ return memcheck(malloc(size));
+}
+
+static void *
+erealloc(void *ptr, size_t size)
+{
+ return memcheck(realloc(ptr, size));
+}
+
+ATTRIBUTE_MALLOC static char *
+estrdup(char const *str)
+{
+ return memcheck(strdup(str));
+}
+
+static ptrdiff_t
+grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize)
+{
+ ptrdiff_t addend = (*nitems_alloc >> 1) + 1;
+#if defined ckd_add && defined ckd_mul
+ ptrdiff_t product;
+ if (!ckd_add(nitems_alloc, *nitems_alloc, addend)
+ && !ckd_mul(&product, *nitems_alloc, itemsize) && product <= INDEX_MAX)
+ return product;
+#else
+ if (*nitems_alloc <= ((INDEX_MAX - 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)));
+}
+
+/*
+** Error handling.
+*/
+
+/* 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)
+{
+ 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;
+ rfilenum = rfnum;
+ rlinenum = rnum;
+}
+
+static void
+eat(int fnum, lineno num)
+{
+ eats(fnum, num, 0, -1);
+}
+
+ATTRIBUTE_FORMAT((printf, 1, 0)) static void
+verror(const char *const string, va_list args)
+{
+ /*
+ ** Match the format of "cc" to allow sh users to
+ ** zic ... 2>&1 | error -t "*" -v
+ ** on BSD systems.
+ */
+ if (filenum)
+ fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
+ filename(filenum), linenum);
+ vfprintf(stderr, string, args);
+ if (rfilenum)
+ fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
+ filename(rfilenum), rlinenum);
+ fprintf(stderr, "\n");
+}
+
+ATTRIBUTE_FORMAT((printf, 1, 2)) static void
+error(const char *const string, ...)
+{
+ va_list args;
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+ errors = true;
+}
+
+ATTRIBUTE_FORMAT((printf, 1, 2)) static void
+warning(const char *const string, ...)
+{
+ va_list args;
+ fprintf(stderr, _("warning: "));
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+ warnings = true;
+}
+
+/* Close STREAM. If it had an I/O error, report it against DIR/NAME,
+ remove TEMPNAME if nonnull, and then exit. */
+static void
+close_file(FILE *stream, char const *dir, char const *name,
+ char const *tempname)
+{
+ char const *e = (ferror(stream) ? _("I/O error")
+ : fclose(stream) != 0 ? strerror(errno) : NULL);
+ if (e) {
+ fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
+ dir ? dir : "", dir ? "/" : "",
+ name ? name : "", name ? ": " : "",
+ e);
+ if (tempname)
+ remove(tempname);
+ exit(EXIT_FAILURE);
+ }
+}
+
+ATTRIBUTE_NORETURN static void
+usage(FILE *stream, int status)
+{
+ fprintf(stream,
+ _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
+ "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
+ " [ -L leapseconds ] \\\n"
+ "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
+ "\t[ -t localtime-link ] \\\n"
+ "\t[ filename ... ]\n\n"
+ "Report bugs to %s.\n"),
+ progname, progname, REPORT_BUGS_TO);
+ if (status == EXIT_SUCCESS)
+ close_file(stream, NULL, NULL, NULL);
+ exit(status);
+}
+
+/* Change the working directory to DIR, possibly creating DIR and its
+ ancestors. After this is done, all files are accessed with names
+ relative to DIR. */
+static void
+change_directory(char const *dir)
+{
+ if (chdir(dir) != 0) {
+ int chdir_errno = errno;
+ if (chdir_errno == ENOENT) {
+ mkdirs(dir, false);
+ chdir_errno = chdir(dir) == 0 ? 0 : errno;
+ }
+ if (chdir_errno != 0) {
+ fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
+ progname, dir, strerror(chdir_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+/* 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. */
+
+static sig_atomic_t got_signal;
+
+static void
+signal_handler(int sig)
+{
+#ifndef SA_SIGINFO
+ signal(sig, signal_handler);
+#endif
+ got_signal = sig;
+}
+
+/* Arrange for SIGINT etc. to be caught by the handler. */
+static void
+catch_signals(void)
+{
+ static int const signals[] = {
+#ifdef SIGHUP
+ SIGHUP,
+#endif
+ SIGINT,
+#ifdef SIGPIPE
+ SIGPIPE,
+#endif
+ SIGTERM
+ };
+ int i;
+ for (i = 0; i < sizeof signals / sizeof signals[0]; i++) {
+#ifdef SA_SIGINFO
+ struct sigaction act0, act;
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ if (sigaction(signals[i], &act, &act0) == 0
+ && ! (act0.sa_flags & SA_SIGINFO) && act0.sa_handler == SIG_IGN) {
+ sigaction(signals[i], &act0, NULL);
+ got_signal = 0;
+ }
+#else
+ if (signal(signals[i], signal_handler) == SIG_IGN) {
+ signal(signals[i], SIG_IGN);
+ got_signal = 0;
+ }
+#endif
+ }
+}
+
+/* If a signal has arrived, terminate zic with appropriate status. */
+static void
+check_for_signal(void)
+{
+ int sig = got_signal;
+ if (sig) {
+ signal(sig, SIG_DFL);
+ raise(sig);
+ abort(); /* A bug in 'raise'. */
+ }
+}
+
+enum { TIME_T_BITS_IN_FILE = 64 };
+
+/* The minimum and maximum values representable in a TZif file. */
+static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The minimum, and one less than the maximum, values specified by
+ the -r option. These default to MIN_TIME and MAX_TIME. */
+static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The time specified by the -R option, defaulting to MIN_TIME. */
+static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The time specified by an Expires line, or negative if no such line. */
+static zic_t leapexpires = -1;
+
+/* Set the time range of the output to TIMERANGE.
+ Return true if successful. */
+static bool
+timerange_option(char *timerange)
+{
+ intmax_t lo = min_time, hi = max_time;
+ char *lo_end = timerange, *hi_end;
+ if (*timerange == '@') {
+ errno = 0;
+ lo = strtoimax(timerange + 1, &lo_end, 10);
+ if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
+ return false;
+ }
+ hi_end = lo_end;
+ if (lo_end[0] == '/' && lo_end[1] == '@') {
+ errno = 0;
+ hi = strtoimax(lo_end + 2, &hi_end, 10);
+ if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
+ return false;
+ hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
+ }
+ if (*hi_end || hi < lo || max_time < lo || hi < min_time)
+ return false;
+ lo_time = max(lo, min_time);
+ hi_time = min(hi, max_time);
+ return true;
+}
+
+/* Generate redundant time stamps up to OPT. Return true if successful. */
+static bool
+redundant_time_option(char *opt)
+{
+ if (*opt == '@') {
+ intmax_t redundant;
+ char *opt_end;
+ redundant = strtoimax(opt + 1, &opt_end, 10);
+ if (opt_end != opt + 1 && !*opt_end) {
+ redundant_time = max(redundant_time, redundant);
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char * psxrules;
+static const char * lcltime;
+static const char * directory;
+static const char * tzdefault;
+
+/* -1 if the TZif output file should be slim, 0 if default, 1 if the
+ output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT
+ determines the default. */
+static int bloat;
+
+static bool
+want_bloat(void)
+{
+ return 0 <= bloat;
+}
+
+#ifndef ZIC_BLOAT_DEFAULT
+# define ZIC_BLOAT_DEFAULT "slim"
+#endif
+
+int
+main(int argc, char **argv)
+{
+ register int c, k;
+ register ptrdiff_t i, j;
+ bool timerange_given = false;
+
+#ifdef S_IWGRP
+ umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
+#endif
+#if HAVE_GETTEXT
+ setlocale(LC_ALL, "");
+# ifdef TZ_DOMAINDIR
+ bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+# endif /* defined TEXTDOMAINDIR */
+ textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT */
+ main_argv = argv;
+ 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"));
+ return EXIT_FAILURE;
+ }
+ for (k = 1; k < argc; k++)
+ if (strcmp(argv[k], "--version") == 0) {
+ printf("zic %s%s\n", PKGVERSION, TZVERSION);
+ close_file(stdout, NULL, NULL, NULL);
+ return EXIT_SUCCESS;
+ } else if (strcmp(argv[k], "--help") == 0) {
+ usage(stdout, EXIT_SUCCESS);
+ }
+ while ((c = getopt(argc, argv, "b:d:l:L:p:r:R:st:vy:")) != EOF
+ && c != -1)
+ switch (c) {
+ default:
+ usage(stderr, EXIT_FAILURE);
+ case 'b':
+ if (strcmp(optarg, "slim") == 0) {
+ if (0 < bloat)
+ error(_("incompatible -b options"));
+ bloat = -1;
+ } else if (strcmp(optarg, "fat") == 0) {
+ if (bloat < 0)
+ error(_("incompatible -b options"));
+ bloat = 1;
+ } else
+ error(_("invalid option: -b '%s'"), optarg);
+ break;
+ case 'd':
+ if (directory == NULL)
+ directory = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -d option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'l':
+ if (lcltime == NULL)
+ lcltime = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -l option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'p':
+ if (psxrules == NULL)
+ psxrules = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -p option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 't':
+ if (tzdefault != NULL) {
+ fprintf(stderr,
+ _("%s: More than one -t option"
+ " specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ tzdefault = optarg;
+ break;
+ case 'y':
+ warning(_("-y ignored"));
+ break;
+ case 'L':
+ if (leapsec == NULL)
+ leapsec = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -L option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'v':
+ noise = true;
+ break;
+ case 'r':
+ if (timerange_given) {
+ fprintf(stderr,
+_("%s: More than one -r option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ if (! timerange_option(optarg)) {
+ fprintf(stderr,
+_("%s: invalid time range: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ timerange_given = true;
+ break;
+ case 'R':
+ if (! redundant_time_option(optarg)) {
+ fprintf(stderr, _("%s: invalid time: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 's':
+ warning(_("-s ignored"));
+ break;
+ }
+ if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
+ usage(stderr, EXIT_FAILURE); /* usage message by request */
+ if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
+ fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
+ return EXIT_FAILURE;
+ }
+ if (bloat == 0) {
+ static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
+ if (strcmp(bloat_default, "slim") == 0)
+ bloat = -1;
+ else if (strcmp(bloat_default, "fat") == 0)
+ bloat = 1;
+ else
+ abort(); /* Configuration error. */
+ }
+ if (directory == NULL)
+ directory = TZDIR;
+ if (tzdefault == NULL)
+ tzdefault = TZDEFAULT;
+
+ if (optind < argc && leapsec != NULL) {
+ infile(LEAPSEC_FILENUM, leapsec);
+ adjleap();
+ }
+
+ for (k = optind; k < argc; k++)
+ infile(k, argv[k]);
+ if (errors)
+ return EXIT_FAILURE;
+ associate();
+ change_directory(directory);
+ catch_signals();
+ for (i = 0; i < nzones; i = j) {
+ /*
+ ** Find the next non-continuation zone entry.
+ */
+ for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
+ continue;
+ outzone(&zones[i], j - i);
+ }
+ make_links();
+ if (lcltime != NULL) {
+ eat(COMMAND_LINE_FILENUM, 1);
+ dolink(lcltime, tzdefault, true);
+ }
+ if (psxrules != NULL) {
+ eat(COMMAND_LINE_FILENUM, 1);
+ dolink(psxrules, TZDEFRULES, true);
+ }
+ if (warnings && (ferror(stderr) || fclose(stderr) != 0))
+ return EXIT_FAILURE;
+ return errors ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static bool
+componentcheck(char const *name, char const *component,
+ char const *component_end)
+{
+ enum { component_len_max = 14 };
+ ptrdiff_t component_len = component_end - component;
+ if (component_len == 0) {
+ if (!*name)
+ error(_("empty file name"));
+ else
+ error(_(component == name
+ ? "file name '%s' begins with '/'"
+ : *component_end
+ ? "file name '%s' contains '//'"
+ : "file name '%s' ends with '/'"),
+ name);
+ return false;
+ }
+ if (0 < component_len && component_len <= 2
+ && component[0] == '.' && component_end[-1] == '.') {
+ int len = component_len;
+ error(_("file name '%s' contains '%.*s' component"),
+ name, len, component);
+ return false;
+ }
+ if (noise) {
+ if (0 < component_len && component[0] == '-')
+ warning(_("file name '%s' component contains leading '-'"),
+ name);
+ if (component_len_max < component_len)
+ warning(_("file name '%s' contains overlength component"
+ " '%.*s...'"),
+ name, component_len_max, component);
+ }
+ return true;
+}
+
+static bool
+namecheck(const char *name)
+{
+ register char const *cp;
+
+ /* Benign characters in a portable file name. */
+ static char const benign[] =
+ "-/_"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /* Non-control chars in the POSIX portable character set,
+ excluding the benign characters. */
+ static char const printable_and_not_benign[] =
+ " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
+
+ register char const *component = name;
+ for (cp = name; *cp; cp++) {
+ unsigned char c = *cp;
+ if (noise && !strchr(benign, c)) {
+ warning((strchr(printable_and_not_benign, c)
+ ? _("file name '%s' contains byte '%c'")
+ : _("file name '%s' contains byte '\\%o'")),
+ name, c);
+ }
+ if (c == '/') {
+ if (!componentcheck(name, component, cp))
+ return false;
+ component = cp + 1;
+ }
+ }
+ 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 isn't cryptographically random and
+ 'rand' might not be cryptographically secure. */
+ {
+ static bool initialized;
+ if (!initialized) {
+ srand(time(NULL));
+ 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,
+ 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;
+ 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);
+
+ 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
+ and equal to *NAME; otherwise, allocate a new name and put its
+ address into both *NAMEALLOC and *NAME. */
+static void
+random_dirent(char const **name, char **namealloc)
+{
+ char const *src = *name;
+ char *dst = *namealloc;
+ static char const prefix[] = ".zic";
+ static char const alphabet[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ enum { prefixlen = sizeof prefix - 1, alphabetlen = sizeof alphabet - 1 };
+ int suffixlen = 6;
+ char const *lastslash = strrchr(src, '/');
+ ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0;
+ 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(size_sum(dirlen, prefixlen + suffixlen + 1));
+ memcpy(dst, src, dirlen);
+ memcpy(dst + dirlen, prefix, prefixlen);
+ dst[dirlen + prefixlen + suffixlen] = '\0';
+ *name = *namealloc = dst;
+ }
+
+ do
+ r = get_rand_u64();
+ while (unfair_min <= r);
+
+ for (i = 0; i < suffixlen; i++) {
+ dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
+ r /= alphabetlen;
+ }
+}
+
+/* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
+ name of the temporary file that will eventually be renamed to
+ *OUTNAME. Assign the temporary file's name to both *OUTNAME and
+ *TEMPNAME. If *TEMPNAME is null, allocate the name of any such
+ temporary file; otherwise, reuse *TEMPNAME's storage, which is
+ already set up and only needs its trailing suffix updated. */
+static FILE *
+open_outfile(char const **outname, char **tempname)
+{
+#if __STDC_VERSION__ < 201112
+ static char const fopen_mode[] = "wb";
+#else
+ static char const fopen_mode[] = "wbx";
+#endif
+
+ FILE *fp;
+ bool dirs_made = false;
+ if (!*tempname)
+ random_dirent(outname, tempname);
+
+ while (! (fp = fopen(*outname, fopen_mode))) {
+ int fopen_errno = errno;
+ if (fopen_errno == ENOENT && !dirs_made) {
+ mkdirs(*outname, true);
+ dirs_made = true;
+ } else if (fopen_errno == EEXIST)
+ random_dirent(outname, tempname);
+ else {
+ fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
+ progname, directory, *outname, strerror(fopen_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return fp;
+}
+
+/* If TEMPNAME, the result is in the temporary file TEMPNAME even
+ though the user wanted it in NAME, so rename TEMPNAME to NAME.
+ Report an error and exit if there is trouble. Also, free TEMPNAME. */
+static void
+rename_dest(char *tempname, char const *name)
+{
+ if (tempname) {
+ if (rename(tempname, name) != 0) {
+ int rename_errno = errno;
+ remove(tempname);
+ fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
+ progname, directory, name, strerror(rename_errno));
+ exit(EXIT_FAILURE);
+ }
+ free(tempname);
+ }
+}
+
+/* Create symlink contents suitable for symlinking FROM to TO, as a
+ freshly allocated string. FROM should be a relative file name, and
+ is relative to the global variable DIRECTORY. TO can be either
+ relative or absolute. */
+static char *
+relname(char const *target, char const *linkname)
+{
+ size_t i, taillen, dir_len = 0, dotdots = 0;
+ ptrdiff_t dotdotetcsize, linksize = INDEX_MAX;
+ char const *f = target;
+ char *result = NULL;
+ if (*linkname == '/') {
+ /* Make F absolute too. */
+ size_t len = strlen(directory);
+ size_t lenslash = len + (len && directory[len - 1] != '/');
+ size_t targetsize = strlen(target) + 1;
+ linksize = size_sum(lenslash, targetsize);
+ f = result = emalloc(linksize);
+ memcpy(result, directory, len);
+ result[len] = '/';
+ memcpy(result + lenslash, target, targetsize);
+ }
+ for (i = 0; f[i] && f[i] == linkname[i]; i++)
+ if (f[i] == '/')
+ dir_len = i + 1;
+ for (; linkname[i]; i++)
+ dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
+ taillen = strlen(f + dir_len);
+ dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
+ if (dotdotetcsize <= linksize) {
+ if (!result)
+ result = emalloc(dotdotetcsize);
+ for (i = 0; i < dotdots; i++)
+ memcpy(result + 3 * i, "../", 3);
+ memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
+ }
+ return result;
+}
+
+static void
+dolink(char const *target, char const *linkname, bool staysymlink)
+{
+ bool linkdirs_made = false;
+ int link_errno;
+ char *tempname = NULL;
+ char const *outname = linkname;
+
+ check_for_signal();
+
+ if (strcmp(target, "-") == 0) {
+ if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR)
+ return;
+ else {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
+ progname, directory, linkname, e);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ while (true) {
+ if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW)
+ == 0) {
+ link_errno = 0;
+ break;
+ }
+ link_errno = errno;
+ if (link_errno == EXDEV || link_errno == ENOTSUP)
+ break;
+
+ if (link_errno == EEXIST) {
+ staysymlink &= !tempname;
+ random_dirent(&outname, &tempname);
+ if (staysymlink && itssymlink(linkname))
+ break;
+ } else if (link_errno == ENOENT && !linkdirs_made) {
+ mkdirs(linkname, true);
+ linkdirs_made = true;
+ } else {
+ fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
+ progname, directory, target, directory, outname,
+ strerror(link_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (link_errno != 0) {
+ bool absolute = *target == '/';
+ char *linkalloc = absolute ? NULL : relname(target, linkname);
+ char const *contents = absolute ? target : linkalloc;
+ int symlink_errno;
+
+ while (true) {
+ if (symlink(contents, outname) == 0) {
+ symlink_errno = 0;
+ break;
+ }
+ symlink_errno = errno;
+ if (symlink_errno == EEXIST)
+ random_dirent(&outname, &tempname);
+ else if (symlink_errno == ENOENT && !linkdirs_made) {
+ mkdirs(linkname, true);
+ linkdirs_made = true;
+ } else
+ break;
+ }
+ free(linkalloc);
+ if (symlink_errno == 0) {
+ if (link_errno != ENOTSUP && link_errno != EEXIST)
+ warning(_("symbolic link used because hard link failed: %s"),
+ strerror(link_errno));
+ } else {
+ FILE *fp, *tp;
+ int c;
+ fp = fopen(target, "rb");
+ if (!fp) {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
+ progname, directory, target, e);
+ exit(EXIT_FAILURE);
+ }
+ tp = open_outfile(&outname, &tempname);
+ while ((c = getc(fp)) != EOF)
+ putc(c, tp);
+ close_file(tp, directory, linkname, tempname);
+ close_file(fp, directory, target, NULL);
+ if (link_errno != ENOTSUP)
+ warning(_("copy used because hard link failed: %s"),
+ strerror(link_errno));
+ else if (symlink_errno != ENOTSUP)
+ warning(_("copy used because symbolic link failed: %s"),
+ strerror(symlink_errno));
+ }
+ }
+ rename_dest(tempname, linkname);
+}
+
+/* Return true if NAME is a symbolic link. */
+static bool
+itssymlink(char const *name)
+{
+ char c;
+ return 0 <= readlink(name, &c, 1);
+}
+
+/*
+** Associate sets of rules with zones.
+*/
+
+/*
+** Sort by rule name.
+*/
+
+static int
+rcomp(const void *cp1, const void *cp2)
+{
+ struct rule const *r1 = cp1, *r2 = cp2;
+ return strcmp(r1->r_name, r2->r_name);
+}
+
+static void
+associate(void)
+{
+ register struct zone * zp;
+ register struct rule * rp;
+ register ptrdiff_t i, j, base, out;
+
+ 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 (rules[i].r_filenum == rules[i + 1].r_filenum)
+ continue;
+ eat(rules[i].r_filenum, rules[i].r_linenum);
+ warning(_("same rule name in multiple files"));
+ 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 (rules[i].r_filenum == rules[j].r_filenum)
+ continue;
+ if (rules[i + 1].r_filenum
+ == rules[j].r_filenum)
+ continue;
+ break;
+ }
+ i = j - 1;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ zp->z_rules = NULL;
+ zp->z_nrules = 0;
+ }
+ for (base = 0; base < nrules; base = out) {
+ rp = &rules[base];
+ for (out = base + 1; out < nrules; ++out)
+ if (strcmp(rp->r_name, rules[out].r_name) != 0)
+ break;
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (strcmp(zp->z_rule, rp->r_name) != 0)
+ continue;
+ zp->z_rules = rp;
+ zp->z_nrules = out - base;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (zp->z_nrules == 0) {
+ /*
+ ** Maybe we have a local standard time offset.
+ */
+ 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,
+ ** a '%s' in the format is a bad thing.
+ */
+ if (zp->z_format_specifier == 's')
+ error("%s", _("%s in ruleless zone"));
+ }
+ }
+ if (errors)
+ exit(EXIT_FAILURE);
+}
+
+/* Read a text line from FP into BUF, which is of size BUFSIZE.
+ Terminate it with a NUL byte instead of a newline.
+ Return true if successful, false if EOF.
+ On error, report the error and exit. */
+static bool
+inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
+{
+ ptrdiff_t linelen = 0, ch;
+ while ((ch = getc(fp)) != '\n') {
+ if (ch < 0) {
+ if (ferror(fp)) {
+ error(_("input error"));
+ exit(EXIT_FAILURE);
+ }
+ if (linelen == 0)
+ return false;
+ error(_("unterminated line"));
+ exit(EXIT_FAILURE);
+ }
+ if (!ch) {
+ error(_("NUL input byte"));
+ exit(EXIT_FAILURE);
+ }
+ buf[linelen++] = ch;
+ if (linelen == bufsize) {
+ error(_("line too long"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ buf[linelen] = '\0';
+ return true;
+}
+
+static void
+infile(int fnum, char const *name)
+{
+ register FILE * fp;
+ register const struct lookup * lp;
+ register bool wantcont;
+ register lineno num;
+
+ if (strcmp(name, "-") == 0) {
+ fp = stdin;
+ } else if ((fp = fopen(name, "r")) == NULL) {
+ const char *e = strerror(errno);
+
+ fprintf(stderr, _("%s: Can't open %s: %s\n"),
+ progname, name, e);
+ exit(EXIT_FAILURE);
+ }
+ wantcont = false;
+ for (num = 1; ; ++num) {
+ enum { bufsize_bound
+ = (min(INT_MAX, INDEX_MAX) / FORMAT_LEN_GROWTH_BOUND) };
+ char buf[min(_POSIX2_LINE_MAX, bufsize_bound)];
+ int nfields;
+ char *fields[MAX_FIELDS];
+ eat(fnum, num);
+ if (!inputline(fp, buf, sizeof buf))
+ break;
+ nfields = getfields(buf, fields,
+ sizeof fields / sizeof *fields);
+ if (nfields == 0) {
+ /* nothing to do */
+ } else if (wantcont) {
+ wantcont = inzcont(fields, nfields);
+ } else {
+ struct lookup const *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"));
+ else switch (lp->l_value) {
+ case LC_RULE:
+ inrule(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_ZONE:
+ wantcont = inzone(fields, nfields);
+ break;
+ case LC_LINK:
+ inlink(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_LEAP:
+ inleap(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_EXPIRES:
+ inexpires(fields, nfields);
+ wantcont = false;
+ break;
+ default: unreachable();
+ }
+ }
+ }
+ close_file(fp, NULL, filename(fnum), NULL);
+ if (wantcont)
+ error(_("expected continuation line not found"));
+}
+
+/*
+** Convert a string of one of the forms
+** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
+** into a number of seconds.
+** A null string maps to zero.
+** Call error with errstring and return zero on errors.
+*/
+
+static zic_t
+gethms(char const *string, char const *errstring)
+{
+ zic_t hh;
+ int sign, mm = 0, ss = 0;
+ char hhx, mmx, ssx, xr = '0', xs;
+ int tenths = 0;
+ bool ok = true;
+
+ if (string == NULL || *string == '\0')
+ return 0;
+ if (*string == '-') {
+ sign = -1;
+ ++string;
+ } else sign = 1;
+ switch (sscanf(string,
+ "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+ &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) {
+ default: ok = false; break;
+ case 8:
+ ok = '0' <= xr && xr <= '9';
+ ATTRIBUTE_FALLTHROUGH;
+ case 7:
+ ok &= ssx == '.';
+ if (ok && noise)
+ warning(_("fractional seconds rejected by"
+ " pre-2018 versions of zic"));
+ ATTRIBUTE_FALLTHROUGH;
+ case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH;
+ case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH;
+ case 1: break;
+ }
+ if (!ok) {
+ error("%s", errstring);
+ return 0;
+ }
+ if (hh < 0 ||
+ mm < 0 || mm >= MINSPERHOUR ||
+ ss < 0 || ss > SECSPERMIN) {
+ error("%s", errstring);
+ return 0;
+ }
+ if (ZIC_MAX / SECSPERHOUR < hh) {
+ error(_("time overflow"));
+ return 0;
+ }
+ ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
+ if (noise && (hh > HOURSPERDAY ||
+ (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
+warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
+ return oadd(sign * hh * SECSPERHOUR,
+ sign * (mm * SECSPERMIN + ss));
+}
+
+static zic_t
+getsave(char *field, bool *isdst)
+{
+ int dst = -1;
+ zic_t save;
+ ptrdiff_t fieldlen = strlen(field);
+ if (fieldlen != 0) {
+ char *ep = field + fieldlen - 1;
+ switch (*ep) {
+ case 'd': dst = 1; *ep = '\0'; break;
+ case 's': dst = 0; *ep = '\0'; break;
+ }
+ }
+ save = gethms(field, _("invalid saved time"));
+ *isdst = dst < 0 ? save != 0 : dst;
+ return save;
+}
+
+static void
+inrule(char **fields, int nfields)
+{
+ struct rule r;
+
+ if (nfields != RULE_FIELDS) {
+ error(_("wrong number of fields on Rule line"));
+ return;
+ }
+ switch (*fields[RF_NAME]) {
+ case '\0':
+ case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
+ case '+': case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
+ return;
+ }
+ 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],
+ fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
+ fields[RF_TOD]))
+ return;
+ 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);
+ rules[nrules++] = r;
+}
+
+static bool
+inzone(char **fields, int nfields)
+{
+ register ptrdiff_t i;
+
+ if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
+ error(_("wrong number of fields on Zone line"));
+ return false;
+ }
+ if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
+ error(
+_("\"Zone %s\" line and -l option are mutually exclusive"),
+ tzdefault);
+ return false;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
+ error(
+_("\"Zone %s\" line and -p option are mutually exclusive"),
+ TZDEFRULES);
+ return false;
+ }
+ for (i = 0; i < nzones; ++i)
+ if (zones[i].z_name != NULL &&
+ strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
+ error(_("duplicate zone name %s"
+ " (file \"%s\", line %"PRIdMAX")"),
+ fields[ZF_NAME],
+ filename(zones[i].z_filenum),
+ zones[i].z_linenum);
+ return false;
+ }
+ return inzsub(fields, nfields, false);
+}
+
+static bool
+inzcont(char **fields, int nfields)
+{
+ if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
+ error(_("wrong number of fields on Zone continuation line"));
+ return false;
+ }
+ return inzsub(fields, nfields, true);
+}
+
+static bool
+inzsub(char **fields, int nfields, bool iscont)
+{
+ register char * cp;
+ char * cp1;
+ struct zone z;
+ int format_len;
+ register int i_stdoff, i_rule, i_format;
+ register int i_untilyear, i_untilmonth;
+ register int i_untilday, i_untiltime;
+ register bool hasuntil;
+
+ if (iscont) {
+ i_stdoff = ZFC_STDOFF;
+ i_rule = ZFC_RULE;
+ i_format = ZFC_FORMAT;
+ i_untilyear = ZFC_TILYEAR;
+ i_untilmonth = ZFC_TILMONTH;
+ i_untilday = ZFC_TILDAY;
+ i_untiltime = ZFC_TILTIME;
+ } else if (!namecheck(fields[ZF_NAME]))
+ return false;
+ else {
+ i_stdoff = ZF_STDOFF;
+ i_rule = ZF_RULE;
+ i_format = ZF_FORMAT;
+ i_untilyear = ZF_TILYEAR;
+ i_untilmonth = ZF_TILMONTH;
+ i_untilday = ZF_TILDAY;
+ i_untiltime = ZF_TILTIME;
+ }
+ 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) {
+ if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
+ || strchr(fields[i_format], '/')) {
+ error(_("invalid abbreviation format"));
+ return false;
+ }
+ }
+ z.z_format_specifier = cp ? *cp : '\0';
+ format_len = strlen(fields[i_format]);
+ if (max_format_len < format_len)
+ max_format_len = format_len;
+ hasuntil = nfields > i_untilyear;
+ if (hasuntil) {
+ z.z_untilrule.r_filenum = filenum;
+ z.z_untilrule.r_linenum = linenum;
+ if (!rulesub(
+ &z.z_untilrule,
+ fields[i_untilyear],
+ "only",
+ "",
+ (nfields > i_untilmonth) ?
+ fields[i_untilmonth] : "Jan",
+ (nfields > i_untilday) ? fields[i_untilday] : "1",
+ (nfields > i_untiltime) ? fields[i_untiltime] : "0"))
+ return false;
+ z.z_untiltime = rpytime(&z.z_untilrule,
+ z.z_untilrule.r_loyear);
+ if (iscont && nzones > 0 &&
+ z.z_untiltime > min_time &&
+ z.z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime > min_time &&
+ zones[nzones - 1].z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime >= z.z_untiltime) {
+ error(_(
+"Zone continuation line end time is not after end time of previous line"
+ ));
+ return false;
+ }
+ }
+ 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)
+ warning(_("format '%s' not handled by pre-2015 versions of zic"),
+ fields[i_format]);
+ }
+ zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
+ zones[nzones++] = z;
+ /*
+ ** If there was an UNTIL field on this line,
+ ** there's more information about the zone on the next line.
+ */
+ return hasuntil;
+}
+
+static zic_t
+getleapdatetime(char **fields, bool expire_line)
+{
+ register const char * cp;
+ register const struct lookup * lp;
+ register zic_t i, j;
+ zic_t year;
+ int month, day;
+ zic_t dayoff, tod;
+ zic_t t;
+ char xs;
+
+ dayoff = 0;
+ cp = fields[LP_YEAR];
+ if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) {
+ /*
+ ** Leapin' Lizards!
+ */
+ error(_("invalid leaping year"));
+ return -1;
+ }
+ if (!expire_line) {
+ if (!leapseen || leapmaxyear < year)
+ leapmaxyear = year;
+ if (!leapseen || leapminyear > year)
+ leapminyear = year;
+ leapseen = true;
+ }
+ j = EPOCH_YEAR;
+ while (j != year) {
+ if (year > j) {
+ i = len_years[isleap(j)];
+ ++j;
+ } else {
+ --j;
+ i = -len_years[isleap(j)];
+ }
+ dayoff = oadd(dayoff, i);
+ }
+ if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return -1;
+ }
+ month = lp->l_value;
+ j = TM_JANUARY;
+ while (j != month) {
+ i = len_months[isleap(year)][j];
+ dayoff = oadd(dayoff, i);
+ ++j;
+ }
+ cp = fields[LP_DAY];
+ if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
+ day <= 0 || day > len_months[isleap(year)][month]) {
+ error(_("invalid day of month"));
+ return -1;
+ }
+ dayoff = oadd(dayoff, day - 1);
+ if (dayoff < min_time / SECSPERDAY) {
+ error(_("time too small"));
+ return -1;
+ }
+ if (dayoff > max_time / SECSPERDAY) {
+ error(_("time too large"));
+ return -1;
+ }
+ t = dayoff * SECSPERDAY;
+ tod = gethms(fields[LP_TIME], _("invalid time of day"));
+ t = tadd(t, tod);
+ if (t < 0)
+ error(_("leap second precedes Epoch"));
+ return t;
+}
+
+static void
+inleap(char **fields, int nfields)
+{
+ if (nfields != LEAP_FIELDS)
+ error(_("wrong number of fields on Leap line"));
+ else {
+ zic_t t = getleapdatetime(fields, false);
+ if (0 <= t) {
+ struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
+ if (!lp)
+ error(_("invalid Rolling/Stationary field on Leap line"));
+ else {
+ int correction = 0;
+ if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
+ correction = -1;
+ else if (strcmp(fields[LP_CORR], "+") == 0)
+ correction = 1;
+ else
+ error(_("invalid CORRECTION field on Leap line"));
+ if (correction)
+ leapadd(t, correction, lp->l_value);
+ }
+ }
+ }
+}
+
+static void
+inexpires(char **fields, int nfields)
+{
+ if (nfields != EXPIRES_FIELDS)
+ error(_("wrong number of fields on Expires line"));
+ else if (0 <= leapexpires)
+ error(_("multiple Expires lines"));
+ else
+ leapexpires = getleapdatetime(fields, true);
+}
+
+static void
+inlink(char **fields, int nfields)
+{
+ struct link l;
+
+ if (nfields != LINK_FIELDS) {
+ error(_("wrong number of fields on Link line"));
+ return;
+ }
+ if (*fields[LF_TARGET] == '\0') {
+ error(_("blank TARGET field on Link line"));
+ return;
+ }
+ if (! namecheck(fields[LF_LINKNAME]))
+ return;
+ l.l_filenum = filenum;
+ l.l_linenum = linenum;
+ 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;
+}
+
+static bool
+rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
+ const char *typep, const char *monthp, const char *dayp,
+ const char *timep)
+{
+ register const struct lookup * lp;
+ register const char * cp;
+ register char * dp;
+ register char * ep;
+ char xs;
+
+ if ((lp = byword(monthp, mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return false;
+ }
+ rp->r_month = lp->l_value;
+ rp->r_todisstd = false;
+ rp->r_todisut = false;
+ dp = estrdup(timep);
+ if (*dp != '\0') {
+ ep = dp + strlen(dp) - 1;
+ switch (lowerit(*ep)) {
+ case 's': /* Standard */
+ rp->r_todisstd = true;
+ rp->r_todisut = false;
+ *ep = '\0';
+ break;
+ case 'w': /* Wall */
+ rp->r_todisstd = false;
+ rp->r_todisut = false;
+ *ep = '\0';
+ break;
+ case 'g': /* Greenwich */
+ case 'u': /* Universal */
+ case 'z': /* Zulu */
+ rp->r_todisstd = true;
+ rp->r_todisut = true;
+ *ep = '\0';
+ break;
+ }
+ }
+ rp->r_tod = gethms(dp, _("invalid time of day"));
+ free(dp);
+ /*
+ ** Year work.
+ */
+ cp = loyearp;
+ lp = byword(cp, begin_years);
+ rp->r_lowasnum = lp == NULL;
+ if (!rp->r_lowasnum) switch (lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_loyear = ZIC_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_loyear = ZIC_MAX;
+ break;
+ default: unreachable();
+ } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
+ error(_("invalid starting year"));
+ return false;
+ }
+ cp = hiyearp;
+ lp = byword(cp, end_years);
+ rp->r_hiwasnum = lp == NULL;
+ if (!rp->r_hiwasnum) switch (lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_hiyear = ZIC_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_hiyear = ZIC_MAX;
+ break;
+ case YR_ONLY:
+ rp->r_hiyear = rp->r_loyear;
+ break;
+ default: unreachable();
+ } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
+ error(_("invalid ending year"));
+ return false;
+ }
+ if (rp->r_loyear > rp->r_hiyear) {
+ error(_("starting year greater than ending year"));
+ return false;
+ }
+ if (*typep != '\0') {
+ error(_("year type \"%s\" is unsupported; use \"-\" instead"),
+ typep);
+ return false;
+ }
+ /*
+ ** Day work.
+ ** Accept things such as:
+ ** 1
+ ** lastSunday
+ ** last-Sunday (undocumented; warn about this)
+ ** Sun<=20
+ ** Sun>=7
+ */
+ dp = estrdup(dayp);
+ if ((lp = byword(dp, lasts)) != NULL) {
+ rp->r_dycode = DC_DOWLEQ;
+ rp->r_wday = lp->l_value;
+ rp->r_dayofmonth = len_months[1][rp->r_month];
+ } else {
+ if ((ep = strchr(dp, '<')) != 0)
+ rp->r_dycode = DC_DOWLEQ;
+ else if ((ep = strchr(dp, '>')) != 0)
+ rp->r_dycode = DC_DOWGEQ;
+ else {
+ ep = dp;
+ rp->r_dycode = DC_DOM;
+ }
+ if (rp->r_dycode != DC_DOM) {
+ *ep++ = 0;
+ if (*ep++ != '=') {
+ error(_("invalid day of month"));
+ free(dp);
+ return false;
+ }
+ if ((lp = byword(dp, wday_names)) == NULL) {
+ error(_("invalid weekday name"));
+ free(dp);
+ return false;
+ }
+ rp->r_wday = lp->l_value;
+ }
+ if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
+ rp->r_dayofmonth <= 0 ||
+ (rp->r_dayofmonth > len_months[1][rp->r_month])) {
+ error(_("invalid day of month"));
+ free(dp);
+ return false;
+ }
+ }
+ free(dp);
+ return true;
+}
+
+static void
+convert(uint_fast32_t val, char *buf)
+{
+ register int i;
+ register int shift;
+ unsigned char *const b = (unsigned char *) buf;
+
+ for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
+ b[i] = (val >> shift) & 0xff;
+}
+
+static void
+convert64(uint_fast64_t val, char *buf)
+{
+ register int i;
+ register int shift;
+ unsigned char *const b = (unsigned char *) buf;
+
+ for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
+ b[i] = (val >> shift) & 0xff;
+}
+
+static void
+puttzcode(zic_t val, FILE *fp)
+{
+ char buf[4];
+
+ convert(val, buf);
+ fwrite(buf, sizeof buf, 1, fp);
+}
+
+static void
+puttzcodepass(zic_t val, FILE *fp, int pass)
+{
+ if (pass == 1)
+ puttzcode(val, fp);
+ else {
+ char buf[8];
+
+ convert64(val, buf);
+ fwrite(buf, sizeof buf, 1, fp);
+ }
+}
+
+static int
+atcomp(const void *avp, const void *bvp)
+{
+ struct attype const *ap = avp, *bp = bvp;
+ zic_t a = ap->at, b = bp->at;
+ return a < b ? -1 : a > b;
+}
+
+struct timerange {
+ int defaulttype;
+ ptrdiff_t base, count;
+ int leapbase, leapcount;
+ bool leapexpiry;
+};
+
+static struct timerange
+limitrange(struct timerange r, zic_t lo, zic_t hi,
+ zic_t const *ats, unsigned char const *types)
+{
+ /* Omit ordinary transitions < LO. */
+ while (0 < r.count && ats[r.base] < lo) {
+ r.defaulttype = types[r.base];
+ r.count--;
+ r.base++;
+ }
+
+ /* Omit as many initial leap seconds as possible, such that the
+ first leap second in the truncated list is <= LO, and is a
+ positive leap second if and only if it has a positive correction.
+ This supports common TZif readers that assume that the first leap
+ second is positive if and only if its correction is positive. */
+ while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) {
+ r.leapcount--;
+ r.leapbase++;
+ }
+ while (0 < r.leapbase
+ && ((corr[r.leapbase - 1] < corr[r.leapbase])
+ != (0 < corr[r.leapbase]))) {
+ r.leapcount++;
+ r.leapbase--;
+ }
+
+
+ /* Omit ordinary and leap second transitions greater than HI + 1. */
+ if (hi < max_time) {
+ while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
+ r.count--;
+ while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
+ r.leapcount--;
+ }
+
+ /* Determine whether to append an expiration to the leap second table. */
+ r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
+
+ return r;
+}
+
+static void
+writezone(const char *const name, const char *const string, char version,
+ int defaulttype)
+{
+ register FILE * fp;
+ register ptrdiff_t i, j;
+ register int pass;
+ char *tempname = NULL;
+ char const *outname = name;
+
+ /* Allocate the ATS and TYPES arrays via a single malloc,
+ 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;
+ struct timerange rangeall = {0}, range32, range64;
+
+ /*
+ ** Sort.
+ */
+ if (timecnt > 1)
+ qsort(attypes, timecnt, sizeof *attypes, atcomp);
+ /*
+ ** Optimize.
+ */
+ {
+ ptrdiff_t fromi, toi;
+
+ toi = 0;
+ fromi = 0;
+ for ( ; fromi < timecnt; ++fromi) {
+ if (toi != 0
+ && ((attypes[fromi].at
+ + utoffs[attypes[toi - 1].type])
+ <= (attypes[toi - 1].at
+ + utoffs[toi == 1 ? 0
+ : attypes[toi - 2].type]))) {
+ attypes[toi - 1].type =
+ attypes[fromi].type;
+ continue;
+ }
+ if (toi == 0
+ || attypes[fromi].dontmerge
+ || (utoffs[attypes[toi - 1].type]
+ != utoffs[attypes[fromi].type])
+ || (isdsts[attypes[toi - 1].type]
+ != isdsts[attypes[fromi].type])
+ || (desigidx[attypes[toi - 1].type]
+ != desigidx[attypes[fromi].type]))
+ attypes[toi++] = attypes[fromi];
+ }
+ timecnt = toi;
+ }
+
+ if (noise && timecnt > 1200) {
+ if (timecnt > TZ_MAX_TIMES)
+ warning(_("reference clients mishandle"
+ " more than %d transition times"),
+ TZ_MAX_TIMES);
+ else
+ warning(_("pre-2014 clients may mishandle"
+ " more than 1200 transition times"));
+ }
+ /*
+ ** Transfer.
+ */
+ for (i = 0; i < timecnt; ++i) {
+ ats[i] = attypes[i].at;
+ types[i] = attypes[i].type;
+ }
+
+ /*
+ ** Correct for leap seconds.
+ */
+ for (i = 0; i < timecnt; ++i) {
+ j = leapcnt;
+ while (--j >= 0)
+ if (ats[i] > trans[j] - corr[j]) {
+ ats[i] = tadd(ats[i], corr[j]);
+ break;
+ }
+ }
+
+ rangeall.defaulttype = defaulttype;
+ rangeall.count = timecnt;
+ rangeall.leapcount = leapcnt;
+ range64 = limitrange(rangeall, lo_time,
+ max(hi_time,
+ redundant_time - (ZIC_MIN < redundant_time)),
+ 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
+ leap second transition is not to a +1 or -1 correction. */
+ for (pass = 1; pass <= 2; pass++) {
+ struct timerange const *r = pass == 1 ? &range32 : &range64;
+ if (pass == 1 && !want_bloat())
+ continue;
+ if (r->leapexpiry) {
+ if (noise)
+ warning(_("%s: pre-2021b clients may mishandle"
+ " leap second expiry"),
+ name);
+ version = '4';
+ }
+ if (0 < r->leapcount
+ && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) {
+ if (noise)
+ warning(_("%s: pre-2021b clients may mishandle"
+ " leap second table truncation"),
+ name);
+ version = '4';
+ }
+ if (version == '4')
+ break;
+ }
+
+ fp = open_outfile(&outname, &tempname);
+
+ for (pass = 1; pass <= 2; ++pass) {
+ register ptrdiff_t thistimei, thistimecnt, thistimelim;
+ register int thisleapi, thisleapcnt, thisleaplim;
+ struct tzhead tzh;
+ int pretranstype = -1, thisdefaulttype;
+ bool locut, hicut, thisleapexpiry;
+ zic_t lo, thismin, thismax;
+ int old0;
+ char omittype[TZ_MAX_TYPES];
+ int typemap[TZ_MAX_TYPES];
+ int thistypecnt, stdcnt, utcnt;
+ char thischars[TZ_MAX_CHARS];
+ int thischarcnt;
+ bool toomanytimes;
+ int indmap[TZ_MAX_CHARS];
+
+ if (pass == 1) {
+ thisdefaulttype = range32.defaulttype;
+ thistimei = range32.base;
+ thistimecnt = range32.count;
+ toomanytimes = thistimecnt >> 31 >> 1 != 0;
+ thisleapi = range32.leapbase;
+ thisleapcnt = range32.leapcount;
+ thisleapexpiry = range32.leapexpiry;
+ thismin = ZIC32_MIN;
+ thismax = ZIC32_MAX;
+ } else {
+ thisdefaulttype = range64.defaulttype;
+ thistimei = range64.base;
+ thistimecnt = range64.count;
+ toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
+ thisleapi = range64.leapbase;
+ thisleapcnt = range64.leapcount;
+ thisleapexpiry = range64.leapexpiry;
+ thismin = min_time;
+ thismax = max_time;
+ }
+ if (toomanytimes)
+ error(_("too many transition times"));
+
+ locut = thismin < lo_time && lo_time <= thismax;
+ hicut = thismin <= hi_time && hi_time < thismax;
+ thistimelim = thistimei + thistimecnt;
+ memset(omittype, true, typecnt);
+
+ /* Determine whether to output a transition before the first
+ transition in range. This is needed when the output is
+ truncated at the start, and is also useful when catering to
+ buggy 32-bit clients that do not use time type 0 for
+ timestamps before the first transition. */
+ if ((locut || (pass == 1 && thistimei))
+ && ! (thistimecnt && ats[thistimei] == lo_time)) {
+ pretranstype = thisdefaulttype;
+ omittype[pretranstype] = false;
+ }
+
+ /* Arguably the default time type in the 32-bit data
+ should be range32.defaulttype, which is suited for
+ 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
+ to obsolete readers where the traditional type might
+ be more appropriate even if it's "wrong". So, use
+ the historical zic value, unless -r specifies a low
+ cutoff that excludes some 32-bit timestamps. */
+ if (pass == 1 && lo_time <= thismin)
+ thisdefaulttype = range64.defaulttype;
+
+ if (locut)
+ thisdefaulttype = unspecifiedtype;
+ omittype[thisdefaulttype] = false;
+ for (i = thistimei; i < thistimelim; i++)
+ omittype[types[i]] = false;
+ if (hicut)
+ omittype[unspecifiedtype] = false;
+
+ /* Reorder types to make THISDEFAULTTYPE type 0.
+ Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that
+ THISDEFAULTTYPE appears as type 0 in the output instead
+ of OLD0. TYPEMAP also omits unused types. */
+ old0 = strlen(omittype);
+
+#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
+ /*
+ ** For some pre-2011 systems: if the last-to-be-written
+ ** standard (or daylight) type has an offset different from the
+ ** most recently used offset,
+ ** append an (unused) copy of the most recently used type
+ ** (to help get global "altzone" and "timezone" variables
+ ** set correctly).
+ */
+ if (want_bloat()) {
+ register int mrudst, mrustd, hidst, histd, type;
+
+ hidst = histd = mrudst = mrustd = -1;
+ if (0 <= pretranstype) {
+ if (isdsts[pretranstype])
+ mrudst = pretranstype;
+ else
+ mrustd = pretranstype;
+ }
+ for (i = thistimei; i < thistimelim; i++)
+ if (isdsts[types[i]])
+ mrudst = types[i];
+ else mrustd = types[i];
+ for (i = old0; i < typecnt; i++) {
+ int h = (i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i);
+ if (!omittype[h]) {
+ if (isdsts[h])
+ hidst = i;
+ else
+ histd = i;
+ }
+ }
+ if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
+ utoffs[hidst] != utoffs[mrudst]) {
+ isdsts[mrudst] = -1;
+ type = addtype(utoffs[mrudst],
+ &chars[desigidx[mrudst]],
+ true,
+ ttisstds[mrudst],
+ ttisuts[mrudst]);
+ isdsts[mrudst] = 1;
+ omittype[type] = false;
+ }
+ if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
+ utoffs[histd] != utoffs[mrustd]) {
+ isdsts[mrustd] = -1;
+ type = addtype(utoffs[mrustd],
+ &chars[desigidx[mrustd]],
+ false,
+ ttisstds[mrustd],
+ ttisuts[mrustd]);
+ isdsts[mrustd] = 0;
+ omittype[type] = false;
+ }
+ }
+#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
+ thistypecnt = 0;
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ typemap[i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i]
+ = thistypecnt++;
+
+ for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
+ indmap[i] = -1;
+ thischarcnt = stdcnt = utcnt = 0;
+ for (i = old0; i < typecnt; i++) {
+ register char * thisabbr;
+
+ if (omittype[i])
+ continue;
+ if (ttisstds[i])
+ stdcnt = thistypecnt;
+ if (ttisuts[i])
+ utcnt = thistypecnt;
+ if (indmap[desigidx[i]] >= 0)
+ continue;
+ thisabbr = &chars[desigidx[i]];
+ for (j = 0; j < thischarcnt; ++j)
+ if (strcmp(&thischars[j], thisabbr) == 0)
+ break;
+ if (j == thischarcnt) {
+ strcpy(&thischars[thischarcnt], thisabbr);
+ thischarcnt += strlen(thisabbr) + 1;
+ }
+ indmap[desigidx[i]] = j;
+ }
+ if (pass == 1 && !want_bloat()) {
+ hicut = thisleapexpiry = false;
+ pretranstype = -1;
+ thistimecnt = thisleapcnt = 0;
+ thistypecnt = thischarcnt = 1;
+ }
+#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
+ memset(&tzh, 0, sizeof tzh);
+ memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+ tzh.tzh_version[0] = version;
+ convert(utcnt, tzh.tzh_ttisutcnt);
+ convert(stdcnt, tzh.tzh_ttisstdcnt);
+ convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
+ convert((0 <= pretranstype) + thistimecnt + hicut,
+ tzh.tzh_timecnt);
+ convert(thistypecnt, tzh.tzh_typecnt);
+ convert(thischarcnt, tzh.tzh_charcnt);
+ DO(tzh_magic);
+ DO(tzh_version);
+ DO(tzh_reserved);
+ DO(tzh_ttisutcnt);
+ DO(tzh_ttisstdcnt);
+ DO(tzh_leapcnt);
+ DO(tzh_timecnt);
+ DO(tzh_typecnt);
+ DO(tzh_charcnt);
+#undef DO
+ if (pass == 1 && !want_bloat()) {
+ /* Output a minimal data block with just one time type. */
+ puttzcode(0, fp); /* utoff */
+ putc(0, fp); /* dst */
+ putc(0, fp); /* index of abbreviation */
+ putc(0, fp); /* empty-string abbreviation */
+ continue;
+ }
+
+ /* 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 < ZIC32_MIN ? ZIC32_MIN : lo_time;
+
+ if (0 <= pretranstype)
+ puttzcodepass(lo, fp, pass);
+ for (i = thistimei; i < thistimelim; ++i) {
+ puttzcodepass(ats[i], fp, pass);
+ }
+ if (hicut)
+ puttzcodepass(hi_time + 1, fp, pass);
+ if (0 <= pretranstype)
+ putc(typemap[pretranstype], fp);
+ for (i = thistimei; i < thistimelim; i++)
+ putc(typemap[types[i]], fp);
+ if (hicut)
+ putc(typemap[unspecifiedtype], fp);
+
+ for (i = old0; i < typecnt; i++) {
+ int h = (i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i);
+ if (!omittype[h]) {
+ puttzcode(utoffs[h], fp);
+ putc(isdsts[h], fp);
+ putc(indmap[desigidx[h]], fp);
+ }
+ }
+ if (thischarcnt != 0)
+ fwrite(thischars, sizeof thischars[0],
+ thischarcnt, fp);
+ thisleaplim = thisleapi + thisleapcnt;
+ for (i = thisleapi; i < thisleaplim; ++i) {
+ register zic_t todo;
+
+ if (roll[i]) {
+ if (timecnt == 0 || trans[i] < ats[0]) {
+ j = 0;
+ while (isdsts[j])
+ if (++j >= typecnt) {
+ j = 0;
+ break;
+ }
+ } else {
+ j = 1;
+ while (j < timecnt &&
+ trans[i] >= ats[j])
+ ++j;
+ j = types[j - 1];
+ }
+ todo = tadd(trans[i], -utoffs[j]);
+ } else todo = trans[i];
+ puttzcodepass(todo, fp, pass);
+ puttzcode(corr[i], fp);
+ }
+ if (thisleapexpiry) {
+ /* Append a no-op leap correction indicating when the leap
+ second table expires. Although this does not conform to
+ Internet RFC 8536, most clients seem to accept this and
+ the plan is to amend the RFC to allow this in version 4
+ TZif files. */
+ puttzcodepass(leapexpires, fp, pass);
+ puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp);
+ }
+ if (stdcnt != 0)
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ putc(ttisstds[i], fp);
+ if (utcnt != 0)
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ putc(ttisuts[i], fp);
+ }
+ fprintf(fp, "\n%s\n", string);
+ close_file(fp, directory, name, tempname);
+ rename_dest(tempname, name);
+ free(ats);
+}
+
+static char const *
+abbroffset(char *buf, zic_t offset)
+{
+ char sign = '+';
+ int seconds, minutes;
+
+ if (offset < 0) {
+ offset = -offset;
+ sign = '-';
+ }
+
+ seconds = offset % SECSPERMIN;
+ offset /= SECSPERMIN;
+ minutes = offset % MINSPERHOUR;
+ offset /= MINSPERHOUR;
+ if (100 <= offset) {
+ error(_("%%z UT offset magnitude exceeds 99:59:59"));
+ return "%z";
+ } else {
+ char *p = buf;
+ *p++ = sign;
+ *p++ = '0' + offset / 10;
+ *p++ = '0' + offset % 10;
+ if (minutes | seconds) {
+ *p++ = '0' + minutes / 10;
+ *p++ = '0' + minutes % 10;
+ if (seconds) {
+ *p++ = '0' + seconds / 10;
+ *p++ = '0' + seconds % 10;
+ }
+ }
+ *p = '\0';
+ return buf;
+ }
+}
+
+static char const disable_percent_s[] = "";
+
+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;
+ ptrdiff_t len;
+ char const *format = zp->z_format;
+
+ slashp = strchr(format, '/');
+ if (slashp == NULL) {
+ char letterbuf[PERCENT_Z_LEN_BOUND + 1];
+ if (zp->z_format_specifier == 'z')
+ letters = abbroffset(letterbuf, zp->z_stdoff + save);
+ else if (!letters)
+ letters = "%s";
+ else if (letters == disable_percent_s)
+ return 0;
+ sprintf(abbr, format, letters);
+ } else if (isdst) {
+ strcpy(abbr, slashp + 1);
+ } else {
+ memcpy(abbr, format, slashp - format);
+ abbr[slashp - format] = '\0';
+ }
+ len = strlen(abbr);
+ if (!doquotes)
+ return len;
+ for (cp = abbr; is_alpha(*cp); cp++)
+ continue;
+ if (len > 0 && *cp == '\0')
+ return len;
+ abbr[len + 2] = '\0';
+ abbr[len + 1] = '>';
+ memmove(abbr + 1, abbr, len);
+ abbr[0] = '<';
+ return len + 2;
+}
+
+static void
+updateminmax(const zic_t x)
+{
+ if (min_year > x)
+ min_year = x;
+ if (max_year < x)
+ max_year = x;
+}
+
+static int
+stringoffset(char *result, zic_t offset)
+{
+ register int hours;
+ register int minutes;
+ register int seconds;
+ bool negative = offset < 0;
+ int len = negative;
+
+ if (negative) {
+ offset = -offset;
+ result[0] = '-';
+ }
+ seconds = offset % SECSPERMIN;
+ offset /= SECSPERMIN;
+ minutes = offset % MINSPERHOUR;
+ offset /= MINSPERHOUR;
+ hours = offset;
+ if (hours >= HOURSPERDAY * DAYSPERWEEK) {
+ result[0] = '\0';
+ return 0;
+ }
+ len += sprintf(result + len, "%d", hours);
+ if (minutes != 0 || seconds != 0) {
+ len += sprintf(result + len, ":%02d", minutes);
+ if (seconds != 0)
+ len += sprintf(result + len, ":%02d", seconds);
+ }
+ return len;
+}
+
+static int
+stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff)
+{
+ register zic_t tod = rp->r_tod;
+ register int compat = 0;
+
+ if (rp->r_dycode == DC_DOM) {
+ register int month, total;
+
+ if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
+ return -1;
+ total = 0;
+ for (month = 0; month < rp->r_month; ++month)
+ total += len_months[0][month];
+ /* Omit the "J" in Jan and Feb, as that's shorter. */
+ if (rp->r_month <= 1)
+ result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
+ else
+ result += sprintf(result, "J%d", total + rp->r_dayofmonth);
+ } else {
+ register int week;
+ register int wday = rp->r_wday;
+ register int wdayoff;
+
+ if (rp->r_dycode == DC_DOWGEQ) {
+ wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
+ wday -= wdayoff;
+ tod += wdayoff * SECSPERDAY;
+ week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
+ } else if (rp->r_dycode == DC_DOWLEQ) {
+ if (rp->r_dayofmonth == len_months[1][rp->r_month])
+ week = 5;
+ else {
+ wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
+ wday -= wdayoff;
+ tod += wdayoff * SECSPERDAY;
+ week = rp->r_dayofmonth / DAYSPERWEEK;
+ }
+ } else return -1; /* "cannot happen" */
+ if (wday < 0)
+ wday += DAYSPERWEEK;
+ result += sprintf(result, "M%d.%d.%d",
+ rp->r_month + 1, week, wday);
+ }
+ if (rp->r_todisut)
+ tod += stdoff;
+ if (rp->r_todisstd && !rp->r_isdst)
+ tod += save;
+ if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
+ *result++ = '/';
+ if (! stringoffset(result, tod))
+ return -1;
+ if (tod < 0) {
+ if (compat < 2013)
+ compat = 2013;
+ } else if (SECSPERDAY <= tod) {
+ if (compat < 1994)
+ compat = 1994;
+ }
+ }
+ return compat;
+}
+
+static int
+rule_cmp(struct rule const *a, struct rule const *b)
+{
+ if (!a)
+ return -!!b;
+ if (!b)
+ return 1;
+ if (a->r_hiyear != b->r_hiyear)
+ return a->r_hiyear < b->r_hiyear ? -1 : 1;
+ if (a->r_hiyear == ZIC_MAX)
+ return 0;
+ if (a->r_month - b->r_month != 0)
+ return a->r_month - b->r_month;
+ return a->r_dayofmonth - b->r_dayofmonth;
+}
+
+static int
+stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
+{
+ register const struct zone * zp;
+ register struct rule * rp;
+ register struct rule * stdrp;
+ register struct rule * dstrp;
+ register ptrdiff_t i;
+ register int compat = 0;
+ register int c;
+ int offsetlen;
+ struct rule stdr, dstr;
+ ptrdiff_t len;
+ int dstcmp;
+ struct rule *lastrp[2] = { NULL, NULL };
+ struct zone zstr[2];
+ struct zone const *stdzp;
+ struct zone const *dstzp;
+
+ result[0] = '\0';
+
+ /* Internet RFC 8536 section 5.1 says to use an empty TZ string if
+ future timestamps are truncated. */
+ if (hi_time < max_time)
+ return -1;
+
+ zp = zpfirst + zonecount - 1;
+ for (i = 0; i < zp->z_nrules; ++i) {
+ struct rule **last;
+ int cmp;
+ rp = &zp->z_rules[i];
+ last = &lastrp[rp->r_isdst];
+ cmp = rule_cmp(*last, rp);
+ if (cmp < 0)
+ *last = rp;
+ else if (cmp == 0)
+ return -1;
+ }
+ stdrp = lastrp[false];
+ dstrp = lastrp[true];
+ dstcmp = zp->z_nrules ? rule_cmp(dstrp, stdrp) : zp->z_isdst ? 1 : -1;
+ stdzp = dstzp = zp;
+
+ if (dstcmp < 0) {
+ /* Standard time all year. */
+ dstrp = NULL;
+ } else if (0 < dstcmp) {
+ /* DST all year. Use an abbreviation like
+ "XXX3EDT4,0/0,J365/23" for EDT (-04) all year. */
+ zic_t save = dstrp ? dstrp->r_save : zp->z_save;
+ if (0 <= save)
+ {
+ /* Positive DST, the typical case for all-year DST.
+ Fake a timezone with negative DST. */
+ stdzp = &zstr[0];
+ dstzp = &zstr[1];
+ zstr[0].z_stdoff = zp->z_stdoff + 2 * save;
+ zstr[0].z_format = "XXX"; /* Any 3 letters will do. */
+ zstr[0].z_format_specifier = 0;
+ zstr[1].z_stdoff = zstr[0].z_stdoff;
+ zstr[1].z_format = zp->z_format;
+ zstr[1].z_format_specifier = zp->z_format_specifier;
+ }
+ dstr.r_month = TM_JANUARY;
+ dstr.r_dycode = DC_DOM;
+ dstr.r_dayofmonth = 1;
+ dstr.r_tod = 0;
+ dstr.r_todisstd = dstr.r_todisut = false;
+ dstr.r_isdst = true;
+ dstr.r_save = save < 0 ? save : -save;
+ dstr.r_abbrvar = dstrp ? dstrp->r_abbrvar : NULL;
+ stdr.r_month = TM_DECEMBER;
+ stdr.r_dycode = DC_DOM;
+ stdr.r_dayofmonth = 31;
+ stdr.r_tod = SECSPERDAY + dstr.r_save;
+ stdr.r_todisstd = stdr.r_todisut = false;
+ stdr.r_isdst = false;
+ stdr.r_save = 0;
+ stdr.r_abbrvar = save < 0 && stdrp ? stdrp->r_abbrvar : NULL;
+ dstrp = &dstr;
+ stdrp = &stdr;
+ }
+ len = doabbr(result, stdzp, stdrp ? stdrp->r_abbrvar : NULL,
+ false, 0, true);
+ offsetlen = stringoffset(result + len, - stdzp->z_stdoff);
+ if (! offsetlen) {
+ result[0] = '\0';
+ return -1;
+ }
+ len += offsetlen;
+ if (dstrp == NULL)
+ return compat;
+ len += doabbr(result + len, dstzp, dstrp->r_abbrvar,
+ dstrp->r_isdst, dstrp->r_save, true);
+ if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) {
+ offsetlen = stringoffset(result + len,
+ - (dstzp->z_stdoff + dstrp->r_save));
+ if (! offsetlen) {
+ result[0] = '\0';
+ return -1;
+ }
+ len += offsetlen;
+ }
+ result[len++] = ',';
+ c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff);
+ if (c < 0) {
+ result[0] = '\0';
+ return -1;
+ }
+ if (compat < c)
+ compat = c;
+ len += strlen(result + len);
+ result[len++] = ',';
+ c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff);
+ if (c < 0) {
+ result[0] = '\0';
+ return -1;
+ }
+ if (compat < c)
+ compat = c;
+ return compat;
+}
+
+static void
+outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
+{
+ register ptrdiff_t i, j;
+ register zic_t starttime = 0, untiltime = 0;
+ register bool startttisstd;
+ register bool startttisut;
+ register char * startbuf;
+ register char * ab;
+ register char * envvar;
+ register int max_abbr_len;
+ register int max_envvar_len;
+ register bool prodstic; /* all rules are min to max */
+ register int compat;
+ register bool do_extend;
+ register char version;
+ ptrdiff_t lastatmax = -1;
+ zic_t max_year0;
+ int defaulttype = -1;
+
+ 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);
+ INITIALIZE(untiltime);
+ INITIALIZE(starttime);
+ /*
+ ** Now. . .finally. . .generate some useful data!
+ */
+ timecnt = 0;
+ typecnt = 0;
+ charcnt = 0;
+ prodstic = zonecount == 1;
+ /*
+ ** Thanks to Earl Chew
+ ** for noting the need to unconditionally initialize startttisstd.
+ */
+ startttisstd = false;
+ startttisut = false;
+ min_year = max_year = EPOCH_YEAR;
+ if (leapseen) {
+ updateminmax(leapminyear);
+ updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
+ }
+ for (i = 0; i < zonecount; ++i) {
+ struct zone const *zp = &zpfirst[i];
+ if (i < zonecount - 1)
+ updateminmax(zp->z_untilrule.r_loyear);
+ for (j = 0; j < zp->z_nrules; ++j) {
+ struct rule *rp = &zp->z_rules[j];
+ if (rp->r_lowasnum)
+ updateminmax(rp->r_loyear);
+ if (rp->r_hiwasnum)
+ updateminmax(rp->r_hiyear);
+ if (rp->r_lowasnum || rp->r_hiwasnum)
+ prodstic = false;
+ }
+ }
+ /*
+ ** Generate lots of data if a rule can't cover all future times.
+ */
+ compat = stringzone(envvar, zpfirst, zonecount);
+ version = compat < 2013 ? '2' : '3';
+ do_extend = compat < 0;
+ if (noise) {
+ if (!*envvar)
+ warning("%s %s",
+ _("no POSIX environment variable for zone"),
+ zpfirst->z_name);
+ else if (compat != 0) {
+ /* Circa-COMPAT clients, and earlier clients, might
+ not work for this zone when given dates before
+ 1970 or after 2038. */
+ warning(_("%s: pre-%d clients may mishandle"
+ " distant timestamps"),
+ zpfirst->z_name, compat);
+ }
+ }
+ if (do_extend) {
+ /*
+ ** Search through a couple of extra years past the obvious
+ ** 400, to avoid edge cases. For example, suppose a non-POSIX
+ ** rule applies from 2012 onwards and has transitions in March
+ ** and September, plus some one-off transitions in November
+ ** 2013. If zic looked only at the last 400 years, it would
+ ** set max_year=2413, with the intent that the 400 years 2014
+ ** through 2413 will be repeated. The last transition listed
+ ** in the tzfile would be in 2413-09, less than 400 years
+ ** after the last one-off transition in 2013-11. Two years
+ ** might be overkill, but with the kind of edge cases
+ ** available we're not sure that one year would suffice.
+ */
+ enum { years_of_observations = YEARSPERREPEAT + 2 };
+
+ if (min_year >= ZIC_MIN + years_of_observations)
+ min_year -= years_of_observations;
+ else min_year = ZIC_MIN;
+ if (max_year <= ZIC_MAX - years_of_observations)
+ max_year += years_of_observations;
+ else max_year = ZIC_MAX;
+ /*
+ ** Regardless of any of the above,
+ ** for a "proDSTic" zone which specifies that its rules
+ ** always have and always will be in effect,
+ ** we only need one cycle to define the zone.
+ */
+ if (prodstic) {
+ min_year = 1900;
+ max_year = min_year + years_of_observations;
+ }
+ }
+ max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
+ + EPOCH_YEAR + 1));
+ max_year0 = max_year;
+ if (want_bloat()) {
+ /* For the benefit of older systems,
+ generate data from 1900 through 2038. */
+ if (min_year > 1900)
+ min_year = 1900;
+ if (max_year < 2038)
+ max_year = 2038;
+ }
+
+ if (min_time < lo_time || hi_time < max_time)
+ unspecifiedtype = addtype(0, "-00", false, false, false);
+
+ for (i = 0; i < zonecount; ++i) {
+ struct rule *prevrp = NULL;
+ /*
+ ** A guess that may well be corrected later.
+ */
+ zic_t save = 0;
+ struct zone const *zp = &zpfirst[i];
+ bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ bool useuntil = i < (zonecount - 1);
+ zic_t stdoff = zp->z_stdoff;
+ zic_t startoff = stdoff;
+ zic_t prevktime;
+ INITIALIZE(prevktime);
+ if (useuntil && zp->z_untiltime <= min_time)
+ continue;
+ eat(zp->z_filenum, zp->z_linenum);
+ *startbuf = '\0';
+ if (zp->z_nrules == 0) {
+ int type;
+ save = zp->z_save;
+ doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
+ type = addtype(oadd(zp->z_stdoff, save),
+ startbuf, zp->z_isdst, startttisstd,
+ startttisut);
+ if (usestart) {
+ addtt(starttime, type);
+ usestart = false;
+ } else
+ defaulttype = type;
+ } else {
+ zic_t year;
+ for (year = min_year; year <= max_year; ++year) {
+ if (useuntil && year > zp->z_untilrule.r_hiyear)
+ break;
+ /*
+ ** Mark which rules to do in the current year.
+ ** For those to do, calculate rpytime(rp, year);
+ ** The former TYPE field was also considered here.
+ */
+ for (j = 0; j < zp->z_nrules; ++j) {
+ zic_t one = 1;
+ zic_t y2038_boundary = one << 31;
+ struct rule *rp = &zp->z_rules[j];
+ 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) {
+ rp->r_temp = rpytime(rp, year);
+ rp->r_todo
+ = (rp->r_temp < y2038_boundary
+ || year <= max_year0);
+ }
+ }
+ for ( ; ; ) {
+ register ptrdiff_t k;
+ register zic_t jtime = 0, ktime = 0;
+ register zic_t offset;
+ struct rule *rp;
+ int type;
+
+ INITIALIZE(ktime);
+ if (useuntil) {
+ /*
+ ** Turn untiltime into UT
+ ** assuming the current stdoff and
+ ** save values.
+ */
+ untiltime = zp->z_untiltime;
+ if (!zp->z_untilrule.r_todisut)
+ untiltime = tadd(untiltime,
+ -stdoff);
+ if (!zp->z_untilrule.r_todisstd)
+ untiltime = tadd(untiltime,
+ -save);
+ }
+ /*
+ ** Find the rule (of those to do, if any)
+ ** that takes effect earliest in the year.
+ */
+ k = -1;
+ for (j = 0; j < zp->z_nrules; ++j) {
+ struct rule *r = &zp->z_rules[j];
+ if (!r->r_todo)
+ continue;
+ 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);
+ jtime = r->r_temp;
+ if (jtime == min_time ||
+ jtime == max_time)
+ continue;
+ jtime = tadd(jtime, -offset);
+ if (k < 0 || jtime < ktime) {
+ k = j;
+ ktime = jtime;
+ } else if (jtime == ktime) {
+ char const *dup_rules_msg =
+ _("two rules for same instant");
+ 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_filenum, zp->z_linenum,
+ r->r_filenum, r->r_linenum);
+ error("%s", dup_rules_msg);
+ }
+ }
+ if (k < 0)
+ break; /* go on to next year */
+ rp = &zp->z_rules[k];
+ rp->r_todo = false;
+ if (useuntil && ktime >= untiltime) {
+ if (!*startbuf
+ && (oadd(zp->z_stdoff, rp->r_save)
+ == startoff))
+ doabbr(startbuf, zp, rp->r_abbrvar,
+ rp->r_isdst, rp->r_save,
+ false);
+ break;
+ }
+ save = rp->r_save;
+ if (usestart && ktime == starttime)
+ usestart = false;
+ if (usestart) {
+ if (ktime < starttime) {
+ startoff = oadd(zp->z_stdoff,
+ save);
+ doabbr(startbuf, zp,
+ rp->r_abbrvar,
+ rp->r_isdst,
+ rp->r_save,
+ false);
+ continue;
+ }
+ if (*startbuf == '\0'
+ && startoff == oadd(zp->z_stdoff,
+ save)) {
+ doabbr(startbuf,
+ zp,
+ rp->r_abbrvar,
+ rp->r_isdst,
+ rp->r_save,
+ false);
+ }
+ }
+ 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);
+ if (!want_bloat() && !useuntil && !do_extend
+ && prevrp && lo_time <= prevktime
+ && redundant_time <= ktime
+ && rp->r_hiyear == ZIC_MAX
+ && prevrp->r_hiyear == ZIC_MAX)
+ break;
+ type = addtype(offset, ab, rp->r_isdst,
+ rp->r_todisstd, rp->r_todisut);
+ if (defaulttype < 0 && !rp->r_isdst)
+ defaulttype = type;
+ if (rp->r_hiyear == ZIC_MAX
+ && ! (0 <= lastatmax
+ && ktime < attypes[lastatmax].at))
+ lastatmax = timecnt;
+ addtt(ktime, type);
+ prevrp = rp;
+ prevktime = ktime;
+ }
+ }
+ }
+ if (usestart) {
+ bool isdst = startoff != zp->z_stdoff;
+ if (*startbuf == '\0' && zp->z_format)
+ doabbr(startbuf, zp, disable_percent_s,
+ isdst, save, false);
+ eat(zp->z_filenum, zp->z_linenum);
+ if (*startbuf == '\0')
+error(_("can't determine time zone abbreviation to use just after until time"));
+ else {
+ int type = addtype(startoff, startbuf, isdst,
+ startttisstd, startttisut);
+ if (defaulttype < 0 && !isdst)
+ defaulttype = type;
+ addtt(starttime, type);
+ }
+ }
+ /*
+ ** Now we may get to set starttime for the next zone line.
+ */
+ if (useuntil) {
+ startttisstd = zp->z_untilrule.r_todisstd;
+ startttisut = zp->z_untilrule.r_todisut;
+ starttime = zp->z_untiltime;
+ if (!startttisstd)
+ starttime = tadd(starttime, -save);
+ if (!startttisut)
+ starttime = tadd(starttime, -stdoff);
+ }
+ }
+ if (defaulttype < 0)
+ defaulttype = 0;
+ if (0 <= lastatmax)
+ attypes[lastatmax].dontmerge = true;
+ if (do_extend) {
+ /*
+ ** If we're extending the explicitly listed observations
+ ** for 400 years because we can't fill the POSIX-TZ field,
+ ** check whether we actually ended up explicitly listing
+ ** observations through that period. If there aren't any
+ ** near the end of the 400-year period, add a redundant
+ ** one at the end of the final year, to make it clear
+ ** that we are claiming to have definite knowledge of
+ ** the lack of transitions up to that point.
+ */
+ struct rule xr;
+ struct attype *lastat;
+ xr.r_month = TM_JANUARY;
+ xr.r_dycode = DC_DOM;
+ xr.r_dayofmonth = 1;
+ xr.r_tod = 0;
+ for (lastat = attypes, i = 1; i < timecnt; i++)
+ if (attypes[i].at > lastat->at)
+ lastat = &attypes[i];
+ if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) {
+ addtt(rpytime(&xr, max_year + 1),
+ lastat ? lastat->type : defaulttype);
+ attypes[timecnt - 1].dontmerge = true;
+ }
+ }
+ writezone(zpfirst->z_name, envvar, version, defaulttype);
+ free(startbuf);
+ free(ab);
+ free(envvar);
+}
+
+static void
+addtt(zic_t starttime, int type)
+{
+ attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
+ attypes[timecnt].at = starttime;
+ attypes[timecnt].dontmerge = false;
+ attypes[timecnt].type = type;
+ ++timecnt;
+}
+
+static int
+addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
+{
+ register int i, j;
+
+ if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) {
+ error(_("UT offset out of range"));
+ exit(EXIT_FAILURE);
+ }
+ if (!want_bloat())
+ ttisstd = ttisut = false;
+
+ for (j = 0; j < charcnt; ++j)
+ if (strcmp(&chars[j], abbr) == 0)
+ break;
+ if (j == charcnt)
+ newabbr(abbr);
+ else {
+ /* If there's already an entry, return its index. */
+ for (i = 0; i < typecnt; i++)
+ if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
+ && ttisstd == ttisstds[i] && ttisut == ttisuts[i])
+ return i;
+ }
+ /*
+ ** There isn't one; add a new one, unless there are already too
+ ** many.
+ */
+ if (typecnt >= TZ_MAX_TYPES) {
+ error(_("too many local time types"));
+ exit(EXIT_FAILURE);
+ }
+ i = typecnt++;
+ utoffs[i] = utoff;
+ isdsts[i] = isdst;
+ ttisstds[i] = ttisstd;
+ ttisuts[i] = ttisut;
+ desigidx[i] = j;
+ return i;
+}
+
+static void
+leapadd(zic_t t, int correction, int rolling)
+{
+ register int i;
+
+ if (TZ_MAX_LEAPS <= leapcnt) {
+ error(_("too many leap seconds"));
+ exit(EXIT_FAILURE);
+ }
+ if (rolling && (lo_time != min_time || hi_time != max_time)) {
+ error(_("Rolling leap seconds not supported with -r"));
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < leapcnt; ++i)
+ if (t <= trans[i])
+ break;
+ memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
+ memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
+ memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
+ trans[i] = t;
+ corr[i] = correction;
+ roll[i] = rolling;
+ ++leapcnt;
+}
+
+static void
+adjleap(void)
+{
+ register int i;
+ register zic_t last = 0;
+ register zic_t prevtrans = 0;
+
+ /*
+ ** propagate leap seconds forward
+ */
+ for (i = 0; i < leapcnt; ++i) {
+ if (trans[i] - prevtrans < 28 * SECSPERDAY) {
+ error(_("Leap seconds too close together"));
+ exit(EXIT_FAILURE);
+ }
+ prevtrans = trans[i];
+ trans[i] = tadd(trans[i], last);
+ last = corr[i] += last;
+ }
+
+ if (0 <= leapexpires) {
+ leapexpires = oadd(leapexpires, last);
+ if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) {
+ error(_("last Leap time does not precede Expires time"));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+/* Is A a space character in the C locale? */
+static bool
+is_space(char a)
+{
+ switch (a) {
+ default:
+ return false;
+ case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
+ return true;
+ }
+}
+
+/* Is A an alphabetic character in the C locale? */
+static bool
+is_alpha(char a)
+{
+ switch (a) {
+ default:
+ return false;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ return true;
+ }
+}
+
+/* If A is an uppercase character in the C locale, return its lowercase
+ counterpart. Otherwise, return A. */
+static char
+lowerit(char a)
+{
+ switch (a) {
+ default: return a;
+ case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
+ case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
+ case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
+ case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
+ case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
+ case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
+ case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
+ case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
+ case 'Y': return 'y'; case 'Z': return 'z';
+ }
+}
+
+/* case-insensitive equality */
+ATTRIBUTE_REPRODUCIBLE static bool
+ciequal(register const char *ap, register const char *bp)
+{
+ while (lowerit(*ap) == lowerit(*bp++))
+ if (*ap++ == '\0')
+ return true;
+ return false;
+}
+
+ATTRIBUTE_REPRODUCIBLE static bool
+itsabbr(register const char *abbr, register const char *word)
+{
+ if (lowerit(*abbr) != lowerit(*word))
+ return false;
+ ++word;
+ while (*++abbr != '\0')
+ do {
+ if (*word == '\0')
+ return false;
+ } while (lowerit(*word++) != lowerit(*abbr));
+ return true;
+}
+
+/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
+
+ATTRIBUTE_REPRODUCIBLE static bool
+ciprefix(char const *abbr, char const *word)
+{
+ do
+ if (!*abbr)
+ return true;
+ while (lowerit(*abbr++) == lowerit(*word++));
+
+ return false;
+}
+
+static const struct lookup *
+byword(const char *word, const struct lookup *table)
+{
+ register const struct lookup * foundlp;
+ register const struct lookup * lp;
+
+ if (word == NULL || table == NULL)
+ return NULL;
+
+ /* If TABLE is LASTS and the word starts with "last" followed
+ by a non-'-', skip the "last" and look in WDAY_NAMES instead.
+ Warn about any usage of the undocumented prefix "last-". */
+ if (table == lasts && ciprefix("last", word) && word[4]) {
+ if (word[4] == '-')
+ warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
+ word, word + 5);
+ else {
+ word += 4;
+ table = wday_names;
+ }
+ }
+
+ /*
+ ** Look for exact match.
+ */
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciequal(word, lp->l_word))
+ return lp;
+ /*
+ ** Look for inexact match.
+ */
+ foundlp = NULL;
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciprefix(word, lp->l_word)) {
+ if (foundlp == NULL)
+ foundlp = lp;
+ else return NULL; /* multiple inexact matches */
+ }
+
+ if (foundlp && noise) {
+ /* Warn about any backward-compatibility issue with pre-2017c zic. */
+ bool pre_2017c_match = false;
+ for (lp = table; lp->l_word; lp++)
+ if (itsabbr(word, lp->l_word)) {
+ if (pre_2017c_match) {
+ warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
+ break;
+ }
+ pre_2017c_match = true;
+ }
+ }
+
+ return foundlp;
+}
+
+static int
+getfields(char *cp, char **array, int arrayelts)
+{
+ register char * dp;
+ register int nsubs;
+
+ nsubs = 0;
+ for ( ; ; ) {
+ char *dstart;
+ while (is_space(*cp))
+ ++cp;
+ if (*cp == '\0' || *cp == '#')
+ break;
+ dstart = dp = cp;
+ do {
+ if ((*dp = *cp++) != '"')
+ ++dp;
+ else while ((*dp = *cp++) != '"')
+ if (*dp != '\0')
+ ++dp;
+ else {
+ error(_("Odd number of quotation marks"));
+ exit(EXIT_FAILURE);
+ }
+ } while (*cp && *cp != '#' && !is_space(*cp));
+ if (is_space(*cp))
+ ++cp;
+ *dp = '\0';
+ if (nsubs == arrayelts) {
+ error(_("Too many input fields"));
+ exit(EXIT_FAILURE);
+ }
+ array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
+ }
+ return nsubs;
+}
+
+ATTRIBUTE_NORETURN static void
+time_overflow(void)
+{
+ error(_("time overflow"));
+ exit(EXIT_FAILURE);
+}
+
+ATTRIBUTE_REPRODUCIBLE static zic_t
+oadd(zic_t t1, zic_t 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();
+}
+
+ATTRIBUTE_REPRODUCIBLE static zic_t
+tadd(zic_t t1, zic_t t2)
+{
+#ifdef ckd_add
+ zic_t sum;
+ if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time)
+ 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();
+}
+
+/*
+** Given a rule, and a year, compute the date (in seconds since January 1,
+** 1970, 00:00 LOCAL time) in that year that the rule refers to.
+*/
+
+static zic_t
+rpytime(const struct rule *rp, zic_t wantedy)
+{
+ register int m, i;
+ register zic_t dayoff; /* with a nod to Margaret O. */
+ register zic_t t, y;
+ int yrem;
+
+ if (wantedy == ZIC_MIN)
+ return min_time;
+ if (wantedy == ZIC_MAX)
+ return max_time;
+ m = TM_JANUARY;
+ y = EPOCH_YEAR;
+
+ /* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
+ sans overflow. */
+ yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
+ dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
+ + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
+ * DAYSPERREPEAT);
+ /* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow. */
+ wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
+
+ while (wantedy != y) {
+ i = len_years[isleap(y)];
+ dayoff = oadd(dayoff, i);
+ y++;
+ }
+ while (m != rp->r_month) {
+ i = len_months[isleap(y)][m];
+ dayoff = oadd(dayoff, i);
+ ++m;
+ }
+ i = rp->r_dayofmonth;
+ if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
+ if (rp->r_dycode == DC_DOWLEQ)
+ --i;
+ else {
+ error(_("use of 2/29 in non leap-year"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ --i;
+ dayoff = oadd(dayoff, i);
+ if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
+ /*
+ ** Don't trust mod of negative numbers.
+ */
+ zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK)
+ % DAYSPERWEEK);
+ while (wday != rp->r_wday)
+ if (rp->r_dycode == DC_DOWGEQ) {
+ dayoff = oadd(dayoff, 1);
+ if (++wday >= DAYSPERWEEK)
+ wday = 0;
+ ++i;
+ } else {
+ dayoff = oadd(dayoff, -1);
+ if (--wday < 0)
+ wday = DAYSPERWEEK - 1;
+ --i;
+ }
+ if (i < 0 || i >= len_months[isleap(y)][m]) {
+ if (noise)
+ warning(_("rule goes past start/end of month; \
+will not work with pre-2004 versions of zic"));
+ }
+ }
+ if (dayoff < min_time / SECSPERDAY)
+ return min_time;
+ if (dayoff > max_time / SECSPERDAY)
+ return max_time;
+ t = (zic_t) dayoff * SECSPERDAY;
+ return tadd(t, rp->r_tod);
+}
+
+static void
+newabbr(const char *string)
+{
+ register int i;
+
+ if (strcmp(string, GRANDPARENTED) != 0) {
+ register const char * cp;
+ const char * mp;
+
+ cp = string;
+ mp = NULL;
+ while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
+ || *cp == '-' || *cp == '+')
+ ++cp;
+ if (noise && cp - string < 3)
+ mp = _("time zone abbreviation has fewer than 3 characters");
+ if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
+ mp = _("time zone abbreviation has too many characters");
+ if (*cp != '\0')
+mp = _("time zone abbreviation differs from POSIX standard");
+ if (mp != NULL)
+ warning("%s (%s)", mp, string);
+ }
+ i = strlen(string) + 1;
+ if (charcnt + i > TZ_MAX_CHARS) {
+ error(_("too many, or too long, time zone abbreviations"));
+ exit(EXIT_FAILURE);
+ }
+ strcpy(&chars[charcnt], string);
+ charcnt += i;
+}
+
+/* Ensure that the directories of ARGNAME exist, by making any missing
+ ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
+ do it for ARGNAME too. Exit with failure if there is trouble.
+ Do not consider an existing file to be trouble. */
+static void
+mkdirs(char const *argname, bool ancestors)
+{
+ 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
+ names do not use drive letters and backslashes. If the -d
+ option of zic does not name an already-existing directory,
+ it can use slashes to separate the already-existing
+ ancestor prefix from the to-be-created subdirectories. */
+
+ /* Do not mkdir a root directory, as it must exist. */
+ while (*cp == '/')
+ cp++;
+
+ while (cp && ((cp = strchr(cp, '/')) || !ancestors)) {
+ if (cp)
+ *cp = '\0';
+ /*
+ ** Try to create it. It's OK if creation fails because
+ ** the directory already exists, perhaps because some
+ ** other process just created it. For simplicity do
+ ** not check first whether it already exists, as that
+ ** is checked anyway if the mkdir fails.
+ */
+ if (mkdir(name, MKDIR_UMASK) != 0) {
+ /* Do not report an error if err == EEXIST, because
+ some other process might have made the directory
+ in the meantime. Likewise for ENOSYS, because
+ Solaris 10 mkdir fails with ENOSYS if the
+ directory is an automounted mount point.
+ Likewise for EACCES, since mkdir can fail
+ with EACCES merely because the parent directory
+ is unwritable. Likewise for most other error
+ numbers. */
+ int err = errno;
+ if (err == ELOOP || err == ENAMETOOLONG
+ || err == ENOENT || err == ENOTDIR) {
+ error(_("%s: Can't create directory %s: %s"),
+ progname, name, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (cp)
+ *cp++ = '/';
+ }
+ free(name);
+}
Index: tz/create-2023c-version-patch/create.patch.sh
===================================================================
--- tz/create-2023c-version-patch/create.patch.sh (nonexistent)
+++ tz/create-2023c-version-patch/create.patch.sh (revision 385)
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+VERSION=2023c
+
+mkdir -p timezone
+tar -C timezone --files-from=file.list -xzvf ../tzcode$VERSION.tar.gz
+mv timezone timezone-orig
+
+cp -rf ./timezone-new ./timezone
+
+diff --unified -Nr timezone-orig timezone > tzcode-$VERSION-version.patch
+
+mv tzcode-$VERSION-version.patch ../patches
+
+rm -rf ./timezone
+rm -rf ./timezone-orig
Property changes on: tz/create-2023c-version-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: tz/create-2023c-version-patch/file.list
===================================================================
--- tz/create-2023c-version-patch/file.list (nonexistent)
+++ tz/create-2023c-version-patch/file.list (revision 385)
@@ -0,0 +1 @@
+Makefile
Index: tz/create-2023c-version-patch/timezone-new/Makefile
===================================================================
--- tz/create-2023c-version-patch/timezone-new/Makefile (nonexistent)
+++ tz/create-2023c-version-patch/timezone-new/Makefile (revision 385)
@@ -0,0 +1,1249 @@
+# Make and install tzdb code and data.
+
+# This file is in the public domain, so clarified as of
+# 2009-05-17 by Arthur David Olson.
+
+# Package name for the code distribution.
+PACKAGE= tzcode
+
+# Version number for the distribution, overridden in the 'tarballs' rule below.
+VERSION= 2023c
+
+# Email address for bug reports.
+BUGEMAIL= tz@iana.org
+
+# DATAFORM selects the data format.
+# Available formats represent essentially the same data, albeit
+# possibly with minor discrepancies that users are not likely to notice.
+# To get new features and the best data right away, use:
+# DATAFORM= vanguard
+# To wait a while before using new features, to give downstream users
+# time to upgrade zic (the default), use:
+# DATAFORM= main
+# To wait even longer for new features, use:
+# DATAFORM= rearguard
+# Rearguard users might also want "ZFLAGS = -b fat"; see below.
+DATAFORM= main
+
+# Change the line below for your timezone (after finding the one you want in
+# one of the $(TDATA) source files, or adding it to a source file).
+# Alternatively, if you discover you've got the wrong timezone, you can just
+# 'zic -l -' to remove it, or 'zic -l rightzone' to change it.
+# Use the command
+# make zonenames
+# to get a list of the values you can use for LOCALTIME.
+
+LOCALTIME= Factory
+
+# The POSIXRULES macro controls interpretation of POSIX-like TZ
+# settings like TZ='EET-2EEST' that lack DST transition rules.
+# If POSIXRULES is '-', no template is installed; this is the default.
+# Any other value for POSIXRULES is obsolete and should not be relied on, as:
+# * It does not work correctly in popular implementations such as GNU/Linux.
+# * It does not work even in tzcode, except for historical timestamps
+# that precede the last explicit transition in the POSIXRULES file.
+# Hence it typically does not work for current and future timestamps.
+# If, despite the above, you want a template for handling these settings,
+# you can change the line below (after finding the timezone you want in the
+# one of the $(TDATA) source files, or adding it to a source file).
+# Alternatively, if you discover you've got the wrong timezone, you can just
+# 'zic -p -' to remove it, or 'zic -p rightzone' to change it.
+# Use the command
+# make zonenames
+# to get a list of the values you can use for POSIXRULES.
+
+POSIXRULES= -
+
+# Also see TZDEFRULESTRING below, which takes effect only
+# if POSIXRULES is '-' or if the template file cannot be accessed.
+
+
+# Installation locations.
+#
+# The defaults are suitable for Debian, except that if REDO is
+# posix_right or right_posix then files that Debian puts under
+# /usr/share/zoneinfo/posix and /usr/share/zoneinfo/right are instead
+# put under /usr/share/zoneinfo-posix and /usr/share/zoneinfo-leaps,
+# respectively. Problems with the Debian approach are discussed in
+# the commentary for the right_posix rule (below).
+
+# Destination directory, which can be used for staging.
+# 'make DESTDIR=/stage install' installs under /stage (e.g., to
+# /stage/etc/localtime instead of to /etc/localtime). Files under
+# /stage are not intended to work as-is, but can be copied by hand to
+# the root directory later. If DESTDIR is empty, 'make install' does
+# not stage, but installs directly into production locations.
+DESTDIR =
+
+# Everything is installed into subdirectories of TOPDIR, and used there.
+# TOPDIR should be empty (meaning the root directory),
+# or a directory name that does not end in "/".
+# TOPDIR should be empty or an absolute name unless you're just testing.
+TOPDIR =
+
+# The default local timezone is taken from the file TZDEFAULT.
+TZDEFAULT = $(TOPDIR)/etc/localtime
+
+# The subdirectory containing installed program and data files, and
+# likewise for installed files that can be shared among architectures.
+# These should be relative file names.
+USRDIR = usr
+USRSHAREDIR = $(USRDIR)/share
+
+# "Compiled" timezone information is placed in the "TZDIR" directory
+# (and subdirectories).
+# TZDIR_BASENAME should not contain "/" and should not be ".", ".." or empty.
+TZDIR_BASENAME= zoneinfo
+TZDIR = $(TOPDIR)/$(USRSHAREDIR)/$(TZDIR_BASENAME)
+
+# The "tzselect" and (if you do "make INSTALL") "date" commands go in:
+BINDIR = $(TOPDIR)/$(USRDIR)/bin
+
+# The "zdump" command goes in:
+ZDUMPDIR = $(BINDIR)
+
+# The "zic" command goes in:
+ZICDIR = $(TOPDIR)/$(USRDIR)/sbin
+
+# Manual pages go in subdirectories of. . .
+MANDIR = $(TOPDIR)/$(USRSHAREDIR)/man
+
+# Library functions are put in an archive in LIBDIR.
+LIBDIR = $(TOPDIR)/$(USRDIR)/lib
+
+
+# Types to try, as an alternative to time_t.
+TIME_T_ALTERNATIVES = $(TIME_T_ALTERNATIVES_HEAD) $(TIME_T_ALTERNATIVES_TAIL)
+TIME_T_ALTERNATIVES_HEAD = int_least64_t
+TIME_T_ALTERNATIVES_TAIL = int_least32_t uint_least32_t uint_least64_t
+
+# What kind of TZif data files to generate. (TZif is the binary time
+# zone data format that zic generates; see Internet RFC 8536.)
+# If you want only POSIX time, with time values interpreted as
+# seconds since the epoch (not counting leap seconds), use
+# REDO= posix_only
+# below. If you want only "right" time, with values interpreted
+# as seconds since the epoch (counting leap seconds), use
+# REDO= right_only
+# below. If you want both sets of data available, with leap seconds not
+# counted normally, use
+# REDO= posix_right
+# below. If you want both sets of data available, with leap seconds counted
+# normally, use
+# REDO= right_posix
+# below. POSIX mandates that leap seconds not be counted; for compatibility
+# with it, use "posix_only" or "posix_right". Use POSIX time on systems with
+# leap smearing; this can work better than unsmeared "right" time with
+# applications that are not leap second aware, and is closer to unsmeared
+# "right" time than unsmeared POSIX time is (e.g., 0.5 vs 1.0 s max error).
+
+REDO= posix_right
+
+# Whether to put an "Expires" line in the leapseconds file.
+# Use EXPIRES_LINE=1 to put the line in, 0 to omit it.
+# The EXPIRES_LINE value matters only if REDO's value contains "right".
+# If you change EXPIRES_LINE, remove the leapseconds file before running "make".
+# zic's support for the Expires line was introduced in tzdb 2020a,
+# and was modified in tzdb 2021b to generate version 4 TZif files.
+# EXPIRES_LINE defaults to 0 for now so that the leapseconds file
+# can be given to pre-2020a zic implementations and so that TZif files
+# built by newer zic implementations can be read by pre-2021b libraries.
+EXPIRES_LINE= 0
+
+# To install data in text form that has all the information of the TZif data,
+# (optionally incorporating leap second information), use
+# TZDATA_TEXT= tzdata.zi leapseconds
+# To install text data without leap second information (e.g., because
+# REDO='posix_only'), use
+# TZDATA_TEXT= tzdata.zi
+# To avoid installing text data, use
+# TZDATA_TEXT=
+
+TZDATA_TEXT= leapseconds tzdata.zi
+
+# For backward-compatibility links for old zone names, use
+# BACKWARD= backward
+# To omit these links, use
+# BACKWARD=
+
+BACKWARD= backward
+
+# If you want out-of-scope and often-wrong data from the file 'backzone',
+# but only for entries listed in the backward-compatibility file zone.tab, use
+# PACKRATDATA= backzone
+# PACKRATLIST= zone.tab
+# If you want all the 'backzone' data, use
+# PACKRATDATA= backzone
+# PACKRATLIST=
+# To omit this data, use
+# PACKRATDATA=
+# PACKRATLIST=
+
+PACKRATDATA=
+PACKRATLIST=
+
+# The name of a locale using the UTF-8 encoding, used during self-tests.
+# The tests are skipped if the name does not appear to work on this system.
+
+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
+# defaults specified in the source code. "-DFOO" is equivalent to "-DFOO=1".
+# -DDEPRECATE_TWO_DIGIT_YEARS for optional runtime warnings about strftime
+# formats that generate only the last two digits of year numbers
+# -DEPOCH_LOCAL if the 'time' function returns local time not UT
+# -DEPOCH_OFFSET=N if the 'time' function returns a value N greater
+# than what POSIX specifies, assuming local time is UT.
+# 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 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=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
+# localtime_rz can make zdump significantly faster, but is nonstandard.
+# -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_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 <sys/stat.h> does not work*
+# -DHAVE_TZSET=0 if your system lacks a tzset function
+# -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
+# -DPORT_TO_C89 if tzcode should also run on C89 platforms+
+# -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers
+# with external linkage, e.g., applications cannot define 'localtime'.
+# -Dssize_t=long on hosts like MS-Windows that lack ssize_t
+# -DSUPPORT_C89 if the tzcode library should support C89 callers+
+# -DSUPPRESS_TZDIR to not prepend TZDIR to file names; this has
+# security implications and is not recommended for general use
+# -DTHREAD_SAFE to make localtime.c thread-safe, as POSIX requires;
+# not needed by the main-program tz code, which is single-threaded.
+# Append other compiler flags as needed, e.g., -pthread on GNU/Linux.
+# -Dtime_tz=\"T\" to use T as the time_t type, rather than the system time_t
+# This is intended for internal use only; it mangles external names.
+# -DTZ_DOMAIN=\"foo\" to use "foo" for gettext domain name; default is "tz"
+# -DTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
+# the default is system-supplied, typically "/usr/lib/locale"
+# -DTZDEFRULESTRING=\",date/time,date/time\" to default to the specified
+# DST transitions for POSIX-style TZ strings lacking them,
+# in the usual case where POSIXRULES is '-'. If not specified,
+# TZDEFRULESTRING defaults to US rules for future DST transitions.
+# This mishandles some past timestamps, as US DST rules have changed.
+# It also mishandles settings like TZ='EET-2EEST' for eastern Europe,
+# as Europe and US DST rules differ.
+# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 255)
+# -DUNINIT_TRAP if reading uninitialized storage can cause problems
+# other than simply getting garbage data
+# -DUSE_LTZ=0 to build zdump with the system time zone library
+# 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 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.
+# * Options marked "+" are obsolescent and are planned to be removed
+# once the code assumes C99 or later.
+#
+# Select instrumentation via "make GCC_INSTRUMENT='whatever'".
+GCC_INSTRUMENT = \
+ -fsanitize=undefined -fsanitize-address-use-after-scope \
+ -fsanitize-undefined-trap-on-error -fstack-protector
+# Omit -fanalyzer from GCC_DEBUG_FLAGS, as it makes GCC too slow.
+GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
+ $(GCC_INSTRUMENT) \
+ -Wall -Wextra \
+ -Walloc-size-larger-than=100000 -Warray-bounds=2 \
+ -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wdate-time \
+ -Wdeclaration-after-statement -Wdouble-promotion \
+ -Wduplicated-branches -Wduplicated-cond \
+ -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
+ -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wnull-dereference \
+ -Wold-style-definition -Woverlength-strings -Wpointer-arith \
+ -Wshadow -Wshift-overflow=2 -Wstrict-overflow \
+ -Wstrict-prototypes -Wstringop-overflow=4 \
+ -Wstringop-truncation -Wsuggest-attribute=cold \
+ -Wsuggest-attribute=const -Wsuggest-attribute=format \
+ -Wsuggest-attribute=malloc \
+ -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure \
+ -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
+#
+# 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),
+# add the name to a define such as
+# -DTM_GMTOFF=tm_gmtoff
+# to the end of the "CFLAGS=" line. If not defined, the code attempts to
+# guess TM_GMTOFF from other macros; define NO_TM_GMTOFF to suppress this.
+# Similarly, if your system has a "zone abbreviation" field, define
+# -DTM_ZONE=tm_zone
+# and define NO_TM_ZONE to suppress any guessing. Although these two fields
+# not required by POSIX, a future version of POSIX is planned to require them
+# and they are widely available on GNU/Linux and BSD systems.
+#
+# The next batch of options control support for external variables
+# exported by tzcode. In practice these variables are less useful
+# than TM_GMTOFF and TM_ZONE. However, most of them are standardized.
+# #
+# # To omit or support the external variable "tzname", add one of:
+# # -DHAVE_TZNAME=0 # do not support "tzname"
+# # -DHAVE_TZNAME=1 # support "tzname", which is defined by system library
+# # -DHAVE_TZNAME=2 # support and define "tzname"
+# # to the "CFLAGS=" line. "tzname" is required by POSIX 1988 and later.
+# # If not defined, the code attempts to guess HAVE_TZNAME from other macros.
+# # Warning: unless time_tz is also defined, HAVE_TZNAME=1 can cause
+# # crashes when combined with some platforms' standard libraries,
+# # presumably due to memory allocation issues.
+# #
+# # To omit or support the external variables "timezone" and "daylight", add
+# # -DUSG_COMPAT=0 # do not support
+# # -DUSG_COMPAT=1 # support, and variables are defined by system library
+# # -DUSG_COMPAT=2 # support and define variables
+# # to the "CFLAGS=" line; "timezone" and "daylight" are inspired by
+# # Unix Systems Group code and are required by POSIX 2008 (with XSI) and later.
+# # If not defined, the code attempts to guess USG_COMPAT from other macros.
+# #
+# # To support the external variable "altzone", add
+# # -DALTZONE=0 # do not support
+# # -DALTZONE=1 # support "altzone", which is defined by system library
+# # -DALTZONE=2 # support and define "altzone"
+# # to the end of the "CFLAGS=" line; although "altzone" appeared in
+# # System V Release 3.1 it has not been standardized.
+# # If not defined, the code attempts to guess ALTZONE from other macros.
+#
+# 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 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".
+# "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.
+# X3J11's work does not describe any of these functions.
+# These functions may well disappear in future releases of the time
+# conversion package.
+#
+# If you don't want functions that were inspired by NetBSD, add
+# -DNETBSD_INSPIRED=0
+# to the end of the "CFLAGS=" line. Otherwise, the functions
+# "localtime_rz", "mktime_z", "tzalloc", and "tzfree" are added to the
+# time library, and if STD_INSPIRED is also defined to nonzero the functions
+# "posix2time_z" and "time2posix_z" are added as well.
+# The functions ending in "_z" (or "_rz") are like their unsuffixed
+# (or suffixed-by-"_r") counterparts, except with an extra first
+# argument of opaque type timezone_t that specifies the timezone.
+# "tzalloc" allocates a timezone_t value, and "tzfree" frees it.
+#
+# If you want to allocate state structures in localtime, add
+# -DALL_STATE
+# to the end of the "CFLAGS=" line. Storage is obtained by calling malloc.
+#
+# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
+# out by the National Institute of Standards and Technology
+# which claims to test C and Posix conformance. If you want to pass PCTS, add
+# -DPCTS
+# to the end of the "CFLAGS=" line.
+#
+# If you want strict compliance with XPG4 as of 1994-04-09, add
+# -DXPG4_1994_04_09
+# to the end of the "CFLAGS=" line. This causes "strftime" to always return
+# 53 as a week number (rather than 52 or 53) for January days before
+# January's first Monday when a "%V" format is used and January 1
+# falls on a Friday, Saturday, or Sunday.
+
+CFLAGS=
+
+# Linker flags. Default to $(LFLAGS) for backwards compatibility
+# to release 2012h and earlier.
+
+LDFLAGS= $(LFLAGS)
+
+# For leap seconds, this Makefile uses LEAPSECONDS='-L leapseconds' in
+# submake command lines. The default is no leap seconds.
+
+LEAPSECONDS=
+
+# The zic command and its arguments.
+
+zic= ./zic
+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 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=
+
+# How to use zic to install TZif files.
+
+ZIC_INSTALL= $(ZIC) -d '$(DESTDIR)$(TZDIR)' $(LEAPSECONDS)
+
+# The name of a Posix-compliant 'awk' on your system.
+# mawk 1.3.3 and Solaris 10 /usr/bin/awk do not work.
+# Also, it is better (though not essential) if 'awk' supports UTF-8,
+# and unfortunately mawk and busybox awk do not support UTF-8.
+# Try AWK=gawk or AWK=nawk if your awk has the abovementioned problems.
+AWK= awk
+
+# The full path name of a Posix-compliant shell, preferably one that supports
+# the Korn shell's 'select' statement as an extension.
+# These days, Bash is the most popular.
+# It should be OK to set this to /bin/sh, on platforms where /bin/sh
+# lacks 'select' or doesn't completely conform to Posix, but /bin/bash
+# is typically nicer if it works.
+KSHELL= /bin/bash
+
+# Name of curl <https://curl.haxx.se/>, used for HTML validation.
+CURL= curl
+
+# Name of GNU Privacy Guard <https://gnupg.org/>, used to sign distributions.
+GPG= gpg
+
+# This expensive test requires USE_LTZ.
+# To suppress it, define this macro to be empty.
+CHECK_TIME_T_ALTERNATIVES = check_time_t_alternatives
+
+# SAFE_CHAR is a regular expression that matches a safe character.
+# Some parts of this distribution are limited to safe characters;
+# others can use any UTF-8 character.
+# For now, the safe characters are a safe subset of ASCII.
+# The caller must set the shell variable 'sharp' to the character '#',
+# since Makefile macros cannot contain '#'.
+# TAB_CHAR is a single tab character, in single quotes.
+TAB_CHAR= ' '
+SAFE_CHARSET1= $(TAB_CHAR)' !\"'$$sharp'$$%&'\''()*+,./0123456789:;<=>?@'
+SAFE_CHARSET2= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\^_`'
+SAFE_CHARSET3= 'abcdefghijklmnopqrstuvwxyz{|}~'
+SAFE_CHARSET= $(SAFE_CHARSET1)$(SAFE_CHARSET2)$(SAFE_CHARSET3)
+SAFE_CHAR= '[]'$(SAFE_CHARSET)'-]'
+
+# These non-alphabetic, non-ASCII printable characters are Latin-1,
+# and so are likely displayable even in editors like XEmacs 21
+# that have limited display capabilities.
+UNUSUAL_OK_LATIN_1 = ¡¢£¤¥¦§¨©«¬®¯°±²³´¶·¸¹»¼½¾¿×÷
+# Non-ASCII non-letters that OK_CHAR allows, as these characters are
+# useful in commentary.
+UNUSUAL_OK_CHARSET= $(UNUSUAL_OK_LATIN_1)
+
+# Put this in a bracket expression to match spaces.
+s = [:space:]
+
+# OK_CHAR matches any character allowed in the distributed files.
+# This is the same as SAFE_CHAR, except that UNUSUAL_OK_CHARSET and
+# multibyte letters are also allowed so that commentary can contain a
+# few safe symbols and people's names and can quote non-English sources.
+# Other non-letters are limited to ASCII renderings for the
+# convenience of maintainers using XEmacs 21.5.34, which by default
+# mishandles Unicode characters U+0100 and greater.
+OK_CHAR= '[][:alpha:]$(UNUSUAL_OK_CHARSET)'$(SAFE_CHARSET)'-]'
+
+# SAFE_LINE matches a line of safe characters.
+# SAFE_SHARP_LINE is similar, except any OK character can follow '#';
+# this is so that comments can contain non-ASCII characters.
+# OK_LINE matches a line of OK characters.
+SAFE_LINE= '^'$(SAFE_CHAR)'*$$'
+SAFE_SHARP_LINE='^'$(SAFE_CHAR)'*('$$sharp$(OK_CHAR)'*)?$$'
+OK_LINE= '^'$(OK_CHAR)'*$$'
+
+# Flags to give 'tar' when making a distribution.
+# Try to use flags appropriate for GNU tar.
+GNUTARFLAGS= --format=pax --pax-option='delete=atime,delete=ctime' \
+ --numeric-owner --owner=0 --group=0 \
+ --mode=go+u,go-w --sort=name
+TARFLAGS= `if tar $(GNUTARFLAGS) --version >/dev/null 2>&1; \
+ then echo $(GNUTARFLAGS); \
+ else :; \
+ fi`
+
+# 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
+
+cc= cc
+CC= $(cc) -DTZDIR='"$(TZDIR)"'
+
+AR= ar
+
+# ':' on typical hosts; 'ranlib' on the ancient hosts that still need ranlib.
+RANLIB= :
+
+TZCOBJS= zic.o
+TZDOBJS= zdump.o localtime.o asctime.o strftime.o
+DATEOBJS= date.o localtime.o strftime.o asctime.o
+LIBSRCS= localtime.c asctime.c difftime.c strftime.c
+LIBOBJS= localtime.o asctime.o difftime.o strftime.o
+HEADERS= tzfile.h private.h
+NONLIBSRCS= zic.c zdump.c
+NEWUCBSRCS= date.c
+SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) \
+ tzselect.ksh workman.sh
+MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
+ tzfile.5 tzselect.8 zic.8 zdump.8
+MANTXTS= newctime.3.txt newstrftime.3.txt newtzset.3.txt \
+ time2posix.3.txt \
+ tzfile.5.txt tzselect.8.txt zic.8.txt zdump.8.txt \
+ date.1.txt
+COMMON= calendars CONTRIBUTING LICENSE Makefile \
+ NEWS README SECURITY theory.html version
+WEB_PAGES= tz-art.html tz-how-to.html tz-link.html
+CHECK_WEB_PAGES=check_theory.html check_tz-art.html \
+ check_tz-how-to.html check_tz-link.html
+DOCS= $(MANS) date.1 $(MANTXTS) $(WEB_PAGES)
+PRIMARY_YDATA= africa antarctica asia australasia \
+ europe northamerica southamerica
+YDATA= $(PRIMARY_YDATA) etcetera
+NDATA= factory
+TDATA_TO_CHECK= $(YDATA) $(NDATA) backward
+TDATA= $(YDATA) $(NDATA) $(BACKWARD)
+ZONETABLES= zone1970.tab zone.tab
+TABDATA= iso3166.tab $(TZDATA_TEXT) $(ZONETABLES)
+LEAP_DEPS= leapseconds.awk leap-seconds.list
+TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \
+ $(PACKRATDATA) $(PACKRATLIST)
+DSTDATA_ZI_DEPS= ziguard.awk $(TDATA) $(PACKRATDATA) $(PACKRATLIST)
+DATA= $(TDATA_TO_CHECK) backzone iso3166.tab leap-seconds.list \
+ leapseconds $(ZONETABLES)
+AWK_SCRIPTS= checklinks.awk checktab.awk leapseconds.awk \
+ ziguard.awk zishrink.awk
+MISC= $(AWK_SCRIPTS)
+TZS_YEAR= 2050
+TZS_CUTOFF_FLAG= -c $(TZS_YEAR)
+TZS= to$(TZS_YEAR).tzs
+TZS_NEW= to$(TZS_YEAR)new.tzs
+TZS_DEPS= $(YDATA) asctime.c localtime.c \
+ private.h tzfile.h zdump.c zic.c
+TZDATA_DIST = $(COMMON) $(DATA) $(MISC)
+# EIGHT_YARDS is just a yard short of the whole ENCHILADA.
+EIGHT_YARDS = $(TZDATA_DIST) $(DOCS) $(SOURCES) tzdata.zi
+ENCHILADA = $(EIGHT_YARDS) $(TZS)
+
+# Consult these files when deciding whether to rebuild the 'version' file.
+# This list is not the same as the output of 'git ls-files', since
+# .gitignore is not distributed.
+VERSION_DEPS= \
+ calendars CONTRIBUTING LICENSE Makefile NEWS README SECURITY \
+ africa antarctica asctime.c asia australasia \
+ backward backzone \
+ checklinks.awk checktab.awk \
+ date.1 date.c difftime.c \
+ etcetera europe factory iso3166.tab \
+ leap-seconds.list leapseconds.awk localtime.c \
+ newctime.3 newstrftime.3 newtzset.3 northamerica \
+ private.h southamerica strftime.c theory.html \
+ time2posix.3 tz-art.html tz-how-to.html tz-link.html \
+ tzfile.5 tzfile.h tzselect.8 tzselect.ksh \
+ workman.sh zdump.8 zdump.c zic.8 zic.c \
+ ziguard.awk zishrink.awk \
+ zone.tab zone1970.tab
+
+# And for the benefit of csh users on systems that assume the user
+# shell should be used to handle commands in Makefiles. . .
+
+SHELL= /bin/sh
+
+all: tzselect zic zdump libtz.a $(TABDATA) \
+ vanguard.zi main.zi rearguard.zi
+
+ALL: all date $(ENCHILADA)
+
+install: all $(DATA) $(REDO) $(MANS)
+ mkdir -p '$(DESTDIR)$(BINDIR)' \
+ '$(DESTDIR)$(ZDUMPDIR)' '$(DESTDIR)$(ZICDIR)' \
+ '$(DESTDIR)$(LIBDIR)' \
+ '$(DESTDIR)$(MANDIR)/man3' '$(DESTDIR)$(MANDIR)/man5' \
+ '$(DESTDIR)$(MANDIR)/man8'
+ $(ZIC_INSTALL) -l $(LOCALTIME) \
+ `case '$(POSIXRULES)' in ?*) echo '-p';; esac \
+ ` $(POSIXRULES) \
+ -t '$(DESTDIR)$(TZDEFAULT)'
+ cp -f $(TABDATA) '$(DESTDIR)$(TZDIR)/.'
+ cp tzselect '$(DESTDIR)$(BINDIR)/.'
+ cp zdump '$(DESTDIR)$(ZDUMPDIR)/.'
+ cp zic '$(DESTDIR)$(ZICDIR)/.'
+ cp libtz.a '$(DESTDIR)$(LIBDIR)/.'
+ $(RANLIB) '$(DESTDIR)$(LIBDIR)/libtz.a'
+ cp -f newctime.3 newtzset.3 '$(DESTDIR)$(MANDIR)/man3/.'
+ cp -f tzfile.5 '$(DESTDIR)$(MANDIR)/man5/.'
+ cp -f tzselect.8 zdump.8 zic.8 '$(DESTDIR)$(MANDIR)/man8/.'
+
+INSTALL: ALL install date.1
+ mkdir -p '$(DESTDIR)$(BINDIR)' '$(DESTDIR)$(MANDIR)/man1'
+ cp date '$(DESTDIR)$(BINDIR)/.'
+ cp -f date.1 '$(DESTDIR)$(MANDIR)/man1/.'
+
+# Calculate version number from git, if available.
+# Otherwise, use $(VERSION) unless it is "unknown" and there is already
+# a 'version' file, in which case reuse the existing 'version' contents
+# and append "-dirty" if the contents do not already end in "-dirty".
+version: $(VERSION_DEPS)
+ { (type git) >/dev/null 2>&1 && \
+ V=`git describe --match '[0-9][0-9][0-9][0-9][a-z]*' \
+ --abbrev=7 --dirty` || \
+ if test '$(VERSION)' = unknown && V=`cat $@`; then \
+ case $$V in *-dirty);; *) V=$$V-dirty;; esac; \
+ else \
+ V='$(VERSION)'; \
+ fi; } && \
+ printf '%s\n' "$$V" >$@.out
+ mv $@.out $@
+
+# These files can be tailored by setting BACKWARD, PACKRATDATA, PACKRATLIST.
+vanguard.zi main.zi rearguard.zi: $(DSTDATA_ZI_DEPS)
+ $(AWK) \
+ -v DATAFORM=`expr $@ : '\(.*\).zi'` \
+ -v PACKRATDATA='$(PACKRATDATA)' \
+ -v PACKRATLIST='$(PACKRATLIST)' \
+ -f ziguard.awk \
+ $(TDATA) $(PACKRATDATA) >$@.out
+ mv $@.out $@
+# This file has a version comment that attempts to capture any tailoring
+# via BACKWARD, DATAFORM, PACKRATDATA, PACKRATLIST, and REDO.
+tzdata.zi: $(DATAFORM).zi version zishrink.awk
+ version=`sed 1q version` && \
+ LC_ALL=C $(AWK) \
+ -v dataform='$(DATAFORM)' \
+ -v deps='$(DSTDATA_ZI_DEPS) zishrink.awk' \
+ -v redo='$(REDO)' \
+ -v version="$$version" \
+ -f zishrink.awk \
+ $(DATAFORM).zi >$@.out
+ mv $@.out $@
+
+version.h: version
+ VERSION=`cat version` && printf '%s\n' \
+ 'static char const PKGVERSION[]="($(PACKAGE)) ";' \
+ "static char const TZVERSION[]=\"$$VERSION\";" \
+ 'static char const REPORT_BUGS_TO[]="$(BUGEMAIL)";' \
+ >$@.out
+ mv $@.out $@
+
+zdump: $(TZDOBJS)
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZDOBJS) $(LDLIBS)
+
+zic: $(TZCOBJS)
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZCOBJS) $(LDLIBS)
+
+leapseconds: $(LEAP_DEPS)
+ $(AWK) -v EXPIRES_LINE=$(EXPIRES_LINE) \
+ -f leapseconds.awk leap-seconds.list >$@.out
+ mv $@.out $@
+
+# Arguments to pass to submakes of install_data.
+# They can be overridden by later submake arguments.
+INSTALLARGS = \
+ BACKWARD='$(BACKWARD)' \
+ DESTDIR='$(DESTDIR)' \
+ LEAPSECONDS='$(LEAPSECONDS)' \
+ PACKRATDATA='$(PACKRATDATA)' \
+ PACKRATLIST='$(PACKRATLIST)' \
+ TZDEFAULT='$(TZDEFAULT)' \
+ TZDIR='$(TZDIR)' \
+ ZIC='$(ZIC)'
+
+INSTALL_DATA_DEPS = zic leapseconds tzdata.zi
+
+# 'make install_data' installs one set of TZif files.
+install_data: $(INSTALL_DATA_DEPS)
+ $(ZIC_INSTALL) tzdata.zi
+
+posix_only: $(INSTALL_DATA_DEPS)
+ $(MAKE) $(INSTALLARGS) LEAPSECONDS= install_data
+
+right_only: $(INSTALL_DATA_DEPS)
+ $(MAKE) $(INSTALLARGS) LEAPSECONDS='-L leapseconds' \
+ install_data
+
+# In earlier versions of this makefile, the other two directories were
+# subdirectories of $(TZDIR). However, this led to configuration errors.
+# For example, with posix_right under the earlier scheme,
+# TZ='right/Australia/Adelaide' got you localtime with leap seconds,
+# but gmtime without leap seconds, which led to problems with applications
+# like sendmail that subtract gmtime from localtime.
+# Therefore, the other two directories are now siblings of $(TZDIR).
+# You must replace all of $(TZDIR) to switch from not using leap seconds
+# to using them, or vice versa.
+right_posix: right_only
+ rm -fr '$(DESTDIR)$(TZDIR)-leaps'
+ ln -s '$(TZDIR_BASENAME)' '$(DESTDIR)$(TZDIR)-leaps' || \
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-leaps' right_only
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-posix' posix_only
+
+posix_right: posix_only
+ rm -fr '$(DESTDIR)$(TZDIR)-posix'
+ ln -s '$(TZDIR_BASENAME)' '$(DESTDIR)$(TZDIR)-posix' || \
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-posix' posix_only
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-leaps' right_only
+
+zones: $(REDO)
+
+# dummy.zd is not a real file; it is mentioned here only so that the
+# top-level 'make' does not have a syntax error.
+ZDS = dummy.zd
+# Rule used only by submakes invoked by the $(TZS_NEW) rule.
+# It is separate so that GNU 'make -j' can run instances in parallel.
+$(ZDS): zdump
+ ./zdump -i $(TZS_CUTOFF_FLAG) '$(wd)/'$$(expr $@ : '\(.*\).zd') \
+ >$@
+
+TZS_NEW_DEPS = tzdata.zi zdump zic
+$(TZS_NEW): $(TZS_NEW_DEPS)
+ rm -fr tzs$(TZS_YEAR).dir
+ mkdir tzs$(TZS_YEAR).dir
+ $(zic) -d tzs$(TZS_YEAR).dir tzdata.zi
+ $(AWK) '/^L/{print "Link\t" $$2 "\t" $$3}' \
+ tzdata.zi | LC_ALL=C sort >$@.out
+ wd=`pwd` && \
+ x=`$(AWK) '/^Z/{print "tzs$(TZS_YEAR).dir/" $$2 ".zd"}' \
+ tzdata.zi \
+ | LC_ALL=C sort -t . -k 2,2` && \
+ set x $$x && \
+ shift && \
+ ZDS=$$* && \
+ $(MAKE) wd="$$wd" TZS_CUTOFF_FLAG="$(TZS_CUTOFF_FLAG)" \
+ ZDS="$$ZDS" $$ZDS && \
+ sed 's,^TZ=".*\.dir/,TZ=",' $$ZDS >>$@.out
+ rm -fr tzs$(TZS_YEAR).dir
+ mv $@.out $@
+
+# If $(TZS) exists but 'make check_tzs' fails, a maintainer should inspect the
+# failed output and fix the inconsistency, perhaps by running 'make force_tzs'.
+$(TZS):
+ touch $@
+
+force_tzs: $(TZS_NEW)
+ cp $(TZS_NEW) $(TZS)
+
+libtz.a: $(LIBOBJS)
+ rm -f $@
+ $(AR) -rc $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+date: $(DATEOBJS)
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(DATEOBJS) $(LDLIBS)
+
+tzselect: tzselect.ksh version
+ VERSION=`cat version` && sed \
+ -e 's|#!/bin/bash|#!$(KSHELL)|g' \
+ -e 's|AWK=[^}]*|AWK='\''$(AWK)'\''|g' \
+ -e 's|\(PKGVERSION\)=.*|\1='\''($(PACKAGE)) '\''|' \
+ -e 's|\(REPORT_BUGS_TO\)=.*|\1=$(BUGEMAIL)|' \
+ -e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \
+ -e 's|\(TZVERSION\)=.*|\1='"$$VERSION"'|' \
+ <$@.ksh >$@.out
+ chmod +x $@.out
+ mv $@.out $@
+
+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
+
+check_character_set: $(ENCHILADA)
+ test ! '$(UTF8_LOCALE)' || \
+ ! printf 'A\304\200B\n' | \
+ LC_ALL='$(UTF8_LOCALE)' grep -q '^A.B$$' >/dev/null 2>&1 || { \
+ LC_ALL='$(UTF8_LOCALE)' && export LC_ALL && \
+ sharp='#' && \
+ ! grep -Env $(SAFE_LINE) $(MANS) date.1 $(MANTXTS) \
+ $(MISC) $(SOURCES) $(WEB_PAGES) \
+ CONTRIBUTING LICENSE README SECURITY \
+ version tzdata.zi && \
+ ! grep -Env $(SAFE_LINE)'|^UNUSUAL_OK_'$(OK_CHAR)'*$$' \
+ Makefile && \
+ ! grep -Env $(SAFE_SHARP_LINE) $(TDATA_TO_CHECK) backzone \
+ leapseconds zone.tab && \
+ ! grep -Env $(OK_LINE) $(ENCHILADA); \
+ }
+ touch $@
+
+check_white_space: $(ENCHILADA)
+ patfmt=' \t|[\f\r\v]' && pat=`printf "$$patfmt\\n"` && \
+ ! grep -En "$$pat" \
+ $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list)
+ ! grep -n '[$s]$$' \
+ $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list)
+ touch $@
+
+PRECEDES_FILE_NAME = ^(Zone|Link[$s]+[^$s]+)[$s]+
+FILE_NAME_COMPONENT_TOO_LONG = $(PRECEDES_FILE_NAME)[^$s]*[^/$s]{15}
+
+check_name_lengths: $(TDATA_TO_CHECK) backzone
+ ! grep -En '$(FILE_NAME_COMPONENT_TOO_LONG)' \
+ $(TDATA_TO_CHECK) backzone
+ touch $@
+
+PRECEDES_STDOFF = ^(Zone[$s]+[^$s]+)?[$s]+
+STDOFF = [-+]?[0-9:.]+
+RULELESS_SAVE = (-|$(STDOFF)[sd]?)
+RULELESS_SLASHED_ABBRS = \
+ $(PRECEDES_STDOFF)$(STDOFF)[$s]+$(RULELESS_SAVE)[$s]+[^$s]*/
+
+check_slashed_abbrs: $(TDATA_TO_CHECK)
+ ! grep -En '$(RULELESS_SLASHED_ABBRS)' $(TDATA_TO_CHECK)
+ touch $@
+
+CHECK_CC_LIST = { n = split($$1,a,/,/); for (i=2; i<=n; i++) print a[1], a[i]; }
+
+check_sorted: backward backzone
+ $(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_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
+ touch $@
+
+check_tables: checktab.awk $(YDATA) backward $(ZONETABLES)
+ for tab in $(ZONETABLES); do \
+ test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \
+ $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \
+ || exit; \
+ done
+ touch $@
+
+check_tzs: $(TZS) $(TZS_NEW)
+ if test -s $(TZS); then \
+ $(DIFF_TZS) $(TZS) $(TZS_NEW); \
+ else \
+ cp $(TZS_NEW) $(TZS); \
+ fi
+ touch $@
+
+check_web: $(CHECK_WEB_PAGES)
+check_theory.html: theory.html
+check_tz-art.html: tz-art.html
+check_tz-how-to.html: tz-how-to.html
+check_tz-link.html: tz-link.html
+check_theory.html check_tz-art.html check_tz-how-to.html check_tz-link.html:
+ $(CURL) -sS --url https://validator.w3.org/nu/ -F out=gnu \
+ -F file=@$$(expr $@ : 'check_\(.*\)') -o $@.out && \
+ test ! -s $@.out || { cat $@.out; exit 1; }
+ mv $@.out $@
+
+check_ziguard: rearguard.zi vanguard.zi ziguard.awk
+ $(AWK) -v DATAFORM=rearguard -f ziguard.awk vanguard.zi | \
+ diff -u rearguard.zi -
+ $(AWK) -v DATAFORM=vanguard -f ziguard.awk rearguard.zi | \
+ diff -u vanguard.zi -
+ touch $@
+
+# Check that zishrink.awk does not alter the data, and that ziguard.awk
+# preserves main-format data.
+check_zishrink: check_zishrink_posix check_zishrink_right
+check_zishrink_posix check_zishrink_right: \
+ zic leapseconds $(PACKRATDATA) $(PACKRATLIST) \
+ $(TDATA) $(DATAFORM).zi tzdata.zi
+ rm -fr $@.dir $@-t.dir $@-shrunk.dir
+ mkdir $@.dir $@-t.dir $@-shrunk.dir
+ case $@ in \
+ *_right) leap='-L leapseconds';; \
+ *) leap=;; \
+ esac && \
+ $(ZIC) $$leap -d $@.dir $(DATAFORM).zi && \
+ $(ZIC) $$leap -d $@-shrunk.dir tzdata.zi && \
+ case $(DATAFORM),$(PACKRATLIST) in \
+ main,) \
+ $(ZIC) $$leap -d $@-t.dir $(TDATA) && \
+ $(AWK) '/^Rule/' $(TDATA) | \
+ $(ZIC) $$leap -d $@-t.dir - $(PACKRATDATA) && \
+ diff -r $@.dir $@-t.dir;; \
+ esac
+ diff -r $@.dir $@-shrunk.dir
+ rm -fr $@.dir $@-t.dir $@-shrunk.dir
+ touch $@
+
+clean_misc:
+ rm -fr check_*.dir
+ rm -f *.o *.out $(TIME_T_ALTERNATIVES) \
+ check_* core typecheck_* \
+ date tzselect version.h zdump zic libtz.a
+clean: clean_misc
+ rm -fr *.dir tzdb-*/
+ rm -f *.zi $(TZS_NEW)
+
+maintainer-clean: clean
+ @echo 'This command is intended for maintainers to use; it'
+ @echo 'deletes files that may need special tools to rebuild.'
+ rm -f leapseconds version $(MANTXTS) $(TZS) *.asc *.tar.*
+
+names:
+ @echo $(ENCHILADA)
+
+public: check check_public $(CHECK_TIME_T_ALTERNATIVES) \
+ tarballs signatures
+
+date.1.txt: date.1
+newctime.3.txt: newctime.3
+newstrftime.3.txt: newstrftime.3
+newtzset.3.txt: newtzset.3
+time2posix.3.txt: time2posix.3
+tzfile.5.txt: tzfile.5
+tzselect.8.txt: tzselect.8
+zdump.8.txt: zdump.8
+zic.8.txt: zic.8
+
+$(MANTXTS): workman.sh
+ LC_ALL=C sh workman.sh `expr $@ : '\(.*\)\.txt$$'` >$@.out
+ mv $@.out $@
+
+# Set file timestamps deterministically if possible,
+# so that tarballs containing the timestamps are reproducible.
+#
+# '$(SET_TIMESTAMP_N) N DEST A B C ...' sets the timestamp of the
+# file DEST to the maximum of the timestamps of the files A B C ...,
+# plus N if GNU ls and touch are available.
+SET_TIMESTAMP_N = sh -c '\
+ n=$$0 dest=$$1; shift; \
+ touch -cmr `ls -t "$$@" | sed 1q` "$$dest" && \
+ if test $$n != 0 && \
+ lsout=`ls -n --time-style="+%s" "$$dest" 2>/dev/null`; then \
+ set x $$lsout && \
+ touch -cmd @`expr $$7 + $$n` "$$dest"; \
+ else :; fi'
+# If DEST depends on A B C ... in this Makefile, callers should use
+# $(SET_TIMESTAMP_DEP) DEST A B C ..., for the benefit of any
+# downstream 'make' that considers equal timestamps to be out of date.
+# POSIX allows this 'make' behavior, and HP-UX 'make' does it.
+# If all that matters is that the timestamp be reproducible
+# and plausible, use $(SET_TIMESTAMP).
+SET_TIMESTAMP = $(SET_TIMESTAMP_N) 0
+SET_TIMESTAMP_DEP = $(SET_TIMESTAMP_N) 1
+
+# Set the timestamps to those of the git repository, if available,
+# and if the files have not changed since then.
+# This uses GNU 'ls --time-style=+%s', which outputs the seconds count,
+# and GNU 'touch -d@N FILE', where N is the number of seconds since 1970.
+# If git or GNU is absent, don't bother to sync with git timestamps.
+# Also, set the timestamp of each prebuilt file like 'leapseconds'
+# to be the maximum of the files it depends on.
+set-timestamps.out: $(EIGHT_YARDS)
+ rm -f $@
+ if (type git) >/dev/null 2>&1 && \
+ files=`git ls-files $(EIGHT_YARDS)` && \
+ touch -md @1 test.out; then \
+ rm -f test.out && \
+ for file in $$files; do \
+ if git diff --quiet $$file; then \
+ time=`git log -1 --format='tformat:%ct' $$file` && \
+ touch -cmd @$$time $$file; \
+ else \
+ echo >&2 "$$file: warning: does not match repository"; \
+ fi || exit; \
+ done; \
+ fi
+ $(SET_TIMESTAMP_DEP) leapseconds $(LEAP_DEPS)
+ for file in `ls $(MANTXTS) | sed 's/\.txt$$//'`; do \
+ $(SET_TIMESTAMP_DEP) $$file.txt $$file workman.sh || \
+ exit; \
+ done
+ $(SET_TIMESTAMP_DEP) version $(VERSION_DEPS)
+ $(SET_TIMESTAMP_DEP) tzdata.zi $(TZDATA_ZI_DEPS)
+ touch $@
+set-tzs-timestamp.out: $(TZS)
+ $(SET_TIMESTAMP_DEP) $(TZS) $(TZS_DEPS)
+ touch $@
+
+# The zics below ensure that each data file can stand on its own.
+# We also do an all-files run to catch links to links.
+
+check_public: $(VERSION_DEPS)
+ rm -fr public.dir
+ mkdir public.dir
+ ln $(VERSION_DEPS) public.dir
+ cd public.dir && $(MAKE) CFLAGS='$(GCC_DEBUG_FLAGS)' ALL
+ for i in $(TDATA_TO_CHECK) public.dir/tzdata.zi \
+ public.dir/vanguard.zi public.dir/main.zi \
+ public.dir/rearguard.zi; \
+ do \
+ public.dir/zic -v -d public.dir/zoneinfo $$i 2>&1 || exit; \
+ done
+ public.dir/zic -v -d public.dir/zoneinfo-all $(TDATA_TO_CHECK)
+ :
+ : Also check 'backzone' syntax.
+ rm public.dir/main.zi
+ cd public.dir && $(MAKE) PACKRATDATA=backzone main.zi
+ public.dir/zic -d public.dir/zoneinfo main.zi
+ rm public.dir/main.zi
+ cd public.dir && \
+ $(MAKE) PACKRATDATA=backzone PACKRATLIST=zone.tab main.zi
+ public.dir/zic -d public.dir/zoneinfo main.zi
+ :
+ rm -fr public.dir
+ touch $@
+
+# Check that the code works under various alternative
+# implementations of time_t.
+check_time_t_alternatives: $(TIME_T_ALTERNATIVES)
+$(TIME_T_ALTERNATIVES_TAIL): $(TIME_T_ALTERNATIVES_HEAD)
+$(TIME_T_ALTERNATIVES): $(VERSION_DEPS)
+ rm -fr $@.dir
+ mkdir $@.dir
+ ln $(VERSION_DEPS) $@.dir
+ case $@ in \
+ int*32_t) range=-2147483648,2147483648;; \
+ u*) range=0,4294967296;; \
+ *) range=-4294967296,4294967296;; \
+ esac && \
+ wd=`pwd` && \
+ zones=`$(AWK) '/^[^#]/ { print $$3 }' <zone1970.tab` && \
+ if test $@ = $(TIME_T_ALTERNATIVES_HEAD); then \
+ range_target=; \
+ else \
+ range_target=to$$range.tzs; \
+ fi && \
+ (cd $@.dir && \
+ $(MAKE) TOPDIR="$$wd/$@.dir" \
+ CFLAGS='$(CFLAGS) -Dtime_tz='"'$@'" \
+ REDO='$(REDO)' \
+ D=$$wd/$@.dir \
+ TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
+ install $$range_target) && \
+ test $@ = $(TIME_T_ALTERNATIVES_HEAD) || { \
+ (cd $(TIME_T_ALTERNATIVES_HEAD).dir && \
+ $(MAKE) TOPDIR="$$wd/$@.dir" \
+ TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
+ D=$$wd/$@.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'; \
+ else \
+ quiet_option=''; \
+ fi && \
+ diff $$quiet_option -r $(TIME_T_ALTERNATIVES_HEAD).dir/etc \
+ $@.dir/etc && \
+ diff $$quiet_option -r \
+ $(TIME_T_ALTERNATIVES_HEAD).dir/usr/share \
+ $@.dir/usr/share; \
+ }
+ touch $@
+
+TRADITIONAL_ASC = \
+ tzcode$(VERSION).tar.gz.asc \
+ tzdata$(VERSION).tar.gz.asc
+REARGUARD_ASC = \
+ tzdata$(VERSION)-rearguard.tar.gz.asc
+ALL_ASC = $(TRADITIONAL_ASC) $(REARGUARD_ASC) \
+ tzdb-$(VERSION).tar.lz.asc
+
+tarballs rearguard_tarballs tailored_tarballs traditional_tarballs \
+signatures rearguard_signatures traditional_signatures: \
+ version set-timestamps.out rearguard.zi vanguard.zi
+ VERSION=`cat version` && \
+ $(MAKE) AWK='$(AWK)' VERSION="$$VERSION" $@_version
+
+# These *_version rules are intended for use if VERSION is set by some
+# other means. Ordinarily these rules are used only by the above
+# non-_version rules, which set VERSION on the 'make' command line.
+tarballs_version: traditional_tarballs_version rearguard_tarballs_version \
+ tzdb-$(VERSION).tar.lz
+rearguard_tarballs_version: \
+ tzdata$(VERSION)-rearguard.tar.gz
+traditional_tarballs_version: \
+ tzcode$(VERSION).tar.gz tzdata$(VERSION).tar.gz
+tailored_tarballs_version: \
+ tzdata$(VERSION)-tailored.tar.gz
+signatures_version: $(ALL_ASC)
+rearguard_signatures_version: $(REARGUARD_ASC)
+traditional_signatures_version: $(TRADITIONAL_ASC)
+
+tzcode$(VERSION).tar.gz: set-timestamps.out
+ LC_ALL=C && export LC_ALL && \
+ tar $(TARFLAGS) -cf - \
+ $(COMMON) $(DOCS) $(SOURCES) | \
+ gzip $(GZIPFLAGS) >$@.out
+ mv $@.out $@
+
+tzdata$(VERSION).tar.gz: set-timestamps.out
+ LC_ALL=C && export LC_ALL && \
+ tar $(TARFLAGS) -cf - $(TZDATA_DIST) | \
+ gzip $(GZIPFLAGS) >$@.out
+ mv $@.out $@
+
+# Create empty files with a reproducible timestamp.
+CREATE_EMPTY = TZ=UTC0 touch -mt 202010122253.00
+
+# The obsolescent *rearguard* targets and related macros are present
+# for backwards compatibility with tz releases 2018e through 2022a.
+# They should go away eventually. To build rearguard tarballs you
+# can instead use 'make DATAFORM=rearguard tailored_tarballs'.
+tzdata$(VERSION)-rearguard.tar.gz: rearguard.zi set-timestamps.out
+ rm -fr $@.dir
+ mkdir $@.dir
+ ln $(TZDATA_DIST) $@.dir
+ cd $@.dir && rm -f $(TDATA) $(PACKRATDATA) version
+ for f in $(TDATA) $(PACKRATDATA); do \
+ rearf=$@.dir/$$f; \
+ $(AWK) -v DATAFORM=rearguard -f ziguard.awk $$f >$$rearf && \
+ $(SET_TIMESTAMP_DEP) $$rearf ziguard.awk $$f || exit; \
+ done
+ sed '1s/$$/-rearguard/' <version >$@.dir/version
+ : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
+ $(CREATE_EMPTY) $@.dir/pacificnew
+ touch -cmr version $@.dir/version
+ LC_ALL=C && export LC_ALL && \
+ (cd $@.dir && \
+ tar $(TARFLAGS) -cf - \
+ $(TZDATA_DIST) pacificnew | \
+ gzip $(GZIPFLAGS)) >$@.out
+ mv $@.out $@
+
+# Create a tailored tarball suitable for TZUpdater and compatible tools.
+# For example, 'make DATAFORM=vanguard tailored_tarballs' makes a tarball
+# useful for testing whether TZUpdater supports vanguard form.
+# The generated tarball is not byte-for-byte equivalent to a hand-tailored
+# traditional tarball, as data entries are put into 'etcetera' even if they
+# came from some other source file. However, the effect should be the same
+# for ordinary use, which reads all the source files.
+tzdata$(VERSION)-tailored.tar.gz: set-timestamps.out
+ rm -fr $@.dir
+ mkdir $@.dir
+ : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
+ cd $@.dir && \
+ $(CREATE_EMPTY) $(PRIMARY_YDATA) $(NDATA) backward \
+ `test $(DATAFORM) = vanguard || echo pacificnew`
+ (grep '^#' tzdata.zi && echo && cat $(DATAFORM).zi) \
+ >$@.dir/etcetera
+ touch -cmr tzdata.zi $@.dir/etcetera
+ sed -n \
+ -e '/^# *version *\(.*\)/h' \
+ -e '/^# *ddeps */H' \
+ -e '$$!d' \
+ -e 'g' \
+ -e 's/^# *version *//' \
+ -e 's/\n# *ddeps */-/' \
+ -e 's/ /-/g' \
+ -e 'p' \
+ <tzdata.zi >$@.dir/version
+ touch -cmr version $@.dir/version
+ links= && \
+ for file in $(TZDATA_DIST); do \
+ test -f $@.dir/$$file || links="$$links $$file"; \
+ done && \
+ ln $$links $@.dir
+ LC_ALL=C && export LC_ALL && \
+ (cd $@.dir && \
+ tar $(TARFLAGS) -cf - * | gzip $(GZIPFLAGS)) >$@.out
+ mv $@.out $@
+
+tzdb-$(VERSION).tar.lz: set-timestamps.out set-tzs-timestamp.out
+ rm -fr tzdb-$(VERSION)
+ mkdir tzdb-$(VERSION)
+ ln $(ENCHILADA) tzdb-$(VERSION)
+ $(SET_TIMESTAMP) tzdb-$(VERSION) tzdb-$(VERSION)/*
+ LC_ALL=C && export LC_ALL && \
+ tar $(TARFLAGS) -cf - tzdb-$(VERSION) | lzip -9 >$@.out
+ mv $@.out $@
+
+tzcode$(VERSION).tar.gz.asc: tzcode$(VERSION).tar.gz
+tzdata$(VERSION).tar.gz.asc: tzdata$(VERSION).tar.gz
+tzdata$(VERSION)-rearguard.tar.gz.asc: tzdata$(VERSION)-rearguard.tar.gz
+tzdb-$(VERSION).tar.lz.asc: tzdb-$(VERSION).tar.lz
+$(ALL_ASC):
+ $(GPG) --armor --detach-sign $?
+
+TYPECHECK_CFLAGS = $(CFLAGS) -DTYPECHECK -D__time_t_defined -D_TIME_T
+typecheck: typecheck_long_long typecheck_unsigned
+typecheck_long_long typecheck_unsigned: $(VERSION_DEPS)
+ rm -fr $@.dir
+ mkdir $@.dir
+ ln $(VERSION_DEPS) $@.dir
+ cd $@.dir && \
+ case $@ in \
+ *_long_long) i="long long";; \
+ *_unsigned ) i="unsigned" ;; \
+ esac && \
+ typecheck_cflags='' && \
+ $(MAKE) \
+ CFLAGS="$(TYPECHECK_CFLAGS) \"-Dtime_t=$$i\"" \
+ TOPDIR="`pwd`" \
+ install
+ $@.dir/zdump -i -c 1970,1971 Europe/Rome
+ touch $@
+
+zonenames: tzdata.zi
+ @$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi
+
+asctime.o: private.h tzfile.h
+date.o: private.h
+difftime.o: private.h
+localtime.o: private.h tzfile.h
+strftime.o: private.h tzfile.h
+zdump.o: version.h
+zic.o: private.h tzfile.h version.h
+
+.PHONY: ALL INSTALL all
+.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
+.PHONY: posix_only posix_right public
+.PHONY: rearguard_signatures rearguard_signatures_version
+.PHONY: rearguard_tarballs rearguard_tarballs_version
+.PHONY: right_only right_posix signatures signatures_version
+.PHONY: tarballs tarballs_version
+.PHONY: traditional_signatures traditional_signatures_version
+.PHONY: traditional_tarballs traditional_tarballs_version
+.PHONY: tailored_tarballs tailored_tarballs_version
+.PHONY: typecheck
+.PHONY: zonenames zones
+.PHONY: $(ZDS)