diff options
Diffstat (limited to 'timezone/zdump.c')
-rw-r--r-- | timezone/zdump.c | 688 |
1 files changed, 475 insertions, 213 deletions
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 <errno.h> /* ** Substitutes for pre-C99 compilers. @@ -58,24 +65,59 @@ typedef int int_fast32_t; # endif #endif +/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ +#if !defined LLONG_MAX && defined __LONG_LONG_MAX__ +# define LLONG_MAX __LONG_LONG_MAX__ +#endif + #ifndef INTMAX_MAX -# if defined LLONG_MAX || defined __LONG_LONG_MAX__ +# ifdef LLONG_MAX typedef long long intmax_t; # define strtoimax strtoll -# define PRIdMAX "lld" -# ifdef LLONG_MAX -# define INTMAX_MAX LLONG_MAX -# else -# define INTMAX_MAX __LONG_LONG_MAX__ -# endif +# define INTMAX_MAX LLONG_MAX # else typedef long intmax_t; # define strtoimax strtol -# define PRIdMAX "ld" # define INTMAX_MAX LONG_MAX # endif #endif +#ifndef PRIdMAX +# if INTMAX_MAX == LLONG_MAX +# define PRIdMAX "lld" +# else +# define PRIdMAX "ld" +# endif +#endif + +/* Infer TM_ZONE on systems where this information is known, but suppress + guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ +#if (defined __GLIBC__ \ + || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__)) +# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF +# define TM_GMTOFF tm_gmtoff +# endif +# if !defined TM_ZONE && !defined NO_TM_ZONE +# define TM_ZONE tm_zone +# endif +#endif + +#ifndef HAVE_LOCALTIME_R +# define HAVE_LOCALTIME_R 1 +#endif + +#ifndef HAVE_LOCALTIME_RZ +# ifdef TM_ZONE +# define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ) +# else +# define HAVE_LOCALTIME_RZ 0 +# endif +#endif + +#ifndef HAVE_TZSET +# define HAVE_TZSET 1 +#endif #ifndef ZDUMP_LO_YEAR #define ZDUMP_LO_YEAR (-500) @@ -89,13 +131,13 @@ typedef long intmax_t; #define MAX_STRING_LENGTH 1024 #endif /* !defined MAX_STRING_LENGTH */ -#ifndef TRUE -#define TRUE 1 -#endif /* !defined TRUE */ - -#ifndef FALSE -#define FALSE 0 -#endif /* !defined FALSE */ +#if __STDC_VERSION__ < 199901 +# define true 1 +# define false 0 +# define bool int +#else +# include <stdbool.h> +#endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 @@ -167,16 +209,6 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; #include "libintl.h" #endif /* HAVE_GETTEXT */ -#ifndef GNUC_or_lint -#ifdef lint -#define GNUC_or_lint -#else /* !defined lint */ -#ifdef __GNUC__ -#define GNUC_or_lint -#endif /* defined __GNUC__ */ -#endif /* !defined lint */ -#endif /* !defined GNUC_or_lint */ - #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) # define ATTRIBUTE_PURE __attribute__ ((__pure__)) #else @@ -185,7 +217,7 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; /* ** For the benefit of GNU folk... -** `_(MSGID)' uses the current locale's message library string for MSGID. +** '_(MSGID)' uses the current locale's message library string for MSGID. ** The default is to use gettext if available, and use MSGID otherwise. */ @@ -197,9 +229,14 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; #endif /* !HAVE_GETTEXT */ #endif /* !defined _ */ -#ifndef TZ_DOMAIN -#define TZ_DOMAIN "tz" -#endif /* !defined TZ_DOMAIN */ +#if !defined TZ_DOMAIN && defined HAVE_GETTEXT +# define TZ_DOMAIN "tz" +#endif + +#if ! HAVE_LOCALTIME_RZ +# undef timezone_t +# define timezone_t char ** +#endif extern char ** environ; extern int getopt(int argc, char * const argv[], @@ -209,57 +246,233 @@ extern int optind; extern char * tzname[2]; /* The minimum and maximum finite time values. */ +enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 }; static time_t const absolute_min_time = ((time_t) -1 < 0 - ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) + ? (- ((time_t) ~ (time_t) 0 < 0) + - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))) : 0); static time_t const absolute_max_time = ((time_t) -1 < 0 - ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) + ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) : -1); -static size_t longest; +static int longest; static char * progname; -static int warned; +static bool warned; +static bool errout; + +static char const *abbr(struct tm const *); +static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE; +static void dumptime(struct tm const *); +static time_t hunt(timezone_t, char *, time_t, time_t); +static void show(timezone_t, char *, time_t, bool); +static const char *tformat(void); +static time_t yeartot(intmax_t) ATTRIBUTE_PURE; + +/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ +#define is_digit(c) ((unsigned)(c) - '0' <= 9) + +/* Is A an alphabetic character in the C locale? */ +static bool +is_alpha(char a) +{ + switch (a) { + default: + return false; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + return true; + } +} + +/* Return A + B, exiting if the result would overflow. */ +static size_t +sumsize(size_t a, size_t b) +{ + size_t sum = a + b; + if (sum < a) { + fprintf(stderr, "%s: size overflow\n", progname); + exit(EXIT_FAILURE); + } + return sum; +} + +#if ! HAVE_TZSET +# undef tzset +# define tzset zdump_tzset +static void tzset(void) { } +#endif + +/* Assume gmtime_r works if localtime_r does. + A replacement localtime_r is defined below if needed. */ +#if ! HAVE_LOCALTIME_R + +# undef gmtime_r +# define gmtime_r zdump_gmtime_r + +static struct tm * +gmtime_r(time_t *tp, struct tm *tmp) +{ + struct tm *r = gmtime(tp); + if (r) { + *tmp = *r; + r = tmp; + } + return r; +} + +#endif + +/* Platforms with TM_ZONE don't need tzname, so they can use the + faster localtime_rz or localtime_r if available. */ + +#if defined TM_ZONE && HAVE_LOCALTIME_RZ +# define USE_LOCALTIME_RZ true +#else +# define USE_LOCALTIME_RZ false +#endif -static char * abbr(struct tm * tmp); -static void abbrok(const char * abbrp, const char * zone); -static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE; -static void dumptime(const struct tm * tmp); -static time_t hunt(char * name, time_t lot, time_t hit); -static void show(char * zone, time_t t, int v); -static const char * tformat(void); -static time_t yeartot(intmax_t y) ATTRIBUTE_PURE; +#if ! USE_LOCALTIME_RZ + +# if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET +# undef localtime_r +# define localtime_r zdump_localtime_r +static struct tm * +localtime_r(time_t *tp, struct tm *tmp) +{ + struct tm *r = localtime(tp); + if (r) { + *tmp = *r; + r = tmp; + } + return r; +} +# endif + +# undef localtime_rz +# define localtime_rz zdump_localtime_rz +static struct tm * +localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp) +{ + return localtime_r(tp, tmp); +} + +# ifdef TYPECHECK +# undef mktime_z +# define mktime_z zdump_mktime_z +static time_t +mktime_z(timezone_t tz, struct tm *tmp) +{ + return mktime(tmp); +} +# endif + +# undef tzalloc +# undef tzfree +# define tzalloc zdump_tzalloc +# define tzfree zdump_tzfree + +static timezone_t +tzalloc(char const *val) +{ + static char **fakeenv; + char **env = fakeenv; + char *env0; + if (! env) { + char **e = environ; + int to; + + while (*e++) + continue; + env = malloc(sumsize(sizeof *environ, + (e - environ) * sizeof *environ)); + if (! env) { + perror(progname); + exit(EXIT_FAILURE); + } + to = 1; + for (e = environ; (env[to] = *e); e++) + to += strncmp(*e, "TZ=", 3) != 0; + } + env0 = malloc(sumsize(sizeof "TZ=", strlen(val))); + if (! env0) { + perror(progname); + exit(EXIT_FAILURE); + } + env[0] = strcat(strcpy(env0, "TZ="), val); + environ = fakeenv = env; + tzset(); + return env; +} + +static void +tzfree(timezone_t env) +{ + environ = env + 1; + free(env[0]); +} +#endif /* ! USE_LOCALTIME_RZ */ + +/* A UTC time zone, and its initializer. */ +static timezone_t gmtz; +static void +gmtzinit(void) +{ + if (USE_LOCALTIME_RZ) { + static char const utc[] = "UTC0"; + gmtz = tzalloc(utc); + if (!gmtz) { + perror(utc); + exit(EXIT_FAILURE); + } + } +} + +/* Convert *TP to UTC, storing the broken-down time into *TMP. + Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP), + except typically faster if USE_LOCALTIME_RZ. */ +static struct tm * +my_gmtime_r(time_t *tp, struct tm *tmp) +{ + return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp); +} #ifndef TYPECHECK -#define my_localtime localtime +# define my_localtime_rz localtime_rz #else /* !defined TYPECHECK */ + static struct tm * -my_localtime(time_t *tp) +my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp) { - register struct tm * tmp; - - tmp = localtime(tp); - if (tp != NULL && tmp != NULL) { + tmp = localtime_rz(tz, tp, tmp); + if (tmp) { struct tm tm; register time_t t; tm = *tmp; - t = mktime(&tm); + t = mktime_z(tz, &tm); if (t != *tp) { - (void) fflush(stdout); - (void) fprintf(stderr, "\n%s: ", progname); - (void) fprintf(stderr, tformat(), *tp); - (void) fprintf(stderr, " ->"); - (void) fprintf(stderr, " year=%d", tmp->tm_year); - (void) fprintf(stderr, " mon=%d", tmp->tm_mon); - (void) fprintf(stderr, " mday=%d", tmp->tm_mday); - (void) fprintf(stderr, " hour=%d", tmp->tm_hour); - (void) fprintf(stderr, " min=%d", tmp->tm_min); - (void) fprintf(stderr, " sec=%d", tmp->tm_sec); - (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); - (void) fprintf(stderr, " -> "); - (void) fprintf(stderr, tformat(), t); - (void) fprintf(stderr, "\n"); + fflush(stdout); + fprintf(stderr, "\n%s: ", progname); + fprintf(stderr, tformat(), *tp); + fprintf(stderr, " ->"); + fprintf(stderr, " year=%d", tmp->tm_year); + fprintf(stderr, " mon=%d", tmp->tm_mon); + fprintf(stderr, " mday=%d", tmp->tm_mday); + fprintf(stderr, " hour=%d", tmp->tm_hour); + fprintf(stderr, " min=%d", tmp->tm_min); + fprintf(stderr, " sec=%d", tmp->tm_sec); + fprintf(stderr, " isdst=%d", tmp->tm_isdst); + fprintf(stderr, " -> "); + fprintf(stderr, tformat(), t); + fprintf(stderr, "\n"); + errout = true; } } return tmp; @@ -275,88 +488,124 @@ abbrok(const char *const abbrp, const char *const zone) if (warned) return; cp = abbrp; - wp = NULL; - while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp)) + while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+') ++cp; - if (cp - abbrp == 0) - wp = _("lacks alphabetic at start"); - else if (cp - abbrp < 3) - wp = _("has fewer than 3 alphabetics"); + if (cp - abbrp < 3) + wp = _("has fewer than 3 characters"); else if (cp - abbrp > 6) - wp = _("has more than 6 alphabetics"); - if (wp == NULL && (*cp == '+' || *cp == '-')) { - ++cp; - if (isascii((unsigned char) *cp) && - isdigit((unsigned char) *cp)) - if (*cp++ == '1' && *cp >= '0' && *cp <= '4') - ++cp; - if (*cp != '\0') - wp = _("differs from POSIX standard"); - } - if (wp == NULL) - return; - (void) fflush(stdout); - (void) fprintf(stderr, + wp = _("has more than 6 characters"); + else if (*cp) + wp = _("has characters other than ASCII alphanumerics, '-' or '+'"); + else + return; + fflush(stdout); + fprintf(stderr, _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), progname, zone, abbrp, wp); - warned = TRUE; + warned = errout = true; +} + +/* Return a time zone abbreviation. If the abbreviation needs to be + saved, use *BUF (of size *BUFALLOC) to save it, and return the + abbreviation in the possibly-reallocated *BUF. Otherwise, just + return the abbreviation. Get the abbreviation from TMP. + Exit on memory allocation failure. */ +static char const * +saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp) +{ + char const *ab = abbr(tmp); + if (HAVE_LOCALTIME_RZ) + return ab; + else { + size_t ablen = strlen(ab); + if (*bufalloc <= ablen) { + free(*buf); + + /* Make the new buffer at least twice as long as the old, + to avoid O(N**2) behavior on repeated calls. */ + *bufalloc = sumsize(*bufalloc, ablen + 1); + + *buf = malloc(*bufalloc); + if (! *buf) { + perror(progname); + exit(EXIT_FAILURE); + } + } + return strcpy(*buf, ab); + } +} + +static void +close_file(FILE *stream) +{ + char const *e = (ferror(stream) ? _("I/O error") + : fclose(stream) != 0 ? strerror(errno) : NULL); + if (e) { + fprintf(stderr, "%s: %s\n", progname, e); + exit(EXIT_FAILURE); + } } static void usage(FILE * const stream, const int status) { - (void) fprintf(stream, + fprintf(stream, _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n" "\n" "Report bugs to %s.\n"), progname, progname, REPORT_BUGS_TO); + if (status == EXIT_SUCCESS) + close_file(stream); exit(status); } int main(int argc, char *argv[]) { + /* These are static so that they're initially zero. */ + static char * abbrev; + static size_t abbrevsize; + static struct tm newtm; + register int i; - register int vflag; - register int Vflag; + register bool vflag; + register bool Vflag; register char * cutarg; register char * cuttimes; register time_t cutlotime; register time_t cuthitime; - register char ** fakeenv; time_t now; time_t t; time_t newt; struct tm tm; - struct tm newtm; register struct tm * tmp; register struct tm * newtmp; cutlotime = absolute_min_time; cuthitime = absolute_max_time; #if HAVE_GETTEXT - (void) setlocale(LC_ALL, ""); + setlocale(LC_ALL, ""); #ifdef TZ_DOMAINDIR - (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); + bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); #endif /* defined TEXTDOMAINDIR */ - (void) textdomain(TZ_DOMAIN); + textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ progname = argv[0]; for (i = 1; i < argc; ++i) if (strcmp(argv[i], "--version") == 0) { - (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); - exit(EXIT_SUCCESS); + printf("zdump %s%s\n", PKGVERSION, TZVERSION); + return EXIT_SUCCESS; } else if (strcmp(argv[i], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } - vflag = Vflag = 0; + vflag = Vflag = false; cutarg = cuttimes = NULL; for (;;) switch (getopt(argc, argv, "c:t:vV")) { case 'c': cutarg = optarg; break; case 't': cuttimes = optarg; break; - case 'v': vflag = 1; break; - case 'V': Vflag = 1; break; + case 'v': vflag = true; break; + case 'V': Vflag = true; break; case -1: if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) goto arg_processing_done; @@ -383,9 +632,9 @@ main(int argc, char *argv[]) cutloyear = lo; cuthiyear = hi; } else { -(void) fprintf(stderr, _("%s: wild -c argument %s\n"), + fprintf(stderr, _("%s: wild -c argument %s\n"), progname, cutarg); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } } if (cutarg != NULL || cuttimes == NULL) { @@ -415,81 +664,61 @@ main(int argc, char *argv[]) cuthitime = hi; } } else { - (void) fprintf(stderr, + fprintf(stderr, _("%s: wild -t argument %s\n"), progname, cuttimes); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } } } - (void) time(&now); + gmtzinit(); + now = time(NULL); longest = 0; - for (i = optind; i < argc; ++i) - if (strlen(argv[i]) > longest) - longest = strlen(argv[i]); - { - register int from; - register int to; - - for (i = 0; environ[i] != NULL; ++i) - continue; - fakeenv = malloc((i + 2) * sizeof *fakeenv); - if (fakeenv == NULL - || (fakeenv[0] = malloc(longest + 4)) == NULL) { - (void) perror(progname); - exit(EXIT_FAILURE); - } - to = 0; - (void) strcpy(fakeenv[to++], "TZ="); - for (from = 0; environ[from] != NULL; ++from) - if (strncmp(environ[from], "TZ=", 3) != 0) - fakeenv[to++] = environ[from]; - fakeenv[to] = NULL; - environ = fakeenv; + for (i = optind; i < argc; i++) { + size_t arglen = strlen(argv[i]); + if (longest < arglen) + longest = arglen < INT_MAX ? arglen : INT_MAX; } - for (i = optind; i < argc; ++i) { - static char buf[MAX_STRING_LENGTH]; - (void) strcpy(&fakeenv[0][3], argv[i]); + for (i = optind; i < argc; ++i) { + timezone_t tz = tzalloc(argv[i]); + char const *ab; + if (!tz) { + perror(argv[i]); + return EXIT_FAILURE; + } if (! (vflag | Vflag)) { - show(argv[i], now, FALSE); + show(tz, argv[i], now, false); + tzfree(tz); continue; } - warned = FALSE; + warned = false; t = absolute_min_time; if (!Vflag) { - show(argv[i], t, TRUE); + show(tz, argv[i], t, true); t += SECSPERDAY; - show(argv[i], t, TRUE); + show(tz, argv[i], t, true); } if (t < cutlotime) t = cutlotime; - tmp = my_localtime(&t); - if (tmp != NULL) { - tm = *tmp; - (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); - } - for ( ; ; ) { - newt = (t < absolute_max_time - SECSPERDAY / 2 + tmp = my_localtime_rz(tz, &t, &tm); + if (tmp) + ab = saveabbr(&abbrev, &abbrevsize, &tm); + while (t < cuthitime) { + newt = ((t < absolute_max_time - SECSPERDAY / 2 + && t + SECSPERDAY / 2 < cuthitime) ? t + SECSPERDAY / 2 - : absolute_max_time); - if (cuthitime <= newt) - break; - newtmp = localtime(&newt); - if (newtmp != NULL) - newtm = *newtmp; + : cuthitime); + newtmp = localtime_rz(tz, &newt, &newtm); if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : (delta(&newtm, &tm) != (newt - t) || newtm.tm_isdst != tm.tm_isdst || - strcmp(abbr(&newtm), buf) != 0)) { - newt = hunt(argv[i], t, newt); - newtmp = localtime(&newt); - if (newtmp != NULL) { - newtm = *newtmp; - (void) strncpy(buf, - abbr(&newtm), - (sizeof buf) - 1); - } + strcmp(abbr(&newtm), ab) != 0)) { + newt = hunt(tz, argv[i], t, newt); + newtmp = localtime_rz(tz, &newt, &newtm); + if (newtmp) + ab = saveabbr(&abbrev, &abbrevsize, + &newtm); } t = newt; tm = newtm; @@ -498,23 +727,20 @@ main(int argc, char *argv[]) if (!Vflag) { t = absolute_max_time; t -= SECSPERDAY; - show(argv[i], t, TRUE); + show(tz, argv[i], t, true); t += SECSPERDAY; - show(argv[i], t, TRUE); + show(tz, argv[i], t, true); } + tzfree(tz); } - if (fflush(stdout) || ferror(stdout)) { - (void) fprintf(stderr, "%s: ", progname); - (void) perror(_("Error writing to standard output")); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); - /* If exit fails to exit... */ - return EXIT_FAILURE; + close_file(stdout); + if (errout && (ferror(stderr) || fclose(stderr) != 0)) + return EXIT_FAILURE; + return EXIT_SUCCESS; } static time_t -yeartot(const intmax_t y) +yeartot(intmax_t y) { register intmax_t myy, seconds, years; register time_t t; @@ -557,20 +783,20 @@ yeartot(const intmax_t y) } static time_t -hunt(char *name, time_t lot, time_t hit) +hunt(timezone_t tz, char *name, time_t lot, time_t hit) { + static char * loab; + static size_t loabsize; + char const * ab; time_t t; struct tm lotm; register struct tm * lotmp; struct tm tm; register struct tm * tmp; - char loab[MAX_STRING_LENGTH]; - lotmp = my_localtime(&lot); - if (lotmp != NULL) { - lotm = *lotmp; - (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); - } + lotmp = my_localtime_rz(tz, &lot, &lotm); + if (lotmp) + ab = saveabbr(&loab, &loabsize, &lotm); for ( ; ; ) { time_t diff = hit - lot; if (diff < 2) @@ -581,20 +807,18 @@ hunt(char *name, time_t lot, time_t hit) ++t; else if (t >= hit) --t; - tmp = my_localtime(&t); - if (tmp != NULL) - tm = *tmp; + tmp = my_localtime_rz(tz, &t, &tm); if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : (delta(&tm, &lotm) == (t - lot) && tm.tm_isdst == lotm.tm_isdst && - strcmp(abbr(&tm), loab) == 0)) { + strcmp(abbr(&tm), ab) == 0)) { lot = t; lotm = tm; lotmp = tmp; } else hit = t; } - show(name, lot, TRUE); - show(name, hit, TRUE); + show(tz, name, lot, true); + show(tz, name, hit, true); return hit; } @@ -623,49 +847,87 @@ delta(struct tm * newp, struct tm *oldp) return result; } +#ifndef TM_GMTOFF +/* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday. + Assume A and B differ by at most one year. */ +static int +adjusted_yday(struct tm const *a, struct tm const *b) +{ + int yday = a->tm_yday; + if (b->tm_year < a->tm_year) + yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE); + return yday; +} +#endif + +/* If A is the broken-down local time and B the broken-down UTC for + the same instant, return A's UTC offset in seconds, where positive + offsets are east of Greenwich. On failure, return LONG_MIN. */ +static long +gmtoff(struct tm const *a, struct tm const *b) +{ +#ifdef TM_GMTOFF + return a->TM_GMTOFF; +#else + if (! b) + return LONG_MIN; + else { + int ayday = adjusted_yday(a, b); + int byday = adjusted_yday(b, a); + int days = ayday - byday; + long hours = a->tm_hour - b->tm_hour + 24 * days; + long minutes = a->tm_min - b->tm_min + 60 * hours; + long seconds = a->tm_sec - b->tm_sec + 60 * minutes; + return seconds; + } +#endif +} + static void -show(char *zone, time_t t, int v) +show(timezone_t tz, char *zone, time_t t, bool v) { register struct tm * tmp; + register struct tm * gmtmp; + struct tm tm, gmtm; - (void) printf("%-*s ", (int) longest, zone); + printf("%-*s ", longest, zone); if (v) { - tmp = gmtime(&t); - if (tmp == NULL) { - (void) printf(tformat(), t); + gmtmp = my_gmtime_r(&t, &gmtm); + if (gmtmp == NULL) { + printf(tformat(), t); } else { - dumptime(tmp); - (void) printf(" UT"); + dumptime(gmtmp); + printf(" UT"); } - (void) printf(" = "); + printf(" = "); } - tmp = my_localtime(&t); + tmp = my_localtime_rz(tz, &t, &tm); dumptime(tmp); if (tmp != NULL) { if (*abbr(tmp) != '\0') - (void) printf(" %s", abbr(tmp)); + printf(" %s", abbr(tmp)); if (v) { - (void) printf(" isdst=%d", tmp->tm_isdst); -#ifdef TM_GMTOFF - (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); -#endif /* defined TM_GMTOFF */ + long off = gmtoff(tmp, gmtmp); + printf(" isdst=%d", tmp->tm_isdst); + if (off != LONG_MIN) + printf(" gmtoff=%ld", off); } } - (void) printf("\n"); + printf("\n"); if (tmp != NULL && *abbr(tmp) != '\0') abbrok(abbr(tmp), zone); } -static char * -abbr(struct tm *tmp) +static char const * +abbr(struct tm const *tmp) { - register char * result; - static char nada; - - if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) - return &nada; - result = tzname[tmp->tm_isdst]; - return (result == NULL) ? &nada : result; +#ifdef TM_ZONE + return tmp->TM_ZONE; +#else + return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst] + ? tzname[0 < tmp->tm_isdst] + : ""); +#endif } /* @@ -712,11 +974,11 @@ dumptime(register const struct tm *timeptr) register int trail; if (timeptr == NULL) { - (void) printf("NULL"); + printf("NULL"); return; } /* - ** The packaged versions of localtime and gmtime never put out-of-range + ** The packaged localtime_rz and gmtime_r never put out-of-range ** values in tm_wday or tm_mon, but since this code might be compiled ** with other (perhaps experimental) versions, paranoia is in order. */ @@ -728,7 +990,7 @@ dumptime(register const struct tm *timeptr) (int) (sizeof mon_name / sizeof mon_name[0])) mn = "???"; else mn = mon_name[timeptr->tm_mon]; - (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", + printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); @@ -745,6 +1007,6 @@ dumptime(register const struct tm *timeptr) ++lead; } if (lead == 0) - (void) printf("%d", trail); - else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); + printf("%d", trail); + else printf("%d%d", lead, ((trail < 0) ? -trail : trail)); } |