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