This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]