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.
This commit is contained in:
Paul Eggert 2016-01-07 11:45:07 +00:00 committed by Joseph Myers
parent 45c4f3665a
commit 670a687dea
10 changed files with 1478 additions and 920 deletions

View File

@ -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> 2016-01-07 Khem Raj <raj.khem@gmail.com>
* argp/argp-fmtstream.c (__argp_fmtstream_free): Use fwrite_unlocked * argp/argp-fmtstream.c (__argp_fmtstream_free): Use fwrite_unlocked

View File

@ -22,8 +22,6 @@ subdir := timezone
include ../Makeconfig include ../Makeconfig
extra-objs := scheck.o ialloc.o
others := zdump zic others := zdump zic
tests := test-tz tst-timezone tst-tzset tests := test-tz tst-timezone tst-tzset
@ -49,8 +47,6 @@ endif
include ../Rules include ../Rules
$(objpfx)zic: $(objpfx)scheck.o $(objpfx)ialloc.o
$(objpfx)zic.o $(objpfx)zdump.o: $(objpfx)version.h $(objpfx)zic.o $(objpfx)zdump.o: $(objpfx)version.h
$(objpfx)version.h: $(common-objpfx)config.make $(objpfx)version.h: $(common-objpfx)config.make
@ -61,15 +57,14 @@ $(objpfx)version.h: $(common-objpfx)config.make
tz-cflags = -DTZDIR='"$(zonedir)"' \ tz-cflags = -DTZDIR='"$(zonedir)"' \
-DTZDEFAULT='"$(localtime-file)"' \ -DTZDEFAULT='"$(localtime-file)"' \
-DTZDEFRULES='"$(posixrules-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 # The -Wno-unused-variable flag is used to prevent GCC 6
# from warning about time_t_min and time_t_max which are # from warning about time_t_min and time_t_max which are
# defined in private.h but not used. # defined in private.h but not used.
CFLAGS-zdump.c = -fwrapv -DNOID $(tz-cflags) -DHAVE_GETTEXT CFLAGS-zdump.c = $(tz-cflags)
CFLAGS-zic.c = -DNOID $(tz-cflags) -DHAVE_GETTEXT -Wno-unused-variable CFLAGS-zic.c = $(tz-cflags) -Wno-unused-variable
CFLAGS-ialloc.c = -DNOID -DHAVE_GETTEXT -Wno-unused-variable
CFLAGS-scheck.c = -DNOID -DHAVE_GETTEXT -Wno-unused-variable
# We have to make sure the data for testing the tz functions is available. # 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 # Don't add leapseconds here since test-tz made checks that work only without

View File

@ -1,5 +1,5 @@
The files The files
zic.c zdump.c ialloc.c scheck.c tzfile.h zic.c zdump.c tzfile.h
private.h tzselect.ksh checktab.awk private.h tzselect.ksh checktab.awk
come from the tzcode package by Arthur David Olson et.al. come from the tzcode package by Arthur David Olson et.al.

View File

@ -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);
}

View File

@ -19,13 +19,9 @@
/* /*
** Defaults for preprocessor symbols. ** 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 #ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0 #define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */ #endif /* !defined HAVE_GETTEXT */
@ -38,9 +34,9 @@
#define HAVE_LINK 1 #define HAVE_LINK 1
#endif /* !defined HAVE_LINK */ #endif /* !defined HAVE_LINK */
#ifndef HAVE_SETTIMEOFDAY #ifndef HAVE_STRDUP
#define HAVE_SETTIMEOFDAY 3 #define HAVE_STRDUP 1
#endif /* !defined HAVE_SETTIMEOFDAY */ #endif
#ifndef HAVE_SYMLINK #ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1 #define HAVE_SYMLINK 1
@ -59,30 +55,61 @@
#endif /* !defined HAVE_UNISTD_H */ #endif /* !defined HAVE_UNISTD_H */
#ifndef HAVE_UTMPX_H #ifndef HAVE_UTMPX_H
#define HAVE_UTMPX_H 0 #define HAVE_UTMPX_H 1
#endif /* !defined HAVE_UTMPX_H */ #endif /* !defined HAVE_UTMPX_H */
#ifndef LOCALE_HOME #ifndef NETBSD_INSPIRED
#define LOCALE_HOME "/usr/lib/locale" # define NETBSD_INSPIRED 1
#endif /* !defined LOCALE_HOME */ #endif
#if HAVE_INCOMPATIBLE_CTIME_R #if HAVE_INCOMPATIBLE_CTIME_R
#define asctime_r _incompatible_asctime_r #define asctime_r _incompatible_asctime_r
#define ctime_r _incompatible_ctime_r #define ctime_r _incompatible_ctime_r
#endif /* HAVE_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 ** 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 "sys/types.h" /* for time_t */
#include "stdio.h" #include "stdio.h"
#include "errno.h"
#include "string.h" #include "string.h"
#include "limits.h" /* for CHAR_BIT et al. */ #include "limits.h" /* for CHAR_BIT et al. */
#include "time.h"
#include "stdlib.h" #include "stdlib.h"
#include "errno.h"
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
#ifndef EOVERFLOW
# define EOVERFLOW EINVAL
#endif
#if HAVE_GETTEXT #if HAVE_GETTEXT
#include "libintl.h" #include "libintl.h"
#endif /* HAVE_GETTEXT */ #endif /* HAVE_GETTEXT */
@ -102,6 +129,14 @@
#include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */ #include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */
#endif /* HAVE_UNISTD_H */ #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 #ifndef F_OK
#define F_OK 0 #define F_OK 0
#endif /* !defined F_OK */ #endif /* !defined F_OK */
@ -136,65 +171,98 @@
# include <inttypes.h> # include <inttypes.h>
#endif #endif
#ifndef INT_FAST64_MAX
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
#if defined LLONG_MAX || defined __LONG_LONG_MAX__ #ifdef __LONG_LONG_MAX__
typedef long long int_fast64_t; # 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 # ifdef LLONG_MAX
typedef long long int_fast64_t;
# define INT_FAST64_MIN LLONG_MIN # define INT_FAST64_MIN LLONG_MIN
# define INT_FAST64_MAX LLONG_MAX # define INT_FAST64_MAX LLONG_MAX
# else # else
# define INT_FAST64_MIN __LONG_LONG_MIN__ # if LONG_MAX >> 31 < 0xffffffff
# define INT_FAST64_MAX __LONG_LONG_MAX__
# endif
# define SCNdFAST64 "lld"
#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#if (LONG_MAX >> 31) < 0xffffffff
Please use a compiler that supports a 64-bit integer type (or wider); Please use a compiler that supports a 64-bit integer type (or wider);
you may need to compile with "-DHAVE_STDINT_H". you may need to compile with "-DHAVE_STDINT_H".
#endif /* (LONG_MAX >> 31) < 0xffffffff */ # endif
typedef long int_fast64_t; typedef long int_fast64_t;
# define INT_FAST64_MIN LONG_MIN # define INT_FAST64_MIN LONG_MIN
# define INT_FAST64_MAX LONG_MAX # define INT_FAST64_MAX LONG_MAX
# define SCNdFAST64 "ld" # endif
#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ #endif
#endif /* !defined INT_FAST64_MAX */
#ifndef SCNdFAST64
# if INT_FAST64_MAX == LLONG_MAX
# define SCNdFAST64 "lld"
# else
# define SCNdFAST64 "ld"
# endif
#endif
#ifndef INT_FAST32_MAX #ifndef INT_FAST32_MAX
# if INT_MAX >> 31 == 0 # if INT_MAX >> 31 == 0
typedef long int_fast32_t; typedef long int_fast32_t;
# define INT_FAST32_MAX LONG_MAX
# define INT_FAST32_MIN LONG_MIN
# else # else
typedef int int_fast32_t; typedef int int_fast32_t;
# define INT_FAST32_MAX INT_MAX
# define INT_FAST32_MIN INT_MIN
# endif # endif
#endif #endif
#ifndef INTMAX_MAX #ifndef INTMAX_MAX
# if defined LLONG_MAX || defined __LONG_LONG_MAX__ # ifdef LLONG_MAX
typedef long long intmax_t; typedef long long intmax_t;
# define strtoimax strtoll # define strtoimax strtoll
# define PRIdMAX "lld" # define INTMAX_MAX LLONG_MAX
# ifdef LLONG_MAX # define INTMAX_MIN LLONG_MIN
# define INTMAX_MAX LLONG_MAX
# define INTMAX_MIN LLONG_MIN
# else
# define INTMAX_MAX __LONG_LONG_MAX__
# define INTMAX_MIN __LONG_LONG_MIN__
# endif
# else # else
typedef long intmax_t; typedef long intmax_t;
# define strtoimax strtol # define strtoimax strtol
# define PRIdMAX "ld"
# define INTMAX_MAX LONG_MAX # define INTMAX_MAX LONG_MAX
# define INTMAX_MIN LONG_MIN # define INTMAX_MIN LONG_MIN
# endif # endif
#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 #ifndef UINTMAX_MAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ # if defined ULLONG_MAX || defined __LONG_LONG_MAX__
typedef unsigned long long uintmax_t; typedef unsigned long long uintmax_t;
# define PRIuMAX "llu"
# else # else
typedef unsigned long uintmax_t; typedef unsigned long uintmax_t;
# endif
#endif
#ifndef PRIuMAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
# define PRIuMAX "llu"
# else
# define PRIuMAX "lu" # define PRIuMAX "lu"
# endif # endif
#endif #endif
@ -236,16 +304,6 @@ typedef unsigned long uintmax_t;
** Workarounds for compilers/systems. ** Workarounds for compilers/systems.
*/ */
/*
** 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 ** 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. ** time_t type equivalent to T rather than the system-supplied time_t.
@ -254,7 +312,11 @@ extern char * asctime_r(struct tm const *, char *);
** typical platforms. ** typical platforms.
*/ */
#ifdef time_tz #ifdef time_tz
# ifdef LOCALTIME_IMPLEMENTATION
static time_t sys_time(time_t *x) { return time(x); } static time_t sys_time(time_t *x) { return time(x); }
# endif
typedef time_tz tz_time_t;
# undef ctime # undef ctime
# define ctime tz_ctime # define ctime tz_ctime
@ -270,14 +332,40 @@ static time_t sys_time(time_t *x) { return time(x); }
# define localtime tz_localtime # define localtime tz_localtime
# undef localtime_r # undef localtime_r
# define localtime_r tz_localtime_r # define localtime_r tz_localtime_r
# undef localtime_rz
# define localtime_rz tz_localtime_rz
# undef mktime # undef mktime
# define mktime tz_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 # undef time
# define time tz_time # define time tz_time
# undef time2posix
# define time2posix tz_time2posix
# undef time2posix_z
# define time2posix_z tz_time2posix_z
# undef time_t # undef time_t
# define time_t tz_time_t # define time_t tz_time_t
# undef timegm
typedef time_tz time_t; # 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(time_t const *);
char *ctime_r(time_t const *, char *); 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(time_t const *);
struct tm *localtime_r(time_t const *restrict, struct tm *restrict); struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
time_t mktime(struct tm *); time_t mktime(struct tm *);
time_t time(time_t *);
static time_t void tzset(void);
time(time_t *p)
{
time_t r = sys_time(0);
if (p)
*p = r;
return r;
}
#endif #endif
/* /*
** Private function declarations. ** 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.
*/ */
char * icatalloc(char * old, const char * new); #ifndef asctime_r
char * icpyalloc(const char * string); extern char * asctime_r(struct tm const *restrict, char *restrict);
const char * scheck(const char * string, const char * format); #endif
#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
/*
** The STD_INSPIRED functions are similar, but most also need
** declarations if time_tz is defined.
*/
#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
/*
** 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
/* /*
** Finally, some convenience items. ** Finally, some convenience items.
*/ */
#ifndef TRUE #if __STDC_VERSION__ < 199901
#define TRUE 1 # define true 1
#endif /* !defined TRUE */ # define false 0
# define bool int
#ifndef FALSE #else
#define FALSE 0 # include <stdbool.h>
#endif /* !defined FALSE */ #endif
#ifndef TYPE_BIT #ifndef TYPE_BIT
#define TYPE_BIT(type) (sizeof (type) * CHAR_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) #define TYPE_SIGNED(type) (((type) -1) < 0)
#endif /* !defined TYPE_SIGNED */ #endif /* !defined TYPE_SIGNED */
/* The minimum and maximum finite time values. */ #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
static time_t const time_t_min =
(TYPE_SIGNED(time_t) /* Max and min values of the integer type T, of which only the bottom
? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) B bits are used, and where the highest-order used bit is considered
: 0); to be a sign bit if T is signed. */
static time_t const time_t_max = #define MAXVAL(t, b) \
(TYPE_SIGNED(time_t) ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
: -1); #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 #ifndef INT_STRLEN_MAXIMUM
/* /*
@ -352,29 +520,19 @@ static time_t const time_t_max =
** INITIALIZE(x) ** INITIALIZE(x)
*/ */
#ifndef GNUC_or_lint
#ifdef lint #ifdef lint
#define GNUC_or_lint # define INITIALIZE(x) ((x) = 0)
#endif /* defined lint */ #else
#ifndef lint # define INITIALIZE(x)
#ifdef __GNUC__ #endif
#define GNUC_or_lint
#endif /* defined __GNUC__ */
#endif /* !defined lint */
#endif /* !defined GNUC_or_lint */
#ifndef INITIALIZE #ifndef UNINIT_TRAP
#ifdef GNUC_or_lint # define UNINIT_TRAP 0
#define INITIALIZE(x) ((x) = 0) #endif
#endif /* defined GNUC_or_lint */
#ifndef GNUC_or_lint
#define INITIALIZE(x)
#endif /* !defined GNUC_or_lint */
#endif /* !defined INITIALIZE */
/* /*
** For the benefit of GNU folk... ** 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. ** 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 /* !HAVE_GETTEXT */
#endif /* !defined _ */ #endif /* !defined _ */
#ifndef TZ_DOMAIN #if !defined TZ_DOMAIN && defined HAVE_GETTEXT
#define TZ_DOMAIN "tz" # define TZ_DOMAIN "tz"
#endif /* !defined TZ_DOMAIN */ #endif
#if HAVE_INCOMPATIBLE_CTIME_R #if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r #undef asctime_r
@ -417,8 +575,4 @@ char *ctime_r(time_t const *, char *);
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ #define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined SECSPERREPEAT_BITS */ #endif /* !defined SECSPERREPEAT_BITS */
/*
** UNIX was a registered trademark of The Open Group in 2003.
*/
#endif /* !defined PRIVATE_H */ #endif /* !defined PRIVATE_H */

View File

@ -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;
}

View File

@ -40,7 +40,7 @@
struct tzhead { struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */ char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ 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_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[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 */ char tzh_leapcnt[4]; /* coded number of leap seconds */
@ -62,13 +62,13 @@ struct tzhead {
** tzh_leapcnt repetitions of ** tzh_leapcnt repetitions of
** one (char [4]) coded leap second transition times ** one (char [4]) coded leap second transition times
** one (char [4]) total correction after above ** one (char [4]) total correction after above
** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition ** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
** time is standard time, if FALSE, ** time is standard time, if 0,
** transition time is wall clock time ** transition time is wall clock time
** if absent, transition times are ** if absent, transition times are
** assumed to be wall clock time ** assumed to be wall clock time
** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition ** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition
** time is UT, if FALSE, ** time is UT, if 0,
** transition time is local time ** transition time is local time
** if absent, transition times are ** if absent, transition times are
** assumed to be local time ** assumed to be local time

View File

@ -37,15 +37,22 @@ REPORT_BUGS_TO=tz@iana.org
: ${AWK=awk} : ${AWK=awk}
: ${TZDIR=`pwd`} : ${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. # Check for awk Posix compliance.
($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1 ($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
[ $? = 123 ] || { [ $? = 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 exit 1
} }
coord= coord=
location_limit=10 location_limit=10
zonetabtype=zone1970
usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT] usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
Select a time zone interactively. Select a time zone interactively.
@ -80,7 +87,7 @@ if
?*) : ;; ?*) : ;;
'') '')
# '; exit' should be redundant, but Dash doesn't properly fail without it. # '; 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 esac
then then
# Do this inside 'eval', as otherwise the shell might exit when parsing it # Do this inside 'eval', as otherwise the shell might exit when parsing it
@ -139,41 +146,58 @@ else
} }
fi fi
while getopts c:n:-: opt while getopts c:n:t:-: opt
do do
case $opt$OPTARG in case $opt$OPTARG in
c*) c*)
coord=$OPTARG ;; coord=$OPTARG ;;
n*) n*)
location_limit=$OPTARG ;; location_limit=$OPTARG ;;
t*) # Undocumented option, used for developer testing.
zonetabtype=$OPTARG ;;
-help) -help)
exec echo "$usage" ;; exec echo "$usage" ;;
-version) -version)
exec echo "tzselect $PKGVERSION$TZVERSION" ;; 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 esac
done done
shift `expr $OPTIND - 1` shift `expr $OPTIND - 1`
case $# in case $# in
0) ;; 0) ;;
*) echo >&2 "$0: $1: unknown argument"; exit 1 ;; *) say >&2 "$0: $1: unknown argument"; exit 1 ;;
esac esac
# Make sure the tables are readable. # Make sure the tables are readable.
TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab 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 for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
do do
<$f || { <"$f" || {
echo >&2 "$0: time zone files are not set up correctly" say >&2 "$0: time zone files are not set up correctly"
exit 1 exit 1
} }
done 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=' newline='
' '
IFS=$newline IFS=$newline
@ -189,7 +213,13 @@ output_distances='
country[$1] = $2 country[$1] = $2
country["US"] = "US" # Otherwise the strings get too long. 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]|$)/) { if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
degminsec = coord degminsec = coord
intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000) 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]|$)/) { } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
degmin = coord degmin = coord
intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100) intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
min = degmin - intdeg * 100 minute = degmin - intdeg * 100
deg = (intdeg * 60 + min) / 60 deg = (intdeg * 60 + minute) / 60
} else } else
deg = coord deg = coord
return deg * 0.017453292519943296 return deg * 0.017453292519943296
@ -217,14 +247,27 @@ output_distances='
# Great-circle distance between points with given latitude and longitude. # Great-circle distance between points with given latitude and longitude.
# Inputs and output are in radians. This uses the great-circle special # Inputs and output are in radians. This uses the great-circle special
# case of the Vicenty formula for distances on ellipsoids. # 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 dlong = long2 - long1
x = cos (lat2) * sin (dlong) x = cos(lat2) * sin(dlong)
y = cos (lat1) * sin (lat2) - sin (lat1) * cos (lat2) * cos (dlong) y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlong)
num = sqrt (x * x + y * y) num = sqrt(x * x + y * y)
denom = sin (lat1) * sin (lat2) + cos (lat1) * cos (lat2) * cos (dlong) denom = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(dlong)
return atan2(num, denom) 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 { BEGIN {
coord_lat = convert_latitude(coord) coord_lat = convert_latitude(coord)
coord_long = convert_longitude(coord) coord_long = convert_longitude(coord)
@ -232,7 +275,13 @@ output_distances='
/^[^#]/ { /^[^#]/ {
here_lat = convert_latitude($2) here_lat = convert_latitude($2)
here_long = convert_longitude($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) if (NF == 4)
line = line " - " $4 line = line " - " $4
printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
@ -269,7 +318,7 @@ while
entry = entry " Ocean" entry = entry " Ocean"
printf "'\''%s'\''\n", entry printf "'\''%s'\''\n", entry
} }
' $TZ_ZONE_TABLE | ' <"$TZ_ZONE_TABLE" |
sort -u | sort -u |
tr '\n' ' ' tr '\n' ' '
echo '' echo ''
@ -300,7 +349,7 @@ while
tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+" tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+"
time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?" time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?"
offset = "[-+]?" time 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 ")?" datetime = "," date "(/" time ")?"
tzpattern = "^(:.*|" tzname offset "(" tzname \ tzpattern = "^(:.*|" tzname offset "(" tzname \
"(" offset ")?(" datetime datetime ")?)?)$" "(" offset ")?(" datetime datetime ")?)?)$"
@ -308,8 +357,7 @@ while
exit 0 exit 0
}' }'
do do
echo >&2 "\`$TZ' is not a conforming" \ say >&2 "'$TZ' is not a conforming Posix time zone string."
'Posix time zone string.'
done done
TZ_for_date=$TZ;; TZ_for_date=$TZ;;
*) *)
@ -327,11 +375,11 @@ while
distance_table=`$AWK \ distance_table=`$AWK \
-v coord="$coord" \ -v coord="$coord" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
"$output_distances" <$TZ_ZONE_TABLE | "$output_distances" <"$TZ_ZONE_TABLE" |
sort -n | sort -n |
sed "${location_limit}q" sed "${location_limit}q"
` `
regions=`echo "$distance_table" | $AWK ' regions=`say "$distance_table" | $AWK '
BEGIN { FS = "\t" } BEGIN { FS = "\t" }
{ print $NF } { print $NF }
'` '`
@ -341,7 +389,7 @@ while
"of distance from $coord". "of distance from $coord".
doselect $regions doselect $regions
region=$select_result region=$select_result
TZ=`echo "$distance_table" | $AWK -v region="$region" ' TZ=`say "$distance_table" | $AWK -v region="$region" '
BEGIN { FS="\t" } BEGIN { FS="\t" }
$NF == region { print $4 } $NF == region { print $4 }
'` '`
@ -355,7 +403,9 @@ while
BEGIN { FS = "\t" } BEGIN { FS = "\t" }
/^#/ { next } /^#/ { next }
$3 ~ ("^" continent "/") { $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 { END {
while (getline <TZ_COUNTRY_TABLE) { while (getline <TZ_COUNTRY_TABLE) {
@ -369,7 +419,7 @@ while
print country print country
} }
} }
' <$TZ_ZONE_TABLE | sort -f` ' <"$TZ_ZONE_TABLE" | sort -f`
# If there's more than one country, ask the user which one. # If there's more than one country, ask the user which one.
@ -399,8 +449,9 @@ while
} }
} }
} }
$1 == cc { print $4 } /^#/ { next }
' <$TZ_ZONE_TABLE` $1 ~ cc { print $4 }
' <"$TZ_ZONE_TABLE"`
# If there's more than one region, ask the user which one. # If there's more than one region, ask the user which one.
@ -430,14 +481,15 @@ while
} }
} }
} }
$1 == cc && $4 == region { print $3 } /^#/ { next }
' <$TZ_ZONE_TABLE` $1 ~ cc && $4 == region { print $3 }
' <"$TZ_ZONE_TABLE"`
esac esac
# Make sure the corresponding zoneinfo file exists. # Make sure the corresponding zoneinfo file exists.
TZ_for_date=$TZDIR/$TZ TZ_for_date=$TZDIR/$TZ
<$TZ_for_date || { <"$TZ_for_date" || {
echo >&2 "$0: time zone files are not set up correctly" say >&2 "$0: time zone files are not set up correctly"
exit 1 exit 1
} }
esac esac
@ -470,15 +522,15 @@ Universal Time is now: $UTdate."
echo >&2 "The following information has been given:" echo >&2 "The following information has been given:"
echo >&2 "" echo >&2 ""
case $country%$region%$coord in case $country%$region%$coord in
?*%?*%) echo >&2 " $country$newline $region";; ?*%?*%) say >&2 " $country$newline $region";;
?*%%) echo >&2 " $country";; ?*%%) say >&2 " $country";;
%?*%?*) echo >&2 " coord $coord$newline $region";; %?*%?*) say >&2 " coord $coord$newline $region";;
%%?*) echo >&2 " coord $coord";; %%?*) say >&2 " coord $coord";;
+) echo >&2 " TZ='$TZ'" *) say >&2 " TZ='$TZ'"
esac esac
echo >&2 "" say >&2 ""
echo >&2 "Therefore TZ='$TZ' will be used.$extra_info" say >&2 "Therefore TZ='$TZ' will be used.$extra_info"
echo >&2 "Is the above information OK?" say >&2 "Is the above information OK?"
doselect Yes No doselect Yes No
ok=$select_result ok=$select_result
@ -493,7 +545,7 @@ case $SHELL in
*) file=.profile line="TZ='$TZ'; export TZ" *) file=.profile line="TZ='$TZ'; export TZ"
esac esac
echo >&2 " say >&2 "
You can make this change permanent for yourself by appending the line You can make this change permanent for yourself by appending the line
$line $line
to the file '$file' in your home directory; then log out and log in again. 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 Here is that TZ value again, this time on standard output so that you
can use the $0 command in shell scripts:" can use the $0 command in shell scripts:"
echo "$TZ" say "$TZ"

View File

@ -9,25 +9,32 @@
** This code has been made independent of the rest of the time ** This code has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides. ** conversion package to increase confidence in the verification it provides.
** You can use this code to help in verifying other implementations. ** You can use this code to help in verifying other implementations.
** ** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
** However, include private.h when debugging, so that it overrides
** time_t consistently with the rest of the package.
*/ */
#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" # include "private.h"
#endif #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 "stdio.h" /* for stdout, stderr, perror */
#include "string.h" /* for strcpy */ #include "string.h" /* for strcpy */
#include "sys/types.h" /* for time_t */ #include "sys/types.h" /* for time_t */
#include "time.h" /* for struct tm */ #include "time.h" /* for struct tm */
#include "stdlib.h" /* for exit, malloc, atoi */ #include "stdlib.h" /* for exit, malloc, atoi */
#include "limits.h" /* for CHAR_BIT, LLONG_MAX */ #include "limits.h" /* for CHAR_BIT, LLONG_MAX */
#include "ctype.h" /* for isalpha et al. */ #include <errno.h>
#ifndef isascii
#define isascii(x) 1
#endif /* !defined isascii */
/* /*
** Substitutes for pre-C99 compilers. ** Substitutes for pre-C99 compilers.
@ -58,24 +65,59 @@ typedef int int_fast32_t;
# endif # endif
#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 #ifndef INTMAX_MAX
# if defined LLONG_MAX || defined __LONG_LONG_MAX__ # ifdef LLONG_MAX
typedef long long intmax_t; typedef long long intmax_t;
# define strtoimax strtoll # define strtoimax strtoll
# define PRIdMAX "lld" # define INTMAX_MAX LLONG_MAX
# ifdef LLONG_MAX
# define INTMAX_MAX LLONG_MAX
# else
# define INTMAX_MAX __LONG_LONG_MAX__
# endif
# else # else
typedef long intmax_t; typedef long intmax_t;
# define strtoimax strtol # define strtoimax strtol
# define PRIdMAX "ld"
# define INTMAX_MAX LONG_MAX # define INTMAX_MAX LONG_MAX
# endif # endif
#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 #ifndef ZDUMP_LO_YEAR
#define ZDUMP_LO_YEAR (-500) #define ZDUMP_LO_YEAR (-500)
@ -89,13 +131,13 @@ typedef long intmax_t;
#define MAX_STRING_LENGTH 1024 #define MAX_STRING_LENGTH 1024
#endif /* !defined MAX_STRING_LENGTH */ #endif /* !defined MAX_STRING_LENGTH */
#ifndef TRUE #if __STDC_VERSION__ < 199901
#define TRUE 1 # define true 1
#endif /* !defined TRUE */ # define false 0
# define bool int
#ifndef FALSE #else
#define FALSE 0 # include <stdbool.h>
#endif /* !defined FALSE */ #endif
#ifndef EXIT_SUCCESS #ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0 #define EXIT_SUCCESS 0
@ -167,16 +209,6 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
#include "libintl.h" #include "libintl.h"
#endif /* HAVE_GETTEXT */ #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__) #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
# define ATTRIBUTE_PURE __attribute__ ((__pure__)) # define ATTRIBUTE_PURE __attribute__ ((__pure__))
#else #else
@ -185,7 +217,7 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
/* /*
** For the benefit of GNU folk... ** 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. ** 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 /* !HAVE_GETTEXT */
#endif /* !defined _ */ #endif /* !defined _ */
#ifndef TZ_DOMAIN #if !defined TZ_DOMAIN && defined HAVE_GETTEXT
#define TZ_DOMAIN "tz" # define TZ_DOMAIN "tz"
#endif /* !defined TZ_DOMAIN */ #endif
#if ! HAVE_LOCALTIME_RZ
# undef timezone_t
# define timezone_t char **
#endif
extern char ** environ; extern char ** environ;
extern int getopt(int argc, char * const argv[], extern int getopt(int argc, char * const argv[],
@ -209,57 +246,233 @@ extern int optind;
extern char * tzname[2]; extern char * tzname[2];
/* The minimum and maximum finite time values. */ /* The minimum and maximum finite time values. */
enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
static time_t const absolute_min_time = static time_t const absolute_min_time =
((time_t) -1 < 0 ((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); : 0);
static time_t const absolute_max_time = static time_t const absolute_max_time =
((time_t) -1 < 0 ((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); : -1);
static size_t longest; static int longest;
static char * progname; static char * progname;
static int warned; static bool warned;
static bool errout;
static char * abbr(struct tm * tmp); static char const *abbr(struct tm const *);
static void abbrok(const char * abbrp, const char * zone); static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE; static void dumptime(struct tm const *);
static void dumptime(const struct tm * tmp); static time_t hunt(timezone_t, char *, time_t, time_t);
static time_t hunt(char * name, time_t lot, time_t hit); static void show(timezone_t, char *, time_t, bool);
static void show(char * zone, time_t t, int v); static const char *tformat(void);
static const char * tformat(void); static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
static time_t yeartot(intmax_t y) 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
#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 #ifndef TYPECHECK
#define my_localtime localtime # define my_localtime_rz localtime_rz
#else /* !defined TYPECHECK */ #else /* !defined TYPECHECK */
static struct tm *
my_localtime(time_t *tp)
{
register struct tm * tmp;
tmp = localtime(tp); static struct tm *
if (tp != NULL && tmp != NULL) { my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
{
tmp = localtime_rz(tz, tp, tmp);
if (tmp) {
struct tm tm; struct tm tm;
register time_t t; register time_t t;
tm = *tmp; tm = *tmp;
t = mktime(&tm); t = mktime_z(tz, &tm);
if (t != *tp) { if (t != *tp) {
(void) fflush(stdout); fflush(stdout);
(void) fprintf(stderr, "\n%s: ", progname); fprintf(stderr, "\n%s: ", progname);
(void) fprintf(stderr, tformat(), *tp); fprintf(stderr, tformat(), *tp);
(void) fprintf(stderr, " ->"); fprintf(stderr, " ->");
(void) fprintf(stderr, " year=%d", tmp->tm_year); fprintf(stderr, " year=%d", tmp->tm_year);
(void) fprintf(stderr, " mon=%d", tmp->tm_mon); fprintf(stderr, " mon=%d", tmp->tm_mon);
(void) fprintf(stderr, " mday=%d", tmp->tm_mday); fprintf(stderr, " mday=%d", tmp->tm_mday);
(void) fprintf(stderr, " hour=%d", tmp->tm_hour); fprintf(stderr, " hour=%d", tmp->tm_hour);
(void) fprintf(stderr, " min=%d", tmp->tm_min); fprintf(stderr, " min=%d", tmp->tm_min);
(void) fprintf(stderr, " sec=%d", tmp->tm_sec); fprintf(stderr, " sec=%d", tmp->tm_sec);
(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); fprintf(stderr, " isdst=%d", tmp->tm_isdst);
(void) fprintf(stderr, " -> "); fprintf(stderr, " -> ");
(void) fprintf(stderr, tformat(), t); fprintf(stderr, tformat(), t);
(void) fprintf(stderr, "\n"); fprintf(stderr, "\n");
errout = true;
} }
} }
return tmp; return tmp;
@ -275,88 +488,124 @@ abbrok(const char *const abbrp, const char *const zone)
if (warned) if (warned)
return; return;
cp = abbrp; cp = abbrp;
wp = NULL; while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
++cp; ++cp;
if (cp - abbrp == 0) if (cp - abbrp < 3)
wp = _("lacks alphabetic at start"); wp = _("has fewer than 3 characters");
else if (cp - abbrp < 3)
wp = _("has fewer than 3 alphabetics");
else if (cp - abbrp > 6) else if (cp - abbrp > 6)
wp = _("has more than 6 alphabetics"); wp = _("has more than 6 characters");
if (wp == NULL && (*cp == '+' || *cp == '-')) { else if (*cp)
++cp; wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
if (isascii((unsigned char) *cp) && else
isdigit((unsigned char) *cp)) return;
if (*cp++ == '1' && *cp >= '0' && *cp <= '4') fflush(stdout);
++cp; fprintf(stderr,
if (*cp != '\0')
wp = _("differs from POSIX standard");
}
if (wp == NULL)
return;
(void) fflush(stdout);
(void) fprintf(stderr,
_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
progname, zone, abbrp, wp); 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 static void
usage(FILE * const stream, const int status) usage(FILE * const stream, const int status)
{ {
(void) fprintf(stream, fprintf(stream,
_("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n" _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
"\n" "\n"
"Report bugs to %s.\n"), "Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO); progname, progname, REPORT_BUGS_TO);
if (status == EXIT_SUCCESS)
close_file(stream);
exit(status); exit(status);
} }
int int
main(int argc, char *argv[]) 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 i;
register int vflag; register bool vflag;
register int Vflag; register bool Vflag;
register char * cutarg; register char * cutarg;
register char * cuttimes; register char * cuttimes;
register time_t cutlotime; register time_t cutlotime;
register time_t cuthitime; register time_t cuthitime;
register char ** fakeenv;
time_t now; time_t now;
time_t t; time_t t;
time_t newt; time_t newt;
struct tm tm; struct tm tm;
struct tm newtm;
register struct tm * tmp; register struct tm * tmp;
register struct tm * newtmp; register struct tm * newtmp;
cutlotime = absolute_min_time; cutlotime = absolute_min_time;
cuthitime = absolute_max_time; cuthitime = absolute_max_time;
#if HAVE_GETTEXT #if HAVE_GETTEXT
(void) setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
#ifdef TZ_DOMAINDIR #ifdef TZ_DOMAINDIR
(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
#endif /* defined TEXTDOMAINDIR */ #endif /* defined TEXTDOMAINDIR */
(void) textdomain(TZ_DOMAIN); textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */ #endif /* HAVE_GETTEXT */
progname = argv[0]; progname = argv[0];
for (i = 1; i < argc; ++i) for (i = 1; i < argc; ++i)
if (strcmp(argv[i], "--version") == 0) { if (strcmp(argv[i], "--version") == 0) {
(void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); printf("zdump %s%s\n", PKGVERSION, TZVERSION);
exit(EXIT_SUCCESS); return EXIT_SUCCESS;
} else if (strcmp(argv[i], "--help") == 0) { } else if (strcmp(argv[i], "--help") == 0) {
usage(stdout, EXIT_SUCCESS); usage(stdout, EXIT_SUCCESS);
} }
vflag = Vflag = 0; vflag = Vflag = false;
cutarg = cuttimes = NULL; cutarg = cuttimes = NULL;
for (;;) for (;;)
switch (getopt(argc, argv, "c:t:vV")) { switch (getopt(argc, argv, "c:t:vV")) {
case 'c': cutarg = optarg; break; case 'c': cutarg = optarg; break;
case 't': cuttimes = optarg; break; case 't': cuttimes = optarg; break;
case 'v': vflag = 1; break; case 'v': vflag = true; break;
case 'V': Vflag = 1; break; case 'V': Vflag = true; break;
case -1: case -1:
if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
goto arg_processing_done; goto arg_processing_done;
@ -383,9 +632,9 @@ main(int argc, char *argv[])
cutloyear = lo; cutloyear = lo;
cuthiyear = hi; cuthiyear = hi;
} else { } else {
(void) fprintf(stderr, _("%s: wild -c argument %s\n"), fprintf(stderr, _("%s: wild -c argument %s\n"),
progname, cutarg); progname, cutarg);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
} }
if (cutarg != NULL || cuttimes == NULL) { if (cutarg != NULL || cuttimes == NULL) {
@ -415,81 +664,61 @@ main(int argc, char *argv[])
cuthitime = hi; cuthitime = hi;
} }
} else { } else {
(void) fprintf(stderr, fprintf(stderr,
_("%s: wild -t argument %s\n"), _("%s: wild -t argument %s\n"),
progname, cuttimes); progname, cuttimes);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
} }
} }
(void) time(&now); gmtzinit();
now = time(NULL);
longest = 0; longest = 0;
for (i = optind; i < argc; ++i) for (i = optind; i < argc; i++) {
if (strlen(argv[i]) > longest) size_t arglen = strlen(argv[i]);
longest = strlen(argv[i]); if (longest < arglen)
{ longest = arglen < INT_MAX ? arglen : INT_MAX;
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) {
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)) { if (! (vflag | Vflag)) {
show(argv[i], now, FALSE); show(tz, argv[i], now, false);
tzfree(tz);
continue; continue;
} }
warned = FALSE; warned = false;
t = absolute_min_time; t = absolute_min_time;
if (!Vflag) { if (!Vflag) {
show(argv[i], t, TRUE); show(tz, argv[i], t, true);
t += SECSPERDAY; t += SECSPERDAY;
show(argv[i], t, TRUE); show(tz, argv[i], t, true);
} }
if (t < cutlotime) if (t < cutlotime)
t = cutlotime; t = cutlotime;
tmp = my_localtime(&t); tmp = my_localtime_rz(tz, &t, &tm);
if (tmp != NULL) { if (tmp)
tm = *tmp; ab = saveabbr(&abbrev, &abbrevsize, &tm);
(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); while (t < cuthitime) {
} newt = ((t < absolute_max_time - SECSPERDAY / 2
for ( ; ; ) { && t + SECSPERDAY / 2 < cuthitime)
newt = (t < absolute_max_time - SECSPERDAY / 2
? t + SECSPERDAY / 2 ? t + SECSPERDAY / 2
: absolute_max_time); : cuthitime);
if (cuthitime <= newt) newtmp = localtime_rz(tz, &newt, &newtm);
break;
newtmp = localtime(&newt);
if (newtmp != NULL)
newtm = *newtmp;
if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
(delta(&newtm, &tm) != (newt - t) || (delta(&newtm, &tm) != (newt - t) ||
newtm.tm_isdst != tm.tm_isdst || newtm.tm_isdst != tm.tm_isdst ||
strcmp(abbr(&newtm), buf) != 0)) { strcmp(abbr(&newtm), ab) != 0)) {
newt = hunt(argv[i], t, newt); newt = hunt(tz, argv[i], t, newt);
newtmp = localtime(&newt); newtmp = localtime_rz(tz, &newt, &newtm);
if (newtmp != NULL) { if (newtmp)
newtm = *newtmp; ab = saveabbr(&abbrev, &abbrevsize,
(void) strncpy(buf, &newtm);
abbr(&newtm),
(sizeof buf) - 1);
}
} }
t = newt; t = newt;
tm = newtm; tm = newtm;
@ -498,23 +727,20 @@ main(int argc, char *argv[])
if (!Vflag) { if (!Vflag) {
t = absolute_max_time; t = absolute_max_time;
t -= SECSPERDAY; t -= SECSPERDAY;
show(argv[i], t, TRUE); show(tz, argv[i], t, true);
t += SECSPERDAY; t += SECSPERDAY;
show(argv[i], t, TRUE); show(tz, argv[i], t, true);
} }
tzfree(tz);
} }
if (fflush(stdout) || ferror(stdout)) { close_file(stdout);
(void) fprintf(stderr, "%s: ", progname); if (errout && (ferror(stderr) || fclose(stderr) != 0))
(void) perror(_("Error writing to standard output")); return EXIT_FAILURE;
exit(EXIT_FAILURE); return EXIT_SUCCESS;
}
exit(EXIT_SUCCESS);
/* If exit fails to exit... */
return EXIT_FAILURE;
} }
static time_t static time_t
yeartot(const intmax_t y) yeartot(intmax_t y)
{ {
register intmax_t myy, seconds, years; register intmax_t myy, seconds, years;
register time_t t; register time_t t;
@ -557,20 +783,20 @@ yeartot(const intmax_t y)
} }
static time_t 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; time_t t;
struct tm lotm; struct tm lotm;
register struct tm * lotmp; register struct tm * lotmp;
struct tm tm; struct tm tm;
register struct tm * tmp; register struct tm * tmp;
char loab[MAX_STRING_LENGTH];
lotmp = my_localtime(&lot); lotmp = my_localtime_rz(tz, &lot, &lotm);
if (lotmp != NULL) { if (lotmp)
lotm = *lotmp; ab = saveabbr(&loab, &loabsize, &lotm);
(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
}
for ( ; ; ) { for ( ; ; ) {
time_t diff = hit - lot; time_t diff = hit - lot;
if (diff < 2) if (diff < 2)
@ -581,20 +807,18 @@ hunt(char *name, time_t lot, time_t hit)
++t; ++t;
else if (t >= hit) else if (t >= hit)
--t; --t;
tmp = my_localtime(&t); tmp = my_localtime_rz(tz, &t, &tm);
if (tmp != NULL)
tm = *tmp;
if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
(delta(&tm, &lotm) == (t - lot) && (delta(&tm, &lotm) == (t - lot) &&
tm.tm_isdst == lotm.tm_isdst && tm.tm_isdst == lotm.tm_isdst &&
strcmp(abbr(&tm), loab) == 0)) { strcmp(abbr(&tm), ab) == 0)) {
lot = t; lot = t;
lotm = tm; lotm = tm;
lotmp = tmp; lotmp = tmp;
} else hit = t; } else hit = t;
} }
show(name, lot, TRUE); show(tz, name, lot, true);
show(name, hit, TRUE); show(tz, name, hit, true);
return hit; return hit;
} }
@ -623,49 +847,87 @@ delta(struct tm * newp, struct tm *oldp)
return result; 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 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 * tmp;
register struct tm * gmtmp;
struct tm tm, gmtm;
(void) printf("%-*s ", (int) longest, zone); printf("%-*s ", longest, zone);
if (v) { if (v) {
tmp = gmtime(&t); gmtmp = my_gmtime_r(&t, &gmtm);
if (tmp == NULL) { if (gmtmp == NULL) {
(void) printf(tformat(), t); printf(tformat(), t);
} else { } else {
dumptime(tmp); dumptime(gmtmp);
(void) printf(" UT"); printf(" UT");
} }
(void) printf(" = "); printf(" = ");
} }
tmp = my_localtime(&t); tmp = my_localtime_rz(tz, &t, &tm);
dumptime(tmp); dumptime(tmp);
if (tmp != NULL) { if (tmp != NULL) {
if (*abbr(tmp) != '\0') if (*abbr(tmp) != '\0')
(void) printf(" %s", abbr(tmp)); printf(" %s", abbr(tmp));
if (v) { if (v) {
(void) printf(" isdst=%d", tmp->tm_isdst); long off = gmtoff(tmp, gmtmp);
#ifdef TM_GMTOFF printf(" isdst=%d", tmp->tm_isdst);
(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); if (off != LONG_MIN)
#endif /* defined TM_GMTOFF */ printf(" gmtoff=%ld", off);
} }
} }
(void) printf("\n"); printf("\n");
if (tmp != NULL && *abbr(tmp) != '\0') if (tmp != NULL && *abbr(tmp) != '\0')
abbrok(abbr(tmp), zone); abbrok(abbr(tmp), zone);
} }
static char * static char const *
abbr(struct tm *tmp) abbr(struct tm const *tmp)
{ {
register char * result; #ifdef TM_ZONE
static char nada; return tmp->TM_ZONE;
#else
if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
return &nada; ? tzname[0 < tmp->tm_isdst]
result = tzname[tmp->tm_isdst]; : "");
return (result == NULL) ? &nada : result; #endif
} }
/* /*
@ -712,11 +974,11 @@ dumptime(register const struct tm *timeptr)
register int trail; register int trail;
if (timeptr == NULL) { if (timeptr == NULL) {
(void) printf("NULL"); printf("NULL");
return; 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 ** values in tm_wday or tm_mon, but since this code might be compiled
** with other (perhaps experimental) versions, paranoia is in order. ** 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])) (int) (sizeof mon_name / sizeof mon_name[0]))
mn = "???"; mn = "???";
else mn = mon_name[timeptr->tm_mon]; else mn = mon_name[timeptr->tm_mon];
(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
wn, mn, wn, mn,
timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec); timeptr->tm_min, timeptr->tm_sec);
@ -745,6 +1007,6 @@ dumptime(register const struct tm *timeptr)
++lead; ++lead;
} }
if (lead == 0) if (lead == 0)
(void) printf("%d", trail); printf("%d", trail);
else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); else printf("%d%d", lead, ((trail < 0) ? -trail : trail));
} }

File diff suppressed because it is too large Load Diff