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>
* argp/argp-fmtstream.c (__argp_fmtstream_free): Use fwrite_unlocked

View File

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

View File

@ -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.

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.
** 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
@ -236,16 +304,6 @@ typedef unsigned long uintmax_t;
** 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
** 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.
*/
#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 *);
static time_t
time(time_t *p)
{
time_t r = sys_time(0);
if (p)
*p = r;
return r;
}
time_t time(time_t *);
void tzset(void);
#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);
char * icpyalloc(const char * string);
const char * scheck(const char * string, const char * format);
#ifndef asctime_r
extern char * asctime_r(struct tm const *restrict, char *restrict);
#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.
*/
#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 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 */
# define INITIALIZE(x) ((x) = 0)
#else
# define INITIALIZE(x)
#endif
#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 */
#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 */

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 {
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

View File

@ -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"

View File

@ -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 * 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;
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
#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)
{
register struct tm * tmp;
tmp = localtime(tp);
if (tp != NULL && tmp != NULL) {
static struct tm *
my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
{
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));
}

File diff suppressed because it is too large Load Diff