This is the mail archive of the
glibc-cvs@sourceware.org
mailing list for the glibc project.
GNU C Library master sources branch master updated. glibc-2.18-682-g85bff96
- From: jsm28 at sourceware dot org
- To: glibc-cvs at sourceware dot org
- Date: 20 Dec 2013 13:10:37 -0000
- Subject: GNU C Library master sources branch master updated. glibc-2.18-682-g85bff96
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".
The branch, master has been updated
via 85bff96ad652b463f83d4cf19239eff1535e186a (commit)
from b7867a3bfb9d7e00204c088feb227abddfc7bb43 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=85bff96ad652b463f83d4cf19239eff1535e186a
commit 85bff96ad652b463f83d4cf19239eff1535e186a
Author: Joseph Myers <joseph@codesourcery.com>
Date: Fri Dec 20 13:10:07 2013 +0000
Update timezone code from tzcode 2013i.
Now we have Paul's support for version-3 tz files checked in, this
patch updates all the code we take (unmodified) from tzcode to version
2013i (which includes the support for generating version-3 tz files
where necessary).
Tested x86_64.
* 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.
diff --git a/ChangeLog b/ChangeLog
index 1a1b932..80fab8e 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 c88b12f..fec4f62 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 1d1d391..4eb0ab6 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 ed60980..8bd01a8 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 0f6c687..529650d 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 8e66b44..9d70691 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 9255aff..209b79d 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 91f0d20..e7b0081 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.
*/
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 8 +
timezone/checktab.awk | 29 ++-
timezone/private.h | 148 +++++++++++-
timezone/scheck.c | 29 ++-
timezone/tzfile.h | 25 +-
timezone/tzselect.ksh | 366 ++++++++++++++++++++++-------
timezone/zdump.c | 327 ++++++++++++++++----------
timezone/zic.c | 620 +++++++++++++++++++++++++++++++------------------
8 files changed, 1072 insertions(+), 480 deletions(-)
hooks/post-receive
--
GNU C Library master sources