aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--timezone/checktab.awk29
-rw-r--r--timezone/private.h148
-rw-r--r--timezone/scheck.c29
-rw-r--r--timezone/tzfile.h25
-rw-r--r--timezone/tzselect.ksh366
-rw-r--r--timezone/zdump.c327
-rw-r--r--timezone/zic.c620
8 files changed, 1072 insertions, 480 deletions
diff --git a/ChangeLog b/ChangeLog
index 1a1b93237e..80fab8e00e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
2013-12-20 Joseph Myers <joseph@codesourcery.com>
+ * timezone/checktab.awk: Update from tzcode 2013i.
+ * timezone/private.h: Likewise.
+ * timezone/scheck.c: Likewise.
+ * timezone/tzfile.h: Likewise.
+ * timezone/tzselect.ksh: Likewise.
+ * timezone/zdump.c: Likewise.
+ * timezone/zic.c: Likewise.
+
* math/auto-libm-test-in: Add tests of cpow.
* math/auto-libm-test-out: Regenerated.
* math/libm-test.inc (cpow_test_data): Use AUTO_TESTS_cc_c.
diff --git a/timezone/checktab.awk b/timezone/checktab.awk
index c88b12f1ba..fec4f628e5 100644
--- a/timezone/checktab.awk
+++ b/timezone/checktab.awk
@@ -9,6 +9,9 @@ BEGIN {
if (!zone_table) zone_table = "zone.tab"
if (!want_warnings) want_warnings = -1
+ # A special (and we hope temporary) case.
+ tztab["America/Montreal"] = 1
+
while (getline <iso_table) {
iso_NR++
if ($0 ~ /^#/) continue
@@ -69,13 +72,10 @@ BEGIN {
status = 1
}
cc0 = cc
- if (tz2cc[tz]) {
- printf "%s:%d: %s: duplicate TZ column\n", \
- zone_table, zone_NR, tz >>"/dev/stderr"
- status = 1
- }
- tz2cc[tz] = cc
- tz2comments[tz] = comments
+ cctz = cc tz
+ cctztab[cctz] = 1
+ tztab[tz] = 1
+ tz2comments[cctz] = comments
tz2NR[tz] = zone_NR
if (cc2name[cc]) {
cc_used[cc]++
@@ -92,16 +92,19 @@ BEGIN {
}
}
- for (tz in tz2cc) {
- if (cc_used[tz2cc[tz]] == 1) {
- if (tz2comments[tz]) {
+ for (cctz in cctztab) {
+ cc = substr (cctz, 1, 2)
+ tz = substr (cctz, 3)
+ if (cc_used[cc] == 1) {
+ if (tz2comments[cctz]) {
printf "%s:%d: unnecessary comment `%s'\n", \
- zone_table, tz2NR[tz], tz2comments[tz] \
+ zone_table, tz2NR[tz], \
+ tz2comments[cctz] \
>>"/dev/stderr"
status = 1
}
} else {
- if (!tz2comments[tz]) {
+ if (!tz2comments[cctz]) {
printf "%s:%d: missing comment\n", \
zone_table, tz2NR[tz] >>"/dev/stderr"
status = 1
@@ -125,7 +128,7 @@ BEGIN {
if (src != dst) tz = $3
}
if (tz && tz ~ /\//) {
- if (!tz2cc[tz]) {
+ if (!tztab[tz]) {
printf "%s: no data for `%s'\n", zone_table, tz \
>>"/dev/stderr"
status = 1
diff --git a/timezone/private.h b/timezone/private.h
index 1d1d391f56..4eb0ab6221 100644
--- a/timezone/private.h
+++ b/timezone/private.h
@@ -34,6 +34,10 @@
#define HAVE_INCOMPATIBLE_CTIME_R 0
#endif /* !defined INCOMPATIBLE_CTIME_R */
+#ifndef HAVE_LINK
+#define HAVE_LINK 1
+#endif /* !defined HAVE_LINK */
+
#ifndef HAVE_SETTIMEOFDAY
#define HAVE_SETTIMEOFDAY 3
#endif /* !defined HAVE_SETTIMEOFDAY */
@@ -124,19 +128,76 @@
#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
+
#ifndef INT_FAST64_MAX
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
#if defined LLONG_MAX || defined __LONG_LONG_MAX__
typedef long long int_fast64_t;
+# ifdef LLONG_MAX
+# define INT_FAST64_MIN LLONG_MIN
+# define INT_FAST64_MAX LLONG_MAX
+# else
+# define INT_FAST64_MIN __LONG_LONG_MIN__
+# define INT_FAST64_MAX __LONG_LONG_MAX__
+# endif
+# define SCNdFAST64 "lld"
#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#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 /* (LONG_MAX >> 31) < 0xffffffff */
typedef long int_fast64_t;
+# define INT_FAST64_MIN LONG_MIN
+# define INT_FAST64_MAX LONG_MAX
+# define SCNdFAST64 "ld"
#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#endif /* !defined INT_FAST64_MAX */
+#ifndef INT_FAST32_MAX
+# if INT_MAX >> 31 == 0
+typedef long int_fast32_t;
+# else
+typedef int int_fast32_t;
+# endif
+#endif
+
+#ifndef INTMAX_MAX
+# if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long intmax_t;
+# define strtoimax strtoll
+# define PRIdMAX "lld"
+# ifdef LLONG_MAX
+# define INTMAX_MAX LLONG_MAX
+# define INTMAX_MIN LLONG_MIN
+# else
+# define INTMAX_MAX __LONG_LONG_MAX__
+# define INTMAX_MIN __LONG_LONG_MIN__
+# endif
+# else
+typedef long intmax_t;
+# define strtoimax strtol
+# define PRIdMAX "ld"
+# define INTMAX_MAX LONG_MAX
+# define INTMAX_MIN LONG_MIN
+# endif
+#endif
+
+#ifndef UINTMAX_MAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+typedef unsigned long long uintmax_t;
+# define PRIuMAX "llu"
+# else
+typedef unsigned long uintmax_t;
+# define PRIuMAX "lu"
+# endif
+#endif
+
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif /* !defined INT32_MAX */
@@ -144,10 +205,26 @@ typedef long int_fast64_t;
#define INT32_MIN (-1 - INT32_MAX)
#endif /* !defined INT32_MIN */
-#if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
+#if 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+# define ATTRIBUTE_CONST __attribute__ ((const))
# define ATTRIBUTE_PURE __attribute__ ((__pure__))
+# define ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
#else
+# define ATTRIBUTE_CONST /* 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
/*
@@ -165,6 +242,58 @@ extern char * asctime_r(struct tm const *, char *);
#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.
+*/
+#ifdef time_tz
+static time_t sys_time(time_t *x) { return time(x); }
+
+# 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 mktime
+# define mktime tz_mktime
+# undef time
+# define time tz_time
+# undef time_t
+# define time_t tz_time_t
+
+typedef time_tz time_t;
+
+char *ctime(time_t const *);
+char *ctime_r(time_t const *, char *);
+double difftime(time_t, time_t);
+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 *);
+
+static time_t
+time(time_t *p)
+{
+ time_t r = sys_time(0);
+ if (p)
+ *p = r;
+ return r;
+}
+#endif
+
+/*
** Private function declarations.
*/
@@ -192,14 +321,15 @@ const char * scheck(const char * string, const char * format);
#define TYPE_SIGNED(type) (((type) -1) < 0)
#endif /* !defined TYPE_SIGNED */
-/*
-** Since the definition of TYPE_INTEGRAL contains floating point numbers,
-** it cannot be used in preprocessor directives.
-*/
-
-#ifndef TYPE_INTEGRAL
-#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
-#endif /* !defined TYPE_INTEGRAL */
+/* The minimum and maximum finite time values. */
+static time_t const time_t_min =
+ (TYPE_SIGNED(time_t)
+ ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
+ : 0);
+static time_t const time_t_max =
+ (TYPE_SIGNED(time_t)
+ ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
+ : -1);
#ifndef INT_STRLEN_MAXIMUM
/*
diff --git a/timezone/scheck.c b/timezone/scheck.c
index ed60980d83..8bd01a858f 100644
--- a/timezone/scheck.c
+++ b/timezone/scheck.c
@@ -25,26 +25,35 @@ scheck(const char *const string, const char *const format)
return result;
fp = format;
tp = fbuf;
+
+ /*
+ ** Copy directives, suppressing each conversion that is not
+ ** already suppressed. Scansets containing '%' are not
+ ** supported; e.g., the conversion specification "%[%]" is not
+ ** supported. Also, multibyte characters containing a
+ ** non-leading '%' byte are not supported.
+ */
while ((*tp++ = c = *fp++) != '\0') {
if (c != '%')
continue;
- if (*fp == '%') {
- *tp++ = *fp++;
- continue;
+ if (is_digit(*fp)) {
+ char const *f = fp;
+ char *t = tp;
+ do {
+ *t++ = c = *f++;
+ } while (is_digit(c));
+ if (c == '$') {
+ fp = f;
+ tp = t;
+ }
}
*tp++ = '*';
if (*fp == '*')
++fp;
- while (is_digit(*fp))
- *tp++ = *fp++;
- if (*fp == 'l' || *fp == 'h')
- *tp++ = *fp++;
- else if (*fp == '[')
- do *tp++ = *fp++;
- while (*fp != '\0' && *fp != ']');
if ((*tp++ = *fp++) == '\0')
break;
}
+
*(tp - 1) = '%';
*tp++ = 'c';
*tp = '\0';
diff --git a/timezone/tzfile.h b/timezone/tzfile.h
index 0f6c687f16..529650dd8a 100644
--- a/timezone/tzfile.h
+++ b/timezone/tzfile.h
@@ -39,7 +39,7 @@
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' as of 2005 */
+ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
char tzh_reserved[15]; /* reserved--must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
@@ -55,7 +55,7 @@ struct tzhead {
** tzh_timecnt (char [4])s coded transition times a la time(2)
** tzh_timecnt (unsigned char)s types of local time starting at above
** tzh_typecnt repetitions of
-** one (char [4]) coded UTC offset in seconds
+** one (char [4]) coded UT offset in seconds
** one (unsigned char) used to set tm_isdst
** one (unsigned char) that's an abbreviation list index
** tzh_charcnt (char)s '\0'-terminated zone abbreviations
@@ -68,7 +68,7 @@ struct tzhead {
** if absent, transition times are
** assumed to be wall clock time
** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
-** time is UTC, if FALSE,
+** time is UT, if FALSE,
** transition time is local time
** if absent, transition times are
** assumed to be local time
@@ -82,6 +82,13 @@ struct tzhead {
** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for
** such instants).
+**
+** If tz_version is '3' or greater, the above is extended as follows.
+** First, the POSIX TZ string's hour offset may range from -167
+** through 167 as compared to the POSIX-required 0 through 24.
+** Second, its DST start time may be January 1 at 00:00 and its stop
+** time December 31 at 24:00 plus the difference between DST and
+** standard time, indicating DST all year.
*/
/*
@@ -94,16 +101,8 @@ struct tzhead {
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
-#ifndef NOSOLAR
+/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-#endif /* !defined NOSOLAR */
-#ifdef NOSOLAR
-/*
-** Must be at least 14 for Europe/Riga as of Jan 12 1995,
-** as noted by Earl Chew.
-*/
-#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
-#endif /* !defined NOSOLAR */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
@@ -122,7 +121,7 @@ struct tzhead {
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define TM_SUNDAY 0
diff --git a/timezone/tzselect.ksh b/timezone/tzselect.ksh
index 8e66b44273..9d7069116a 100644
--- a/timezone/tzselect.ksh
+++ b/timezone/tzselect.ksh
@@ -11,7 +11,7 @@ REPORT_BUGS_TO=tz@iana.org
# Porting notes:
#
-# This script requires a Posix-like shell with the extension of a
+# This script requires a Posix-like shell and prefers the extension of a
# 'select' statement. The 'select' statement was introduced in the
# Korn shell and is available in Bash and other shell implementations.
# If your host lacks both Bash and the Korn shell, you can get their
@@ -21,6 +21,10 @@ REPORT_BUGS_TO=tz@iana.org
# Korn Shell <http://www.kornshell.com/>
# Public Domain Korn Shell <http://www.cs.mun.ca/~michael/pdksh/>
#
+# For portability to Solaris 9 /bin/sh this script avoids some POSIX
+# features and common extensions, such as $(...) (which works sometimes
+# but not others), $((...)), and $10.
+#
# This script also uses several features of modern awk programs.
# If your host lacks awk, or has an old awk that does not conform to Posix,
# you can use either of the following free programs instead:
@@ -31,7 +35,7 @@ REPORT_BUGS_TO=tz@iana.org
# Specify default values for environment variables if they are unset.
: ${AWK=awk}
-: ${TZDIR=$(pwd)}
+: ${TZDIR=`pwd`}
# Check for awk Posix compliance.
($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
@@ -40,21 +44,125 @@ REPORT_BUGS_TO=tz@iana.org
exit 1
}
-if [ "$1" = "--help" ]; then
- cat <<EOF
-Usage: tzselect
+coord=
+location_limit=10
+
+usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
Select a time zone interactively.
-Report bugs to $REPORT_BUGS_TO.
-EOF
- exit
-elif [ "$1" = "--version" ]; then
- cat <<EOF
-tzselect $PKGVERSION$TZVERSION
-EOF
- exit
+Options:
+
+ -c COORD
+ Instead of asking for continent and then country and then city,
+ ask for selection from time zones whose largest cities
+ are closest to the location with geographical coordinates COORD.
+ COORD should use ISO 6709 notation, for example, '-c +4852+00220'
+ for Paris (in degrees and minutes, North and East), or
+ '-c -35-058' for Buenos Aires (in degrees, South and West).
+
+ -n LIMIT
+ Display at most LIMIT locations when -c is used (default $location_limit).
+
+ --version
+ Output version information.
+
+ --help
+ Output this help.
+
+Report bugs to $REPORT_BUGS_TO."
+
+# Ask the user to select from the function's arguments,
+# and assign the selected argument to the variable 'select_result'.
+# Exit on EOF or I/O error. Use the shell's 'select' builtin if available,
+# falling back on a less-nice but portable substitute otherwise.
+if
+ case $BASH_VERSION in
+ ?*) : ;;
+ '')
+ # '; exit' should be redundant, but Dash doesn't properly fail without it.
+ (eval 'set --; select x; do break; done; exit') 2>/dev/null
+ esac
+then
+ # Do this inside 'eval', as otherwise the shell might exit when parsing it
+ # even though it is never executed.
+ eval '
+ doselect() {
+ select select_result
+ do
+ case $select_result in
+ "") echo >&2 "Please enter a number in range." ;;
+ ?*) break
+ esac
+ done || exit
+ }
+
+ # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
+ case $BASH_VERSION in
+ [01].*)
+ case `echo 1 | (select x in x; do break; done) 2>/dev/null` in
+ ?*) PS3=
+ esac
+ esac
+ '
+else
+ doselect() {
+ # Field width of the prompt numbers.
+ select_width=`expr $# : '.*'`
+
+ select_i=
+
+ while :
+ do
+ case $select_i in
+ '')
+ select_i=0
+ for select_word
+ do
+ select_i=`expr $select_i + 1`
+ printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
+ done ;;
+ *[!0-9]*)
+ echo >&2 'Please enter a number in range.' ;;
+ *)
+ if test 1 -le $select_i && test $select_i -le $#; then
+ shift `expr $select_i - 1`
+ select_result=$1
+ break
+ fi
+ echo >&2 'Please enter a number in range.'
+ esac
+
+ # Prompt and read input.
+ printf >&2 %s "${PS3-#? }"
+ read select_i || exit
+ done
+ }
fi
+while getopts c:n:-: opt
+do
+ case $opt$OPTARG in
+ c*)
+ coord=$OPTARG ;;
+ n*)
+ location_limit=$OPTARG ;;
+ -help)
+ exec echo "$usage" ;;
+ -version)
+ exec echo "tzselect $PKGVERSION$TZVERSION" ;;
+ -*)
+ echo >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;;
+ *)
+ echo >&2 "$0: try '$0 --help'"; exit 1 ;;
+ esac
+done
+
+shift `expr $OPTIND - 1`
+case $# in
+0) ;;
+*) echo >&2 "$0: $1: unknown argument"; exit 1 ;;
+esac
+
# Make sure the tables are readable.
TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
TZ_ZONE_TABLE=$TZDIR/zone.tab
@@ -71,11 +179,65 @@ newline='
IFS=$newline
-# Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
-case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
-?*) PS3=
-esac
-
+# Awk script to read a time zone table and output the same table,
+# with each column preceded by its distance from 'here'.
+output_distances='
+ BEGIN {
+ FS = "\t"
+ while (getline <TZ_COUNTRY_TABLE)
+ if ($0 ~ /^[^#]/)
+ country[$1] = $2
+ country["US"] = "US" # Otherwise the strings get too long.
+ }
+ function convert_coord(coord, deg, min, ilen, sign, sec) {
+ if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
+ degminsec = coord
+ intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000)
+ minsec = degminsec - intdeg * 10000
+ intmin = minsec < 0 ? -int(-minsec / 100) : int(minsec / 100)
+ sec = minsec - intmin * 100
+ deg = (intdeg * 3600 + intmin * 60 + sec) / 3600
+ } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
+ degmin = coord
+ intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
+ min = degmin - intdeg * 100
+ deg = (intdeg * 60 + min) / 60
+ } else
+ deg = coord
+ return deg * 0.017453292519943296
+ }
+ function convert_latitude(coord) {
+ match(coord, /..*[-+]/)
+ return convert_coord(substr(coord, 1, RLENGTH - 1))
+ }
+ function convert_longitude(coord) {
+ match(coord, /..*[-+]/)
+ return convert_coord(substr(coord, RLENGTH))
+ }
+ # Great-circle distance between points with given latitude and longitude.
+ # Inputs and output are in radians. This uses the great-circle special
+ # case of the Vicenty formula for distances on ellipsoids.
+ function dist(lat1, long1, lat2, long2, dlong, x, y, num, denom) {
+ dlong = long2 - long1
+ x = cos (lat2) * sin (dlong)
+ y = cos (lat1) * sin (lat2) - sin (lat1) * cos (lat2) * cos (dlong)
+ num = sqrt (x * x + y * y)
+ denom = sin (lat1) * sin (lat2) + cos (lat1) * cos (lat2) * cos (dlong)
+ return atan2(num, denom)
+ }
+ BEGIN {
+ coord_lat = convert_latitude(coord)
+ coord_long = convert_longitude(coord)
+ }
+ /^[^#]/ {
+ here_lat = convert_latitude($2)
+ here_long = convert_longitude($2)
+ line = $1 "\t" $2 "\t" $3 "\t" country[$1]
+ if (NF == 4)
+ line = line " - " $4
+ printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
+ }
+'
# Begin the main loop. We come back here if the user wants to retry.
while
@@ -87,39 +249,46 @@ while
country=
region=
+ case $coord in
+ ?*)
+ continent=coord;;
+ '')
# Ask the user for continent or ocean.
- echo >&2 'Please select a continent or ocean.'
-
- select continent in \
- Africa \
- Americas \
- Antarctica \
- 'Arctic Ocean' \
- Asia \
- 'Atlantic Ocean' \
- Australia \
- Europe \
- 'Indian Ocean' \
- 'Pacific Ocean' \
- 'none - I want to specify the time zone using the Posix TZ format.'
- do
+ echo >&2 'Please select a continent, ocean, "coord", or "TZ".'
+
+ quoted_continents=`
+ $AWK '
+ BEGIN { FS = "\t" }
+ /^[^#]/ {
+ entry = substr($3, 1, index($3, "/") - 1)
+ if (entry == "America")
+ entry = entry "s"
+ if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
+ entry = entry " Ocean"
+ printf "'\''%s'\''\n", entry
+ }
+ ' $TZ_ZONE_TABLE |
+ sort -u |
+ tr '\n' ' '
+ echo ''
+ `
+
+ eval '
+ doselect '"$quoted_continents"' \
+ "coord - I want to use geographical coordinates." \
+ "TZ - I want to specify the time zone using the Posix TZ format."
+ continent=$select_result
case $continent in
- '')
- echo >&2 'Please enter a number in range.';;
- ?*)
- case $continent in
- Americas) continent=America;;
- *' '*) continent=$(expr "$continent" : '\([^ ]*\)')
- esac
- break
+ Americas) continent=America;;
+ *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''`
esac
- done
+ '
+ esac
+
case $continent in
- '')
- exit 1;;
- none)
+ TZ)
# Ask the user for a Posix TZ string. Check that it conforms.
while
echo >&2 'Please enter the desired value' \
@@ -144,11 +313,46 @@ while
done
TZ_for_date=$TZ;;
*)
+ case $continent in
+ coord)
+ case $coord in
+ '')
+ echo >&2 'Please enter coordinates' \
+ 'in ISO 6709 notation.'
+ echo >&2 'For example, +4042-07403 stands for'
+ echo >&2 '40 degrees 42 minutes north,' \
+ '74 degrees 3 minutes west.'
+ read coord;;
+ esac
+ distance_table=`$AWK \
+ -v coord="$coord" \
+ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+ "$output_distances" <$TZ_ZONE_TABLE |
+ sort -n |
+ sed "${location_limit}q"
+ `
+ regions=`echo "$distance_table" | $AWK '
+ BEGIN { FS = "\t" }
+ { print $NF }
+ '`
+ echo >&2 'Please select one of the following' \
+ 'time zone regions,'
+ echo >&2 'listed roughly in increasing order' \
+ "of distance from $coord".
+ doselect $regions
+ region=$select_result
+ TZ=`echo "$distance_table" | $AWK -v region="$region" '
+ BEGIN { FS="\t" }
+ $NF == region { print $4 }
+ '`
+ ;;
+ *)
# Get list of names of countries in the continent or ocean.
- countries=$($AWK -F'\t' \
+ countries=`$AWK \
-v continent="$continent" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
+ BEGIN { FS = "\t" }
/^#/ { next }
$3 ~ ("^" continent "/") {
if (!cc_seen[$1]++) cc_list[++ccs] = $1
@@ -165,35 +369,28 @@ while
print country
}
}
- ' <$TZ_ZONE_TABLE | sort -f)
+ ' <$TZ_ZONE_TABLE | sort -f`
# If there's more than one country, ask the user which one.
case $countries in
*"$newline"*)
- echo >&2 'Please select a country.'
- select country in $countries
- do
- case $country in
- '') echo >&2 'Please enter a number in range.';;
- ?*) break
- esac
- done
-
- case $country in
- '') exit 1
- esac;;
+ echo >&2 'Please select a country' \
+ 'whose clocks agree with yours.'
+ doselect $countries
+ country=$select_result;;
*)
country=$countries
esac
# Get list of names of time zone rule regions in the country.
- regions=$($AWK -F'\t' \
+ regions=`$AWK \
-v country="$country" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
BEGIN {
+ FS = "\t"
cc = country
while (getline <TZ_COUNTRY_TABLE) {
if ($0 !~ /^#/ && country == $2) {
@@ -203,7 +400,7 @@ while
}
}
$1 == cc { print $4 }
- ' <$TZ_ZONE_TABLE)
+ ' <$TZ_ZONE_TABLE`
# If there's more than one region, ask the user which one.
@@ -211,27 +408,20 @@ while
*"$newline"*)
echo >&2 'Please select one of the following' \
'time zone regions.'
- select region in $regions
- do
- case $region in
- '') echo >&2 'Please enter a number in range.';;
- ?*) break
- esac
- done
- case $region in
- '') exit 1
- esac;;
+ doselect $regions
+ region=$select_result;;
*)
region=$regions
esac
# Determine TZ from country and region.
- TZ=$($AWK -F'\t' \
+ TZ=`$AWK \
-v country="$country" \
-v region="$region" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
BEGIN {
+ FS = "\t"
cc = country
while (getline <TZ_COUNTRY_TABLE) {
if ($0 !~ /^#/ && country == $2) {
@@ -241,7 +431,8 @@ while
}
}
$1 == cc && $4 == region { print $3 }
- ' <$TZ_ZONE_TABLE)
+ ' <$TZ_ZONE_TABLE`
+ esac
# Make sure the corresponding zoneinfo file exists.
TZ_for_date=$TZDIR/$TZ
@@ -259,10 +450,10 @@ while
extra_info=
for i in 1 2 3 4 5 6 7 8
do
- TZdate=$(LANG=C TZ="$TZ_for_date" date)
- UTdate=$(LANG=C TZ=UTC0 date)
- TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)')
- UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)')
+ TZdate=`LANG=C TZ="$TZ_for_date" date`
+ UTdate=`LANG=C TZ=UTC0 date`
+ TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'`
+ UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'`
case $TZsec in
$UTsec)
extra_info="
@@ -278,28 +469,23 @@ Universal Time is now: $UTdate."
echo >&2 ""
echo >&2 "The following information has been given:"
echo >&2 ""
- case $country+$region in
- ?*+?*) echo >&2 " $country$newline $region";;
- ?*+) echo >&2 " $country";;
+ case $country%$region%$coord in
+ ?*%?*%) echo >&2 " $country$newline $region";;
+ ?*%%) echo >&2 " $country";;
+ %?*%?*) echo >&2 " coord $coord$newline $region";;
+ %%?*) echo >&2 " coord $coord";;
+) echo >&2 " TZ='$TZ'"
esac
echo >&2 ""
echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
echo >&2 "Is the above information OK?"
- ok=
- select ok in Yes No
- do
- case $ok in
- '') echo >&2 'Please enter 1 for Yes, or 2 for No.';;
- ?*) break
- esac
- done
+ doselect Yes No
+ ok=$select_result
case $ok in
- '') exit 1;;
Yes) break
esac
-do :
+do coord=
done
case $SHELL in
diff --git a/timezone/zdump.c b/timezone/zdump.c
index 9255affc16..209b79d06c 100644
--- a/timezone/zdump.c
+++ b/timezone/zdump.c
@@ -9,20 +9,72 @@
** This code has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides.
** You can use this code to help in verifying other implementations.
+**
+** However, include private.h when debugging, so that it overrides
+** time_t consistently with the rest of the package.
*/
+#ifdef time_tz
+# include "private.h"
+#endif
+
#include "stdio.h" /* for stdout, stderr, perror */
#include "string.h" /* for strcpy */
#include "sys/types.h" /* for time_t */
#include "time.h" /* for struct tm */
#include "stdlib.h" /* for exit, malloc, atoi */
-#include "float.h" /* for FLT_MAX and DBL_MAX */
#include "limits.h" /* for CHAR_BIT, LLONG_MAX */
#include "ctype.h" /* for isalpha et al. */
#ifndef isascii
#define isascii(x) 1
#endif /* !defined isascii */
+/*
+** Substitutes for pre-C99 compilers.
+** Much of this section of code is stolen from private.h.
+*/
+
+#ifndef HAVE_STDINT_H
+# define HAVE_STDINT_H \
+ (199901 <= __STDC_VERSION__ || 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif
+#if HAVE_STDINT_H
+# include "stdint.h"
+#endif
+#ifndef HAVE_INTTYPES_H
+# define HAVE_INTTYPES_H HAVE_STDINT_H
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#ifndef INT_FAST32_MAX
+# if INT_MAX >> 31 == 0
+typedef long int_fast32_t;
+# else
+typedef int int_fast32_t;
+# endif
+#endif
+
+#ifndef INTMAX_MAX
+# if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long intmax_t;
+# define strtoimax strtoll
+# define PRIdMAX "lld"
+# ifdef LLONG_MAX
+# define INTMAX_MAX LLONG_MAX
+# else
+# define INTMAX_MAX __LONG_LONG_MAX__
+# endif
+# else
+typedef long intmax_t;
+# define strtoimax strtol
+# define PRIdMAX "ld"
+# define INTMAX_MAX LONG_MAX
+# endif
+#endif
+
+
#ifndef ZDUMP_LO_YEAR
#define ZDUMP_LO_YEAR (-500)
#endif /* !defined ZDUMP_LO_YEAR */
@@ -90,9 +142,20 @@
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined isleap_sum */
-#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
#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 };
#ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0
@@ -112,14 +175,6 @@
#endif /* !defined lint */
#endif /* !defined GNUC_or_lint */
-#ifndef INITIALIZE
-#ifdef GNUC_or_lint
-#define INITIALIZE(x) ((x) = 0)
-#else /* !defined GNUC_or_lint */
-#define INITIALIZE(x)
-#endif /* !defined GNUC_or_lint */
-#endif /* !defined INITIALIZE */
-
#if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
# define ATTRIBUTE_PURE __attribute__ ((__pure__))
#else
@@ -151,48 +206,27 @@ extern char * optarg;
extern int optind;
extern char * tzname[2];
-/* The minimum and maximum finite time values. Shift 'long long' or
- 'long' instead of 'time_t'; this avoids compile-time errors when
- time_t is floating-point. In practice, 'long long' is wide enough. */
+/* The minimum and maximum finite time values. */
static time_t const absolute_min_time =
- ((time_t) 0.5 == 0.5
- ? (sizeof (time_t) == sizeof (float) ? (time_t) -FLT_MAX
- : sizeof (time_t) == sizeof (double) ? (time_t) -DBL_MAX
- : sizeof (time_t) == sizeof (long double) ? (time_t) -LDBL_MAX
- : 0)
- : (time_t) -1 < 0
-#ifdef LLONG_MAX
- ? (time_t) ((long long) -1 << (CHAR_BIT * sizeof (time_t) - 1))
-#else
- ? (time_t) ((long) -1 << (CHAR_BIT * sizeof (time_t) - 1))
-#endif
+ ((time_t) -1 < 0
+ ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
: 0);
static time_t const absolute_max_time =
- ((time_t) 0.5 == 0.5
- ? (sizeof (time_t) == sizeof (float) ? (time_t) FLT_MAX
- : sizeof (time_t) == sizeof (double) ? (time_t) DBL_MAX
- : sizeof (time_t) == sizeof (long double) ? (time_t) LDBL_MAX
- : -1)
- : (time_t) -1 < 0
-#ifdef LLONG_MAX
- ? (time_t) (- (~ 0 < 0) - ((long long) -1 << (CHAR_BIT * sizeof (time_t) - 1)))
-#else
- ? (time_t) (- (~ 0 < 0) - ((long) -1 << (CHAR_BIT * sizeof (time_t) - 1)))
-#endif
- : (time_t) -1);
+ ((time_t) -1 < 0
+ ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
+ : -1);
static size_t longest;
static char * progname;
static int warned;
static char * abbr(struct tm * tmp);
static void abbrok(const char * abbrp, const char * zone);
-static long delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE;
+static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE;
static void dumptime(const struct tm * tmp);
static time_t hunt(char * name, time_t lot, time_t hit);
-static void checkabsolutes(void);
static void show(char * zone, time_t t, int v);
static const char * tformat(void);
-static time_t yeartot(long y) ATTRIBUTE_PURE;
+static time_t yeartot(intmax_t y) ATTRIBUTE_PURE;
#ifndef TYPECHECK
#define my_localtime localtime
@@ -209,7 +243,7 @@ my_localtime(time_t *tp)
tm = *tmp;
t = mktime(&tm);
- if (t - *tp >= 1 || *tp - t >= 1) {
+ if (t != *tp) {
(void) fflush(stdout);
(void) fprintf(stderr, "\n%s: ", progname);
(void) fprintf(stderr, tformat(), *tp);
@@ -270,9 +304,9 @@ static void
usage(FILE * const stream, const int status)
{
(void) fprintf(stream,
-_("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n\
-\n\
-Report bugs to %s.\n"),
+_("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
+ "\n"
+ "Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO);
exit(status);
}
@@ -281,11 +315,10 @@ int
main(int argc, char *argv[])
{
register int i;
- register int c;
register int vflag;
+ register int Vflag;
register char * cutarg;
- register long cutloyear = ZDUMP_LO_YEAR;
- register long cuthiyear = ZDUMP_HI_YEAR;
+ register char * cuttimes;
register time_t cutlotime;
register time_t cuthitime;
register char ** fakeenv;
@@ -297,8 +330,8 @@ main(int argc, char *argv[])
register struct tm * tmp;
register struct tm * newtmp;
- INITIALIZE(cutlotime);
- INITIALIZE(cuthitime);
+ cutlotime = absolute_min_time;
+ cuthitime = absolute_max_time;
#if HAVE_GETTEXT
(void) setlocale(LC_ALL, "");
#ifdef TZ_DOMAINDIR
@@ -314,37 +347,78 @@ main(int argc, char *argv[])
} else if (strcmp(argv[i], "--help") == 0) {
usage(stdout, EXIT_SUCCESS);
}
- vflag = 0;
- cutarg = NULL;
- while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
- if (c == 'v')
- vflag = 1;
- else cutarg = optarg;
- if ((c != EOF && c != -1) ||
- (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
- usage(stderr, EXIT_FAILURE);
- }
- if (vflag) {
+ vflag = Vflag = 0;
+ cutarg = cuttimes = NULL;
+ for (;;)
+ switch (getopt(argc, argv, "c:t:vV")) {
+ case 'c': cutarg = optarg; break;
+ case 't': cuttimes = optarg; break;
+ case 'v': vflag = 1; break;
+ case 'V': Vflag = 1; 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 (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) {
- long lo;
- long hi;
- char dummy;
-
- if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
+ 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 if (sscanf(cutarg, "%ld,%ld%c",
- &lo, &hi, &dummy) == 2) {
- cutloyear = lo;
- cuthiyear = hi;
} else {
(void) fprintf(stderr, _("%s: wild -c argument %s\n"),
progname, cutarg);
exit(EXIT_FAILURE);
}
}
- checkabsolutes();
- cutlotime = yeartot(cutloyear);
- cuthitime = yeartot(cuthiyear);
+ 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)
+ hi = absolute_min_time;
+ 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)
+ hi = absolute_min_time;
+ cuthitime = hi;
+ }
+ } else {
+ (void) fprintf(stderr,
+ _("%s: wild -t argument %s\n"),
+ progname, cuttimes);
+ exit(EXIT_FAILURE);
+ }
+ }
}
(void) time(&now);
longest = 0;
@@ -375,15 +449,17 @@ main(int argc, char *argv[])
static char buf[MAX_STRING_LENGTH];
(void) strcpy(&fakeenv[0][3], argv[i]);
- if (!vflag) {
+ if (! (vflag | Vflag)) {
show(argv[i], now, FALSE);
continue;
}
warned = FALSE;
t = absolute_min_time;
- show(argv[i], t, TRUE);
- t += SECSPERHOUR * HOURSPERDAY;
- show(argv[i], t, TRUE);
+ if (!Vflag) {
+ show(argv[i], t, TRUE);
+ t += SECSPERDAY;
+ show(argv[i], t, TRUE);
+ }
if (t < cutlotime)
t = cutlotime;
tmp = my_localtime(&t);
@@ -392,9 +468,11 @@ main(int argc, char *argv[])
(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
}
for ( ; ; ) {
- if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
+ newt = (t < absolute_max_time - SECSPERDAY / 2
+ ? t + SECSPERDAY / 2
+ : absolute_max_time);
+ if (cuthitime <= newt)
break;
- newt = t + SECSPERHOUR * 12;
newtmp = localtime(&newt);
if (newtmp != NULL)
newtm = *newtmp;
@@ -415,11 +493,13 @@ main(int argc, char *argv[])
tm = newtm;
tmp = newtmp;
}
- t = absolute_max_time;
- t -= SECSPERHOUR * HOURSPERDAY;
- show(argv[i], t, TRUE);
- t += SECSPERHOUR * HOURSPERDAY;
- show(argv[i], t, TRUE);
+ if (!Vflag) {
+ t = absolute_max_time;
+ t -= SECSPERDAY;
+ show(argv[i], t, TRUE);
+ t += SECSPERDAY;
+ show(argv[i], t, TRUE);
+ }
}
if (fflush(stdout) || ferror(stdout)) {
(void) fprintf(stderr, "%s: ", progname);
@@ -431,44 +511,45 @@ main(int argc, char *argv[])
return EXIT_FAILURE;
}
-static void
-checkabsolutes(void)
-{
- if (absolute_max_time < absolute_min_time) {
- (void) fprintf(stderr,
-_("%s: use of -v on system with floating time_t other than float or double\n"),
- progname);
- exit(EXIT_FAILURE);
- }
-}
-
static time_t
-yeartot(const long y)
+yeartot(const intmax_t y)
{
- register long myy;
- register long seconds;
- register time_t t;
+ register intmax_t myy, seconds, years;
+ register time_t t;
myy = EPOCH_YEAR;
t = 0;
- while (myy != y) {
- if (myy < y) {
+ 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;
- ++myy;
- if (t > absolute_max_time - seconds) {
- t = absolute_max_time;
- break;
- }
- t += seconds;
+ 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 {
- --myy;
- seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
- if (t < absolute_min_time + seconds) {
- t = absolute_min_time;
- break;
- }
- t -= seconds;
+ seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
+ years = 1;
}
+ myy -= years;
+ if (t < absolute_min_time + seconds)
+ return absolute_min_time;
+ t -= seconds;
}
return t;
}
@@ -477,7 +558,6 @@ static time_t
hunt(char *name, time_t lot, time_t hit)
{
time_t t;
- long diff;
struct tm lotm;
register struct tm * lotmp;
struct tm tm;
@@ -490,7 +570,7 @@ hunt(char *name, time_t lot, time_t hit)
(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
}
for ( ; ; ) {
- diff = (long) (hit - lot);
+ time_t diff = hit - lot;
if (diff < 2)
break;
t = lot;
@@ -520,11 +600,11 @@ hunt(char *name, time_t lot, time_t hit)
** Thanks to Paul Eggert for logic used in delta.
*/
-static long
+static intmax_t
delta(struct tm * newp, struct tm *oldp)
{
- register long result;
- register int tmy;
+ register intmax_t result;
+ register int tmy;
if (newp->tm_year < oldp->tm_year)
return -delta(oldp, newp);
@@ -553,7 +633,7 @@ show(char *zone, time_t t, int v)
(void) printf(tformat(), t);
} else {
dumptime(tmp);
- (void) printf(" UTC");
+ (void) printf(" UT");
}
(void) printf(" = ");
}
@@ -594,18 +674,19 @@ abbr(struct tm *tmp)
static const char *
tformat(void)
{
- if (0.5 == (time_t) 0.5) { /* floating */
- if (sizeof (time_t) > sizeof (double))
- return "%Lg";
- return "%g";
- }
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))
diff --git a/timezone/zic.c b/timezone/zic.c
index 91f0d20cc1..e7b0081193 100644
--- a/timezone/zic.c
+++ b/timezone/zic.c
@@ -8,9 +8,15 @@
#include "locale.h"
#include "tzfile.h"
-#define ZIC_VERSION '2'
+#include <stdarg.h>
+
+#define ZIC_VERSION_PRE_2013 '2'
+#define ZIC_VERSION '3'
typedef int_fast64_t zic_t;
+#define ZIC_MIN INT_FAST64_MIN
+#define ZIC_MAX INT_FAST64_MAX
+#define SCNdZIC SCNdFAST64
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#define ZIC_MAX_ABBR_LEN_WO_WARN 6
@@ -38,9 +44,6 @@ typedef int_fast64_t zic_t;
#define isascii(x) 1
#endif
-#define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long))
-#define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */
-
#define end(cp) (strchr((cp), '\0'))
struct rule {
@@ -48,8 +51,8 @@ struct rule {
int r_linenum;
const char * r_name;
- int r_loyear; /* for example, 1986 */
- int r_hiyear; /* for example, 1986 */
+ zic_t r_loyear; /* for example, 1986 */
+ zic_t r_hiyear; /* for example, 1986 */
const char * r_yrtype;
int r_lowasnum;
int r_hiwasnum;
@@ -60,12 +63,12 @@ struct rule {
int r_dayofmonth;
int r_wday;
- long r_tod; /* time from midnight */
+ zic_t r_tod; /* time from midnight */
int r_todisstd; /* above is standard time if TRUE */
/* or wall clock time if FALSE */
int r_todisgmt; /* above is GMT if TRUE */
/* or local time if FALSE */
- long r_stdoff; /* offset from standard time */
+ zic_t r_stdoff; /* offset from standard time */
const char * r_abbrvar; /* variable part of abbreviation */
int r_todo; /* a rule to do (used in outzone) */
@@ -85,11 +88,11 @@ struct zone {
int z_linenum;
const char * z_name;
- long z_gmtoff;
+ zic_t z_gmtoff;
const char * z_rule;
const char * z_format;
- long z_stdoff;
+ zic_t z_stdoff;
struct rule * z_rules;
int z_nrules;
@@ -104,17 +107,23 @@ extern int link(const char * fromname, const char * toname);
extern char * optarg;
extern int optind;
+#if ! HAVE_LINK
+# define link(from, to) (-1)
+#endif
+#if ! HAVE_SYMLINK
+# define symlink(from, to) (-1)
+#endif
+
static void addtt(zic_t starttime, int type);
-static int addtype(long gmtoff, const char * abbr, int isdst,
+static int addtype(zic_t gmtoff, const char * abbr, int isdst,
int ttisstd, int ttisgmt);
static void leapadd(zic_t t, int positive, int rolling, int count);
static void adjleap(void);
static void associate(void);
static void dolink(const char * fromfield, const char * tofield);
-static long eitol(int i);
static char ** getfields(char * buf);
-static long gethms(const char * string, const char * errstrng,
- int signable);
+static zic_t gethms(const char * string, const char * errstrng,
+ int signable);
static void infile(const char * filename);
static void inleap(char ** fields, int nfields);
static void inlink(char ** fields, int nfields);
@@ -126,14 +135,14 @@ static int itsdir(const char * name);
static int lowerit(int c);
static int mkdirs(char * filename);
static void newabbr(const char * abbr);
-static long oadd(long t1, long t2);
+static zic_t oadd(zic_t t1, zic_t t2);
static void outzone(const struct zone * zp, int ntzones);
-static zic_t rpytime(const struct rule * rp, int wantedy);
+static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static void 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, long t2);
+static zic_t tadd(zic_t t1, zic_t t2);
static int yearistype(int year, const char * type);
static int charcnt;
@@ -141,13 +150,13 @@ static int errors;
static const char * filename;
static int leapcnt;
static int leapseen;
-static int leapminyear;
-static int leapmaxyear;
+static zic_t leapminyear;
+static zic_t leapmaxyear;
static int linenum;
static int max_abbrvar_len;
static int max_format_len;
-static int max_year;
-static int min_year;
+static zic_t max_year;
+static zic_t min_year;
static int noise;
static const char * rfilename;
static int rlinenum;
@@ -338,14 +347,14 @@ static struct attype {
zic_t at;
unsigned char type;
} attypes[TZ_MAX_TIMES];
-static long gmtoffs[TZ_MAX_TYPES];
+static zic_t gmtoffs[TZ_MAX_TYPES];
static char isdsts[TZ_MAX_TYPES];
static unsigned char abbrinds[TZ_MAX_TYPES];
static char ttisstds[TZ_MAX_TYPES];
static char ttisgmts[TZ_MAX_TYPES];
static char chars[TZ_MAX_CHARS];
static zic_t trans[TZ_MAX_LEAPS];
-static long corr[TZ_MAX_LEAPS];
+static zic_t corr[TZ_MAX_LEAPS];
static char roll[TZ_MAX_LEAPS];
/*
@@ -390,16 +399,16 @@ eat(const char *const name, const int num)
eats(name, num, NULL, -1);
}
-static void
-error(const char *const string)
+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.
*/
- (void) fprintf(stderr, _("\"%s\", line %d: %s"),
- filename, linenum, string);
+ fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
+ vfprintf(stderr, string, args);
if (rfilename != NULL)
(void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
rfilename, rlinenum);
@@ -407,19 +416,27 @@ error(const char *const string)
++errors;
}
-static void
-warning(const char *const string)
+static void ATTRIBUTE_FORMAT((printf, 1, 2))
+error(const char *const string, ...)
{
- char * cp;
+ va_list args;
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+}
- cp = ecpyalloc(_("warning: "));
- cp = ecatalloc(cp, string);
- error(cp);
- free(cp);
+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);
--errors;
}
-static void
+static _Noreturn void
usage(FILE *stream, int status)
{
(void) fprintf(stream, _("%s: usage is %s \
@@ -444,9 +461,9 @@ main(int argc, char **argv)
register int j;
register int c;
-#ifdef unix
+#ifdef S_IWGRP
(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
-#endif /* defined unix */
+#endif
#if HAVE_GETTEXT
(void) setlocale(LC_ALL, "");
#ifdef TZ_DOMAINDIR
@@ -602,41 +619,71 @@ dolink(const char *const fromfield, const char *const tofield)
*/
if (!itsdir(toname))
(void) remove(toname);
- if (link(fromname, toname) != 0) {
+ if (link(fromname, toname) != 0
+ && access(fromname, F_OK) == 0 && !itsdir(fromname)) {
int result;
if (mkdirs(toname) != 0)
exit(EXIT_FAILURE);
result = link(fromname, toname);
-#if HAVE_SYMLINK
- if (result != 0 &&
- access(fromname, F_OK) == 0 &&
- !itsdir(fromname)) {
- const char *s = tofield;
+ if (result != 0) {
+ const char *s = fromfield;
+ const char *t;
register char * symlinkcontents = NULL;
- while ((s = strchr(s+1, '/')) != NULL)
+ do
+ t = s;
+ while ((s = strchr(s, '/'))
+ && ! strncmp (fromfield, tofield,
+ ++s - fromfield));
+
+ for (s = tofield + (t - fromfield);
+ (s = strchr(s, '/'));
+ s++)
symlinkcontents =
ecatalloc(symlinkcontents,
"../");
- symlinkcontents =
- ecatalloc(symlinkcontents,
- fromname);
- result = symlink(symlinkcontents,
- toname);
+ symlinkcontents = ecatalloc(symlinkcontents, t);
+ result = symlink(symlinkcontents, toname);
if (result == 0)
warning(_("hard link failed, symbolic link used"));
free(symlinkcontents);
}
-#endif /* HAVE_SYMLINK */
if (result != 0) {
- const char *e = strerror(errno);
-
- (void) fprintf(stderr,
- _("%s: Can't link from %s to %s: %s\n"),
- progname, fromname, toname, e);
- exit(EXIT_FAILURE);
+ FILE *fp, *tp;
+ int c;
+ fp = fopen(fromname, "rb");
+ if (!fp) {
+ const char *e = strerror(errno);
+ (void) fprintf(stderr,
+ _("%s: Can't read %s: %s\n"),
+ progname, fromname, e);
+ exit(EXIT_FAILURE);
+ }
+ tp = fopen(toname, "wb");
+ if (!tp) {
+ const char *e = strerror(errno);
+ (void) fprintf(stderr,
+ _("%s: Can't create %s: %s\n"),
+ progname, toname, e);
+ exit(EXIT_FAILURE);
+ }
+ while ((c = getc(fp)) != EOF)
+ putc(c, tp);
+ if (ferror(fp) || fclose(fp)) {
+ (void) fprintf(stderr,
+ _("%s: Error reading %s\n"),
+ progname, fromname);
+ exit(EXIT_FAILURE);
+ }
+ if (ferror(tp) || fclose(tp)) {
+ (void) fprintf(stderr,
+ _("%s: Error writing %s\n"),
+ progname, toname);
+ exit(EXIT_FAILURE);
+ }
+ warning(_("link failed, copy used"));
}
}
free(fromname);
@@ -744,7 +791,7 @@ associate(void)
** a '%s' in the format is a bad thing.
*/
if (strchr(zp->z_format, '%') != 0)
- error(_("%s in ruleless zone"));
+ error("%s", _("%s in ruleless zone"));
}
}
if (errors)
@@ -854,10 +901,10 @@ _("%s: panic: Invalid l_value %d\n"),
** Call error with errstring and return zero on errors.
*/
-static long
+static zic_t
gethms(const char *string, const char *const errstring, const int signable)
{
- long hh;
+ zic_t hh;
int mm, ss, sign;
if (string == NULL || *string == '\0')
@@ -868,22 +915,22 @@ gethms(const char *string, const char *const errstring, const int signable)
sign = -1;
++string;
} else sign = 1;
- if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
+ if (sscanf(string, scheck(string, "%"SCNdZIC), &hh) == 1)
mm = ss = 0;
- else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
+ else if (sscanf(string, scheck(string, "%"SCNdZIC":%d"), &hh, &mm) == 2)
ss = 0;
- else if (sscanf(string, scheck(string, "%ld:%d:%d"),
+ else if (sscanf(string, scheck(string, "%"SCNdZIC":%d:%d"),
&hh, &mm, &ss) != 3) {
- error(errstring);
+ error("%s", errstring);
return 0;
}
if (hh < 0 ||
mm < 0 || mm >= MINSPERHOUR ||
ss < 0 || ss > SECSPERMIN) {
- error(errstring);
+ error("%s", errstring);
return 0;
}
- if (LONG_MAX / SECSPERHOUR < hh) {
+ if (ZIC_MAX / SECSPERHOUR < hh) {
error(_("time overflow"));
return 0;
}
@@ -892,8 +939,8 @@ gethms(const char *string, const char *const errstring, const int signable)
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(eitol(sign) * hh * eitol(SECSPERHOUR),
- eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)));
+ return oadd(sign * hh * SECSPERHOUR,
+ sign * (mm * SECSPERMIN + ss));
}
static void
@@ -926,40 +973,31 @@ static int
inzone(register char **const fields, const int nfields)
{
register int i;
- static char * buf;
if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
error(_("wrong number of fields on Zone line"));
return FALSE;
}
if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
- buf = erealloc(buf, 132 + strlen(TZDEFAULT));
- (void) sprintf(buf,
+ error(
_("\"Zone %s\" line and -l option are mutually exclusive"),
TZDEFAULT);
- error(buf);
return FALSE;
}
if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
- buf = erealloc(buf, 132 + strlen(TZDEFRULES));
- (void) sprintf(buf,
+ error(
_("\"Zone %s\" line and -p option are mutually exclusive"),
TZDEFRULES);
- error(buf);
return FALSE;
}
for (i = 0; i < nzones; ++i)
if (zones[i].z_name != NULL &&
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
- buf = erealloc(buf,
- (132 + strlen(fields[ZF_NAME])
- + strlen(zones[i].z_filename)));
- (void) sprintf(buf,
+ error(
_("duplicate zone name %s (file \"%s\", line %d)"),
fields[ZF_NAME],
zones[i].z_filename,
zones[i].z_linenum);
- error(buf);
return FALSE;
}
return inzsub(fields, nfields, FALSE);
@@ -1006,7 +1044,7 @@ inzsub(register char **const fields, const int nfields, const int iscont)
}
z.z_filename = filename;
z.z_linenum = linenum;
- z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
+ z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), TRUE);
if ((cp = strchr(fields[i_format], '%')) != 0) {
if (*++cp != 's' || strchr(cp, '%') != 0) {
error(_("invalid abbreviation format"));
@@ -1058,8 +1096,9 @@ inleap(register char ** const fields, const int nfields)
register const char * cp;
register const struct lookup * lp;
register int i, j;
- int year, month, day;
- long dayoff, tod;
+ zic_t year;
+ int month, day;
+ zic_t dayoff, tod;
zic_t t;
if (nfields != LEAP_FIELDS) {
@@ -1068,7 +1107,7 @@ inleap(register char ** const fields, const int nfields)
}
dayoff = 0;
cp = fields[LP_YEAR];
- if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
+ if (sscanf(cp, scheck(cp, "%"SCNdZIC), &year) != 1) {
/*
** Leapin' Lizards!
*/
@@ -1089,7 +1128,7 @@ inleap(register char ** const fields, const int nfields)
--j;
i = -len_years[isleap(j)];
}
- dayoff = oadd(dayoff, eitol(i));
+ dayoff = oadd(dayoff, i);
}
if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
error(_("invalid month name"));
@@ -1099,7 +1138,7 @@ inleap(register char ** const fields, const int nfields)
j = TM_JANUARY;
while (j != month) {
i = len_months[isleap(year)][j];
- dayoff = oadd(dayoff, eitol(i));
+ dayoff = oadd(dayoff, i);
++j;
}
cp = fields[LP_DAY];
@@ -1108,7 +1147,7 @@ inleap(register char ** const fields, const int nfields)
error(_("invalid day of month"));
return;
}
- dayoff = oadd(dayoff, eitol(day - 1));
+ dayoff = oadd(dayoff, day - 1);
if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
error(_("time before zero"));
return;
@@ -1233,17 +1272,17 @@ rulesub(register struct rule *const rp,
rp->r_lowasnum = lp == NULL;
if (!rp->r_lowasnum) switch ((int) lp->l_value) {
case YR_MINIMUM:
- rp->r_loyear = INT_MIN;
+ rp->r_loyear = ZIC_MIN;
break;
case YR_MAXIMUM:
- rp->r_loyear = INT_MAX;
+ rp->r_loyear = ZIC_MAX;
break;
default: /* "cannot happen" */
(void) fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value);
exit(EXIT_FAILURE);
- } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
+ } else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_loyear) != 1) {
error(_("invalid starting year"));
return;
}
@@ -1252,10 +1291,10 @@ rulesub(register struct rule *const rp,
rp->r_hiwasnum = lp == NULL;
if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
case YR_MINIMUM:
- rp->r_hiyear = INT_MIN;
+ rp->r_hiyear = ZIC_MIN;
break;
case YR_MAXIMUM:
- rp->r_hiyear = INT_MAX;
+ rp->r_hiyear = ZIC_MAX;
break;
case YR_ONLY:
rp->r_hiyear = rp->r_loyear;
@@ -1265,7 +1304,7 @@ rulesub(register struct rule *const rp,
_("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value);
exit(EXIT_FAILURE);
- } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
+ } else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_hiyear) != 1) {
error(_("invalid ending year"));
return;
}
@@ -1330,7 +1369,7 @@ rulesub(register struct rule *const rp,
}
static void
-convert(const long val, char *const buf)
+convert(const int_fast32_t val, char *const buf)
{
register int i;
register int shift;
@@ -1352,7 +1391,7 @@ convert64(const zic_t val, char *const buf)
}
static void
-puttzcode(const long val, FILE *const fp)
+puttzcode(const int_fast32_t val, FILE *const fp)
{
char buf[4];
@@ -1385,7 +1424,7 @@ is32(const zic_t x)
}
static void
-writezone(const char *const name, const char *const string)
+writezone(const char *const name, const char *const string, char version)
{
register FILE * fp;
register int i, j;
@@ -1414,8 +1453,11 @@ writezone(const char *const name, const char *const string)
fromi = 0;
while (fromi < timecnt && attypes[fromi].at < min_time)
++fromi;
- if (isdsts[0] == 0)
- while (fromi < timecnt && attypes[fromi].type == 0)
+ /*
+ ** Remember that type 0 is reserved.
+ */
+ if (isdsts[1] == 0)
+ while (fromi < timecnt && attypes[fromi].type == 1)
++fromi; /* handled by default rule */
for ( ; fromi < timecnt; ++fromi) {
if (toi != 0 && ((attypes[fromi].at +
@@ -1517,7 +1559,11 @@ writezone(const char *const name, const char *const string)
}
thistimelim = thistimei + thistimecnt;
thisleaplim = thisleapi + thisleapcnt;
- for (i = 0; i < typecnt; ++i)
+ /*
+ ** Remember that type 0 is reserved.
+ */
+ writetype[0] = FALSE;
+ for (i = 1; i < typecnt; ++i)
writetype[i] = thistimecnt == timecnt;
if (thistimecnt == 0) {
/*
@@ -1533,8 +1579,11 @@ writezone(const char *const name, const char *const string)
/*
** For America/Godthab and Antarctica/Palmer
*/
+ /*
+ ** Remember that type 0 is reserved.
+ */
if (thistimei == 0)
- writetype[0] = TRUE;
+ writetype[1] = TRUE;
}
#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
/*
@@ -1584,8 +1633,26 @@ writezone(const char *const name, const char *const string)
}
#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
thistypecnt = 0;
+ /*
+ ** Potentially, set type 0 to that of lowest-valued time.
+ */
+ if (thistimei > 0) {
+ for (i = 1; i < typecnt; ++i)
+ if (writetype[i] && !isdsts[i])
+ break;
+ if (i != types[thistimei - 1]) {
+ i = types[thistimei - 1];
+ gmtoffs[0] = gmtoffs[i];
+ isdsts[0] = isdsts[i];
+ ttisstds[0] = ttisstds[i];
+ ttisgmts[0] = ttisgmts[i];
+ abbrinds[0] = abbrinds[i];
+ writetype[0] = TRUE;
+ writetype[i] = FALSE;
+ }
+ }
for (i = 0; i < typecnt; ++i)
- typemap[i] = writetype[i] ? thistypecnt++ : -1;
+ typemap[i] = writetype[i] ? thistypecnt++ : 0;
for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
indmap[i] = -1;
thischarcnt = 0;
@@ -1610,13 +1677,13 @@ writezone(const char *const name, const char *const string)
#define DO(field) ((void) fwrite(tzh.field, sizeof tzh.field, 1, fp))
tzh = tzh0;
(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
- tzh.tzh_version[0] = ZIC_VERSION;
- convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
- convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
- convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
- convert(eitol(thistimecnt), tzh.tzh_timecnt);
- convert(eitol(thistypecnt), tzh.tzh_typecnt);
- convert(eitol(thischarcnt), tzh.tzh_charcnt);
+ tzh.tzh_version[0] = version;
+ convert(thistypecnt, tzh.tzh_ttisgmtcnt);
+ convert(thistypecnt, tzh.tzh_ttisstdcnt);
+ convert(thisleapcnt, tzh.tzh_leapcnt);
+ convert(thistimecnt, tzh.tzh_timecnt);
+ convert(thistypecnt, tzh.tzh_typecnt);
+ convert(thischarcnt, tzh.tzh_charcnt);
DO(tzh_magic);
DO(tzh_version);
DO(tzh_reserved);
@@ -1629,7 +1696,7 @@ writezone(const char *const name, const char *const string)
#undef DO
for (i = thistimei; i < thistimelim; ++i)
if (pass == 1)
- puttzcode((long) ats[i], fp);
+ puttzcode(ats[i], fp);
else puttzcode64(ats[i], fp);
for (i = thistimei; i < thistimelim; ++i) {
unsigned char uc;
@@ -1723,7 +1790,7 @@ doabbr(char *const abbr, const char *const format, const char *const letters,
}
static void
-updateminmax(const int x)
+updateminmax(const zic_t x)
{
if (min_year > x)
min_year = x;
@@ -1732,7 +1799,7 @@ updateminmax(const int x)
}
static int
-stringoffset(char *result, long offset)
+stringoffset(char *result, zic_t offset)
{
register int hours;
register int minutes;
@@ -1748,7 +1815,7 @@ stringoffset(char *result, long offset)
minutes = offset % MINSPERHOUR;
offset /= MINSPERHOUR;
hours = offset;
- if (hours > HOURSPERDAY) {
+ if (hours >= HOURSPERDAY * DAYSPERWEEK) {
result[0] = '\0';
return -1;
}
@@ -1762,10 +1829,11 @@ stringoffset(char *result, long offset)
}
static int
-stringrule(char *result, const struct rule *const rp, const long dstoff,
- const long gmtoff)
+stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
+ const zic_t gmtoff)
{
- register long tod;
+ register zic_t tod = rp->r_tod;
+ register int compat = 0;
result = end(result);
if (rp->r_dycode == DC_DOM) {
@@ -1776,44 +1844,76 @@ stringrule(char *result, const struct rule *const rp, const long dstoff,
total = 0;
for (month = 0; month < rp->r_month; ++month)
total += len_months[0][month];
- (void) sprintf(result, "J%d", total + rp->r_dayofmonth);
+ /* Omit the "J" in Jan and Feb, as that's shorter. */
+ if (rp->r_month <= 1)
+ (void) sprintf(result, "%d", total + rp->r_dayofmonth - 1);
+ else
+ (void) 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) {
- if ((rp->r_dayofmonth % DAYSPERWEEK) != 1)
- return -1;
- week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
+ 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 {
- if ((rp->r_dayofmonth % DAYSPERWEEK) != 0)
- return -1;
+ 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;
(void) sprintf(result, "M%d.%d.%d",
- rp->r_month + 1, week, rp->r_wday);
+ rp->r_month + 1, week, wday);
}
- tod = rp->r_tod;
if (rp->r_todisgmt)
tod += gmtoff;
if (rp->r_todisstd && rp->r_stdoff == 0)
tod += dstoff;
- if (tod < 0) {
- result[0] = '\0';
- return -1;
- }
if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
(void) strcat(result, "/");
if (stringoffset(end(result), tod) != 0)
return -1;
+ if (tod < 0) {
+ if (compat < 2013)
+ compat = 2013;
+ } else if (SECSPERDAY <= tod) {
+ if (compat < 1994)
+ compat = 1994;
+ }
}
- return 0;
+ return compat;
}
-static void
+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_month - b->r_month != 0)
+ return a->r_month - b->r_month;
+ return a->r_dayofmonth - b->r_dayofmonth;
+}
+
+enum { YEAR_BY_YEAR_ZONE = 1 };
+
+static int
stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
{
register const struct zone * zp;
@@ -1822,77 +1922,106 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
register struct rule * dstrp;
register int i;
register const char * abbrvar;
+ register int compat = 0;
+ register int c;
+ struct rule stdr, dstr;
result[0] = '\0';
zp = zpfirst + zonecount - 1;
stdrp = dstrp = NULL;
for (i = 0; i < zp->z_nrules; ++i) {
rp = &zp->z_rules[i];
- if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
+ if (rp->r_hiwasnum || rp->r_hiyear != ZIC_MAX)
continue;
if (rp->r_yrtype != NULL)
continue;
if (rp->r_stdoff == 0) {
if (stdrp == NULL)
stdrp = rp;
- else return;
+ else return -1;
} else {
if (dstrp == NULL)
dstrp = rp;
- else return;
+ else return -1;
}
}
if (stdrp == NULL && dstrp == NULL) {
/*
** There are no rules running through "max".
- ** Let's find the latest rule.
+ ** Find the latest std rule in stdabbrrp
+ ** and latest rule of any type in stdrp.
*/
+ register struct rule *stdabbrrp = NULL;
for (i = 0; i < zp->z_nrules; ++i) {
rp = &zp->z_rules[i];
- if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
- (rp->r_hiyear == stdrp->r_hiyear &&
- (rp->r_month > stdrp->r_month ||
- (rp->r_month == stdrp->r_month &&
- rp->r_dayofmonth > stdrp->r_dayofmonth))))
- stdrp = rp;
+ if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
+ stdabbrrp = rp;
+ if (rule_cmp(stdrp, rp) < 0)
+ stdrp = rp;
}
- if (stdrp != NULL && stdrp->r_stdoff != 0)
- return; /* We end up in DST (a POSIX no-no). */
/*
** Horrid special case: if year is 2037,
** presume this is a zone handled on a year-by-year basis;
** do not try to apply a rule to the zone.
*/
if (stdrp != NULL && stdrp->r_hiyear == 2037)
- return;
+ return YEAR_BY_YEAR_ZONE;
+
+ if (stdrp != NULL && stdrp->r_stdoff != 0) {
+ /* Perpetual DST. */
+ dstr.r_month = TM_JANUARY;
+ dstr.r_dycode = DC_DOM;
+ dstr.r_dayofmonth = 1;
+ dstr.r_tod = 0;
+ dstr.r_todisstd = dstr.r_todisgmt = FALSE;
+ dstr.r_stdoff = stdrp->r_stdoff;
+ dstr.r_abbrvar = stdrp->r_abbrvar;
+ stdr.r_month = TM_DECEMBER;
+ stdr.r_dycode = DC_DOM;
+ stdr.r_dayofmonth = 31;
+ stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
+ stdr.r_todisstd = stdr.r_todisgmt = FALSE;
+ stdr.r_stdoff = 0;
+ stdr.r_abbrvar
+ = (stdabbrrp ? stdabbrrp->r_abbrvar : "");
+ dstrp = &dstr;
+ stdrp = &stdr;
+ }
}
if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
- return;
+ return -1;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
result[0] = '\0';
- return;
+ return -1;
}
if (dstrp == NULL)
- return;
+ return compat;
doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
if (stringoffset(end(result),
-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
result[0] = '\0';
- return;
+ return -1;
}
(void) strcat(result, ",");
- if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+ c = stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
+ if (c < 0) {
result[0] = '\0';
- return;
+ return -1;
}
+ if (compat < c)
+ compat = c;
(void) strcat(result, ",");
- if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+ c = stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff);
+ if (c < 0) {
result[0] = '\0';
- return;
+ return -1;
}
+ if (compat < c)
+ compat = c;
+ return compat;
}
static void
@@ -1903,10 +2032,10 @@ outzone(const struct zone * const zpfirst, const int zonecount)
register int i, j;
register int usestart, useuntil;
register zic_t starttime, untiltime;
- register long gmtoff;
- register long stdoff;
- register int year;
- register long startoff;
+ register zic_t gmtoff;
+ register zic_t stdoff;
+ register zic_t year;
+ register zic_t startoff;
register int startttisstd;
register int startttisgmt;
register int type;
@@ -1916,6 +2045,9 @@ outzone(const struct zone * const zpfirst, const int zonecount)
register int max_abbr_len;
register int max_envvar_len;
register int prodstic; /* all rules are min to max */
+ register int compat;
+ register int do_extend;
+ register char version;
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -1940,8 +2072,13 @@ outzone(const struct zone * const zpfirst, const int zonecount)
min_year = max_year = EPOCH_YEAR;
if (leapseen) {
updateminmax(leapminyear);
- updateminmax(leapmaxyear + (leapmaxyear < INT_MAX));
+ updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
}
+ /*
+ ** Reserve type 0.
+ */
+ gmtoffs[0] = isdsts[0] = ttisstds[0] = ttisgmts[0] = abbrinds[0] = -1;
+ typecnt = 1;
for (i = 0; i < zonecount; ++i) {
zp = &zpfirst[i];
if (i < zonecount - 1)
@@ -1959,23 +2096,45 @@ outzone(const struct zone * const zpfirst, const int zonecount)
/*
** Generate lots of data if a rule can't cover all future times.
*/
- stringzone(envvar, zpfirst, zonecount);
- if (noise && envvar[0] == '\0') {
- register char * wp;
-
-wp = ecpyalloc(_("no POSIX environment variable for zone"));
- wp = ecatalloc(wp, " ");
- wp = ecatalloc(wp, zpfirst->z_name);
- warning(wp);
- free(wp);
- }
- if (envvar[0] == '\0') {
- if (min_year >= INT_MIN + YEARSPERREPEAT)
- min_year -= YEARSPERREPEAT;
- else min_year = INT_MIN;
- if (max_year <= INT_MAX - YEARSPERREPEAT)
- max_year += YEARSPERREPEAT;
- else max_year = INT_MAX;
+ compat = stringzone(envvar, zpfirst, zonecount);
+ version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION;
+ do_extend = compat < 0 || compat == YEAR_BY_YEAR_ZONE;
+ if (noise) {
+ if (!*envvar)
+ warning("%s %s",
+ _("no POSIX environment variable for zone"),
+ zpfirst->z_name);
+ else if (compat != 0 && compat != YEAR_BY_YEAR_ZONE) {
+ /* 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
@@ -1984,7 +2143,7 @@ wp = ecpyalloc(_("no POSIX environment variable for zone"));
*/
if (prodstic) {
min_year = 1900;
- max_year = min_year + YEARSPERREPEAT;
+ max_year = min_year + years_of_observations;
}
}
/*
@@ -2041,12 +2200,12 @@ wp = ecpyalloc(_("no POSIX environment variable for zone"));
for ( ; ; ) {
register int k;
register zic_t jtime, ktime;
- register long offset;
+ register zic_t offset;
INITIALIZE(ktime);
if (useuntil) {
/*
- ** Turn untiltime into UTC
+ ** Turn untiltime into UT
** assuming the current gmtoff and
** stdoff values.
*/
@@ -2150,7 +2309,45 @@ error(_("can't determine time zone abbreviation to use just after until time"));
starttime = tadd(starttime, -gmtoff);
}
}
- writezone(zpfirst->z_name, envvar);
+ 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[0], i = 1; i < timecnt; i++)
+ if (attypes[i].at > lastat->at)
+ lastat = &attypes[i];
+ if (lastat->at < rpytime(&xr, max_year - 1)) {
+ /*
+ ** Create new type code for the redundant entry,
+ ** to prevent it being optimised away.
+ */
+ if (typecnt >= TZ_MAX_TYPES) {
+ error(_("too many local time types"));
+ exit(EXIT_FAILURE);
+ }
+ gmtoffs[typecnt] = gmtoffs[lastat->type];
+ isdsts[typecnt] = isdsts[lastat->type];
+ ttisstds[typecnt] = ttisstds[lastat->type];
+ ttisgmts[typecnt] = ttisgmts[lastat->type];
+ abbrinds[typecnt] = abbrinds[lastat->type];
+ ++typecnt;
+ addtt(rpytime(&xr, max_year + 1), typecnt-1);
+ }
+ }
+ writezone(zpfirst->z_name, envvar, version);
free(startbuf);
free(ab);
free(envvar);
@@ -2183,7 +2380,7 @@ addtt(const zic_t starttime, int type)
}
static int
-addtype(const long gmtoff, const char *const abbr, const int isdst,
+addtype(const zic_t gmtoff, const char *const abbr, const int isdst,
const int ttisstd, const int ttisgmt)
{
register int i, j;
@@ -2220,7 +2417,7 @@ addtype(const long gmtoff, const char *const abbr, const int isdst,
exit(EXIT_FAILURE);
}
if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
- error(_("UTC offset out of range"));
+ error(_("UT offset out of range"));
exit(EXIT_FAILURE);
}
gmtoffs[i] = gmtoff;
@@ -2262,7 +2459,7 @@ leapadd(const zic_t t, const int positive, const int rolling, int count)
roll[j] = roll[j - 1];
}
trans[i] = t;
- corr[i] = positive ? 1L : eitol(-count);
+ corr[i] = positive ? 1 : -count;
roll[i] = rolling;
++leapcnt;
} while (positive && --count != 0);
@@ -2272,7 +2469,7 @@ static void
adjleap(void)
{
register int i;
- register long last = 0;
+ register zic_t last = 0;
/*
** propagate leap seconds forward
@@ -2406,10 +2603,10 @@ getfields(register char *cp)
return array;
}
-static ATTRIBUTE_PURE long
-oadd(const long t1, const long t2)
+static ATTRIBUTE_PURE zic_t
+oadd(const zic_t t1, const zic_t t2)
{
- if (t1 < 0 ? t2 < LONG_MIN - t1 : LONG_MAX - t1 < t2) {
+ if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) {
error(_("time overflow"));
exit(EXIT_FAILURE);
}
@@ -2417,7 +2614,7 @@ oadd(const long t1, const long t2)
}
static ATTRIBUTE_PURE zic_t
-tadd(const zic_t t1, const long t2)
+tadd(const zic_t t1, const zic_t t2)
{
if (t1 == max_time && t2 > 0)
return max_time;
@@ -2436,15 +2633,15 @@ tadd(const zic_t t1, const long t2)
*/
static zic_t
-rpytime(register const struct rule *const rp, register const int wantedy)
+rpytime(register const struct rule *const rp, register const zic_t wantedy)
{
- register int y, m, i;
- register long dayoff; /* with a nod to Margaret O. */
- register zic_t t;
+ register int m, i;
+ register zic_t dayoff; /* with a nod to Margaret O. */
+ register zic_t t, y;
- if (wantedy == INT_MIN)
+ if (wantedy == ZIC_MIN)
return min_time;
- if (wantedy == INT_MAX)
+ if (wantedy == ZIC_MAX)
return max_time;
dayoff = 0;
m = TM_JANUARY;
@@ -2457,11 +2654,11 @@ rpytime(register const struct rule *const rp, register const int wantedy)
--y;
i = -len_years[isleap(y)];
}
- dayoff = oadd(dayoff, eitol(i));
+ dayoff = oadd(dayoff, i);
}
while (m != rp->r_month) {
i = len_months[isleap(y)][m];
- dayoff = oadd(dayoff, eitol(i));
+ dayoff = oadd(dayoff, i);
++m;
}
i = rp->r_dayofmonth;
@@ -2474,12 +2671,12 @@ rpytime(register const struct rule *const rp, register const int wantedy)
}
}
--i;
- dayoff = oadd(dayoff, eitol(i));
+ dayoff = oadd(dayoff, i);
if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
- register long wday;
+ register zic_t wday;
-#define LDAYSPERWEEK ((long) DAYSPERWEEK)
- wday = eitol(EPOCH_WDAY);
+#define LDAYSPERWEEK ((zic_t) DAYSPERWEEK)
+ wday = EPOCH_WDAY;
/*
** Don't trust mod of negative numbers.
*/
@@ -2490,7 +2687,7 @@ rpytime(register const struct rule *const rp, register const int wantedy)
if (wday < 0)
wday += LDAYSPERWEEK;
}
- while (wday != eitol(rp->r_wday))
+ while (wday != rp->r_wday)
if (rp->r_dycode == DC_DOWGEQ) {
dayoff = oadd(dayoff, 1);
if (++wday >= LDAYSPERWEEK)
@@ -2550,14 +2747,8 @@ mp = _("time zone abbreviation has too many alphabetics");
}
if (*cp != '\0')
mp = _("time zone abbreviation differs from POSIX standard");
- if (mp != NULL) {
- char *wp = ecpyalloc(mp);
- wp = ecatalloc(wp, " (");
- wp = ecatalloc(wp, string);
- wp = ecatalloc(wp, ")");
- warning(wp);
- free(wp);
- }
+ if (mp != NULL)
+ warning("%s (%s)", mp, string);
}
i = strlen(string) + 1;
if (charcnt + i > TZ_MAX_CHARS) {
@@ -2565,7 +2756,7 @@ mp = _("time zone abbreviation differs from POSIX standard");
exit(EXIT_FAILURE);
}
(void) strcpy(&chars[charcnt], string);
- charcnt += eitol(i);
+ charcnt += i;
}
static int
@@ -2579,7 +2770,7 @@ mkdirs(char *argname)
cp = name = ecpyalloc(argname);
while ((cp = strchr(cp + 1, '/')) != 0) {
*cp = '\0';
-#ifndef unix
+#ifdef HAVE_DOS_FILE_NAMES
/*
** DOS drive specifier?
*/
@@ -2588,7 +2779,7 @@ mkdirs(char *argname)
*cp = '/';
continue;
}
-#endif /* !defined unix */
+#endif
if (!itsdir(name)) {
/*
** It doesn't seem to exist, so we try to create it.
@@ -2614,21 +2805,6 @@ _("%s: Can't create directory %s: %s\n"),
return 0;
}
-static ATTRIBUTE_PURE long
-eitol(const int i)
-{
- long l;
-
- l = i;
- if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
- (void) fprintf(stderr,
- _("%s: %d did not sign extend correctly\n"),
- progname, i);
- exit(EXIT_FAILURE);
- }
- return l;
-}
-
/*
** UNIX was a registered trademark of The Open Group in 2003.
*/