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.22-650-g670a687


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  670a687dea6773147a227bebfa9d801dae739ee0 (commit)
      from  45c4f3665aaa63cab148cc9cc96fa07c666c1c38 (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=670a687dea6773147a227bebfa9d801dae739ee0

commit 670a687dea6773147a227bebfa9d801dae739ee0
Author: Paul Eggert <eggert@cs.ucla.edu>
Date:   Thu Jan 7 11:45:07 2016 +0000

    Update timezone code from tzcode 2015g.
    
    This patch updates the timezone code from tzcode 2015g.  The Makefile
    and README changes are based on those in Paul's patch
    <https://sourceware.org/ml/libc-alpha/2015-05/msg00553.html>.
    
    Tested for x86_64 and x86.
    
    2016-01-06  Paul Eggert  <eggert@cs.ucla.edu>
    	    Joseph Myers  <joseph@codesourcery.com>
    
    	* timezone/private.h: Update from tzcode 2015g.
    	* timezone/tzfile.h: Likewise.
    	* timezone/tzselect.ksh: Likewise.
    	* timezone/zdump.c: Likewise.
    	* timezone/zic.c: Likewise.
    	* timezone/ialloc.c: Remove file.
    	* timezone/scheck.c: Likewise.
    	* timezone/Makefile (extra-objs): Remove variable.
    	($(objpfx)zic): Do not depend on scheck.o and ialloc.o.
    	(tz-cflags): Add -DHAVE_GETTEXT -DUSE_LTZ=0
    	-Wno-maybe-uninitialized.
    	(CFLAGS-zdump.c): Remove -fwrapv -DNOID -DHAVE_GETTEXT.
    	(CFLAGS-zic.c): Remove -DNOID -DHAVE_GETTEXT.
    	(CFLAGS-ialloc.c): Remove variable.
    	(CFLAGS-scheck.c): Likewise.
    	* timezone/README: Update list of files from tzcode.

diff --git a/ChangeLog b/ChangeLog
index 67f2181..d7dd24b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2016-01-06  Paul Eggert  <eggert@cs.ucla.edu>
+	    Joseph Myers  <joseph@codesourcery.com>
+
+	* timezone/private.h: Update from tzcode 2015g.
+	* timezone/tzfile.h: Likewise.
+	* timezone/tzselect.ksh: Likewise.
+	* timezone/zdump.c: Likewise.
+	* timezone/zic.c: Likewise.
+	* timezone/ialloc.c: Remove file.
+	* timezone/scheck.c: Likewise.
+	* timezone/Makefile (extra-objs): Remove variable.
+	($(objpfx)zic): Do not depend on scheck.o and ialloc.o.
+	(tz-cflags): Add -DHAVE_GETTEXT -DUSE_LTZ=0
+	-Wno-maybe-uninitialized.
+	(CFLAGS-zdump.c): Remove -fwrapv -DNOID -DHAVE_GETTEXT.
+	(CFLAGS-zic.c): Remove -DNOID -DHAVE_GETTEXT.
+	(CFLAGS-ialloc.c): Remove variable.
+	(CFLAGS-scheck.c): Likewise.
+	* timezone/README: Update list of files from tzcode.
+
 2016-01-07  Khem Raj  <raj.khem@gmail.com>
 
 	* argp/argp-fmtstream.c (__argp_fmtstream_free): Use fwrite_unlocked
diff --git a/timezone/Makefile b/timezone/Makefile
index e929a5f..e6a6a08 100644
--- a/timezone/Makefile
+++ b/timezone/Makefile
@@ -22,8 +22,6 @@ subdir	:= timezone
 
 include ../Makeconfig
 
-extra-objs := scheck.o ialloc.o
-
 others	:= zdump zic
 tests	:= test-tz tst-timezone tst-tzset
 
@@ -49,8 +47,6 @@ endif
 include ../Rules
 
 
-$(objpfx)zic: $(objpfx)scheck.o $(objpfx)ialloc.o
-
 $(objpfx)zic.o $(objpfx)zdump.o: $(objpfx)version.h
 
 $(objpfx)version.h: $(common-objpfx)config.make
@@ -61,15 +57,14 @@ $(objpfx)version.h: $(common-objpfx)config.make
 tz-cflags = -DTZDIR='"$(zonedir)"' \
 	    -DTZDEFAULT='"$(localtime-file)"' \
 	    -DTZDEFRULES='"$(posixrules-file)"' \
-	    -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone
+	    -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone \
+	    -DHAVE_GETTEXT -DUSE_LTZ=0 -Wno-maybe-uninitialized
 
 # The -Wno-unused-variable flag is used to prevent GCC 6
 # from warning about time_t_min and time_t_max which are
 # defined in private.h but not used.
-CFLAGS-zdump.c = -fwrapv -DNOID $(tz-cflags) -DHAVE_GETTEXT
-CFLAGS-zic.c = -DNOID $(tz-cflags) -DHAVE_GETTEXT -Wno-unused-variable
-CFLAGS-ialloc.c = -DNOID -DHAVE_GETTEXT -Wno-unused-variable
-CFLAGS-scheck.c = -DNOID -DHAVE_GETTEXT -Wno-unused-variable
+CFLAGS-zdump.c = $(tz-cflags)
+CFLAGS-zic.c = $(tz-cflags) -Wno-unused-variable
 
 # We have to make sure the data for testing the tz functions is available.
 # Don't add leapseconds here since test-tz made checks that work only without
diff --git a/timezone/README b/timezone/README
index 2268f8e..3251b12 100644
--- a/timezone/README
+++ b/timezone/README
@@ -1,5 +1,5 @@
 The files
-	zic.c zdump.c ialloc.c scheck.c tzfile.h
+	zic.c zdump.c tzfile.h
 	private.h tzselect.ksh checktab.awk
 come from the tzcode package by Arthur David Olson et.al.
 
diff --git a/timezone/ialloc.c b/timezone/ialloc.c
deleted file mode 100644
index b6f0188..0000000
--- a/timezone/ialloc.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 2006-07-17 by Arthur David Olson.
-*/
-
-/*LINTLIBRARY*/
-
-#include "private.h"
-
-char *
-icatalloc(char *const old, const char *const new)
-{
-	register char *	result;
-	register int	oldsize, newsize;
-
-	newsize = (new == NULL) ? 0 : strlen(new);
-	if (old == NULL)
-		oldsize = 0;
-	else if (newsize == 0)
-		return old;
-	else	oldsize = strlen(old);
-	if ((result = realloc(old, oldsize + newsize + 1)) != NULL)
-		if (new != NULL)
-			(void) strcpy(result + oldsize, new);
-	return result;
-}
-
-char *
-icpyalloc(const char *const string)
-{
-	return icatalloc(NULL, string);
-}
diff --git a/timezone/private.h b/timezone/private.h
index 4e8f4ae..1c176e6 100644
--- a/timezone/private.h
+++ b/timezone/private.h
@@ -19,13 +19,9 @@
 
 /*
 ** Defaults for preprocessor symbols.
-** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
 */
 
-#ifndef HAVE_ADJTIME
-#define HAVE_ADJTIME		1
-#endif /* !defined HAVE_ADJTIME */
-
 #ifndef HAVE_GETTEXT
 #define HAVE_GETTEXT		0
 #endif /* !defined HAVE_GETTEXT */
@@ -38,9 +34,9 @@
 #define HAVE_LINK		1
 #endif /* !defined HAVE_LINK */
 
-#ifndef HAVE_SETTIMEOFDAY
-#define HAVE_SETTIMEOFDAY	3
-#endif /* !defined HAVE_SETTIMEOFDAY */
+#ifndef HAVE_STRDUP
+#define HAVE_STRDUP 1
+#endif
 
 #ifndef HAVE_SYMLINK
 #define HAVE_SYMLINK		1
@@ -59,30 +55,61 @@
 #endif /* !defined HAVE_UNISTD_H */
 
 #ifndef HAVE_UTMPX_H
-#define HAVE_UTMPX_H		0
+#define HAVE_UTMPX_H		1
 #endif /* !defined HAVE_UTMPX_H */
 
-#ifndef LOCALE_HOME
-#define LOCALE_HOME		"/usr/lib/locale"
-#endif /* !defined LOCALE_HOME */
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
 
 #if HAVE_INCOMPATIBLE_CTIME_R
 #define asctime_r _incompatible_asctime_r
 #define ctime_r _incompatible_ctime_r
 #endif /* HAVE_INCOMPATIBLE_CTIME_R */
 
+/* Enable tm_gmtoff and tm_zone on GNUish systems.  */
+#define _GNU_SOURCE 1
+/* Fix asctime_r on Solaris 10.  */
+#define _POSIX_PTHREAD_SEMANTICS 1
+/* Enable strtoimax on Solaris 10.  */
+#define __EXTENSIONS__ 1
+
 /*
 ** Nested includes
 */
 
+/* Avoid clashes with NetBSD by renaming NetBSD's declarations.  */
+#define localtime_rz sys_localtime_rz
+#define mktime_z sys_mktime_z
+#define posix2time_z sys_posix2time_z
+#define time2posix_z sys_time2posix_z
+#define timezone_t sys_timezone_t
+#define tzalloc sys_tzalloc
+#define tzfree sys_tzfree
+#include <time.h>
+#undef localtime_rz
+#undef mktime_z
+#undef posix2time_z
+#undef time2posix_z
+#undef timezone_t
+#undef tzalloc
+#undef tzfree
+
 #include "sys/types.h"	/* for time_t */
 #include "stdio.h"
-#include "errno.h"
 #include "string.h"
 #include "limits.h"	/* for CHAR_BIT et al. */
-#include "time.h"
 #include "stdlib.h"
 
+#include "errno.h"
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+#ifndef EOVERFLOW
+# define EOVERFLOW EINVAL
+#endif
+
 #if HAVE_GETTEXT
 #include "libintl.h"
 #endif /* HAVE_GETTEXT */
@@ -102,6 +129,14 @@
 #include "unistd.h"	/* for F_OK, R_OK, and other POSIX goodness */
 #endif /* HAVE_UNISTD_H */
 
+#ifndef HAVE_STRFTIME_L
+# if _POSIX_VERSION < 200809
+#  define HAVE_STRFTIME_L 0
+# else
+#  define HAVE_STRFTIME_L 1
+# endif
+#endif
+
 #ifndef F_OK
 #define F_OK	0
 #endif /* !defined F_OK */
@@ -136,65 +171,98 @@
 # 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 __LONG_LONG_MAX__
+# ifndef LLONG_MAX
+#  define LLONG_MAX __LONG_LONG_MAX__
+# endif
+# ifndef LLONG_MIN
+#  define LLONG_MIN (-1 - LLONG_MAX)
+# endif
+#endif
+
+#ifndef INT_FAST64_MAX
 # ifdef LLONG_MAX
+typedef long long	int_fast64_t;
 #  define INT_FAST64_MIN LLONG_MIN
 #  define INT_FAST64_MAX LLONG_MAX
 # else
-#  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
+#  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 */
+#  endif
 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 */
+#  define INT_FAST64_MIN LONG_MIN
+#  define INT_FAST64_MAX LONG_MAX
+# endif
+#endif
+
+#ifndef SCNdFAST64
+# if INT_FAST64_MAX == LLONG_MAX
+#  define SCNdFAST64 "lld"
+# else
+#  define SCNdFAST64 "ld"
+# endif
+#endif
 
 #ifndef INT_FAST32_MAX
 # if INT_MAX >> 31 == 0
 typedef long int_fast32_t;
+#  define INT_FAST32_MAX LONG_MAX
+#  define INT_FAST32_MIN LONG_MIN
 # else
 typedef int int_fast32_t;
+#  define INT_FAST32_MAX INT_MAX
+#  define INT_FAST32_MIN INT_MIN
 # endif
 #endif
 
 #ifndef INTMAX_MAX
-# if defined LLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef LLONG_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
+#  define INTMAX_MAX LLONG_MAX
+#  define INTMAX_MIN LLONG_MIN
 # else
 typedef long intmax_t;
 #  define strtoimax strtol
-#  define PRIdMAX "ld"
 #  define INTMAX_MAX LONG_MAX
 #  define INTMAX_MIN LONG_MIN
 # endif
 #endif
 
+#ifndef PRIdMAX
+# if INTMAX_MAX == LLONG_MAX
+#  define PRIdMAX "lld"
+# else
+#  define PRIdMAX "ld"
+# endif
+#endif
+
+#ifndef UINT_FAST64_MAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+typedef unsigned long long uint_fast64_t;
+# else
+#  if ULONG_MAX >> 31 >> 1 < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#  endif
+typedef unsigned long	uint_fast64_t;
+# endif
+#endif
+
 #ifndef UINTMAX_MAX
 # if defined ULLONG_MAX || defined __LONG_LONG_MAX__
 typedef unsigned long long uintmax_t;
-#  define PRIuMAX "llu"
 # else
 typedef unsigned long uintmax_t;
+# endif
+#endif
+
+#ifndef PRIuMAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+#  define PRIuMAX "llu"
+# else
 #  define PRIuMAX "lu"
 # endif
 #endif
@@ -237,16 +305,6 @@ typedef unsigned long uintmax_t;
 */
 
 /*
-** Some time.h implementations don't declare asctime_r.
-** Others might define it as a macro.
-** Fix the former without affecting the latter.
-*/
-
-#ifndef asctime_r
-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
@@ -254,7 +312,11 @@ extern char *	asctime_r(struct tm const *, char *);
 ** typical platforms.
 */
 #ifdef time_tz
+# ifdef LOCALTIME_IMPLEMENTATION
 static time_t sys_time(time_t *x) { return time(x); }
+# endif
+
+typedef time_tz tz_time_t;
 
 # undef  ctime
 # define ctime tz_ctime
@@ -270,14 +332,40 @@ static time_t sys_time(time_t *x) { return time(x); }
 # define localtime tz_localtime
 # undef  localtime_r
 # define localtime_r tz_localtime_r
+# undef  localtime_rz
+# define localtime_rz tz_localtime_rz
 # undef  mktime
 # define mktime tz_mktime
+# undef  mktime_z
+# define mktime_z tz_mktime_z
+# undef  offtime
+# define offtime tz_offtime
+# undef  posix2time
+# define posix2time tz_posix2time
+# undef  posix2time_z
+# define posix2time_z tz_posix2time_z
 # undef  time
 # define time tz_time
+# undef  time2posix
+# define time2posix tz_time2posix
+# undef  time2posix_z
+# define time2posix_z tz_time2posix_z
 # undef  time_t
 # define time_t tz_time_t
-
-typedef time_tz time_t;
+# undef  timegm
+# define timegm tz_timegm
+# undef  timelocal
+# define timelocal tz_timelocal
+# undef  timeoff
+# define timeoff tz_timeoff
+# undef  tzalloc
+# define tzalloc tz_tzalloc
+# undef  tzfree
+# define tzfree tz_tzfree
+# undef  tzset
+# define tzset tz_tzset
+# undef  tzsetwall
+# define tzsetwall tz_tzsetwall
 
 char *ctime(time_t const *);
 char *ctime_r(time_t const *, char *);
@@ -287,36 +375,111 @@ struct tm *gmtime_r(time_t const *restrict, struct tm *restrict);
 struct tm *localtime(time_t const *);
 struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
 time_t mktime(struct tm *);
+time_t time(time_t *);
+void tzset(void);
+#endif
+
+/*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+** Similarly for timezone, daylight, and altzone.
+*/
+
+#ifndef asctime_r
+extern char *	asctime_r(struct tm const *restrict, char *restrict);
+#endif
 
-static time_t
-time(time_t *p)
-{
-	time_t r = sys_time(0);
-	if (p)
-		*p = r;
-	return r;
-}
+#ifdef USG_COMPAT
+# ifndef timezone
+extern long timezone;
+# endif
+# ifndef daylight
+extern int daylight;
+# endif
+#endif
+#if defined ALTZONE && !defined altzone
+extern long altzone;
 #endif
 
 /*
-** Private function declarations.
+** The STD_INSPIRED functions are similar, but most also need
+** declarations if time_tz is defined.
 */
 
-char *		icatalloc(char * old, const char * new);
-char *		icpyalloc(const char * string);
-const char *	scheck(const char * string, const char * format);
+#ifdef STD_INSPIRED
+# if !defined tzsetwall || defined time_tz
+void tzsetwall(void);
+# endif
+# if !defined offtime || defined time_tz
+struct tm *offtime(time_t const *, long);
+# endif
+# if !defined timegm || defined time_tz
+time_t timegm(struct tm *);
+# endif
+# if !defined timelocal || defined time_tz
+time_t timelocal(struct tm *);
+# endif
+# if !defined timeoff || defined time_tz
+time_t timeoff(struct tm *, long);
+# endif
+# if !defined time2posix || defined time_tz
+time_t time2posix(time_t);
+# endif
+# if !defined posix2time || defined time_tz
+time_t posix2time(time_t);
+# endif
+#endif
+
+/* Infer TM_ZONE on systems where this information is known, but suppress
+   guessing if NO_TM_ZONE is defined.  Similarly for TM_GMTOFF.  */
+#if (defined __GLIBC__ \
+     || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+     || (defined __APPLE__ && defined __MACH__))
+# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
+#  define TM_GMTOFF tm_gmtoff
+# endif
+# if !defined TM_ZONE && !defined NO_TM_ZONE
+#  define TM_ZONE tm_zone
+# endif
+#endif
 
 /*
-** Finally, some convenience items.
+** Define functions that are ABI compatible with NetBSD but have
+** better prototypes.  NetBSD 6.1.4 defines a pointer type timezone_t
+** and labors under the misconception that 'const timezone_t' is a
+** pointer to a constant.  This use of 'const' is ineffective, so it
+** is not done here.  What we call 'struct state' NetBSD calls
+** 'struct __state', but this is a private name so it doesn't matter.
 */
+#if NETBSD_INSPIRED
+typedef struct state *timezone_t;
+struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
+			struct tm *restrict);
+time_t mktime_z(timezone_t restrict, struct tm *restrict);
+timezone_t tzalloc(char const *);
+void tzfree(timezone_t);
+# ifdef STD_INSPIRED
+#  if !defined posix2time_z || defined time_tz
+time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
+#  endif
+#  if !defined time2posix_z || defined time_tz
+time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
+#  endif
+# endif
+#endif
 
-#ifndef TRUE
-#define TRUE	1
-#endif /* !defined TRUE */
+/*
+** Finally, some convenience items.
+*/
 
-#ifndef FALSE
-#define FALSE	0
-#endif /* !defined FALSE */
+#if __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#else
+# include <stdbool.h>
+#endif
 
 #ifndef TYPE_BIT
 #define TYPE_BIT(type)	(sizeof (type) * CHAR_BIT)
@@ -326,15 +489,20 @@ const char *	scheck(const char * string, const char * format);
 #define TYPE_SIGNED(type) (((type) -1) < 0)
 #endif /* !defined TYPE_SIGNED */
 
-/* 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);
+#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+
+/* Max and min values of the integer type T, of which only the bottom
+   B bits are used, and where the highest-order used bit is considered
+   to be a sign bit if T is signed.  */
+#define MAXVAL(t, b)						\
+  ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))			\
+	- 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b)						\
+  ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+
+/* The minimum and maximum finite time values.  This assumes no padding.  */
+static time_t const time_t_min = MINVAL(time_t, TYPE_BIT(time_t));
+static time_t const time_t_max = MAXVAL(time_t, TYPE_BIT(time_t));
 
 #ifndef INT_STRLEN_MAXIMUM
 /*
@@ -352,29 +520,19 @@ static time_t const time_t_max =
 ** INITIALIZE(x)
 */
 
-#ifndef GNUC_or_lint
 #ifdef lint
-#define GNUC_or_lint
-#endif /* defined lint */
-#ifndef lint
-#ifdef __GNUC__
-#define GNUC_or_lint
-#endif /* defined __GNUC__ */
-#endif /* !defined lint */
-#endif /* !defined GNUC_or_lint */
-
-#ifndef INITIALIZE
-#ifdef GNUC_or_lint
-#define INITIALIZE(x)	((x) = 0)
-#endif /* defined GNUC_or_lint */
-#ifndef GNUC_or_lint
-#define INITIALIZE(x)
-#endif /* !defined GNUC_or_lint */
-#endif /* !defined INITIALIZE */
+# define INITIALIZE(x)	((x) = 0)
+#else
+# define INITIALIZE(x)
+#endif
+
+#ifndef UNINIT_TRAP
+# define UNINIT_TRAP 0
+#endif
 
 /*
 ** For the benefit of GNU folk...
-** `_(MSGID)' uses the current locale's message library string for MSGID.
+** '_(MSGID)' uses the current locale's message library string for MSGID.
 ** The default is to use gettext if available, and use MSGID otherwise.
 */
 
@@ -386,9 +544,9 @@ static time_t const time_t_max =
 #endif /* !HAVE_GETTEXT */
 #endif /* !defined _ */
 
-#ifndef TZ_DOMAIN
-#define TZ_DOMAIN "tz"
-#endif /* !defined TZ_DOMAIN */
+#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
+# define TZ_DOMAIN "tz"
+#endif
 
 #if HAVE_INCOMPATIBLE_CTIME_R
 #undef asctime_r
@@ -417,8 +575,4 @@ char *ctime_r(time_t const *, char *);
 #define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
 #endif /* !defined SECSPERREPEAT_BITS */
 
-/*
-** UNIX was a registered trademark of The Open Group in 2003.
-*/
-
 #endif /* !defined PRIVATE_H */
diff --git a/timezone/scheck.c b/timezone/scheck.c
deleted file mode 100644
index 8bd01a8..0000000
--- a/timezone/scheck.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 2006-07-17 by Arthur David Olson.
-*/
-
-/*LINTLIBRARY*/
-
-#include "private.h"
-
-const char *
-scheck(const char *const string, const char *const format)
-{
-	register char *		fbuf;
-	register const char *	fp;
-	register char *		tp;
-	register int		c;
-	register const char *	result;
-	char			dummy;
-
-	result = "";
-	if (string == NULL || format == NULL)
-		return result;
-	fbuf = malloc(2 * strlen(format) + 4);
-	if (fbuf == NULL)
-		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 (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;
-		if ((*tp++ = *fp++) == '\0')
-			break;
-	}
-
-	*(tp - 1) = '%';
-	*tp++ = 'c';
-	*tp = '\0';
-	if (sscanf(string, fbuf, &dummy) != 1)
-		result = format;
-	free(fbuf);
-	return result;
-}
diff --git a/timezone/tzfile.h b/timezone/tzfile.h
index 911130e..ebecd68 100644
--- a/timezone/tzfile.h
+++ b/timezone/tzfile.h
@@ -40,7 +40,7 @@
 struct tzhead {
 	char	tzh_magic[4];		/* TZ_MAGIC */
 	char	tzh_version[1];		/* '\0' or '2' or '3' as of 2013 */
-	char	tzh_reserved[15];	/* reserved--must be zero */
+	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 */
 	char	tzh_leapcnt[4];		/* coded number of leap seconds */
@@ -62,13 +62,13 @@ struct tzhead {
 **	tzh_leapcnt repetitions of
 **		one (char [4])		coded leap second transition times
 **		one (char [4])		total correction after above
-**	tzh_ttisstdcnt (char)s		indexed by type; if TRUE, transition
-**					time is standard time, if FALSE,
+**	tzh_ttisstdcnt (char)s		indexed by type; if 1, transition
+**					time is standard time, if 0,
 **					transition time is wall clock time
 **					if absent, transition times are
 **					assumed to be wall clock time
-**	tzh_ttisgmtcnt (char)s		indexed by type; if TRUE, transition
-**					time is UT, if FALSE,
+**	tzh_ttisgmtcnt (char)s		indexed by type; if 1, transition
+**					time is UT, if 0,
 **					transition time is local time
 **					if absent, transition times are
 **					assumed to be local time
diff --git a/timezone/tzselect.ksh b/timezone/tzselect.ksh
index 9d70691..2c3b2f4 100755
--- a/timezone/tzselect.ksh
+++ b/timezone/tzselect.ksh
@@ -37,15 +37,22 @@ REPORT_BUGS_TO=tz@iana.org
 : ${AWK=awk}
 : ${TZDIR=`pwd`}
 
+# Output one argument as-is to standard output.
+# Safer than 'echo', which can mishandle '\' or leading '-'.
+say() {
+    printf '%s\n' "$1"
+}
+
 # Check for awk Posix compliance.
 ($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
 [ $? = 123 ] || {
-	echo >&2 "$0: Sorry, your \`$AWK' program is not Posix compatible."
+	say >&2 "$0: Sorry, your '$AWK' program is not Posix compatible."
 	exit 1
 }
 
 coord=
 location_limit=10
+zonetabtype=zone1970
 
 usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
 Select a time zone interactively.
@@ -80,7 +87,7 @@ if
   ?*) : ;;
   '')
     # '; exit' should be redundant, but Dash doesn't properly fail without it.
-    (eval 'set --; select x; do break; done; exit') 2>/dev/null
+    (eval 'set --; select x; do break; done; exit') </dev/null 2>/dev/null
   esac
 then
   # Do this inside 'eval', as otherwise the shell might exit when parsing it
@@ -139,41 +146,58 @@ else
   }
 fi
 
-while getopts c:n:-: opt
+while getopts c:n:t:-: opt
 do
     case $opt$OPTARG in
     c*)
 	coord=$OPTARG ;;
     n*)
 	location_limit=$OPTARG ;;
+    t*) # Undocumented option, used for developer testing.
+	zonetabtype=$OPTARG ;;
     -help)
 	exec echo "$usage" ;;
     -version)
 	exec echo "tzselect $PKGVERSION$TZVERSION" ;;
     -*)
-	echo >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;;
+	say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;;
     *)
-	echo >&2 "$0: try '$0 --help'"; exit 1 ;;
+	say >&2 "$0: try '$0 --help'"; exit 1 ;;
     esac
 done
 
 shift `expr $OPTIND - 1`
 case $# in
 0) ;;
-*) echo >&2 "$0: $1: unknown argument"; exit 1 ;;
+*) say >&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
+TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab
 for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
 do
-	<$f || {
-		echo >&2 "$0: time zone files are not set up correctly"
+	<"$f" || {
+		say >&2 "$0: time zone files are not set up correctly"
 		exit 1
 	}
 done
 
+# If the current locale does not support UTF-8, convert data to current
+# locale's format if possible, as the shell aligns columns better that way.
+# Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI.
+! $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' &&
+    { tmp=`(mktemp -d) 2>/dev/null` || {
+	tmp=${TMPDIR-/tmp}/tzselect.$$ &&
+	(umask 77 && mkdir -- "$tmp")
+    };} &&
+    trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM &&
+    (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \
+        2>/dev/null &&
+    TZ_COUNTRY_TABLE=$tmp/iso3166.tab &&
+    iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab &&
+    TZ_ZONE_TABLE=$tmp/$zonetabtype.tab
+
 newline='
 '
 IFS=$newline
@@ -189,7 +213,13 @@ output_distances='
         country[$1] = $2
     country["US"] = "US" # Otherwise the strings get too long.
   }
-  function convert_coord(coord, deg, min, ilen, sign, sec) {
+  function abs(x) {
+    return x < 0 ? -x : x;
+  }
+  function min(x, y) {
+    return x < y ? x : y;
+  }
+  function convert_coord(coord, deg, minute, 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)
@@ -200,8 +230,8 @@ output_distances='
     } 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
+      minute = degmin - intdeg * 100
+      deg = (intdeg * 60 + minute) / 60
     } else
       deg = coord
     return deg * 0.017453292519943296
@@ -217,14 +247,27 @@ output_distances='
   # 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) {
+  function gcdist(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)
+    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)
   }
+  # Parallel distance between points with given latitude and longitude.
+  # This is the product of the longitude difference and the cosine
+  # of the latitude of the point that is further from the equator.
+  # I.e., it considers longitudes to be further apart if they are
+  # nearer the equator.
+  function pardist(lat1, long1, lat2, long2) {
+    return abs(long1 - long2) * min(cos(lat1), cos(lat2))
+  }
+  # The distance function is the sum of the great-circle distance and
+  # the parallel distance.  It could be weighted.
+  function dist(lat1, long1, lat2, long2) {
+    return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2)
+  }
   BEGIN {
     coord_lat = convert_latitude(coord)
     coord_long = convert_longitude(coord)
@@ -232,7 +275,13 @@ output_distances='
   /^[^#]/ {
     here_lat = convert_latitude($2)
     here_long = convert_longitude($2)
-    line = $1 "\t" $2 "\t" $3 "\t" country[$1]
+    line = $1 "\t" $2 "\t" $3
+    sep = "\t"
+    ncc = split($1, cc, /,/)
+    for (i = 1; i <= ncc; i++) {
+      line = line sep country[cc[i]]
+      sep = ", "
+    }
     if (NF == 4)
       line = line " - " $4
     printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
@@ -269,7 +318,7 @@ while
 		entry = entry " Ocean"
               printf "'\''%s'\''\n", entry
             }
-          ' $TZ_ZONE_TABLE |
+          ' <"$TZ_ZONE_TABLE" |
 	  sort -u |
 	  tr '\n' ' '
 	  echo ''
@@ -300,7 +349,7 @@ while
 				tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+"
 				time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?"
 				offset = "[-+]?" time
-				date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)"
+				date = "(J?[0-9]+|M[0-9]+\\.[0-9]+\\.[0-9]+)"
 				datetime = "," date "(/" time ")?"
 				tzpattern = "^(:.*|" tzname offset "(" tzname \
 				  "(" offset ")?(" datetime datetime ")?)?)$"
@@ -308,8 +357,7 @@ while
 				exit 0
 			}'
 		do
-			echo >&2 "\`$TZ' is not a conforming" \
-				'Posix time zone string.'
+		    say >&2 "'$TZ' is not a conforming Posix time zone string."
 		done
 		TZ_for_date=$TZ;;
 	*)
@@ -327,11 +375,11 @@ while
 		    distance_table=`$AWK \
 			    -v coord="$coord" \
 			    -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
-			    "$output_distances" <$TZ_ZONE_TABLE |
+			    "$output_distances" <"$TZ_ZONE_TABLE" |
 		      sort -n |
 		      sed "${location_limit}q"
 		    `
-		    regions=`echo "$distance_table" | $AWK '
+		    regions=`say "$distance_table" | $AWK '
 		      BEGIN { FS = "\t" }
 		      { print $NF }
 		    '`
@@ -341,7 +389,7 @@ while
 			    "of distance from $coord".
 		    doselect $regions
 		    region=$select_result
-		    TZ=`echo "$distance_table" | $AWK -v region="$region" '
+		    TZ=`say "$distance_table" | $AWK -v region="$region" '
 		      BEGIN { FS="\t" }
 		      $NF == region { print $4 }
 		    '`
@@ -355,7 +403,9 @@ while
 			BEGIN { FS = "\t" }
 			/^#/ { next }
 			$3 ~ ("^" continent "/") {
-				if (!cc_seen[$1]++) cc_list[++ccs] = $1
+			    ncc = split($1, cc, /,/)
+			    for (i = 1; i <= ncc; i++)
+				if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i]
 			}
 			END {
 				while (getline <TZ_COUNTRY_TABLE) {
@@ -369,7 +419,7 @@ 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.
@@ -399,8 +449,9 @@ while
 					}
 				}
 			}
-			$1 == cc { print $4 }
-		' <$TZ_ZONE_TABLE`
+			/^#/ { next }
+			$1 ~ cc { print $4 }
+		' <"$TZ_ZONE_TABLE"`
 
 
 		# If there's more than one region, ask the user which one.
@@ -430,14 +481,15 @@ while
 					}
 				}
 			}
-			$1 == cc && $4 == region { print $3 }
-		' <$TZ_ZONE_TABLE`
+			/^#/ { next }
+			$1 ~ cc && $4 == region { print $3 }
+		' <"$TZ_ZONE_TABLE"`
 		esac
 
 		# Make sure the corresponding zoneinfo file exists.
 		TZ_for_date=$TZDIR/$TZ
-		<$TZ_for_date || {
-			echo >&2 "$0: time zone files are not set up correctly"
+		<"$TZ_for_date" || {
+			say >&2 "$0: time zone files are not set up correctly"
 			exit 1
 		}
 	esac
@@ -470,15 +522,15 @@ Universal Time is now:	$UTdate."
 	echo >&2 "The following information has been given:"
 	echo >&2 ""
 	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'"
+	?*%?*%)	say >&2 "	$country$newline	$region";;
+	?*%%)	say >&2 "	$country";;
+	%?*%?*) say >&2 "	coord $coord$newline	$region";;
+	%%?*)	say >&2 "	coord $coord";;
+	*)	say >&2 "	TZ='$TZ'"
 	esac
-	echo >&2 ""
-	echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
-	echo >&2 "Is the above information OK?"
+	say >&2 ""
+	say >&2 "Therefore TZ='$TZ' will be used.$extra_info"
+	say >&2 "Is the above information OK?"
 
 	doselect Yes No
 	ok=$select_result
@@ -493,7 +545,7 @@ case $SHELL in
 *) file=.profile line="TZ='$TZ'; export TZ"
 esac
 
-echo >&2 "
+say >&2 "
 You can make this change permanent for yourself by appending the line
 	$line
 to the file '$file' in your home directory; then log out and log in again.
@@ -501,4 +553,4 @@ to the file '$file' in your home directory; then log out and log in again.
 Here is that TZ value again, this time on standard output so that you
 can use the $0 command in shell scripts:"
 
-echo "$TZ"
+say "$TZ"
diff --git a/timezone/zdump.c b/timezone/zdump.c
index c48ac84..063a263 100644
--- a/timezone/zdump.c
+++ b/timezone/zdump.c
@@ -9,25 +9,32 @@
 ** 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.
+** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
 */
 
-#ifdef time_tz
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+#ifndef USE_LTZ
+# define USE_LTZ 1
+#endif
+
+#if USE_LTZ
 # include "private.h"
 #endif
 
+/* Enable tm_gmtoff and tm_zone on GNUish systems.  */
+#define _GNU_SOURCE 1
+/* Enable strtoimax on Solaris 10.  */
+#define __EXTENSIONS__ 1
+
 #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 "limits.h"	/* for CHAR_BIT, LLONG_MAX */
-#include "ctype.h"	/* for isalpha et al. */
-#ifndef isascii
-#define isascii(x) 1
-#endif /* !defined isascii */
+#include <errno.h>
 
 /*
 ** Substitutes for pre-C99 compilers.
@@ -58,24 +65,59 @@ typedef int int_fast32_t;
 # endif
 #endif
 
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
+#if !defined LLONG_MAX && defined __LONG_LONG_MAX__
+# define LLONG_MAX __LONG_LONG_MAX__
+#endif
+
 #ifndef INTMAX_MAX
-# if defined LLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef LLONG_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
+#  define INTMAX_MAX LLONG_MAX
 # else
 typedef long intmax_t;
 #  define strtoimax strtol
-#  define PRIdMAX "ld"
 #  define INTMAX_MAX LONG_MAX
 # endif
 #endif
 
+#ifndef PRIdMAX
+# if INTMAX_MAX == LLONG_MAX
+#  define PRIdMAX "lld"
+# else
+#  define PRIdMAX "ld"
+# endif
+#endif
+
+/* Infer TM_ZONE on systems where this information is known, but suppress
+   guessing if NO_TM_ZONE is defined.  Similarly for TM_GMTOFF.  */
+#if (defined __GLIBC__ \
+     || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+     || (defined __APPLE__ && defined __MACH__))
+# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
+#  define TM_GMTOFF tm_gmtoff
+# endif
+# if !defined TM_ZONE && !defined NO_TM_ZONE
+#  define TM_ZONE tm_zone
+# endif
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+# define HAVE_LOCALTIME_R 1
+#endif
+
+#ifndef HAVE_LOCALTIME_RZ
+# ifdef TM_ZONE
+#  define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
+# else
+#  define HAVE_LOCALTIME_RZ 0
+# endif
+#endif
+
+#ifndef HAVE_TZSET
+# define HAVE_TZSET 1
+#endif
 
 #ifndef ZDUMP_LO_YEAR
 #define ZDUMP_LO_YEAR	(-500)
@@ -89,13 +131,13 @@ typedef long intmax_t;
 #define MAX_STRING_LENGTH	1024
 #endif /* !defined MAX_STRING_LENGTH */
 
-#ifndef TRUE
-#define TRUE		1
-#endif /* !defined TRUE */
-
-#ifndef FALSE
-#define FALSE		0
-#endif /* !defined FALSE */
+#if __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#else
+# include <stdbool.h>
+#endif
 
 #ifndef EXIT_SUCCESS
 #define EXIT_SUCCESS	0
@@ -167,16 +209,6 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
 #include "libintl.h"
 #endif /* HAVE_GETTEXT */
 
-#ifndef GNUC_or_lint
-#ifdef lint
-#define GNUC_or_lint
-#else /* !defined lint */
-#ifdef __GNUC__
-#define GNUC_or_lint
-#endif /* defined __GNUC__ */
-#endif /* !defined lint */
-#endif /* !defined GNUC_or_lint */
-
 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
 # define ATTRIBUTE_PURE __attribute__ ((__pure__))
 #else
@@ -185,7 +217,7 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
 
 /*
 ** For the benefit of GNU folk...
-** `_(MSGID)' uses the current locale's message library string for MSGID.
+** '_(MSGID)' uses the current locale's message library string for MSGID.
 ** The default is to use gettext if available, and use MSGID otherwise.
 */
 
@@ -197,9 +229,14 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
 #endif /* !HAVE_GETTEXT */
 #endif /* !defined _ */
 
-#ifndef TZ_DOMAIN
-#define TZ_DOMAIN "tz"
-#endif /* !defined TZ_DOMAIN */
+#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
+# define TZ_DOMAIN "tz"
+#endif
+
+#if ! HAVE_LOCALTIME_RZ
+# undef  timezone_t
+# define timezone_t char **
+#endif
 
 extern char **	environ;
 extern int	getopt(int argc, char * const argv[],
@@ -209,57 +246,233 @@ extern int	optind;
 extern char *	tzname[2];
 
 /* The minimum and maximum finite time values.  */
+enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
 static time_t const absolute_min_time =
   ((time_t) -1 < 0
-   ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
+   ? (- ((time_t) ~ (time_t) 0 < 0)
+      - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
    : 0);
 static time_t const absolute_max_time =
   ((time_t) -1 < 0
-   ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
+   ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
    : -1);
-static size_t	longest;
+static int	longest;
 static char *	progname;
-static int	warned;
+static bool	warned;
+static bool	errout;
+
+static char const *abbr(struct tm const *);
+static intmax_t	delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
+static void dumptime(struct tm const *);
+static time_t hunt(timezone_t, char *, time_t, time_t);
+static void show(timezone_t, char *, time_t, bool);
+static const char *tformat(void);
+static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/* Is A an alphabetic character in the C locale?  */
+static bool
+is_alpha(char a)
+{
+	switch (a) {
+	  default:
+		return false;
+	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
+	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+	  case 'v': case 'w': case 'x': case 'y': case 'z':
+		return true;
+	}
+}
+
+/* Return A + B, exiting if the result would overflow.  */
+static size_t
+sumsize(size_t a, size_t b)
+{
+  size_t sum = a + b;
+  if (sum < a) {
+    fprintf(stderr, "%s: size overflow\n", progname);
+    exit(EXIT_FAILURE);
+  }
+  return sum;
+}
+
+#if ! HAVE_TZSET
+# undef tzset
+# define tzset zdump_tzset
+static void tzset(void) { }
+#endif
+
+/* Assume gmtime_r works if localtime_r does.
+   A replacement localtime_r is defined below if needed.  */
+#if ! HAVE_LOCALTIME_R
+
+# undef gmtime_r
+# define gmtime_r zdump_gmtime_r
+
+static struct tm *
+gmtime_r(time_t *tp, struct tm *tmp)
+{
+  struct tm *r = gmtime(tp);
+  if (r) {
+    *tmp = *r;
+    r = tmp;
+  }
+  return r;
+}
+
+#endif
+
+/* Platforms with TM_ZONE don't need tzname, so they can use the
+   faster localtime_rz or localtime_r if available.  */
+
+#if defined TM_ZONE && HAVE_LOCALTIME_RZ
+# define USE_LOCALTIME_RZ true
+#else
+# define USE_LOCALTIME_RZ false
+#endif
 
-static char *	abbr(struct tm * tmp);
-static void	abbrok(const char * abbrp, const char * zone);
-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	show(char * zone, time_t t, int v);
-static const char *	tformat(void);
-static time_t	yeartot(intmax_t y) ATTRIBUTE_PURE;
+#if ! USE_LOCALTIME_RZ
+
+# if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
+#  undef localtime_r
+#  define localtime_r zdump_localtime_r
+static struct tm *
+localtime_r(time_t *tp, struct tm *tmp)
+{
+  struct tm *r = localtime(tp);
+  if (r) {
+    *tmp = *r;
+    r = tmp;
+  }
+  return r;
+}
+# endif
+
+# undef localtime_rz
+# define localtime_rz zdump_localtime_rz
+static struct tm *
+localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp)
+{
+  return localtime_r(tp, tmp);
+}
+
+# ifdef TYPECHECK
+#  undef mktime_z
+#  define mktime_z zdump_mktime_z
+static time_t
+mktime_z(timezone_t tz, struct tm *tmp)
+{
+  return mktime(tmp);
+}
+# endif
+
+# undef tzalloc
+# undef tzfree
+# define tzalloc zdump_tzalloc
+# define tzfree zdump_tzfree
+
+static timezone_t
+tzalloc(char const *val)
+{
+  static char **fakeenv;
+  char **env = fakeenv;
+  char *env0;
+  if (! env) {
+    char **e = environ;
+    int to;
+
+    while (*e++)
+      continue;
+    env = malloc(sumsize(sizeof *environ,
+			 (e - environ) * sizeof *environ));
+    if (! env) {
+      perror(progname);
+      exit(EXIT_FAILURE);
+    }
+    to = 1;
+    for (e = environ; (env[to] = *e); e++)
+      to += strncmp(*e, "TZ=", 3) != 0;
+  }
+  env0 = malloc(sumsize(sizeof "TZ=", strlen(val)));
+  if (! env0) {
+    perror(progname);
+    exit(EXIT_FAILURE);
+  }
+  env[0] = strcat(strcpy(env0, "TZ="), val);
+  environ = fakeenv = env;
+  tzset();
+  return env;
+}
+
+static void
+tzfree(timezone_t env)
+{
+  environ = env + 1;
+  free(env[0]);
+}
+#endif /* ! USE_LOCALTIME_RZ */
+
+/* A UTC time zone, and its initializer.  */
+static timezone_t gmtz;
+static void
+gmtzinit(void)
+{
+  if (USE_LOCALTIME_RZ) {
+    static char const utc[] = "UTC0";
+    gmtz = tzalloc(utc);
+    if (!gmtz) {
+      perror(utc);
+      exit(EXIT_FAILURE);
+    }
+  }
+}
+
+/* Convert *TP to UTC, storing the broken-down time into *TMP.
+   Return TMP if successful, NULL otherwise.  This is like gmtime_r(TP, TMP),
+   except typically faster if USE_LOCALTIME_RZ.  */
+static struct tm *
+my_gmtime_r(time_t *tp, struct tm *tmp)
+{
+  return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
+}
 
 #ifndef TYPECHECK
-#define my_localtime	localtime
+# define my_localtime_rz localtime_rz
 #else /* !defined TYPECHECK */
+
 static struct tm *
-my_localtime(time_t *tp)
+my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
 {
-	register struct tm *	tmp;
-
-	tmp = localtime(tp);
-	if (tp != NULL && tmp != NULL) {
+	tmp = localtime_rz(tz, tp, tmp);
+	if (tmp) {
 		struct tm	tm;
 		register time_t	t;
 
 		tm = *tmp;
-		t = mktime(&tm);
+		t = mktime_z(tz, &tm);
 		if (t != *tp) {
-			(void) fflush(stdout);
-			(void) fprintf(stderr, "\n%s: ", progname);
-			(void) fprintf(stderr, tformat(), *tp);
-			(void) fprintf(stderr, " ->");
-			(void) fprintf(stderr, " year=%d", tmp->tm_year);
-			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
-			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
-			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
-			(void) fprintf(stderr, " min=%d", tmp->tm_min);
-			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
-			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
-			(void) fprintf(stderr, " -> ");
-			(void) fprintf(stderr, tformat(), t);
-			(void) fprintf(stderr, "\n");
+			fflush(stdout);
+			fprintf(stderr, "\n%s: ", progname);
+			fprintf(stderr, tformat(), *tp);
+			fprintf(stderr, " ->");
+			fprintf(stderr, " year=%d", tmp->tm_year);
+			fprintf(stderr, " mon=%d", tmp->tm_mon);
+			fprintf(stderr, " mday=%d", tmp->tm_mday);
+			fprintf(stderr, " hour=%d", tmp->tm_hour);
+			fprintf(stderr, " min=%d", tmp->tm_min);
+			fprintf(stderr, " sec=%d", tmp->tm_sec);
+			fprintf(stderr, " isdst=%d", tmp->tm_isdst);
+			fprintf(stderr, " -> ");
+			fprintf(stderr, tformat(), t);
+			fprintf(stderr, "\n");
+			errout = true;
 		}
 	}
 	return tmp;
@@ -275,88 +488,124 @@ abbrok(const char *const abbrp, const char *const zone)
 	if (warned)
 		return;
 	cp = abbrp;
-	wp = NULL;
-	while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
+	while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
 		++cp;
-	if (cp - abbrp == 0)
-		wp = _("lacks alphabetic at start");
-	else if (cp - abbrp < 3)
-		wp = _("has fewer than 3 alphabetics");
+	if (cp - abbrp < 3)
+	  wp = _("has fewer than 3 characters");
 	else if (cp - abbrp > 6)
-		wp = _("has more than 6 alphabetics");
-	if (wp == NULL && (*cp == '+' || *cp == '-')) {
-		++cp;
-		if (isascii((unsigned char) *cp) &&
-			isdigit((unsigned char) *cp))
-				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
-					++cp;
-		if (*cp != '\0')
-			wp = _("differs from POSIX standard");
-	}
-	if (wp == NULL)
-		return;
-	(void) fflush(stdout);
-	(void) fprintf(stderr,
+	  wp = _("has more than 6 characters");
+	else if (*cp)
+	  wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
+	else
+	  return;
+	fflush(stdout);
+	fprintf(stderr,
 		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
 		progname, zone, abbrp, wp);
-	warned = TRUE;
+	warned = errout = true;
+}
+
+/* Return a time zone abbreviation.  If the abbreviation needs to be
+   saved, use *BUF (of size *BUFALLOC) to save it, and return the
+   abbreviation in the possibly-reallocated *BUF.  Otherwise, just
+   return the abbreviation.  Get the abbreviation from TMP.
+   Exit on memory allocation failure.  */
+static char const *
+saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp)
+{
+  char const *ab = abbr(tmp);
+  if (HAVE_LOCALTIME_RZ)
+    return ab;
+  else {
+    size_t ablen = strlen(ab);
+    if (*bufalloc <= ablen) {
+      free(*buf);
+
+      /* Make the new buffer at least twice as long as the old,
+	 to avoid O(N**2) behavior on repeated calls.  */
+      *bufalloc = sumsize(*bufalloc, ablen + 1);
+
+      *buf = malloc(*bufalloc);
+      if (! *buf) {
+	perror(progname);
+	exit(EXIT_FAILURE);
+      }
+    }
+    return strcpy(*buf, ab);
+  }
+}
+
+static void
+close_file(FILE *stream)
+{
+  char const *e = (ferror(stream) ? _("I/O error")
+		   : fclose(stream) != 0 ? strerror(errno) : NULL);
+  if (e) {
+    fprintf(stderr, "%s: %s\n", progname, e);
+    exit(EXIT_FAILURE);
+  }
 }
 
 static void
 usage(FILE * const stream, const int status)
 {
-	(void) fprintf(stream,
+	fprintf(stream,
 _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
   "\n"
   "Report bugs to %s.\n"),
 		       progname, progname, REPORT_BUGS_TO);
+	if (status == EXIT_SUCCESS)
+	  close_file(stream);
 	exit(status);
 }
 
 int
 main(int argc, char *argv[])
 {
+	/* These are static so that they're initially zero.  */
+	static char *		abbrev;
+	static size_t		abbrevsize;
+	static struct tm	newtm;
+
 	register int		i;
-	register int		vflag;
-	register int		Vflag;
+	register bool		vflag;
+	register bool		Vflag;
 	register char *		cutarg;
 	register char *		cuttimes;
 	register time_t		cutlotime;
 	register time_t		cuthitime;
-	register char **	fakeenv;
 	time_t			now;
 	time_t			t;
 	time_t			newt;
 	struct tm		tm;
-	struct tm		newtm;
 	register struct tm *	tmp;
 	register struct tm *	newtmp;
 
 	cutlotime = absolute_min_time;
 	cuthitime = absolute_max_time;
 #if HAVE_GETTEXT
-	(void) setlocale(LC_ALL, "");
+	setlocale(LC_ALL, "");
 #ifdef TZ_DOMAINDIR
-	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
 #endif /* defined TEXTDOMAINDIR */
-	(void) textdomain(TZ_DOMAIN);
+	textdomain(TZ_DOMAIN);
 #endif /* HAVE_GETTEXT */
 	progname = argv[0];
 	for (i = 1; i < argc; ++i)
 		if (strcmp(argv[i], "--version") == 0) {
-			(void) printf("zdump %s%s\n", PKGVERSION, TZVERSION);
-			exit(EXIT_SUCCESS);
+			printf("zdump %s%s\n", PKGVERSION, TZVERSION);
+			return EXIT_SUCCESS;
 		} else if (strcmp(argv[i], "--help") == 0) {
 			usage(stdout, EXIT_SUCCESS);
 		}
-	vflag = Vflag = 0;
+	vflag = Vflag = false;
 	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 'v': vflag = true; break;
+	  case 'V': Vflag = true; break;
 	  case -1:
 	    if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
 	      goto arg_processing_done;
@@ -383,9 +632,9 @@ main(int argc, char *argv[])
 				cutloyear = lo;
 				cuthiyear = hi;
 			} else {
-(void) fprintf(stderr, _("%s: wild -c argument %s\n"),
+				fprintf(stderr, _("%s: wild -c argument %s\n"),
 					progname, cutarg);
-				exit(EXIT_FAILURE);
+				return EXIT_FAILURE;
 			}
 		}
 		if (cutarg != NULL || cuttimes == NULL) {
@@ -415,81 +664,61 @@ main(int argc, char *argv[])
 					cuthitime = hi;
 				}
 			} else {
-				(void) fprintf(stderr,
+				fprintf(stderr,
 					_("%s: wild -t argument %s\n"),
 					progname, cuttimes);
-				exit(EXIT_FAILURE);
+				return EXIT_FAILURE;
 			}
 		}
 	}
-	(void) time(&now);
+	gmtzinit();
+	now = time(NULL);
 	longest = 0;
-	for (i = optind; i < argc; ++i)
-		if (strlen(argv[i]) > longest)
-			longest = strlen(argv[i]);
-	{
-		register int	from;
-		register int	to;
-
-		for (i = 0; environ[i] != NULL; ++i)
-			continue;
-		fakeenv = malloc((i + 2) * sizeof *fakeenv);
-		if (fakeenv == NULL
-		    || (fakeenv[0] = malloc(longest + 4)) == NULL) {
-					(void) perror(progname);
-					exit(EXIT_FAILURE);
-		}
-		to = 0;
-		(void) strcpy(fakeenv[to++], "TZ=");
-		for (from = 0; environ[from] != NULL; ++from)
-			if (strncmp(environ[from], "TZ=", 3) != 0)
-				fakeenv[to++] = environ[from];
-		fakeenv[to] = NULL;
-		environ = fakeenv;
+	for (i = optind; i < argc; i++) {
+	  size_t arglen = strlen(argv[i]);
+	  if (longest < arglen)
+	    longest = arglen < INT_MAX ? arglen : INT_MAX;
 	}
-	for (i = optind; i < argc; ++i) {
-		static char	buf[MAX_STRING_LENGTH];
 
-		(void) strcpy(&fakeenv[0][3], argv[i]);
+	for (i = optind; i < argc; ++i) {
+		timezone_t tz = tzalloc(argv[i]);
+		char const *ab;
+		if (!tz) {
+		  perror(argv[i]);
+		  return EXIT_FAILURE;
+		}
 		if (! (vflag | Vflag)) {
-			show(argv[i], now, FALSE);
+			show(tz, argv[i], now, false);
+			tzfree(tz);
 			continue;
 		}
-		warned = FALSE;
+		warned = false;
 		t = absolute_min_time;
 		if (!Vflag) {
-			show(argv[i], t, TRUE);
+			show(tz, argv[i], t, true);
 			t += SECSPERDAY;
-			show(argv[i], t, TRUE);
+			show(tz, argv[i], t, true);
 		}
 		if (t < cutlotime)
 			t = cutlotime;
-		tmp = my_localtime(&t);
-		if (tmp != NULL) {
-			tm = *tmp;
-			(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
-		}
-		for ( ; ; ) {
-			newt = (t < absolute_max_time - SECSPERDAY / 2
+		tmp = my_localtime_rz(tz, &t, &tm);
+		if (tmp)
+		  ab = saveabbr(&abbrev, &abbrevsize, &tm);
+		while (t < cuthitime) {
+			newt = ((t < absolute_max_time - SECSPERDAY / 2
+				 && t + SECSPERDAY / 2 < cuthitime)
 				? t + SECSPERDAY / 2
-				: absolute_max_time);
-			if (cuthitime <= newt)
-				break;
-			newtmp = localtime(&newt);
-			if (newtmp != NULL)
-				newtm = *newtmp;
+				: cuthitime);
+			newtmp = localtime_rz(tz, &newt, &newtm);
 			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
 				(delta(&newtm, &tm) != (newt - t) ||
 				newtm.tm_isdst != tm.tm_isdst ||
-				strcmp(abbr(&newtm), buf) != 0)) {
-					newt = hunt(argv[i], t, newt);
-					newtmp = localtime(&newt);
-					if (newtmp != NULL) {
-						newtm = *newtmp;
-						(void) strncpy(buf,
-							abbr(&newtm),
-							(sizeof buf) - 1);
-					}
+				strcmp(abbr(&newtm), ab) != 0)) {
+					newt = hunt(tz, argv[i], t, newt);
+					newtmp = localtime_rz(tz, &newt, &newtm);
+					if (newtmp)
+					  ab = saveabbr(&abbrev, &abbrevsize,
+							&newtm);
 			}
 			t = newt;
 			tm = newtm;
@@ -498,23 +727,20 @@ main(int argc, char *argv[])
 		if (!Vflag) {
 			t = absolute_max_time;
 			t -= SECSPERDAY;
-			show(argv[i], t, TRUE);
+			show(tz, argv[i], t, true);
 			t += SECSPERDAY;
-			show(argv[i], t, TRUE);
+			show(tz, argv[i], t, true);
 		}
+		tzfree(tz);
 	}
-	if (fflush(stdout) || ferror(stdout)) {
-		(void) fprintf(stderr, "%s: ", progname);
-		(void) perror(_("Error writing to standard output"));
-		exit(EXIT_FAILURE);
-	}
-	exit(EXIT_SUCCESS);
-	/* If exit fails to exit... */
-	return EXIT_FAILURE;
+	close_file(stdout);
+	if (errout && (ferror(stderr) || fclose(stderr) != 0))
+	  return EXIT_FAILURE;
+	return EXIT_SUCCESS;
 }
 
 static time_t
-yeartot(const intmax_t y)
+yeartot(intmax_t y)
 {
 	register intmax_t	myy, seconds, years;
 	register time_t		t;
@@ -557,20 +783,20 @@ yeartot(const intmax_t y)
 }
 
 static time_t
-hunt(char *name, time_t lot, time_t hit)
+hunt(timezone_t tz, char *name, time_t lot, time_t hit)
 {
+	static char *		loab;
+	static size_t		loabsize;
+	char const *		ab;
 	time_t			t;
 	struct tm		lotm;
 	register struct tm *	lotmp;
 	struct tm		tm;
 	register struct tm *	tmp;
-	char			loab[MAX_STRING_LENGTH];
 
-	lotmp = my_localtime(&lot);
-	if (lotmp != NULL) {
-		lotm = *lotmp;
-		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
-	}
+	lotmp = my_localtime_rz(tz, &lot, &lotm);
+	if (lotmp)
+	  ab = saveabbr(&loab, &loabsize, &lotm);
 	for ( ; ; ) {
 		time_t diff = hit - lot;
 		if (diff < 2)
@@ -581,20 +807,18 @@ hunt(char *name, time_t lot, time_t hit)
 			++t;
 		else if (t >= hit)
 			--t;
-		tmp = my_localtime(&t);
-		if (tmp != NULL)
-			tm = *tmp;
+		tmp = my_localtime_rz(tz, &t, &tm);
 		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
 			(delta(&tm, &lotm) == (t - lot) &&
 			tm.tm_isdst == lotm.tm_isdst &&
-			strcmp(abbr(&tm), loab) == 0)) {
+			strcmp(abbr(&tm), ab) == 0)) {
 				lot = t;
 				lotm = tm;
 				lotmp = tmp;
 		} else	hit = t;
 	}
-	show(name, lot, TRUE);
-	show(name, hit, TRUE);
+	show(tz, name, lot, true);
+	show(tz, name, hit, true);
 	return hit;
 }
 
@@ -623,49 +847,87 @@ delta(struct tm * newp, struct tm *oldp)
 	return result;
 }
 
+#ifndef TM_GMTOFF
+/* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
+   Assume A and B differ by at most one year.  */
+static int
+adjusted_yday(struct tm const *a, struct tm const *b)
+{
+  int yday = a->tm_yday;
+  if (b->tm_year < a->tm_year)
+    yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
+  return yday;
+}
+#endif
+
+/* If A is the broken-down local time and B the broken-down UTC for
+   the same instant, return A's UTC offset in seconds, where positive
+   offsets are east of Greenwich.  On failure, return LONG_MIN.  */
+static long
+gmtoff(struct tm const *a, struct tm const *b)
+{
+#ifdef TM_GMTOFF
+  return a->TM_GMTOFF;
+#else
+  if (! b)
+    return LONG_MIN;
+  else {
+    int ayday = adjusted_yday(a, b);
+    int byday = adjusted_yday(b, a);
+    int days = ayday - byday;
+    long hours = a->tm_hour - b->tm_hour + 24 * days;
+    long minutes = a->tm_min - b->tm_min + 60 * hours;
+    long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
+    return seconds;
+  }
+#endif
+}
+
 static void
-show(char *zone, time_t t, int v)
+show(timezone_t tz, char *zone, time_t t, bool v)
 {
 	register struct tm *	tmp;
+	register struct tm *	gmtmp;
+	struct tm tm, gmtm;
 
-	(void) printf("%-*s  ", (int) longest, zone);
+	printf("%-*s  ", longest, zone);
 	if (v) {
-		tmp = gmtime(&t);
-		if (tmp == NULL) {
-			(void) printf(tformat(), t);
+		gmtmp = my_gmtime_r(&t, &gmtm);
+		if (gmtmp == NULL) {
+			printf(tformat(), t);
 		} else {
-			dumptime(tmp);
-			(void) printf(" UT");
+			dumptime(gmtmp);
+			printf(" UT");
 		}
-		(void) printf(" = ");
+		printf(" = ");
 	}
-	tmp = my_localtime(&t);
+	tmp = my_localtime_rz(tz, &t, &tm);
 	dumptime(tmp);
 	if (tmp != NULL) {
 		if (*abbr(tmp) != '\0')
-			(void) printf(" %s", abbr(tmp));
+			printf(" %s", abbr(tmp));
 		if (v) {
-			(void) printf(" isdst=%d", tmp->tm_isdst);
-#ifdef TM_GMTOFF
-			(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
-#endif /* defined TM_GMTOFF */
+			long off = gmtoff(tmp, gmtmp);
+			printf(" isdst=%d", tmp->tm_isdst);
+			if (off != LONG_MIN)
+			  printf(" gmtoff=%ld", off);
 		}
 	}
-	(void) printf("\n");
+	printf("\n");
 	if (tmp != NULL && *abbr(tmp) != '\0')
 		abbrok(abbr(tmp), zone);
 }
 
-static char *
-abbr(struct tm *tmp)
+static char const *
+abbr(struct tm const *tmp)
 {
-	register char *	result;
-	static char	nada;
-
-	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
-		return &nada;
-	result = tzname[tmp->tm_isdst];
-	return (result == NULL) ? &nada : result;
+#ifdef TM_ZONE
+	return tmp->TM_ZONE;
+#else
+	return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
+		? tzname[0 < tmp->tm_isdst]
+		: "");
+#endif
 }
 
 /*
@@ -712,11 +974,11 @@ dumptime(register const struct tm *timeptr)
 	register int		trail;
 
 	if (timeptr == NULL) {
-		(void) printf("NULL");
+		printf("NULL");
 		return;
 	}
 	/*
-	** The packaged versions of localtime and gmtime never put out-of-range
+	** The packaged localtime_rz and gmtime_r never put out-of-range
 	** values in tm_wday or tm_mon, but since this code might be compiled
 	** with other (perhaps experimental) versions, paranoia is in order.
 	*/
@@ -728,7 +990,7 @@ dumptime(register const struct tm *timeptr)
 		(int) (sizeof mon_name / sizeof mon_name[0]))
 			mn = "???";
 	else		mn = mon_name[timeptr->tm_mon];
-	(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
+	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
 		wn, mn,
 		timeptr->tm_mday, timeptr->tm_hour,
 		timeptr->tm_min, timeptr->tm_sec);
@@ -745,6 +1007,6 @@ dumptime(register const struct tm *timeptr)
 		++lead;
 	}
 	if (lead == 0)
-		(void) printf("%d", trail);
-	else	(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
+		printf("%d", trail);
+	else	printf("%d%d", lead, ((trail < 0) ? -trail : trail));
 }
diff --git a/timezone/zic.c b/timezone/zic.c
index 07d6c30..78ab870 100644
--- a/timezone/zic.c
+++ b/timezone/zic.c
@@ -23,7 +23,7 @@ typedef int_fast64_t	zic_t;
 #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
 
 #if HAVE_SYS_STAT_H
-#include "sys/stat.h"
+#include <sys/stat.h>
 #endif
 #ifdef S_IRUSR
 #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
@@ -31,21 +31,6 @@ typedef int_fast64_t	zic_t;
 #define MKDIR_UMASK 0755
 #endif
 
-/*
-** On some ancient hosts, predicates like `isspace(C)' are defined
-** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
-** which says they are defined only if C == ((unsigned char) C) || C == EOF.
-** Neither the C Standard nor Posix require that `isascii' exist.
-** For portability, we check both ancient and modern requirements.
-** If isascii is not defined, the isascii check succeeds trivially.
-*/
-#include "ctype.h"
-#ifndef isascii
-#define isascii(x) 1
-#endif
-
-#define end(cp)	(strchr((cp), '\0'))
-
 struct rule {
 	const char *	r_filename;
 	int		r_linenum;
@@ -54,8 +39,8 @@ struct rule {
 	zic_t		r_loyear;	/* for example, 1986 */
 	zic_t		r_hiyear;	/* for example, 1986 */
 	const char *	r_yrtype;
-	int		r_lowasnum;
-	int		r_hiwasnum;
+	bool		r_lowasnum;
+	bool		r_hiwasnum;
 
 	int		r_month;	/* 0..11 */
 
@@ -64,10 +49,10 @@ struct rule {
 	int		r_wday;
 
 	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 */
+	bool		r_todisstd;	/* above is standard time if 1 */
+					/* or wall clock time if 0 */
+	bool		r_todisgmt;	/* above is GMT if 1 */
+					/* or local time if 0 */
 	zic_t		r_stdoff;	/* offset from standard time */
 	const char *	r_abbrvar;	/* variable part of abbreviation */
 
@@ -91,6 +76,7 @@ struct zone {
 	zic_t		z_gmtoff;
 	const char *	z_rule;
 	const char *	z_format;
+	char		z_format_specifier;
 
 	zic_t		z_stdoff;
 
@@ -115,25 +101,25 @@ extern int	optind;
 #endif
 
 static void	addtt(zic_t starttime, int type);
-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 int	addtype(zic_t, char const *, bool, bool, bool);
+static void	leapadd(zic_t, bool, int, int);
 static void	adjleap(void);
 static void	associate(void);
 static void	dolink(const char * fromfield, const char * tofield);
 static char **	getfields(char * buf);
-static zic_t	gethms(const char * string, const char * errstrng,
-		       int signable);
+static zic_t	gethms(const char * string, const char * errstring,
+		       bool);
 static void	infile(const char * filename);
 static void	inleap(char ** fields, int nfields);
 static void	inlink(char ** fields, int nfields);
 static void	inrule(char ** fields, int nfields);
-static int	inzcont(char ** fields, int nfields);
-static int	inzone(char ** fields, int nfields);
-static int	inzsub(char ** fields, int nfields, int iscont);
+static bool	inzcont(char ** fields, int nfields);
+static bool	inzone(char ** fields, int nfields);
+static bool	inzsub(char **, int, bool);
 static int	itsdir(const char * name);
-static int	lowerit(int c);
-static int	mkdirs(char * filename);
+static bool	is_alpha(char a);
+static char	lowerit(char);
+static bool	mkdirs(char *);
 static void	newabbr(const char * abbr);
 static zic_t	oadd(zic_t t1, zic_t t2);
 static void	outzone(const struct zone * zp, int ntzones);
@@ -143,21 +129,25 @@ static void	rulesub(struct rule * rp,
 			const char * typep, const char * monthp,
 			const char * dayp, const char * timep);
 static zic_t	tadd(zic_t t1, zic_t t2);
-static int	yearistype(int year, const char * type);
+static bool	yearistype(int year, const char * type);
+
+/* Bound on length of what %z can expand to.  */
+enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
 
 static int		charcnt;
-static int		errors;
+static bool		errors;
+static bool		warnings;
 static const char *	filename;
 static int		leapcnt;
-static int		leapseen;
+static bool		leapseen;
 static zic_t		leapminyear;
 static zic_t		leapmaxyear;
 static int		linenum;
-static int		max_abbrvar_len;
+static int		max_abbrvar_len = PERCENT_Z_LEN_BOUND;
 static int		max_format_len;
 static zic_t		max_year;
 static zic_t		min_year;
-static int		noise;
+static bool		noise;
 static const char *	rfilename;
 static int		rlinenum;
 static const char *	progname;
@@ -333,8 +323,8 @@ static struct lookup const	end_years[] = {
 };
 
 static struct lookup const	leap_types[] = {
-	{ "Rolling",	TRUE },
-	{ "Stationary",	FALSE },
+	{ "Rolling",	true },
+	{ "Stationary",	false },
 	{ NULL,		0 }
 };
 
@@ -354,8 +344,8 @@ static struct attype {
 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 bool		ttisstds[TZ_MAX_TYPES];
+static bool		ttisgmts[TZ_MAX_TYPES];
 static char		chars[TZ_MAX_CHARS];
 static zic_t		trans[TZ_MAX_LEAPS];
 static zic_t		corr[TZ_MAX_LEAPS];
@@ -376,22 +366,44 @@ static ATTRIBUTE_PURE size_t
 size_product(size_t nitems, size_t itemsize)
 {
 	if (SIZE_MAX / itemsize < nitems)
-		memory_exhausted("size overflow");
+		memory_exhausted(_("size overflow"));
 	return nitems * itemsize;
 }
 
+#if !HAVE_STRDUP
+static char *
+strdup(char const *str)
+{
+  char *result = malloc(strlen(str) + 1);
+  return result ? strcpy(result, str) : result;
+}
+#endif
+
 static ATTRIBUTE_PURE void *
-memcheck(void *const ptr)
+memcheck(void *ptr)
 {
 	if (ptr == NULL)
 		memory_exhausted(strerror(errno));
 	return ptr;
 }
 
-#define emalloc(size)		memcheck(malloc(size))
-#define erealloc(ptr, size)	memcheck(realloc(ptr, size))
-#define ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
-#define ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
+static void *
+emalloc(size_t size)
+{
+  return memcheck(malloc(size));
+}
+
+static void *
+erealloc(void *ptr, size_t size)
+{
+  return memcheck(realloc(ptr, size));
+}
+
+static char *
+ecpyalloc (char const *str)
+{
+  return memcheck(strdup(str));
+}
 
 static void *
 growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
@@ -401,7 +413,7 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
 	else {
 		int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
 		if ((amax - 1) / 3 * 2 < *nitems_alloc)
-			memory_exhausted("int overflow");
+			memory_exhausted(_("int overflow"));
 		*nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1;
 		return erealloc(ptr, size_product(*nitems_alloc, itemsize));
 	}
@@ -435,13 +447,13 @@ verror(const char *const string, va_list args)
 	**	zic ... 2>&1 | error -t "*" -v
 	** on BSD systems.
 	*/
-	fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
+	if (filename)
+	  fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
 	vfprintf(stderr, string, args);
 	if (rfilename != NULL)
-		(void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+		fprintf(stderr, _(" (rule from \"%s\", line %d)"),
 			rfilename, rlinenum);
-	(void) fprintf(stderr, "\n");
-	++errors;
+	fprintf(stderr, "\n");
 }
 
 static void ATTRIBUTE_FORMAT((printf, 1, 2))
@@ -451,6 +463,7 @@ error(const char *const string, ...)
 	va_start(args, string);
 	verror(string, args);
 	va_end(args);
+	errors = true;
 }
 
 static void ATTRIBUTE_FORMAT((printf, 1, 2))
@@ -461,19 +474,35 @@ warning(const char *const string, ...)
 	va_start(args, string);
 	verror(string, args);
 	va_end(args);
-	--errors;
+	warnings = true;
+}
+
+static void
+close_file(FILE *stream, char const *name)
+{
+  char const *e = (ferror(stream) ? _("I/O error")
+		   : fclose(stream) != 0 ? strerror(errno) : NULL);
+  if (e) {
+    fprintf(stderr, "%s: ", progname);
+    if (name)
+      fprintf(stderr, "%s: ", name);
+    fprintf(stderr, "%s\n", e);
+    exit(EXIT_FAILURE);
+  }
 }
 
 static _Noreturn void
 usage(FILE *stream, int status)
 {
-	(void) fprintf(stream, _("%s: usage is %s \
-[ --version ] [ --help ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
-\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\
-\n\
-Report bugs to %s.\n"),
-		       progname, progname, REPORT_BUGS_TO);
-	exit(status);
+  fprintf(stream,
+	  _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
+	    "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
+	    "\t[ -L leapseconds ] [ filename ... ]\n\n"
+	    "Report bugs to %s.\n"),
+	  progname, progname, REPORT_BUGS_TO);
+  if (status == EXIT_SUCCESS)
+    close_file(stream, NULL);
+  exit(status);
 }
 
 static const char *	psxrules;
@@ -490,25 +519,26 @@ main(int argc, char **argv)
 	register int	c;
 
 #ifdef S_IWGRP
-	(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
+	umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
 #endif
 #if HAVE_GETTEXT
-	(void) setlocale(LC_ALL, "");
+	setlocale(LC_ALL, "");
 #ifdef TZ_DOMAINDIR
-	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
 #endif /* defined TEXTDOMAINDIR */
-	(void) textdomain(TZ_DOMAIN);
+	textdomain(TZ_DOMAIN);
 #endif /* HAVE_GETTEXT */
 	progname = argv[0];
 	if (TYPE_BIT(zic_t) < 64) {
-		(void) fprintf(stderr, "%s: %s\n", progname,
+		fprintf(stderr, "%s: %s\n", progname,
 			_("wild compilation-time specification of zic_t"));
-		exit(EXIT_FAILURE);
+		return EXIT_FAILURE;
 	}
 	for (i = 1; i < argc; ++i)
 		if (strcmp(argv[i], "--version") == 0) {
-			(void) printf("zic %s%s\n", PKGVERSION, TZVERSION);
-			exit(EXIT_SUCCESS);
+			printf("zic %s%s\n", PKGVERSION, TZVERSION);
+			close_file(stdout, NULL);
+			return EXIT_SUCCESS;
 		} else if (strcmp(argv[i], "--help") == 0) {
 			usage(stdout, EXIT_SUCCESS);
 		}
@@ -520,57 +550,57 @@ main(int argc, char **argv)
 				if (directory == NULL)
 					directory = optarg;
 				else {
-					(void) fprintf(stderr,
+					fprintf(stderr,
 _("%s: More than one -d option specified\n"),
 						progname);
-					exit(EXIT_FAILURE);
+					return EXIT_FAILURE;
 				}
 				break;
 			case 'l':
 				if (lcltime == NULL)
 					lcltime = optarg;
 				else {
-					(void) fprintf(stderr,
+					fprintf(stderr,
 _("%s: More than one -l option specified\n"),
 						progname);
-					exit(EXIT_FAILURE);
+					return EXIT_FAILURE;
 				}
 				break;
 			case 'p':
 				if (psxrules == NULL)
 					psxrules = optarg;
 				else {
-					(void) fprintf(stderr,
+					fprintf(stderr,
 _("%s: More than one -p option specified\n"),
 						progname);
-					exit(EXIT_FAILURE);
+					return EXIT_FAILURE;
 				}
 				break;
 			case 'y':
 				if (yitcommand == NULL)
 					yitcommand = optarg;
 				else {
-					(void) fprintf(stderr,
+					fprintf(stderr,
 _("%s: More than one -y option specified\n"),
 						progname);
-					exit(EXIT_FAILURE);
+					return EXIT_FAILURE;
 				}
 				break;
 			case 'L':
 				if (leapsec == NULL)
 					leapsec = optarg;
 				else {
-					(void) fprintf(stderr,
+					fprintf(stderr,
 _("%s: More than one -L option specified\n"),
 						progname);
-					exit(EXIT_FAILURE);
+					return EXIT_FAILURE;
 				}
 				break;
 			case 'v':
-				noise = TRUE;
+				noise = true;
 				break;
 			case 's':
-				(void) printf("%s: -s ignored\n", progname);
+				warning(_("-s ignored"));
 				break;
 		}
 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
@@ -588,7 +618,7 @@ _("%s: More than one -L option specified\n"),
 	for (i = optind; i < argc; ++i)
 		infile(argv[i]);
 	if (errors)
-		exit(EXIT_FAILURE);
+		return EXIT_FAILURE;
 	associate();
 	for (i = 0; i < nzones; i = j) {
 		/*
@@ -611,53 +641,137 @@ _("%s: More than one -L option specified\n"),
 						warning(_("link to link"));
 	}
 	if (lcltime != NULL) {
-		eat("command line", 1);
+		eat(_("command line"), 1);
 		dolink(lcltime, TZDEFAULT);
 	}
 	if (psxrules != NULL) {
-		eat("command line", 1);
+		eat(_("command line"), 1);
 		dolink(psxrules, TZDEFRULES);
 	}
-	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+	if (warnings && (ferror(stderr) || fclose(stderr) != 0))
+	  return EXIT_FAILURE;
+	return errors ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static bool
+componentcheck(char const *name, char const *component,
+	       char const *component_end)
+{
+	enum { component_len_max = 14 };
+	size_t component_len = component_end - component;
+	if (component_len == 0) {
+	  if (!*name)
+	    error (_("empty file name"));
+	  else
+	    error (_(component == name
+		     ? "file name '%s' begins with '/'"
+		     : *component_end
+		     ? "file name '%s' contains '//'"
+		     : "file name '%s' ends with '/'"),
+		   name);
+	  return false;
+	}
+	if (0 < component_len && component_len <= 2
+	    && component[0] == '.' && component_end[-1] == '.') {
+	  error(_("file name '%s' contains '%.*s' component"),
+		name, (int) component_len, component);
+	  return false;
+	}
+	if (noise) {
+	  if (0 < component_len && component[0] == '-')
+	    warning(_("file name '%s' component contains leading '-'"),
+		    name);
+	  if (component_len_max < component_len)
+	    warning(_("file name '%s' contains overlength component"
+		      " '%.*s...'"),
+		    name, component_len_max, component);
+	}
+	return true;
+}
+
+static bool
+namecheck(const char *name)
+{
+	register char const *cp;
+
+	/* Benign characters in a portable file name.  */
+	static char const benign[] =
+	  "-/_"
+	  "abcdefghijklmnopqrstuvwxyz"
+	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+	/* Non-control chars in the POSIX portable character set,
+	   excluding the benign characters.  */
+	static char const printable_and_not_benign[] =
+	  " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
+
+	register char const *component = name;
+	for (cp = name; *cp; cp++) {
+		unsigned char c = *cp;
+		if (noise && !strchr(benign, c)) {
+			warning((strchr(printable_and_not_benign, c)
+				 ? _("file name '%s' contains byte '%c'")
+				 : _("file name '%s' contains byte '\\%o'")),
+				name, c);
+		}
+		if (c == '/') {
+			if (!componentcheck(name, component, cp))
+			  return false;
+			component = cp + 1;
+		}
+	}
+	return componentcheck(name, component, cp);
+}
+
+static char *
+relname(char const *dir, char const *base)
+{
+  if (*base == '/')
+    return ecpyalloc(base);
+  else {
+    size_t dir_len = strlen(dir);
+    bool needs_slash = dir_len && dir[dir_len - 1] != '/';
+    char *result = emalloc(dir_len + needs_slash + strlen(base) + 1);
+    result[dir_len] = '/';
+    strcpy(result + dir_len + needs_slash, base);
+    return memcpy(result, dir, dir_len);
+  }
 }
 
 static void
-dolink(const char *const fromfield, const char *const tofield)
+dolink(char const *fromfield, char const *tofield)
 {
 	register char *	fromname;
 	register char *	toname;
+	register int fromisdir;
 
-	if (fromfield[0] == '/')
-		fromname = ecpyalloc(fromfield);
-	else {
-		fromname = ecpyalloc(directory);
-		fromname = ecatalloc(fromname, "/");
-		fromname = ecatalloc(fromname, fromfield);
-	}
-	if (tofield[0] == '/')
-		toname = ecpyalloc(tofield);
-	else {
-		toname = ecpyalloc(directory);
-		toname = ecatalloc(toname, "/");
-		toname = ecatalloc(toname, tofield);
-	}
+	fromname = relname(directory, fromfield);
+	toname = relname(directory, tofield);
 	/*
 	** We get to be careful here since
 	** there's a fair chance of root running us.
 	*/
-	if (!itsdir(toname))
-		(void) remove(toname);
-	if (link(fromname, toname) != 0
-	    && access(fromname, F_OK) == 0 && !itsdir(fromname)) {
+	fromisdir = itsdir(fromname);
+	if (fromisdir) {
+		char const *e = strerror(fromisdir < 0 ? errno : EPERM);
+		fprintf(stderr, _("%s: link from %s failed: %s"),
+			progname, fromname, e);
+		exit(EXIT_FAILURE);
+	}
+	if (itsdir(toname) <= 0)
+		remove(toname);
+	if (link(fromname, toname) != 0) {
 		int	result;
 
-		if (mkdirs(toname) != 0)
+		if (! mkdirs(toname))
 			exit(EXIT_FAILURE);
 
 		result = link(fromname, toname);
 		if (result != 0) {
 				const char *s = fromfield;
 				const char *t;
+				char *p;
+				size_t dotdots = 0;
 				register char * symlinkcontents = NULL;
 
 				do
@@ -666,13 +780,13 @@ dolink(const char *const fromfield, const char *const tofield)
 				       && ! strncmp (fromfield, tofield,
 						     ++s - fromfield));
 
-				for (s = tofield + (t - fromfield);
-				     (s = strchr(s, '/'));
-				     s++)
-					symlinkcontents =
-						ecatalloc(symlinkcontents,
-						"../");
-				symlinkcontents = ecatalloc(symlinkcontents, t);
+				for (s = tofield + (t - fromfield); *s; s++)
+				  dotdots += *s == '/';
+				symlinkcontents
+				  = emalloc(3 * dotdots + strlen(t) + 1);
+				for (p = symlinkcontents; dotdots-- != 0; p += 3)
+				  memcpy(p, "../", 3);
+				strcpy(p, t);
 				result = symlink(symlinkcontents, toname);
 				if (result == 0)
 warning(_("hard link failed, symbolic link used"));
@@ -684,7 +798,7 @@ warning(_("hard link failed, symbolic link used"));
 			fp = fopen(fromname, "rb");
 			if (!fp) {
 				const char *e = strerror(errno);
-				(void) fprintf(stderr,
+				fprintf(stderr,
 					       _("%s: Can't read %s: %s\n"),
 					       progname, fromname, e);
 				exit(EXIT_FAILURE);
@@ -692,25 +806,15 @@ warning(_("hard link failed, symbolic link used"));
 			tp = fopen(toname, "wb");
 			if (!tp) {
 				const char *e = strerror(errno);
-				(void) fprintf(stderr,
+				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);
-			}
+			close_file(fp, fromname);
+			close_file(tp, toname);
 			warning(_("link failed, copy used"));
 		}
 	}
@@ -720,8 +824,8 @@ warning(_("hard link failed, symbolic link used"));
 
 #define TIME_T_BITS_IN_FILE	64
 
-static const zic_t min_time = (zic_t) -1 << (TIME_T_BITS_IN_FILE - 1);
-static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1));
+static zic_t const min_time = MINVAL (zic_t, TIME_T_BITS_IN_FILE);
+static zic_t const max_time = MAXVAL (zic_t, TIME_T_BITS_IN_FILE);
 
 /* Estimated time of the Big Bang, in seconds since the POSIX epoch.
    rounded downward to the negation of a power of two that is
@@ -753,17 +857,24 @@ static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1));
 
 static const zic_t big_bang_time = BIG_BANG;
 
+/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble.  */
 static int
-itsdir(const char *const name)
+itsdir(char const *name)
 {
-	register char *	myname;
-	register int	accres;
-
-	myname = ecpyalloc(name);
-	myname = ecatalloc(myname, "/.");
-	accres = access(myname, F_OK);
-	free(myname);
-	return accres == 0;
+	struct stat st;
+	int res = stat(name, &st);
+	if (res != 0)
+		return res;
+#ifdef S_ISDIR
+	return S_ISDIR(st.st_mode) != 0;
+#else
+	{
+		char *nameslashdot = relname(name, ".");
+		res = stat(nameslashdot, &st);
+		free(nameslashdot);
+		return res == 0;
+	}
+#endif
 }
 
 /*
@@ -790,7 +901,7 @@ associate(void)
 	register int		i, j;
 
 	if (nrules != 0) {
-		(void) qsort(rules, nrules, sizeof *rules, rcomp);
+		qsort(rules, nrules, sizeof *rules, rcomp);
 		for (i = 0; i < nrules - 1; ++i) {
 			if (strcmp(rules[i].r_name,
 				rules[i + 1].r_name) != 0)
@@ -843,12 +954,12 @@ associate(void)
 			*/
 			eat(zp->z_filename, zp->z_linenum);
 			zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
-				TRUE);
+				true);
 			/*
 			** Note, though, that if there's no rule,
 			** a '%s' in the format is a bad thing.
 			*/
-			if (strchr(zp->z_format, '%') != 0)
+			if (zp->z_format_specifier == 's')
 				error("%s", _("%s in ruleless zone"));
 		}
 	}
@@ -864,7 +975,7 @@ infile(const char *name)
 	register char *			cp;
 	register const struct lookup *	lp;
 	register int			nfields;
-	register int			wantcont;
+	register bool			wantcont;
 	register int			num;
 	char				buf[BUFSIZ];
 
@@ -874,11 +985,11 @@ infile(const char *name)
 	} else if ((fp = fopen(name, "r")) == NULL) {
 		const char *e = strerror(errno);
 
-		(void) fprintf(stderr, _("%s: Can't open %s: %s\n"),
+		fprintf(stderr, _("%s: Can't open %s: %s\n"),
 			progname, name, e);
 		exit(EXIT_FAILURE);
 	}
-	wantcont = FALSE;
+	wantcont = false;
 	for (num = 1; ; ++num) {
 		eat(name, num);
 		if (fgets(buf, sizeof buf, fp) != buf)
@@ -909,25 +1020,25 @@ infile(const char *name)
 			else switch ((int) (lp->l_value)) {
 				case LC_RULE:
 					inrule(fields, nfields);
-					wantcont = FALSE;
+					wantcont = false;
 					break;
 				case LC_ZONE:
 					wantcont = inzone(fields, nfields);
 					break;
 				case LC_LINK:
 					inlink(fields, nfields);
-					wantcont = FALSE;
+					wantcont = false;
 					break;
 				case LC_LEAP:
 					if (name != leapsec)
-						(void) fprintf(stderr,
-_("%s: Leap line in non leap seconds file %s\n"),
-							progname, name);
+					  warning(_("%s: Leap line in non leap"
+						    " seconds file %s"),
+						  progname, name);
 					else	inleap(fields, nfields);
-					wantcont = FALSE;
+					wantcont = false;
 					break;
 				default:	/* "cannot happen" */
-					(void) fprintf(stderr,
+					fprintf(stderr,
 _("%s: panic: Invalid l_value %d\n"),
 						progname, lp->l_value);
 					exit(EXIT_FAILURE);
@@ -935,18 +1046,7 @@ _("%s: panic: Invalid l_value %d\n"),
 		}
 		free(fields);
 	}
-	if (ferror(fp)) {
-		(void) fprintf(stderr, _("%s: Error reading %s\n"),
-			progname, filename);
-		exit(EXIT_FAILURE);
-	}
-	if (fp != stdin && fclose(fp)) {
-		const char *e = strerror(errno);
-
-		(void) fprintf(stderr, _("%s: Error closing %s: %s\n"),
-			progname, filename, e);
-		exit(EXIT_FAILURE);
-	}
+	close_file(fp, filename);
 	if (wantcont)
 		error(_("expected continuation line not found"));
 }
@@ -960,10 +1060,11 @@ _("%s: panic: Invalid l_value %d\n"),
 */
 
 static zic_t
-gethms(const char *string, const char *const errstring, const int signable)
+gethms(char const *string, char const *errstring, bool signable)
 {
 	zic_t	hh;
 	int	mm, ss, sign;
+	char xs;
 
 	if (string == NULL || *string == '\0')
 		return 0;
@@ -973,12 +1074,12 @@ gethms(const char *string, const char *const errstring, const int signable)
 		sign = -1;
 		++string;
 	} else	sign = 1;
-	if (sscanf(string, scheck(string, "%"SCNdZIC), &hh) == 1)
+	if (sscanf(string, "%"SCNdZIC"%c", &hh, &xs) == 1)
 		mm = ss = 0;
-	else if (sscanf(string, scheck(string, "%"SCNdZIC":%d"), &hh, &mm) == 2)
+	else if (sscanf(string, "%"SCNdZIC":%d%c", &hh, &mm, &xs) == 2)
 		ss = 0;
-	else if (sscanf(string, scheck(string, "%"SCNdZIC":%d:%d"),
-		&hh, &mm, &ss) != 3) {
+	else if (sscanf(string, "%"SCNdZIC":%d:%d%c", &hh, &mm, &ss, &xs)
+		 != 3) {
 			error("%s", errstring);
 			return 0;
 	}
@@ -1000,7 +1101,7 @@ warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
 }
 
 static void
-inrule(register char **const fields, const int nfields)
+inrule(char **fields, int nfields)
 {
 	static struct rule	r;
 
@@ -1014,7 +1115,7 @@ inrule(register char **const fields, const int nfields)
 	}
 	r.r_filename = filename;
 	r.r_linenum = linenum;
-	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
+	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
 	r.r_name = ecpyalloc(fields[RF_NAME]);
@@ -1025,26 +1126,26 @@ inrule(register char **const fields, const int nfields)
 	rules[nrules++] = r;
 }
 
-static int
-inzone(register char **const fields, const int nfields)
+static bool
+inzone(char **fields, int nfields)
 {
 	register int	i;
 
 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
 		error(_("wrong number of fields on Zone line"));
-		return FALSE;
+		return false;
 	}
 	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
 		error(
 _("\"Zone %s\" line and -l option are mutually exclusive"),
 			TZDEFAULT);
-		return FALSE;
+		return false;
 	}
 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
 		error(
 _("\"Zone %s\" line and -p option are mutually exclusive"),
 			TZDEFRULES);
-		return FALSE;
+		return false;
 	}
 	for (i = 0; i < nzones; ++i)
 		if (zones[i].z_name != NULL &&
@@ -1054,30 +1155,31 @@ _("duplicate zone name %s (file \"%s\", line %d)"),
 					fields[ZF_NAME],
 					zones[i].z_filename,
 					zones[i].z_linenum);
-				return FALSE;
+				return false;
 		}
-	return inzsub(fields, nfields, FALSE);
+	return inzsub(fields, nfields, false);
 }
 
-static int
-inzcont(register char **const fields, const int nfields)
+static bool
+inzcont(char **fields, int nfields)
 {
 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
 		error(_("wrong number of fields on Zone continuation line"));
-		return FALSE;
+		return false;
 	}
-	return inzsub(fields, nfields, TRUE);
+	return inzsub(fields, nfields, true);
 }
 
-static int
-inzsub(register char **const fields, const int nfields, const int iscont)
+static bool
+inzsub(char **fields, int nfields, bool iscont)
 {
 	register char *		cp;
+	char *			cp1;
 	static struct zone	z;
 	register int		i_gmtoff, i_rule, i_format;
 	register int		i_untilyear, i_untilmonth;
 	register int		i_untilday, i_untiltime;
-	register int		hasuntil;
+	register bool		hasuntil;
 
 	if (iscont) {
 		i_gmtoff = ZFC_GMTOFF;
@@ -1088,7 +1190,9 @@ inzsub(register char **const fields, const int nfields, const int iscont)
 		i_untilday = ZFC_TILDAY;
 		i_untiltime = ZFC_TILTIME;
 		z.z_name = NULL;
-	} else {
+	} else if (!namecheck(fields[ZF_NAME]))
+		return false;
+	else {
 		i_gmtoff = ZF_GMTOFF;
 		i_rule = ZF_RULE;
 		i_format = ZF_FORMAT;
@@ -1100,15 +1204,23 @@ 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 UT 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) {
+		if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
+		    || strchr(fields[i_format], '/')) {
 			error(_("invalid abbreviation format"));
-			return FALSE;
+			return false;
 		}
 	}
 	z.z_rule = ecpyalloc(fields[i_rule]);
-	z.z_format = ecpyalloc(fields[i_format]);
+	z.z_format = cp1 = ecpyalloc(fields[i_format]);
+	z.z_format_specifier = cp ? *cp : '\0';
+	if (z.z_format_specifier == 'z') {
+	  if (noise)
+	    warning(_("format '%s' not handled by pre-2015 versions of zic"),
+		    z.z_format);
+	  cp1[cp - fields[i_format]] = 's';
+	}
 	if (max_format_len < strlen(z.z_format))
 		max_format_len = strlen(z.z_format);
 	hasuntil = nfields > i_untilyear;
@@ -1134,7 +1246,7 @@ inzsub(register char **const fields, const int nfields, const int iscont)
 				error(_(
 "Zone continuation line end time is not after end time of previous line"
 					));
-				return FALSE;
+				return false;
 		}
 	}
 	zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
@@ -1147,7 +1259,7 @@ inzsub(register char **const fields, const int nfields, const int iscont)
 }
 
 static void
-inleap(register char ** const fields, const int nfields)
+inleap(char **fields, int nfields)
 {
 	register const char *		cp;
 	register const struct lookup *	lp;
@@ -1156,6 +1268,7 @@ inleap(register char ** const fields, const int nfields)
 	int				month, day;
 	zic_t				dayoff, tod;
 	zic_t				t;
+	char xs;
 
 	if (nfields != LEAP_FIELDS) {
 		error(_("wrong number of fields on Leap line"));
@@ -1163,7 +1276,7 @@ inleap(register char ** const fields, const int nfields)
 	}
 	dayoff = 0;
 	cp = fields[LP_YEAR];
-	if (sscanf(cp, scheck(cp, "%"SCNdZIC), &year) != 1) {
+	if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) {
 		/*
 		** Leapin' Lizards!
 		*/
@@ -1174,7 +1287,7 @@ inleap(register char ** const fields, const int nfields)
 		leapmaxyear = year;
 	if (!leapseen || leapminyear > year)
 		leapminyear = year;
-	leapseen = TRUE;
+	leapseen = true;
 	j = EPOCH_YEAR;
 	while (j != year) {
 		if (year > j) {
@@ -1198,7 +1311,7 @@ inleap(register char ** const fields, const int nfields)
 		++j;
 	}
 	cp = fields[LP_DAY];
-	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
+	if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
 		day <= 0 || day > len_months[isleap(year)][month]) {
 			error(_("invalid day of month"));
 			return;
@@ -1213,23 +1326,23 @@ inleap(register char ** const fields, const int nfields)
 		return;
 	}
 	t = dayoff * SECSPERDAY;
-	tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
+	tod = gethms(fields[LP_TIME], _("invalid time of day"), false);
 	cp = fields[LP_CORR];
 	{
-		register int	positive;
+		register bool	positive;
 		int		count;
 
 		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
-			positive = FALSE;
+			positive = false;
 			count = 1;
 		} else if (strcmp(cp, "--") == 0) {
-			positive = FALSE;
+			positive = false;
 			count = 2;
 		} else if (strcmp(cp, "+") == 0) {
-			positive = TRUE;
+			positive = true;
 			count = 1;
 		} else if (strcmp(cp, "++") == 0) {
-			positive = TRUE;
+			positive = true;
 			count = 2;
 		} else {
 			error(_("illegal CORRECTION field on Leap line"));
@@ -1251,7 +1364,7 @@ inleap(register char ** const fields, const int nfields)
 }
 
 static void
-inlink(register char **const fields, const int nfields)
+inlink(char **fields, int nfields)
 {
 	struct link	l;
 
@@ -1263,10 +1376,8 @@ inlink(register char **const fields, const int nfields)
 		error(_("blank FROM field on Link line"));
 		return;
 	}
-	if (*fields[LF_TO] == '\0') {
-		error(_("blank TO field on Link line"));
-		return;
-	}
+	if (! namecheck(fields[LF_TO]))
+	  return;
 	l.l_filename = filename;
 	l.l_linenum = linenum;
 	l.l_from = ecpyalloc(fields[LF_FROM]);
@@ -1276,50 +1387,47 @@ inlink(register char **const fields, const int nfields)
 }
 
 static void
-rulesub(register struct rule *const rp,
-	const char *const loyearp,
-	const char *const hiyearp,
-	const char *const typep,
-	const char *const monthp,
-	const char *const dayp,
-	const char *const timep)
+rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
+	const char *typep, const char *monthp, const char *dayp,
+	const char *timep)
 {
 	register const struct lookup *	lp;
 	register const char *		cp;
 	register char *			dp;
 	register char *			ep;
+	char xs;
 
 	if ((lp = byword(monthp, mon_names)) == NULL) {
 		error(_("invalid month name"));
 		return;
 	}
 	rp->r_month = lp->l_value;
-	rp->r_todisstd = FALSE;
-	rp->r_todisgmt = FALSE;
+	rp->r_todisstd = false;
+	rp->r_todisgmt = false;
 	dp = ecpyalloc(timep);
 	if (*dp != '\0') {
 		ep = dp + strlen(dp) - 1;
 		switch (lowerit(*ep)) {
 			case 's':	/* Standard */
-				rp->r_todisstd = TRUE;
-				rp->r_todisgmt = FALSE;
+				rp->r_todisstd = true;
+				rp->r_todisgmt = false;
 				*ep = '\0';
 				break;
 			case 'w':	/* Wall */
-				rp->r_todisstd = FALSE;
-				rp->r_todisgmt = FALSE;
+				rp->r_todisstd = false;
+				rp->r_todisgmt = false;
 				*ep = '\0';
 				break;
 			case 'g':	/* Greenwich */
 			case 'u':	/* Universal */
 			case 'z':	/* Zulu */
-				rp->r_todisstd = TRUE;
-				rp->r_todisgmt = TRUE;
+				rp->r_todisstd = true;
+				rp->r_todisgmt = true;
 				*ep = '\0';
 				break;
 		}
 	}
-	rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
+	rp->r_tod = gethms(dp, _("invalid time of day"), false);
 	free(dp);
 	/*
 	** Year work.
@@ -1335,11 +1443,11 @@ rulesub(register struct rule *const rp,
 			rp->r_loyear = ZIC_MAX;
 			break;
 		default:	/* "cannot happen" */
-			(void) fprintf(stderr,
+			fprintf(stderr,
 				_("%s: panic: Invalid l_value %d\n"),
 				progname, lp->l_value);
 			exit(EXIT_FAILURE);
-	} else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_loyear) != 1) {
+	} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
 		error(_("invalid starting year"));
 		return;
 	}
@@ -1357,11 +1465,11 @@ rulesub(register struct rule *const rp,
 			rp->r_hiyear = rp->r_loyear;
 			break;
 		default:	/* "cannot happen" */
-			(void) fprintf(stderr,
+			fprintf(stderr,
 				_("%s: panic: Invalid l_value %d\n"),
 				progname, lp->l_value);
 			exit(EXIT_FAILURE);
-	} else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_hiyear) != 1) {
+	} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
 		error(_("invalid ending year"));
 		return;
 	}
@@ -1414,7 +1522,7 @@ rulesub(register struct rule *const rp,
 			}
 			rp->r_wday = lp->l_value;
 		}
-		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
+		if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
 			rp->r_dayofmonth <= 0 ||
 			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
 				error(_("invalid day of month"));
@@ -1453,7 +1561,7 @@ puttzcode(const int_fast32_t val, FILE *const fp)
 	char	buf[4];
 
 	convert(val, buf);
-	(void) fwrite(buf, sizeof buf, 1, fp);
+	fwrite(buf, sizeof buf, 1, fp);
 }
 
 static void
@@ -1462,7 +1570,7 @@ puttzcode64(const zic_t val, FILE *const fp)
 	char	buf[8];
 
 	convert64(val, buf);
-	(void) fwrite(buf, sizeof buf, 1, fp);
+	fwrite(buf, sizeof buf, 1, fp);
 }
 
 static int
@@ -1474,7 +1582,7 @@ atcomp(const void *avp, const void *bvp)
 	return (a < b) ? -1 : (a > b);
 }
 
-static int
+static bool
 is32(const zic_t x)
 {
 	return INT32_MIN <= x && x <= INT32_MAX;
@@ -1488,7 +1596,7 @@ writezone(const char *const name, const char *const string, char version)
 	register int			leapcnt32, leapi32;
 	register int			timecnt32, timei32;
 	register int			pass;
-	static char *			fullname;
+	char *				fullname;
 	static const struct tzhead	tzh0;
 	static struct tzhead		tzh;
 	zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
@@ -1499,7 +1607,7 @@ writezone(const char *const name, const char *const string, char version)
 	** Sort.
 	*/
 	if (timecnt > 1)
-		(void) qsort(attypes, timecnt, sizeof *attypes, atcomp);
+		qsort(attypes, timecnt, sizeof *attypes, atcomp);
 	/*
 	** Optimize.
 	*/
@@ -1561,7 +1669,7 @@ writezone(const char *const name, const char *const string, char version)
 		++timei32;
 	}
 	/*
-	** Output an INT32_MIN "transition" if appropriate--see below.
+	** Output an INT32_MIN "transition" if appropriate; see below.
 	*/
 	if (timei32 > 0 && ats[timei32] > INT32_MIN) {
 		--timei32;
@@ -1573,26 +1681,24 @@ writezone(const char *const name, const char *const string, char version)
 		--leapcnt32;
 		++leapi32;
 	}
-	fullname = erealloc(fullname,
-			    strlen(directory) + 1 + strlen(name) + 1);
-	(void) sprintf(fullname, "%s/%s", directory, name);
+	fullname = relname(directory, name);
 	/*
 	** Remove old file, if any, to snap links.
 	*/
-	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
+	if (itsdir(fullname) <= 0 && remove(fullname) != 0 && errno != ENOENT) {
 		const char *e = strerror(errno);
 
-		(void) fprintf(stderr, _("%s: Can't remove %s: %s\n"),
+		fprintf(stderr, _("%s: Can't remove %s: %s\n"),
 			progname, fullname, e);
 		exit(EXIT_FAILURE);
 	}
 	if ((fp = fopen(fullname, "wb")) == NULL) {
-		if (mkdirs(fullname) != 0)
+		if (! mkdirs(fullname))
 			exit(EXIT_FAILURE);
 		if ((fp = fopen(fullname, "wb")) == NULL) {
 			const char *e = strerror(errno);
 
-			(void) fprintf(stderr, _("%s: Can't create %s: %s\n"),
+			fprintf(stderr, _("%s: Can't create %s: %s\n"),
 				progname, fullname, e);
 			exit(EXIT_FAILURE);
 		}
@@ -1606,7 +1712,7 @@ writezone(const char *const name, const char *const string, char version)
 		register int	thistypecnt;
 		char		thischars[TZ_MAX_CHARS];
 		char		thischarcnt;
-		int 		indmap[TZ_MAX_CHARS];
+		int		indmap[TZ_MAX_CHARS];
 
 		if (pass == 1) {
 			thistimei = timei32;
@@ -1629,16 +1735,16 @@ writezone(const char *const name, const char *const string, char version)
 			** (32- or 64-bit) window.
 			*/
 			if (typecnt != 0)
-				writetype[typecnt - 1] = TRUE;
+				writetype[typecnt - 1] = true;
 		} else {
 			for (i = thistimei - 1; i < thistimelim; ++i)
 				if (i >= 0)
-					writetype[types[i]] = TRUE;
+					writetype[types[i]] = true;
 			/*
 			** For America/Godthab and Antarctica/Palmer
 			*/
 			if (thistimei == 0)
-				writetype[0] = TRUE;
+				writetype[0] = true;
 		}
 #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
 		/*
@@ -1668,22 +1774,22 @@ writezone(const char *const name, const char *const string, char version)
 					isdsts[mrudst] = -1;
 					type = addtype(gmtoffs[mrudst],
 						&chars[abbrinds[mrudst]],
-						TRUE,
+						true,
 						ttisstds[mrudst],
 						ttisgmts[mrudst]);
-					isdsts[mrudst] = TRUE;
-					writetype[type] = TRUE;
+					isdsts[mrudst] = 1;
+					writetype[type] = true;
 			}
 			if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
 				gmtoffs[histd] != gmtoffs[mrustd]) {
 					isdsts[mrustd] = -1;
 					type = addtype(gmtoffs[mrustd],
 						&chars[abbrinds[mrustd]],
-						FALSE,
+						false,
 						ttisstds[mrustd],
 						ttisgmts[mrustd]);
-					isdsts[mrustd] = FALSE;
-					writetype[type] = TRUE;
+					isdsts[mrustd] = 0;
+					writetype[type] = true;
 			}
 		}
 #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
@@ -1705,15 +1811,15 @@ writezone(const char *const name, const char *const string, char version)
 				if (strcmp(&thischars[j], thisabbr) == 0)
 					break;
 			if (j == thischarcnt) {
-				(void) strcpy(&thischars[(int) thischarcnt],
+				strcpy(&thischars[(int) thischarcnt],
 					thisabbr);
 				thischarcnt += strlen(thisabbr) + 1;
 			}
 			indmap[abbrinds[i]] = j;
 		}
-#define DO(field)	((void) fwrite(tzh.field, sizeof tzh.field, 1, fp))
+#define DO(field)	fwrite(tzh.field, sizeof tzh.field, 1, fp)
 		tzh = tzh0;
-		(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+		strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
 		tzh.tzh_version[0] = version;
 		convert(thistypecnt, tzh.tzh_ttisgmtcnt);
 		convert(thistypecnt, tzh.tzh_ttisstdcnt);
@@ -1735,7 +1841,7 @@ writezone(const char *const name, const char *const string, char version)
 			if (pass == 1)
 				/*
 				** Output an INT32_MIN "transition"
-				** if appropriate--see above.
+				** if appropriate; see above.
 				*/
 				puttzcode(((ats[i] < INT32_MIN) ?
 					INT32_MIN : ats[i]), fp);
@@ -1744,16 +1850,16 @@ writezone(const char *const name, const char *const string, char version)
 			unsigned char	uc;
 
 			uc = typemap[types[i]];
-			(void) fwrite(&uc, sizeof uc, 1, fp);
+			fwrite(&uc, sizeof uc, 1, fp);
 		}
 		for (i = 0; i < typecnt; ++i)
 			if (writetype[i]) {
 				puttzcode(gmtoffs[i], fp);
-				(void) putc(isdsts[i], fp);
-				(void) putc((unsigned char) indmap[abbrinds[i]], fp);
+				putc(isdsts[i], fp);
+				putc((unsigned char) indmap[abbrinds[i]], fp);
 			}
 		if (thischarcnt != 0)
-			(void) fwrite(thischars, sizeof thischars[0],
+			fwrite(thischars, sizeof thischars[0],
 				      thischarcnt, fp);
 		for (i = thisleapi; i < thisleaplim; ++i) {
 			register zic_t	todo;
@@ -1782,54 +1888,88 @@ writezone(const char *const name, const char *const string, char version)
 		}
 		for (i = 0; i < typecnt; ++i)
 			if (writetype[i])
-				(void) putc(ttisstds[i], fp);
+				putc(ttisstds[i], fp);
 		for (i = 0; i < typecnt; ++i)
 			if (writetype[i])
-				(void) putc(ttisgmts[i], fp);
-	}
-	(void) fprintf(fp, "\n%s\n", string);
-	if (ferror(fp) || fclose(fp)) {
-		(void) fprintf(stderr, _("%s: Error writing %s\n"),
-			progname, fullname);
-		exit(EXIT_FAILURE);
+				putc(ttisgmts[i], fp);
 	}
+	fprintf(fp, "\n%s\n", string);
+	close_file(fp, fullname);
 	free(ats);
+	free(fullname);
 }
 
-static void
-doabbr(char *const abbr, const char *const format, const char *const letters,
-       const int isdst, const int doquotes)
+static char const *
+abbroffset(char *buf, zic_t offset)
+{
+  char sign = '+';
+  int seconds, minutes;
+
+  if (offset < 0) {
+    offset = -offset;
+    sign = '-';
+  }
+
+  seconds = offset % SECSPERMIN;
+  offset /= SECSPERMIN;
+  minutes = offset % MINSPERHOUR;
+  offset /= MINSPERHOUR;
+  if (100 <= offset) {
+    error(_("%%z UTC offset magnitude exceeds 99:59:59"));
+    return "%z";
+  } else {
+    char *p = buf;
+    *p++ = sign;
+    *p++ = '0' + offset / 10;
+    *p++ = '0' + offset % 10;
+    if (minutes | seconds) {
+      *p++ = '0' + minutes / 10;
+      *p++ = '0' + minutes % 10;
+      if (seconds) {
+	*p++ = '0' + seconds / 10;
+	*p++ = '0' + seconds % 10;
+      }
+    }
+    *p = '\0';
+    return buf;
+  }
+}
+
+static size_t
+doabbr(char *abbr, struct zone const *zp, char const *letters,
+       zic_t stdoff, bool doquotes)
 {
 	register char *	cp;
 	register char *	slashp;
-	register int	len;
+	register size_t	len;
+	char const *format = zp->z_format;
 
 	slashp = strchr(format, '/');
 	if (slashp == NULL) {
-		if (letters == NULL)
-			(void) strcpy(abbr, format);
-		else	(void) sprintf(abbr, format, letters);
-	} else if (isdst) {
-		(void) strcpy(abbr, slashp + 1);
+	  char letterbuf[PERCENT_Z_LEN_BOUND + 1];
+	  if (zp->z_format_specifier == 'z')
+	    letters = abbroffset(letterbuf, zp->z_gmtoff + stdoff);
+	  else if (!letters)
+	    letters = "%s";
+	  sprintf(abbr, format, letters);
+	} else if (stdoff != 0) {
+		strcpy(abbr, slashp + 1);
 	} else {
-		if (slashp > format)
-			(void) strncpy(abbr, format, slashp - format);
+		memcpy(abbr, format, slashp - format);
 		abbr[slashp - format] = '\0';
 	}
-	if (!doquotes)
-		return;
-	for (cp = abbr; *cp != '\0'; ++cp)
-		if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
-			strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
-				break;
 	len = strlen(abbr);
+	if (!doquotes)
+		return len;
+	for (cp = abbr; is_alpha(*cp); cp++)
+		continue;
 	if (len > 0 && *cp == '\0')
-		return;
+		return len;
 	abbr[len + 2] = '\0';
 	abbr[len + 1] = '>';
-	for ( ; len > 0; --len)
-		abbr[len] = abbr[len - 1];
+	memmove(abbr + 1, abbr, len);
 	abbr[0] = '<';
+	return len + 2;
 }
 
 static void
@@ -1847,11 +1987,12 @@ stringoffset(char *result, zic_t offset)
 	register int	hours;
 	register int	minutes;
 	register int	seconds;
+	bool negative = offset < 0;
+	int len = negative;
 
-	result[0] = '\0';
-	if (offset < 0) {
-		(void) strcpy(result, "-");
+	if (negative) {
 		offset = -offset;
+		result[0] = '-';
 	}
 	seconds = offset % SECSPERMIN;
 	offset /= SECSPERMIN;
@@ -1860,15 +2001,15 @@ stringoffset(char *result, zic_t offset)
 	hours = offset;
 	if (hours >= HOURSPERDAY * DAYSPERWEEK) {
 		result[0] = '\0';
-		return -1;
+		return 0;
 	}
-	(void) sprintf(end(result), "%d", hours);
+	len += sprintf(result + len, "%d", hours);
 	if (minutes != 0 || seconds != 0) {
-		(void) sprintf(end(result), ":%02d", minutes);
+		len += sprintf(result + len, ":%02d", minutes);
 		if (seconds != 0)
-			(void) sprintf(end(result), ":%02d", seconds);
+			len += sprintf(result + len, ":%02d", seconds);
 	}
-	return 0;
+	return len;
 }
 
 static int
@@ -1878,7 +2019,6 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
 	register zic_t	tod = rp->r_tod;
 	register int	compat = 0;
 
-	result = end(result);
 	if (rp->r_dycode == DC_DOM) {
 		register int	month, total;
 
@@ -1889,9 +2029,9 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
 			total += len_months[0][month];
 		/* 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);
+		  result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
 		else
-		  (void) sprintf(result, "J%d", total + rp->r_dayofmonth);
+		  result += sprintf(result, "J%d", total + rp->r_dayofmonth);
 	} else {
 		register int	week;
 		register int	wday = rp->r_wday;
@@ -1918,16 +2058,16 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
 		} else	return -1;	/* "cannot happen" */
 		if (wday < 0)
 			wday += DAYSPERWEEK;
-		(void) sprintf(result, "M%d.%d.%d",
-			rp->r_month + 1, week, wday);
+		result += sprintf(result, "M%d.%d.%d",
+				  rp->r_month + 1, week, wday);
 	}
 	if (rp->r_todisgmt)
 		tod += gmtoff;
 	if (rp->r_todisstd && rp->r_stdoff == 0)
 		tod += dstoff;
 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
-		(void) strcat(result, "/");
-		if (stringoffset(end(result), tod) != 0)
+		*result++ = '/';
+		if (! stringoffset(result, tod))
 			return -1;
 		if (tod < 0) {
 			if (compat < 2013)
@@ -1967,6 +2107,8 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
 	register const char *		abbrvar;
 	register int			compat = 0;
 	register int			c;
+	size_t				len;
+	int				offsetlen;
 	struct rule			stdr, dstr;
 
 	result[0] = '\0';
@@ -2016,14 +2158,14 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
 			dstr.r_dycode = DC_DOM;
 			dstr.r_dayofmonth = 1;
 			dstr.r_tod = 0;
-			dstr.r_todisstd = dstr.r_todisgmt = FALSE;
+			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_todisstd = stdr.r_todisgmt = false;
 			stdr.r_stdoff = 0;
 			stdr.r_abbrvar
 			  = (stdabbrrp ? stdabbrrp->r_abbrvar : "");
@@ -2034,30 +2176,36 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
 	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
 		return -1;
 	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
-	doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
-	if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
+	len = doabbr(result, zp, abbrvar, 0, true);
+	offsetlen = stringoffset(result + len, -zp->z_gmtoff);
+	if (! offsetlen) {
 		result[0] = '\0';
 		return -1;
 	}
+	len += offsetlen;
 	if (dstrp == NULL)
 		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 -1;
-		}
-	(void) strcat(result, ",");
-	c = stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
+	len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true);
+	if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) {
+	  offsetlen = stringoffset(result + len,
+				   -(zp->z_gmtoff + dstrp->r_stdoff));
+	  if (! offsetlen) {
+	    result[0] = '\0';
+	    return -1;
+	  }
+	  len += offsetlen;
+	}
+	result[len++] = ',';
+	c = stringrule(result + len, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
 	if (c < 0) {
 		result[0] = '\0';
 		return -1;
 	}
 	if (compat < c)
 		compat = c;
-	(void) strcat(result, ",");
-	c = stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff);
+	len += strlen(result + len);
+	result[len++] = ',';
+	c = stringrule(result + len, stdrp, dstrp->r_stdoff, zp->z_gmtoff);
 	if (c < 0) {
 		result[0] = '\0';
 		return -1;
@@ -2068,28 +2216,28 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
 }
 
 static void
-outzone(const struct zone * const zpfirst, const int zonecount)
+outzone(const struct zone *zpfirst, int zonecount)
 {
 	register const struct zone *	zp;
 	register struct rule *		rp;
 	register int			i, j;
-	register int			usestart, useuntil;
+	register bool			usestart, useuntil;
 	register zic_t			starttime, untiltime;
 	register zic_t			gmtoff;
 	register zic_t			stdoff;
 	register zic_t			year;
 	register zic_t			startoff;
-	register int			startttisstd;
-	register int			startttisgmt;
+	register bool			startttisstd;
+	register bool			startttisgmt;
 	register int			type;
 	register char *			startbuf;
 	register char *			ab;
 	register char *			envvar;
 	register int			max_abbr_len;
 	register int			max_envvar_len;
-	register int			prodstic; /* all rules are min to max */
+	register bool			prodstic; /* all rules are min to max */
 	register int			compat;
-	register int			do_extend;
+	register bool			do_extend;
 	register char			version;
 
 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
@@ -2110,8 +2258,8 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 	** Thanks to Earl Chew
 	** for noting the need to unconditionally initialize startttisstd.
 	*/
-	startttisstd = FALSE;
-	startttisgmt = FALSE;
+	startttisstd = false;
+	startttisgmt = false;
 	min_year = max_year = EPOCH_YEAR;
 	if (leapseen) {
 		updateminmax(leapminyear);
@@ -2128,7 +2276,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 			if (rp->r_hiwasnum)
 				updateminmax(rp->r_hiyear);
 			if (rp->r_lowasnum || rp->r_hiwasnum)
-				prodstic = FALSE;
+				prodstic = false;
 		}
 	}
 	/*
@@ -2208,14 +2356,13 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 		startoff = zp->z_gmtoff;
 		if (zp->z_nrules == 0) {
 			stdoff = zp->z_stdoff;
-			doabbr(startbuf, zp->z_format,
-			       NULL, stdoff != 0, FALSE);
+			doabbr(startbuf, zp, NULL, stdoff, false);
 			type = addtype(oadd(zp->z_gmtoff, stdoff),
 				startbuf, stdoff != 0, startttisstd,
 				startttisgmt);
 			if (usestart) {
 				addtt(starttime, type);
-				usestart = FALSE;
+				usestart = false;
 			} else	addtt(big_bang_time, type);
 		} else for (year = min_year; year <= max_year; ++year) {
 			if (useuntil && year > zp->z_untilrule.r_hiyear)
@@ -2276,42 +2423,51 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 					if (k < 0 || jtime < ktime) {
 						k = j;
 						ktime = jtime;
+					} else if (jtime == ktime) {
+					  char const *dup_rules_msg =
+					    _("two rules for same instant");
+					  eats(zp->z_filename, zp->z_linenum,
+					       rp->r_filename, rp->r_linenum);
+					  warning("%s", dup_rules_msg);
+					  rp = &zp->z_rules[k];
+					  eats(zp->z_filename, zp->z_linenum,
+					       rp->r_filename, rp->r_linenum);
+					  error("%s", dup_rules_msg);
 					}
 				}
 				if (k < 0)
 					break;	/* go on to next year */
 				rp = &zp->z_rules[k];
-				rp->r_todo = FALSE;
+				rp->r_todo = false;
 				if (useuntil && ktime >= untiltime)
 					break;
 				stdoff = rp->r_stdoff;
 				if (usestart && ktime == starttime)
-					usestart = FALSE;
+					usestart = false;
 				if (usestart) {
 					if (ktime < starttime) {
 						startoff = oadd(zp->z_gmtoff,
 							stdoff);
-						doabbr(startbuf, zp->z_format,
+						doabbr(startbuf, zp,
 							rp->r_abbrvar,
-							rp->r_stdoff != 0,
-							FALSE);
+							rp->r_stdoff,
+							false);
 						continue;
 					}
 					if (*startbuf == '\0' &&
 						startoff == oadd(zp->z_gmtoff,
 						stdoff)) {
 							doabbr(startbuf,
-								zp->z_format,
+								zp,
 								rp->r_abbrvar,
-								rp->r_stdoff !=
-								0,
-								FALSE);
+								rp->r_stdoff,
+								false);
 					}
 				}
 				eats(zp->z_filename, zp->z_linenum,
 					rp->r_filename, rp->r_linenum);
-				doabbr(ab, zp->z_format, rp->r_abbrvar,
-					rp->r_stdoff != 0, FALSE);
+				doabbr(ab, zp, rp->r_abbrvar,
+				       rp->r_stdoff, false);
 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
 				type = addtype(offset, ab, rp->r_stdoff != 0,
 					rp->r_todisstd, rp->r_todisgmt);
@@ -2323,7 +2479,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 				zp->z_format != NULL &&
 				strchr(zp->z_format, '%') == NULL &&
 				strchr(zp->z_format, '/') == NULL)
-					(void) strcpy(startbuf, zp->z_format);
+					strcpy(startbuf, zp->z_format);
 			eat(zp->z_filename, zp->z_linenum);
 			if (*startbuf == '\0')
 error(_("can't determine time zone abbreviation to use just after until time"));
@@ -2369,7 +2525,7 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 		if (lastat->at < rpytime(&xr, max_year - 1)) {
 			/*
 			** Create new type code for the redundant entry,
-			** to prevent it being optimised away.
+			** to prevent it being optimized away.
 			*/
 			if (typecnt >= TZ_MAX_TYPES) {
 				error(_("too many local time types"));
@@ -2391,7 +2547,7 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 }
 
 static void
-addtt(const zic_t starttime, int type)
+addtt(zic_t starttime, int type)
 {
 	if (starttime <= big_bang_time ||
 		(timecnt == 1 && attypes[0].at < big_bang_time)) {
@@ -2400,7 +2556,7 @@ addtt(const zic_t starttime, int type)
 		ttisstds[0] = ttisstds[type];
 		ttisgmts[0] = ttisgmts[type];
 		if (abbrinds[type] != 0)
-			(void) strcpy(chars, &chars[abbrinds[type]]);
+			strcpy(chars, &chars[abbrinds[type]]);
 		abbrinds[0] = 0;
 		charcnt = strlen(chars) + 1;
 		typecnt = 1;
@@ -2414,23 +2570,10 @@ addtt(const zic_t starttime, int type)
 }
 
 static int
-addtype(const zic_t gmtoff, const char *const abbr, const int isdst,
-	const int ttisstd, const int ttisgmt)
+addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt)
 {
 	register int	i, j;
 
-	if (isdst != TRUE && isdst != FALSE) {
-		error(_("internal error - addtype called with bad isdst"));
-		exit(EXIT_FAILURE);
-	}
-	if (ttisstd != TRUE && ttisstd != FALSE) {
-		error(_("internal error - addtype called with bad ttisstd"));
-		exit(EXIT_FAILURE);
-	}
-	if (ttisgmt != TRUE && ttisgmt != FALSE) {
-		error(_("internal error - addtype called with bad ttisgmt"));
-		exit(EXIT_FAILURE);
-	}
 	/*
 	** See if there's already an entry for this zone type.
 	** If so, just return its index.
@@ -2470,7 +2613,7 @@ addtype(const zic_t gmtoff, const char *const abbr, const int isdst,
 }
 
 static void
-leapadd(const zic_t t, const int positive, const int rolling, int count)
+leapadd(zic_t t, bool positive, int rolling, int count)
 {
 	register int	i, j;
 
@@ -2514,64 +2657,106 @@ adjleap(void)
 	}
 }
 
-static int
-yearistype(const int year, const char *const type)
+static bool
+yearistype(int year, const char *type)
 {
 	static char *	buf;
 	int		result;
 
 	if (type == NULL || *type == '\0')
-		return TRUE;
+		return true;
 	buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type));
-	(void) sprintf(buf, "%s %d %s", yitcommand, year, type);
+	sprintf(buf, "%s %d %s", yitcommand, year, type);
 	result = system(buf);
 	if (WIFEXITED(result)) switch (WEXITSTATUS(result)) {
 		case 0:
-			return TRUE;
+			return true;
 		case 1:
-			return FALSE;
+			return false;
 	}
 	error(_("Wild result from command execution"));
-	(void) fprintf(stderr, _("%s: command was '%s', result was %d\n"),
+	fprintf(stderr, _("%s: command was '%s', result was %d\n"),
 		progname, buf, result);
 	for ( ; ; )
 		exit(EXIT_FAILURE);
 }
 
-static int
-lowerit(int a)
+/* Is A a space character in the C locale?  */
+static bool
+is_space(char a)
+{
+	switch (a) {
+	  default:
+		return false;
+	  case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
+		return true;
+	}
+}
+
+/* Is A an alphabetic character in the C locale?  */
+static bool
+is_alpha(char a)
 {
-	a = (unsigned char) a;
-	return (isascii(a) && isupper(a)) ? tolower(a) : a;
+	switch (a) {
+	  default:
+		return false;
+	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
+	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+	  case 'v': case 'w': case 'x': case 'y': case 'z':
+		return true;
+	}
+}
+
+/* If A is an uppercase character in the C locale, return its lowercase
+   counterpart.  Otherwise, return A.  */
+static char
+lowerit(char a)
+{
+	switch (a) {
+	  default: return a;
+	  case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
+	  case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
+	  case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
+	  case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
+	  case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
+	  case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
+	  case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
+	  case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
+	  case 'Y': return 'y'; case 'Z': return 'z';
+	}
 }
 
 /* case-insensitive equality */
-static ATTRIBUTE_PURE int
+static ATTRIBUTE_PURE bool
 ciequal(register const char *ap, register const char *bp)
 {
 	while (lowerit(*ap) == lowerit(*bp++))
 		if (*ap++ == '\0')
-			return TRUE;
-	return FALSE;
+			return true;
+	return false;
 }
 
-static ATTRIBUTE_PURE int
+static ATTRIBUTE_PURE bool
 itsabbr(register const char *abbr, register const char *word)
 {
 	if (lowerit(*abbr) != lowerit(*word))
-		return FALSE;
+		return false;
 	++word;
 	while (*++abbr != '\0')
 		do {
 			if (*word == '\0')
-				return FALSE;
+				return false;
 		} while (lowerit(*word++) != lowerit(*abbr));
-	return TRUE;
+	return true;
 }
 
 static ATTRIBUTE_PURE const struct lookup *
-byword(register const char *const word,
-       register const struct lookup *const table)
+byword(const char *word, const struct lookup *table)
 {
 	register const struct lookup *	foundlp;
 	register const struct lookup *	lp;
@@ -2609,8 +2794,7 @@ getfields(register char *cp)
 	array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
 	nsubs = 0;
 	for ( ; ; ) {
-		while (isascii((unsigned char) *cp) &&
-			isspace((unsigned char) *cp))
+		while (is_space(*cp))
 				++cp;
 		if (*cp == '\0' || *cp == '#')
 			break;
@@ -2627,9 +2811,8 @@ getfields(register char *cp)
 						));
 					exit(1);
 				}
-		} while (*cp != '\0' && *cp != '#' &&
-			(!isascii(*cp) || !isspace((unsigned char) *cp)));
-		if (isascii(*cp) && isspace((unsigned char) *cp))
+		} while (*cp && *cp != '#' && !is_space(*cp));
+		if (is_space(*cp))
 			++cp;
 		*dp = '\0';
 	}
@@ -2637,37 +2820,47 @@ getfields(register char *cp)
 	return array;
 }
 
+static _Noreturn void
+time_overflow(void)
+{
+  error(_("time overflow"));
+  exit(EXIT_FAILURE);
+}
+
 static ATTRIBUTE_PURE zic_t
-oadd(const zic_t t1, const zic_t t2)
+oadd(zic_t t1, zic_t t2)
 {
-	if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) {
-		error(_("time overflow"));
-		exit(EXIT_FAILURE);
-	}
+	if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2)
+	  time_overflow();
 	return t1 + t2;
 }
 
 static ATTRIBUTE_PURE zic_t
-tadd(const zic_t t1, const zic_t t2)
+tadd(zic_t t1, zic_t t2)
 {
-	if (t1 == max_time && t2 > 0)
-		return max_time;
-	if (t1 == min_time && t2 < 0)
-		return min_time;
-	if (t1 < 0 ? t2 < min_time - t1 : max_time - t1 < t2) {
-		error(_("time overflow"));
-		exit(EXIT_FAILURE);
-	}
-	return t1 + t2;
+  if (t1 < 0) {
+    if (t2 < min_time - t1) {
+      if (t1 != min_time)
+	time_overflow();
+      return min_time;
+    }
+  } else {
+    if (max_time - t1 < t2) {
+      if (t1 != max_time)
+	time_overflow();
+      return max_time;
+    }
+  }
+  return t1 + t2;
 }
 
 /*
-** Given a rule, and a year, compute the date - in seconds since January 1,
-** 1970, 00:00 LOCAL time - in that year that the rule refers to.
+** Given a rule, and a year, compute the date (in seconds since January 1,
+** 1970, 00:00 LOCAL time) in that year that the rule refers to.
 */
 
 static zic_t
-rpytime(register const struct rule *const rp, register const zic_t wantedy)
+rpytime(const struct rule *rp, zic_t wantedy)
 {
 	register int	m, i;
 	register zic_t	dayoff;			/* with a nod to Margaret O. */
@@ -2735,7 +2928,7 @@ rpytime(register const struct rule *const rp, register const zic_t wantedy)
 			}
 		if (i < 0 || i >= len_months[isleap(y)][m]) {
 			if (noise)
-				warning(_("rule goes past start/end of month--\
+				warning(_("rule goes past start/end of month; \
 will not work with pre-2004 versions of zic"));
 		}
 	}
@@ -2748,7 +2941,7 @@ will not work with pre-2004 versions of zic"));
 }
 
 static void
-newabbr(const char *const string)
+newabbr(const char *string)
 {
 	register int	i;
 
@@ -2756,29 +2949,15 @@ newabbr(const char *const string)
 		register const char *	cp;
 		const char *		mp;
 
-		/*
-		** Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics
-		** optionally followed by a + or - and a number from 1 to 14.
-		*/
 		cp = string;
 		mp = NULL;
-		while (isascii((unsigned char) *cp) &&
-			isalpha((unsigned char) *cp))
+		while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
+		       || *cp == '-' || *cp == '+')
 				++cp;
-		if (cp - string == 0)
-mp = _("time zone abbreviation lacks alphabetic at start");
 		if (noise && cp - string < 3)
-mp = _("time zone abbreviation has fewer than 3 alphabetics");
+		  mp = _("time zone abbreviation has fewer than 3 characters");
 		if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
-mp = _("time zone abbreviation has too many alphabetics");
-		if (mp == NULL && (*cp == '+' || *cp == '-')) {
-			++cp;
-			if (isascii((unsigned char) *cp) &&
-				isdigit((unsigned char) *cp))
-					if (*cp++ == '1' &&
-						*cp >= '0' && *cp <= '4')
-							++cp;
-		}
+		  mp = _("time zone abbreviation has too many characters");
 		if (*cp != '\0')
 mp = _("time zone abbreviation differs from POSIX standard");
 		if (mp != NULL)
@@ -2789,18 +2968,18 @@ mp = _("time zone abbreviation differs from POSIX standard");
 		error(_("too many, or too long, time zone abbreviations"));
 		exit(EXIT_FAILURE);
 	}
-	(void) strcpy(&chars[charcnt], string);
+	strcpy(&chars[charcnt], string);
 	charcnt += i;
 }
 
-static int
+static bool
 mkdirs(char *argname)
 {
 	register char *	name;
 	register char *	cp;
 
 	if (argname == NULL || *argname == '\0')
-		return 0;
+		return true;
 	cp = name = ecpyalloc(argname);
 	while ((cp = strchr(cp + 1, '/')) != 0) {
 		*cp = '\0';
@@ -2808,37 +2987,29 @@ mkdirs(char *argname)
 		/*
 		** DOS drive specifier?
 		*/
-		if (isalpha((unsigned char) name[0]) &&
-			name[1] == ':' && name[2] == '\0') {
+		if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0') {
 				*cp = '/';
 				continue;
 		}
 #endif
-		if (!itsdir(name)) {
-			/*
-			** It doesn't seem to exist, so we try to create it.
-			** Creation may fail because of the directory being
-			** created by some other multiprocessor, so we get
-			** to do extra checking.
-			*/
-			if (mkdir(name, MKDIR_UMASK) != 0) {
-				const char *e = strerror(errno);
-
-				if (errno != EEXIST || !itsdir(name)) {
-					(void) fprintf(stderr,
-_("%s: Can't create directory %s: %s\n"),
-						progname, name, e);
-					free(name);
-					return -1;
-				}
+		/*
+		** Try to create it.  It's OK if creation fails because
+		** the directory already exists, perhaps because some
+		** other process just created it.
+		*/
+		if (mkdir(name, MKDIR_UMASK) != 0) {
+			int err = errno;
+			if (itsdir(name) <= 0) {
+				char const *e = strerror(err);
+				warning(_("%s: Can't create directory"
+					  " %s: %s"),
+					progname, name, e);
+				free(name);
+				return false;
 			}
 		}
 		*cp = '/';
 	}
 	free(name);
-	return 0;
+	return true;
 }
-
-/*
-** UNIX was a registered trademark of The Open Group in 2003.
-*/

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog             |   20 +
 timezone/Makefile     |   13 +-
 timezone/README       |    2 +-
 timezone/ialloc.c     |   32 --
 timezone/private.h    |  366 ++++++++++++-----
 timezone/scheck.c     |   64 ---
 timezone/tzfile.h     |   10 +-
 timezone/tzselect.ksh |  138 +++++--
 timezone/zdump.c      |  688 ++++++++++++++++++++++----------
 timezone/zic.c        | 1061 ++++++++++++++++++++++++++++---------------------
 10 files changed, 1476 insertions(+), 918 deletions(-)
 delete mode 100644 timezone/ialloc.c
 delete mode 100644 timezone/scheck.c


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]