Home | History | Annotate | Line # | Download | only in time
zdump.c revision 1.43.2.1
      1  1.43.2.1  pgoyette /*	$NetBSD: zdump.c,v 1.43.2.1 2016/11/04 14:48:53 pgoyette Exp $	*/
      2      1.17   mlelstv /*
      3      1.17   mlelstv ** This file is in the public domain, so clarified as of
      4      1.17   mlelstv ** 2009-05-17 by Arthur David Olson.
      5      1.17   mlelstv */
      6       1.2       jtc 
      7       1.6  christos #include <sys/cdefs.h>
      8       1.1       jtc #ifndef lint
      9  1.43.2.1  pgoyette __RCSID("$NetBSD: zdump.c,v 1.43.2.1 2016/11/04 14:48:53 pgoyette Exp $");
     10       1.1       jtc #endif /* !defined lint */
     11       1.1       jtc 
     12       1.1       jtc /*
     13       1.1       jtc ** This code has been made independent of the rest of the time
     14       1.1       jtc ** conversion package to increase confidence in the verification it provides.
     15       1.1       jtc ** You can use this code to help in verifying other implementations.
     16      1.29  christos **
     17      1.36  christos ** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
     18       1.1       jtc */
     19       1.1       jtc 
     20      1.36  christos #ifndef NETBSD_INSPIRED
     21      1.36  christos # define NETBSD_INSPIRED 1
     22      1.36  christos #endif
     23      1.36  christos #ifndef USE_LTZ
     24      1.36  christos # define USE_LTZ 1
     25      1.36  christos #endif
     26      1.36  christos 
     27      1.36  christos #if USE_LTZ
     28      1.29  christos #include "private.h"
     29      1.36  christos #endif
     30      1.36  christos 
     31      1.36  christos /* Enable tm_gmtoff and tm_zone on GNUish systems.  */
     32      1.36  christos #define _GNU_SOURCE 1
     33      1.36  christos /* Enable strtoimax on Solaris 10.  */
     34      1.36  christos #define __EXTENSIONS__ 1
     35      1.29  christos 
     36      1.15  christos #include "stdio.h"	/* for stdout, stderr */
     37       1.1       jtc #include "string.h"	/* for strcpy */
     38       1.1       jtc #include "sys/types.h"	/* for time_t */
     39       1.1       jtc #include "time.h"	/* for struct tm */
     40       1.1       jtc #include "stdlib.h"	/* for exit, malloc, atoi */
     41      1.36  christos #include "limits.h"	/* for CHAR_BIT, LLONG_MAX */
     42      1.36  christos #include <errno.h>
     43      1.15  christos #include <err.h>
     44      1.17   mlelstv 
     45      1.29  christos /*
     46      1.29  christos ** Substitutes for pre-C99 compilers.
     47      1.29  christos ** Much of this section of code is stolen from private.h.
     48      1.29  christos */
     49      1.29  christos 
     50      1.29  christos #ifndef HAVE_STDINT_H
     51      1.29  christos # define HAVE_STDINT_H \
     52      1.33  christos     (199901 <= __STDC_VERSION__ \
     53      1.33  christos      || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__)	\
     54  1.43.2.1  pgoyette      || __CYGWIN__ || INTMAX_MAX)
     55      1.29  christos #endif
     56      1.29  christos #if HAVE_STDINT_H
     57      1.29  christos # include "stdint.h"
     58      1.29  christos #endif
     59      1.29  christos #ifndef HAVE_INTTYPES_H
     60      1.29  christos # define HAVE_INTTYPES_H HAVE_STDINT_H
     61      1.29  christos #endif
     62      1.29  christos #if HAVE_INTTYPES_H
     63      1.29  christos # include <inttypes.h>
     64      1.29  christos #endif
     65      1.29  christos 
     66      1.29  christos #ifndef INT_FAST32_MAX
     67      1.29  christos # if INT_MAX >> 31 == 0
     68      1.29  christos typedef long int_fast32_t;
     69      1.29  christos # else
     70      1.29  christos typedef int int_fast32_t;
     71      1.29  christos # endif
     72      1.29  christos #endif
     73      1.29  christos 
     74      1.36  christos /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
     75      1.36  christos #if !defined LLONG_MAX && defined __LONG_LONG_MAX__
     76      1.36  christos # define LLONG_MAX __LONG_LONG_MAX__
     77      1.36  christos #endif
     78      1.36  christos 
     79      1.29  christos #ifndef INTMAX_MAX
     80      1.36  christos # ifdef LLONG_MAX
     81      1.29  christos typedef long long intmax_t;
     82      1.32  christos #  define strtoimax strtoll
     83      1.36  christos #  define INTMAX_MAX LLONG_MAX
     84      1.29  christos # else
     85      1.29  christos typedef long intmax_t;
     86      1.32  christos #  define strtoimax strtol
     87      1.36  christos #  define INTMAX_MAX LONG_MAX
     88      1.36  christos # endif
     89      1.36  christos #endif
     90      1.36  christos 
     91      1.36  christos #ifndef PRIdMAX
     92      1.36  christos # if INTMAX_MAX == LLONG_MAX
     93      1.36  christos #  define PRIdMAX "lld"
     94      1.36  christos # else
     95      1.29  christos #  define PRIdMAX "ld"
     96      1.29  christos # endif
     97      1.29  christos #endif
     98      1.29  christos 
     99      1.36  christos /* Infer TM_ZONE on systems where this information is known, but suppress
    100      1.36  christos    guessing if NO_TM_ZONE is defined.  Similarly for TM_GMTOFF.  */
    101      1.36  christos #if (defined __GLIBC__ \
    102      1.36  christos      || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
    103      1.36  christos      || (defined __APPLE__ && defined __MACH__))
    104      1.36  christos # if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
    105      1.36  christos #  define TM_GMTOFF tm_gmtoff
    106      1.36  christos # endif
    107      1.36  christos # if !defined TM_ZONE && !defined NO_TM_ZONE
    108      1.36  christos #  define TM_ZONE tm_zone
    109      1.36  christos # endif
    110      1.36  christos #endif
    111      1.36  christos 
    112      1.36  christos #ifndef HAVE_LOCALTIME_R
    113      1.36  christos # define HAVE_LOCALTIME_R 1
    114      1.36  christos #endif
    115      1.36  christos 
    116      1.36  christos #ifndef HAVE_LOCALTIME_RZ
    117      1.36  christos # ifdef TM_ZONE
    118      1.36  christos #  define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
    119      1.36  christos # else
    120      1.36  christos #  define HAVE_LOCALTIME_RZ 0
    121      1.36  christos # endif
    122      1.36  christos #endif
    123      1.36  christos 
    124      1.36  christos #ifndef HAVE_TZSET
    125      1.36  christos # define HAVE_TZSET 1
    126      1.36  christos #endif
    127      1.27  christos 
    128      1.17   mlelstv #ifndef ZDUMP_LO_YEAR
    129      1.17   mlelstv #define ZDUMP_LO_YEAR	(-500)
    130      1.17   mlelstv #endif /* !defined ZDUMP_LO_YEAR */
    131      1.17   mlelstv 
    132      1.17   mlelstv #ifndef ZDUMP_HI_YEAR
    133      1.17   mlelstv #define ZDUMP_HI_YEAR	2500
    134      1.17   mlelstv #endif /* !defined ZDUMP_HI_YEAR */
    135       1.1       jtc 
    136       1.1       jtc #ifndef MAX_STRING_LENGTH
    137       1.1       jtc #define MAX_STRING_LENGTH	1024
    138       1.1       jtc #endif /* !defined MAX_STRING_LENGTH */
    139       1.1       jtc 
    140      1.36  christos #if __STDC_VERSION__ < 199901
    141      1.36  christos # define true 1
    142      1.36  christos # define false 0
    143      1.36  christos # define bool int
    144      1.36  christos #else
    145      1.36  christos # include <stdbool.h>
    146      1.36  christos #endif
    147       1.1       jtc 
    148  1.43.2.1  pgoyette #ifndef TYPE_BIT
    149  1.43.2.1  pgoyette #define TYPE_BIT(type)	(sizeof (type) * CHAR_BIT)
    150  1.43.2.1  pgoyette #endif /* !defined TYPE_BIT */
    151  1.43.2.1  pgoyette 
    152  1.43.2.1  pgoyette #ifndef TYPE_SIGNED
    153  1.43.2.1  pgoyette #define TYPE_SIGNED(type) (((type) -1) < 0)
    154  1.43.2.1  pgoyette #endif /* !defined TYPE_SIGNED */
    155  1.43.2.1  pgoyette 
    156  1.43.2.1  pgoyette #ifndef INT_STRLEN_MAXIMUM
    157  1.43.2.1  pgoyette /*
    158  1.43.2.1  pgoyette ** 302 / 1000 is log10(2.0) rounded up.
    159  1.43.2.1  pgoyette ** Subtract one for the sign bit if the type is signed;
    160  1.43.2.1  pgoyette ** add one for integer division truncation;
    161  1.43.2.1  pgoyette ** add one more for a minus sign if the type is signed.
    162  1.43.2.1  pgoyette */
    163  1.43.2.1  pgoyette #define INT_STRLEN_MAXIMUM(type) \
    164  1.43.2.1  pgoyette 	((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
    165  1.43.2.1  pgoyette 	1 + TYPE_SIGNED(type))
    166  1.43.2.1  pgoyette #endif /* !defined INT_STRLEN_MAXIMUM */
    167  1.43.2.1  pgoyette 
    168       1.1       jtc #ifndef EXIT_SUCCESS
    169       1.1       jtc #define EXIT_SUCCESS	0
    170       1.1       jtc #endif /* !defined EXIT_SUCCESS */
    171       1.1       jtc 
    172       1.1       jtc #ifndef EXIT_FAILURE
    173       1.1       jtc #define EXIT_FAILURE	1
    174       1.1       jtc #endif /* !defined EXIT_FAILURE */
    175       1.1       jtc 
    176       1.1       jtc #ifndef SECSPERMIN
    177       1.1       jtc #define SECSPERMIN	60
    178       1.1       jtc #endif /* !defined SECSPERMIN */
    179       1.1       jtc 
    180       1.1       jtc #ifndef MINSPERHOUR
    181       1.1       jtc #define MINSPERHOUR	60
    182       1.1       jtc #endif /* !defined MINSPERHOUR */
    183       1.1       jtc 
    184       1.1       jtc #ifndef SECSPERHOUR
    185       1.1       jtc #define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
    186       1.1       jtc #endif /* !defined SECSPERHOUR */
    187       1.1       jtc 
    188       1.1       jtc #ifndef HOURSPERDAY
    189       1.1       jtc #define HOURSPERDAY	24
    190       1.1       jtc #endif /* !defined HOURSPERDAY */
    191       1.1       jtc 
    192       1.1       jtc #ifndef EPOCH_YEAR
    193       1.1       jtc #define EPOCH_YEAR	1970
    194       1.1       jtc #endif /* !defined EPOCH_YEAR */
    195       1.1       jtc 
    196       1.1       jtc #ifndef TM_YEAR_BASE
    197       1.1       jtc #define TM_YEAR_BASE	1900
    198       1.1       jtc #endif /* !defined TM_YEAR_BASE */
    199       1.1       jtc 
    200       1.1       jtc #ifndef DAYSPERNYEAR
    201       1.1       jtc #define DAYSPERNYEAR	365
    202       1.1       jtc #endif /* !defined DAYSPERNYEAR */
    203       1.1       jtc 
    204       1.1       jtc #ifndef isleap
    205      1.17   mlelstv #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
    206       1.1       jtc #endif /* !defined isleap */
    207       1.1       jtc 
    208      1.17   mlelstv #ifndef isleap_sum
    209      1.17   mlelstv /*
    210      1.17   mlelstv ** See tzfile.h for details on isleap_sum.
    211      1.17   mlelstv */
    212      1.17   mlelstv #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
    213      1.17   mlelstv #endif /* !defined isleap_sum */
    214      1.17   mlelstv 
    215      1.29  christos #define SECSPERDAY	((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
    216      1.17   mlelstv #define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
    217      1.17   mlelstv #define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
    218      1.31  christos #define SECSPER400YEARS	(SECSPERNYEAR * (intmax_t) (300 + 3)	\
    219      1.31  christos 			 + SECSPERLYEAR * (intmax_t) (100 - 3))
    220      1.31  christos 
    221      1.31  christos /*
    222      1.31  christos ** True if SECSPER400YEARS is known to be representable as an
    223      1.31  christos ** intmax_t.  It's OK that SECSPER400YEARS_FITS can in theory be false
    224      1.31  christos ** even if SECSPER400YEARS is representable, because when that happens
    225      1.31  christos ** the code merely runs a bit more slowly, and this slowness doesn't
    226      1.31  christos ** occur on any practical platform.
    227      1.31  christos */
    228      1.31  christos enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
    229      1.17   mlelstv 
    230      1.17   mlelstv #ifndef HAVE_GETTEXT
    231      1.17   mlelstv #define HAVE_GETTEXT 0
    232      1.17   mlelstv #endif
    233      1.17   mlelstv #if HAVE_GETTEXT
    234       1.3       jtc #include "locale.h"	/* for setlocale */
    235       1.3       jtc #include "libintl.h"
    236      1.17   mlelstv #endif /* HAVE_GETTEXT */
    237       1.3       jtc 
    238      1.29  christos #ifndef ATTRIBUTE_PURE
    239      1.26  christos #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
    240      1.29  christos # define ATTRIBUTE_PURE __attribute__ ((ATTRIBUTE_PURE__))
    241      1.26  christos #else
    242      1.29  christos # define ATTRIBUTE_PURE /* empty */
    243      1.26  christos #endif
    244      1.26  christos #endif
    245      1.26  christos 
    246       1.1       jtc #ifndef INITIALIZE
    247      1.35  christos #if defined(__GNUC__) || defined(__lint__)
    248       1.1       jtc #define INITIALIZE(x)	((x) = 0)
    249      1.35  christos #else /* !defined GNUC || lint */
    250       1.1       jtc #define INITIALIZE(x)
    251      1.35  christos #endif /* !defined GNUC || lint */
    252       1.1       jtc #endif /* !defined INITIALIZE */
    253       1.1       jtc 
    254       1.3       jtc /*
    255       1.3       jtc ** For the benefit of GNU folk...
    256      1.34  christos ** '_(MSGID)' uses the current locale's message library string for MSGID.
    257       1.3       jtc ** The default is to use gettext if available, and use MSGID otherwise.
    258       1.3       jtc */
    259       1.3       jtc 
    260       1.3       jtc #ifndef _
    261      1.17   mlelstv #if HAVE_GETTEXT
    262       1.3       jtc #define _(msgid) gettext(msgid)
    263      1.17   mlelstv #else /* !HAVE_GETTEXT */
    264      1.21  christos #define _(msgid) msgid
    265      1.17   mlelstv #endif /* !HAVE_GETTEXT */
    266       1.3       jtc #endif /* !defined _ */
    267       1.3       jtc 
    268      1.34  christos #if !defined TZ_DOMAIN && defined HAVE_GETTEXT
    269      1.34  christos # define TZ_DOMAIN "tz"
    270      1.34  christos #endif
    271       1.3       jtc 
    272      1.36  christos #if ! HAVE_LOCALTIME_RZ
    273      1.36  christos # undef  timezone_t
    274      1.36  christos # define timezone_t char **
    275      1.36  christos #endif
    276      1.36  christos 
    277       1.1       jtc extern char **	environ;
    278  1.43.2.1  pgoyette 
    279  1.43.2.1  pgoyette #if !HAVE_POSIX_DECLS
    280      1.17   mlelstv extern int	getopt(int argc, char * const argv[],
    281      1.17   mlelstv 			const char * options);
    282       1.1       jtc extern char *	optarg;
    283       1.1       jtc extern int	optind;
    284      1.43  christos extern char *	tzname[];
    285      1.43  christos #endif
    286       1.1       jtc 
    287      1.29  christos /* The minimum and maximum finite time values.  */
    288      1.41  christos enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
    289      1.29  christos static time_t	absolute_min_time =
    290      1.31  christos   ((time_t) -1 < 0
    291      1.41  christos     ? (- ((time_t) ~ (time_t) 0 < 0)
    292      1.41  christos        - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
    293      1.29  christos     : 0);
    294      1.29  christos static time_t	absolute_max_time =
    295      1.31  christos   ((time_t) -1 < 0
    296      1.41  christos     ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
    297      1.29  christos    : -1);
    298       1.5       jtc static size_t	longest;
    299       1.1       jtc static char *	progname;
    300      1.36  christos static bool	warned;
    301      1.36  christos static bool	errout;
    302      1.17   mlelstv 
    303      1.36  christos static char const *abbr(struct tm const *);
    304      1.36  christos static intmax_t	delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
    305      1.36  christos static void dumptime(struct tm const *);
    306      1.36  christos static time_t hunt(timezone_t, char *, time_t, time_t);
    307      1.36  christos static void show(timezone_t, char *, time_t, bool);
    308  1.43.2.1  pgoyette static void showtrans(char const *, struct tm const *, time_t, char const *,
    309  1.43.2.1  pgoyette 		      char const *);
    310      1.36  christos static const char *tformat(void);
    311      1.36  christos static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
    312      1.17   mlelstv 
    313      1.42  christos /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
    314      1.42  christos #define is_digit(c) ((unsigned)(c) - '0' <= 9)
    315      1.42  christos 
    316      1.34  christos /* Is A an alphabetic character in the C locale?  */
    317      1.36  christos static bool
    318      1.34  christos is_alpha(char a)
    319      1.34  christos {
    320      1.34  christos 	switch (a) {
    321      1.34  christos 	  default:
    322      1.36  christos 		return false;
    323      1.34  christos 	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
    324      1.34  christos 	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
    325      1.34  christos 	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
    326      1.34  christos 	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
    327      1.34  christos 	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
    328      1.34  christos 	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
    329      1.34  christos 	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
    330      1.34  christos 	  case 'v': case 'w': case 'x': case 'y': case 'z':
    331      1.36  christos 	  	return true;
    332      1.34  christos 	}
    333      1.34  christos }
    334      1.34  christos 
    335      1.36  christos /* Return A + B, exiting if the result would overflow.  */
    336      1.36  christos static size_t
    337      1.36  christos sumsize(size_t a, size_t b)
    338      1.36  christos {
    339      1.36  christos 	size_t sum = a + b;
    340      1.36  christos 	if (sum < a)
    341      1.36  christos 		errx(EXIT_FAILURE, "size overflow");
    342      1.36  christos 	return sum;
    343      1.36  christos }
    344      1.36  christos 
    345  1.43.2.1  pgoyette /* Return a pointer to a newly allocated buffer of size SIZE, exiting
    346  1.43.2.1  pgoyette    on failure.  SIZE should be nonzero.  */
    347  1.43.2.1  pgoyette static void *
    348  1.43.2.1  pgoyette xmalloc(size_t size)
    349  1.43.2.1  pgoyette {
    350  1.43.2.1  pgoyette   void *p = malloc(size);
    351  1.43.2.1  pgoyette   if (!p) {
    352  1.43.2.1  pgoyette     perror(progname);
    353  1.43.2.1  pgoyette     exit(EXIT_FAILURE);
    354  1.43.2.1  pgoyette   }
    355  1.43.2.1  pgoyette   return p;
    356  1.43.2.1  pgoyette }
    357  1.43.2.1  pgoyette 
    358      1.36  christos #if ! HAVE_TZSET
    359      1.36  christos # undef tzset
    360      1.36  christos # define tzset zdump_tzset
    361      1.36  christos static void tzset(void) { }
    362      1.36  christos #endif
    363      1.36  christos 
    364      1.36  christos /* Assume gmtime_r works if localtime_r does.
    365      1.36  christos    A replacement localtime_r is defined below if needed.  */
    366      1.36  christos #if ! HAVE_LOCALTIME_R
    367      1.36  christos 
    368      1.36  christos # undef gmtime_r
    369      1.36  christos # define gmtime_r zdump_gmtime_r
    370      1.36  christos 
    371      1.36  christos static struct tm *
    372      1.36  christos gmtime_r(time_t *tp, struct tm *tmp)
    373      1.36  christos {
    374      1.36  christos 	struct tm *r = gmtime(tp);
    375      1.36  christos 	if (r) {
    376      1.36  christos 		*tmp = *r;
    377      1.36  christos 		r = tmp;
    378      1.36  christos 	}
    379      1.36  christos 	return r;
    380      1.36  christos }
    381      1.36  christos 
    382      1.36  christos #endif
    383      1.36  christos 
    384      1.36  christos /* Platforms with TM_ZONE don't need tzname, so they can use the
    385      1.36  christos    faster localtime_rz or localtime_r if available.  */
    386      1.36  christos 
    387      1.36  christos #if defined TM_ZONE && HAVE_LOCALTIME_RZ
    388      1.36  christos # define USE_LOCALTIME_RZ true
    389      1.36  christos #else
    390      1.36  christos # define USE_LOCALTIME_RZ false
    391      1.36  christos #endif
    392      1.36  christos 
    393      1.36  christos #if ! USE_LOCALTIME_RZ
    394      1.36  christos 
    395      1.36  christos # if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
    396      1.36  christos #  undef localtime_r
    397      1.36  christos #  define localtime_r zdump_localtime_r
    398      1.36  christos static struct tm *
    399      1.36  christos localtime_r(time_t *tp, struct tm *tmp)
    400      1.36  christos {
    401      1.36  christos 	struct tm *r = localtime(tp);
    402      1.36  christos 	if (r) {
    403      1.36  christos 		*tmp = *r;
    404      1.36  christos 		r = tmp;
    405      1.36  christos 	}
    406      1.36  christos 	return r;
    407      1.36  christos }
    408      1.36  christos # endif
    409      1.36  christos 
    410      1.36  christos # undef localtime_rz
    411      1.36  christos # define localtime_rz zdump_localtime_rz
    412      1.36  christos static struct tm *
    413      1.36  christos localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp)
    414      1.36  christos {
    415      1.36  christos 	return localtime_r(tp, tmp);
    416      1.36  christos }
    417      1.36  christos 
    418      1.36  christos # ifdef TYPECHECK
    419      1.36  christos #  undef mktime_z
    420      1.36  christos #  define mktime_z zdump_mktime_z
    421      1.36  christos static time_t
    422      1.36  christos mktime_z(timezone_t tz, struct tm *tmp)
    423      1.36  christos {
    424      1.36  christos 	return mktime(tmp);
    425      1.36  christos }
    426      1.36  christos # endif
    427      1.36  christos 
    428      1.36  christos # undef tzalloc
    429      1.36  christos # undef tzfree
    430      1.36  christos # define tzalloc zdump_tzalloc
    431      1.36  christos # define tzfree zdump_tzfree
    432      1.36  christos 
    433      1.36  christos static timezone_t
    434      1.36  christos tzalloc(char const *val)
    435      1.36  christos {
    436      1.36  christos 	static char **fakeenv;
    437      1.36  christos 	char **env = fakeenv;
    438      1.36  christos 	char *env0;
    439      1.36  christos 	if (! env) {
    440      1.36  christos 		char **e = environ;
    441      1.36  christos 		int to;
    442      1.36  christos 
    443      1.36  christos 		while (*e++)
    444      1.36  christos 			continue;
    445  1.43.2.1  pgoyette 		env = xmalloc(sumsize(sizeof *environ,
    446      1.36  christos 		    (e - environ) * sizeof *environ));
    447      1.36  christos 		to = 1;
    448      1.36  christos 		for (e = environ; (env[to] = *e); e++)
    449      1.36  christos 			to += strncmp(*e, "TZ=", 3) != 0;
    450      1.36  christos 	}
    451  1.43.2.1  pgoyette 	env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
    452      1.36  christos 	env[0] = strcat(strcpy(env0, "TZ="), val);
    453      1.36  christos 	environ = fakeenv = env;
    454      1.36  christos 	tzset();
    455      1.36  christos 	return env;
    456      1.36  christos }
    457      1.36  christos 
    458      1.36  christos static void
    459      1.36  christos tzfree(timezone_t env)
    460      1.36  christos {
    461      1.36  christos 	environ = env + 1;
    462      1.36  christos 	free(env[0]);
    463      1.36  christos }
    464      1.36  christos #endif /* ! USE_LOCALTIME_RZ */
    465      1.36  christos 
    466      1.36  christos /* A UTC time zone, and its initializer.  */
    467      1.36  christos static timezone_t gmtz;
    468      1.36  christos static void
    469      1.36  christos gmtzinit(void)
    470      1.36  christos {
    471      1.36  christos 	if (USE_LOCALTIME_RZ) {
    472      1.36  christos 		static char const utc[] = "UTC0";
    473      1.36  christos 		gmtz = tzalloc(utc);
    474      1.36  christos 		if (!gmtz) {
    475      1.36  christos 		      err(EXIT_FAILURE, "Cannot create %s", utc);
    476      1.36  christos 		}
    477      1.36  christos 	}
    478      1.36  christos }
    479      1.36  christos 
    480      1.36  christos /* Convert *TP to UTC, storing the broken-down time into *TMP.
    481      1.36  christos    Return TMP if successful, NULL otherwise.  This is like gmtime_r(TP, TMP),
    482      1.36  christos    except typically faster if USE_LOCALTIME_RZ.  */
    483      1.36  christos static struct tm *
    484      1.36  christos my_gmtime_r(time_t *tp, struct tm *tmp)
    485      1.36  christos {
    486      1.36  christos 	return USE_LOCALTIME_RZ ?
    487      1.36  christos 	    localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
    488      1.36  christos }
    489      1.36  christos 
    490      1.17   mlelstv #ifndef TYPECHECK
    491      1.36  christos #define my_localtime_rz	localtime_rz
    492      1.17   mlelstv #else /* !defined TYPECHECK */
    493      1.17   mlelstv static struct tm *
    494      1.36  christos my_localtime_rz(timezone_t tz, const time_t *tp, struct tm *tmp)
    495      1.17   mlelstv {
    496      1.36  christos 	tmp = localtime_rz(tz, tp, tmp);
    497      1.36  christos 	if (tmp) {
    498      1.17   mlelstv 		struct tm	tm;
    499      1.26  christos 		time_t	t;
    500      1.17   mlelstv 
    501      1.17   mlelstv 		tm = *tmp;
    502      1.36  christos 		t = mktime_z(tz, &tm);
    503      1.31  christos 		if (t != *tp) {
    504      1.17   mlelstv 			(void) fflush(stdout);
    505      1.17   mlelstv 			(void) fprintf(stderr, "\n%s: ", progname);
    506      1.17   mlelstv 			(void) fprintf(stderr, tformat(), *tp);
    507      1.17   mlelstv 			(void) fprintf(stderr, " ->");
    508      1.17   mlelstv 			(void) fprintf(stderr, " year=%d", tmp->tm_year);
    509      1.17   mlelstv 			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
    510      1.17   mlelstv 			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
    511      1.17   mlelstv 			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
    512      1.17   mlelstv 			(void) fprintf(stderr, " min=%d", tmp->tm_min);
    513      1.17   mlelstv 			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
    514      1.17   mlelstv 			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
    515      1.17   mlelstv 			(void) fprintf(stderr, " -> ");
    516      1.17   mlelstv 			(void) fprintf(stderr, tformat(), t);
    517      1.17   mlelstv 			(void) fprintf(stderr, "\n");
    518      1.36  christos 			errout = true;
    519      1.17   mlelstv 		}
    520      1.17   mlelstv 	}
    521      1.17   mlelstv 	return tmp;
    522      1.17   mlelstv }
    523      1.17   mlelstv #endif /* !defined TYPECHECK */
    524      1.17   mlelstv 
    525      1.17   mlelstv static void
    526      1.26  christos abbrok(const char *const abbrp, const char *const zone)
    527      1.17   mlelstv {
    528      1.26  christos 	const char *cp;
    529      1.26  christos 	const char *wp;
    530      1.17   mlelstv 
    531      1.17   mlelstv 	if (warned)
    532      1.17   mlelstv 		return;
    533      1.17   mlelstv 	cp = abbrp;
    534      1.42  christos 	while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
    535      1.17   mlelstv 		++cp;
    536      1.42  christos 	if (cp - abbrp < 3)
    537      1.42  christos 		wp = _("has fewer than 3 characters");
    538      1.17   mlelstv 	else if (cp - abbrp > 6)
    539      1.42  christos 		wp = _("has more than 6 characters");
    540      1.42  christos 	else if (*cp)
    541      1.42  christos 		wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
    542      1.42  christos 	else
    543      1.17   mlelstv 		return;
    544      1.17   mlelstv 	(void) fflush(stdout);
    545      1.17   mlelstv 	(void) fprintf(stderr,
    546      1.17   mlelstv 		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
    547      1.17   mlelstv 		progname, zone, abbrp, wp);
    548      1.36  christos 	warned = errout = true;
    549      1.36  christos }
    550      1.36  christos 
    551      1.36  christos /* Return a time zone abbreviation.  If the abbreviation needs to be
    552      1.36  christos    saved, use *BUF (of size *BUFALLOC) to save it, and return the
    553      1.36  christos    abbreviation in the possibly-reallocated *BUF.  Otherwise, just
    554      1.36  christos    return the abbreviation.  Get the abbreviation from TMP.
    555      1.36  christos    Exit on memory allocation failure.  */
    556      1.36  christos static char const *
    557      1.36  christos saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp)
    558      1.36  christos {
    559      1.36  christos 	char const *ab = abbr(tmp);
    560      1.36  christos 	if (HAVE_LOCALTIME_RZ)
    561      1.36  christos 		return ab;
    562      1.36  christos 	else {
    563      1.36  christos 		size_t ablen = strlen(ab);
    564      1.36  christos 		if (*bufalloc <= ablen) {
    565      1.36  christos 			free(*buf);
    566      1.36  christos 
    567      1.36  christos 			/* Make the new buffer at least twice as long as the
    568      1.36  christos 			   old, to avoid O(N**2) behavior on repeated calls.  */
    569      1.36  christos 			*bufalloc = sumsize(*bufalloc, ablen + 1);
    570  1.43.2.1  pgoyette 			*buf = xmalloc(*bufalloc);
    571      1.36  christos 		}
    572      1.36  christos 		return strcpy(*buf, ab);
    573      1.36  christos 	}
    574      1.36  christos }
    575      1.36  christos 
    576      1.36  christos static void
    577      1.36  christos close_file(FILE *stream)
    578      1.36  christos {
    579      1.36  christos 	char const *e = (ferror(stream) ? _("I/O error")
    580      1.36  christos 	    : fclose(stream) != 0 ? strerror(errno) : NULL);
    581      1.36  christos 	if (e) {
    582      1.36  christos 		errx(EXIT_FAILURE, "%s", e);
    583      1.36  christos 	}
    584      1.17   mlelstv }
    585      1.17   mlelstv 
    586      1.24     joerg __dead static void
    587      1.26  christos usage(FILE *const stream, const int status)
    588      1.17   mlelstv {
    589      1.17   mlelstv 	(void) fprintf(stream,
    590  1.43.2.1  pgoyette _("%s: usage: %s OPTIONS ZONENAME ...\n"
    591  1.43.2.1  pgoyette   "Options include:\n"
    592  1.43.2.1  pgoyette   "  -c [L,]U   Start at year L (default -500), end before year U (default 2500)\n"
    593  1.43.2.1  pgoyette   "  -t [L,]U   Start at time L, end before time U (in seconds since 1970)\n"
    594  1.43.2.1  pgoyette   "  -i         List transitions briefly (format is experimental)\n" \
    595  1.43.2.1  pgoyette   "  -v         List transitions verbosely\n"
    596  1.43.2.1  pgoyette   "  -V         List transitions a bit less verbosely\n"
    597  1.43.2.1  pgoyette   "  --help     Output this help\n"
    598  1.43.2.1  pgoyette   "  --version  Output version info\n"
    599      1.29  christos   "\n"
    600      1.29  christos   "Report bugs to %s.\n"),
    601  1.43.2.1  pgoyette 	   progname, progname, REPORT_BUGS_TO);
    602      1.36  christos 	if (status == EXIT_SUCCESS)
    603      1.36  christos 		close_file(stream);
    604      1.17   mlelstv 	exit(status);
    605      1.17   mlelstv }
    606       1.1       jtc 
    607       1.1       jtc int
    608      1.26  christos main(int argc, char *argv[])
    609      1.26  christos {
    610      1.36  christos 	/* These are static so that they're initially zero.  */
    611      1.36  christos 	static char *		abbrev;
    612      1.36  christos 	static size_t		abbrevsize;
    613      1.36  christos 
    614      1.26  christos 	int		i;
    615      1.36  christos 	bool		vflag;
    616      1.36  christos 	bool		Vflag;
    617      1.26  christos 	char *		cutarg;
    618      1.29  christos 	char *		cuttimes;
    619      1.26  christos 	time_t		cutlotime;
    620      1.26  christos 	time_t		cuthitime;
    621      1.26  christos 	time_t		now;
    622  1.43.2.1  pgoyette 	bool iflag = false;
    623       1.1       jtc 
    624      1.29  christos 	cutlotime = absolute_min_time;
    625      1.29  christos 	cuthitime = absolute_max_time;
    626      1.17   mlelstv #if HAVE_GETTEXT
    627      1.17   mlelstv 	(void) setlocale(LC_ALL, "");
    628       1.3       jtc #ifdef TZ_DOMAINDIR
    629       1.3       jtc 	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
    630      1.17   mlelstv #endif /* defined TEXTDOMAINDIR */
    631       1.3       jtc 	(void) textdomain(TZ_DOMAIN);
    632      1.17   mlelstv #endif /* HAVE_GETTEXT */
    633       1.1       jtc 	progname = argv[0];
    634      1.14    kleink 	for (i = 1; i < argc; ++i)
    635      1.14    kleink 		if (strcmp(argv[i], "--version") == 0) {
    636      1.28  christos 			(void) printf("zdump %s%s\n", PKGVERSION, TZVERSION);
    637      1.36  christos 			return EXIT_SUCCESS;
    638      1.17   mlelstv 		} else if (strcmp(argv[i], "--help") == 0) {
    639      1.22  christos 			usage(stdout, EXIT_SUCCESS);
    640      1.14    kleink 		}
    641      1.36  christos 	vflag = Vflag = false;
    642      1.29  christos 	cutarg = cuttimes = NULL;
    643      1.29  christos 	for (;;)
    644  1.43.2.1  pgoyette 	  switch (getopt(argc, argv, "c:it:vV")) {
    645      1.29  christos 	  case 'c': cutarg = optarg; break;
    646      1.29  christos 	  case 't': cuttimes = optarg; break;
    647  1.43.2.1  pgoyette 	  case 'i': iflag = true; break;
    648      1.36  christos 	  case 'v': vflag = true; break;
    649      1.36  christos 	  case 'V': Vflag = true; break;
    650      1.29  christos 	  case -1:
    651      1.29  christos 	    if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
    652      1.29  christos 	      goto arg_processing_done;
    653      1.29  christos 	    /* Fall through.  */
    654      1.29  christos 	  default:
    655      1.29  christos 	    usage(stderr, EXIT_FAILURE);
    656      1.29  christos 	  }
    657      1.29  christos  arg_processing_done:;
    658      1.29  christos 
    659  1.43.2.1  pgoyette 	if (iflag | vflag | Vflag) {
    660      1.29  christos 		intmax_t	lo;
    661      1.29  christos 		intmax_t	hi;
    662      1.32  christos 		char *loend, *hiend;
    663      1.30  christos 		intmax_t cutloyear = ZDUMP_LO_YEAR;
    664      1.30  christos 		intmax_t cuthiyear = ZDUMP_HI_YEAR;
    665      1.17   mlelstv 		if (cutarg != NULL) {
    666      1.32  christos 			lo = strtoimax(cutarg, &loend, 10);
    667      1.32  christos 			if (cutarg != loend && !*loend) {
    668      1.32  christos 				hi = lo;
    669      1.32  christos 				cuthiyear = hi;
    670      1.32  christos 			} else if (cutarg != loend && *loend == ','
    671      1.32  christos 				   && (hi = strtoimax(loend + 1, &hiend, 10),
    672      1.32  christos 				       loend + 1 != hiend && !*hiend)) {
    673      1.32  christos 				cutloyear = lo;
    674      1.17   mlelstv 				cuthiyear = hi;
    675      1.17   mlelstv 			} else {
    676      1.36  christos 				fprintf(stderr, _("%s: wild -c argument %s\n"),
    677      1.17   mlelstv 					progname, cutarg);
    678      1.36  christos 				return EXIT_FAILURE;
    679      1.17   mlelstv 			}
    680      1.17   mlelstv 		}
    681      1.29  christos 		if (cutarg != NULL || cuttimes == NULL) {
    682      1.29  christos 			cutlotime = yeartot(cutloyear);
    683      1.29  christos 			cuthitime = yeartot(cuthiyear);
    684      1.29  christos 		}
    685      1.29  christos 		if (cuttimes != NULL) {
    686      1.32  christos 			lo = strtoimax(cuttimes, &loend, 10);
    687      1.32  christos 			if (cuttimes != loend && !*loend) {
    688      1.32  christos 				hi = lo;
    689      1.29  christos 				if (hi < cuthitime) {
    690      1.29  christos 					if (hi < absolute_min_time)
    691      1.29  christos 						hi = absolute_min_time;
    692      1.29  christos 					cuthitime = hi;
    693      1.29  christos 				}
    694      1.32  christos 			} else if (cuttimes != loend && *loend == ','
    695      1.32  christos 				   && (hi = strtoimax(loend + 1, &hiend, 10),
    696      1.32  christos 				       loend + 1 != hiend && !*hiend)) {
    697      1.29  christos 				if (cutlotime < lo) {
    698      1.29  christos 					if (absolute_max_time < lo)
    699      1.29  christos 						lo = absolute_max_time;
    700      1.29  christos 					cutlotime = lo;
    701      1.29  christos 				}
    702      1.29  christos 				if (hi < cuthitime) {
    703      1.29  christos 					if (hi < absolute_min_time)
    704      1.29  christos 						hi = absolute_min_time;
    705      1.29  christos 					cuthitime = hi;
    706      1.29  christos 				}
    707      1.29  christos 			} else {
    708      1.29  christos 				(void) fprintf(stderr,
    709      1.29  christos 					_("%s: wild -t argument %s\n"),
    710      1.29  christos 					progname, cuttimes);
    711      1.36  christos 				return EXIT_FAILURE;
    712      1.29  christos 			}
    713      1.29  christos 		}
    714       1.1       jtc 	}
    715      1.36  christos 	gmtzinit();
    716  1.43.2.1  pgoyette 	INITIALIZE (now);
    717  1.43.2.1  pgoyette 	if (! (iflag | vflag | Vflag))
    718  1.43.2.1  pgoyette 	  now = time(NULL);
    719       1.1       jtc 	longest = 0;
    720      1.36  christos 	for (i = optind; i < argc; i++) {
    721      1.36  christos 		size_t arglen = strlen(argv[i]);
    722      1.36  christos 		if (longest < arglen)
    723      1.36  christos 			longest = arglen < INT_MAX ? arglen : INT_MAX;
    724      1.36  christos 	}
    725       1.1       jtc 
    726       1.1       jtc 	for (i = optind; i < argc; ++i) {
    727      1.36  christos 		timezone_t tz = tzalloc(argv[i]);
    728      1.36  christos 		char const *ab;
    729  1.43.2.1  pgoyette 		time_t t;
    730  1.43.2.1  pgoyette 		struct tm tm, newtm;
    731  1.43.2.1  pgoyette 		bool tm_ok;
    732  1.43.2.1  pgoyette 
    733      1.36  christos 		if (!tz) {
    734      1.36  christos 			errx(EXIT_FAILURE, "%s", argv[i]);
    735      1.36  christos 		}
    736  1.43.2.1  pgoyette 		if (! (iflag | vflag | Vflag)) {
    737      1.36  christos 			show(tz, argv[i], now, false);
    738      1.36  christos 			tzfree(tz);
    739       1.1       jtc 			continue;
    740       1.3       jtc 		}
    741      1.36  christos 		warned = false;
    742      1.17   mlelstv 		t = absolute_min_time;
    743  1.43.2.1  pgoyette 		if (! (iflag | Vflag)) {
    744      1.36  christos 			show(tz, argv[i], t, true);
    745      1.31  christos 			t += SECSPERDAY;
    746      1.36  christos 			show(tz, argv[i], t, true);
    747      1.29  christos 		}
    748      1.17   mlelstv 		if (t < cutlotime)
    749      1.17   mlelstv 			t = cutlotime;
    750  1.43.2.1  pgoyette 		tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
    751  1.43.2.1  pgoyette 		if (tm_ok) {
    752      1.36  christos 			ab = saveabbr(&abbrev, &abbrevsize, &tm);
    753  1.43.2.1  pgoyette 			if (iflag) {
    754  1.43.2.1  pgoyette 				showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
    755  1.43.2.1  pgoyette 				showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
    756  1.43.2.1  pgoyette 			}
    757  1.43.2.1  pgoyette 		} else
    758      1.36  christos 			ab = NULL;
    759      1.36  christos 		while (t < cuthitime) {
    760  1.43.2.1  pgoyette 			time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
    761  1.43.2.1  pgoyette 			    && t + SECSPERDAY / 2 < cuthitime)
    762  1.43.2.1  pgoyette 			    ? t + SECSPERDAY / 2 : cuthitime);
    763  1.43.2.1  pgoyette 			struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
    764  1.43.2.1  pgoyette 			bool newtm_ok = newtmp != NULL;
    765  1.43.2.1  pgoyette 			if (! (tm_ok & newtm_ok
    766  1.43.2.1  pgoyette 			    ? (delta(&newtm, &tm) == newt - t
    767  1.43.2.1  pgoyette 			    && newtm.tm_isdst == tm.tm_isdst
    768  1.43.2.1  pgoyette 			    && strcmp(abbr(&newtm), ab) == 0)
    769  1.43.2.1  pgoyette 			    : tm_ok == newtm_ok)) {
    770      1.36  christos 				newt = hunt(tz, argv[i], t, newt);
    771      1.36  christos 				newtmp = localtime_rz(tz, &newt, &newtm);
    772  1.43.2.1  pgoyette 				newtm_ok = newtmp != NULL;
    773  1.43.2.1  pgoyette 				if (iflag)
    774  1.43.2.1  pgoyette 					showtrans("%Y-%m-%d\t%L\t%Q",
    775  1.43.2.1  pgoyette 					    newtmp, newt, newtm_ok ?
    776  1.43.2.1  pgoyette 					    abbr(&newtm) : NULL, argv[i]);
    777  1.43.2.1  pgoyette 				else {
    778  1.43.2.1  pgoyette 					show(tz, argv[i], newt - 1, true);
    779  1.43.2.1  pgoyette 					show(tz, argv[i], newt, true);
    780  1.43.2.1  pgoyette 				}
    781       1.1       jtc 			}
    782       1.1       jtc 			t = newt;
    783  1.43.2.1  pgoyette 			tm_ok = newtm_ok;
    784  1.43.2.1  pgoyette 			if (newtm_ok) {
    785  1.43.2.1  pgoyette 				ab = saveabbr(&abbrev, &abbrevsize, &newtm);
    786  1.43.2.1  pgoyette 				tm = newtm;
    787  1.43.2.1  pgoyette 			}
    788       1.1       jtc 		}
    789  1.43.2.1  pgoyette 		if (! (iflag | Vflag)) {
    790      1.29  christos 			t = absolute_max_time;
    791      1.31  christos 			t -= SECSPERDAY;
    792      1.36  christos 			show(tz, argv[i], t, true);
    793      1.31  christos 			t += SECSPERDAY;
    794      1.36  christos 			show(tz, argv[i], t, true);
    795      1.29  christos 		}
    796      1.36  christos 		tzfree(tz);
    797       1.1       jtc 	}
    798      1.36  christos 	close_file(stdout);
    799      1.36  christos 	if (errout && (ferror(stderr) || fclose(stderr) != 0))
    800      1.36  christos 		return EXIT_FAILURE;
    801      1.36  christos 	return EXIT_SUCCESS;
    802      1.17   mlelstv }
    803       1.1       jtc 
    804       1.1       jtc static time_t
    805      1.42  christos yeartot(intmax_t y)
    806      1.17   mlelstv {
    807      1.31  christos 	intmax_t	myy, seconds, years;
    808      1.29  christos 	time_t		t;
    809      1.17   mlelstv 
    810      1.17   mlelstv 	myy = EPOCH_YEAR;
    811      1.17   mlelstv 	t = 0;
    812      1.31  christos 	while (myy < y) {
    813      1.31  christos 		if (SECSPER400YEARS_FITS && 400 <= y - myy) {
    814      1.31  christos 			intmax_t diff400 = (y - myy) / 400;
    815      1.31  christos 			if (INTMAX_MAX / SECSPER400YEARS < diff400)
    816      1.31  christos 				return absolute_max_time;
    817      1.31  christos 			seconds = diff400 * SECSPER400YEARS;
    818      1.31  christos 			years = diff400 * 400;
    819      1.31  christos                 } else {
    820      1.17   mlelstv 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
    821      1.31  christos 			years = 1;
    822      1.31  christos 		}
    823      1.31  christos 		myy += years;
    824      1.31  christos 		if (t > absolute_max_time - seconds)
    825      1.31  christos 			return absolute_max_time;
    826      1.31  christos 		t += seconds;
    827      1.31  christos 	}
    828      1.31  christos 	while (y < myy) {
    829      1.31  christos 		if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
    830      1.31  christos 			intmax_t diff400 = (myy - y) / 400;
    831      1.31  christos 			if (INTMAX_MAX / SECSPER400YEARS < diff400)
    832      1.31  christos 				return absolute_min_time;
    833      1.31  christos 			seconds = diff400 * SECSPER400YEARS;
    834      1.31  christos 			years = diff400 * 400;
    835      1.17   mlelstv 		} else {
    836      1.31  christos 			seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
    837      1.31  christos 			years = 1;
    838      1.17   mlelstv 		}
    839      1.31  christos 		myy -= years;
    840      1.31  christos 		if (t < absolute_min_time + seconds)
    841      1.31  christos 			return absolute_min_time;
    842      1.31  christos 		t -= seconds;
    843      1.17   mlelstv 	}
    844      1.17   mlelstv 	return t;
    845      1.17   mlelstv }
    846      1.17   mlelstv 
    847      1.17   mlelstv static time_t
    848      1.36  christos hunt(timezone_t tz, char *name, time_t lot, time_t hit)
    849      1.17   mlelstv {
    850      1.36  christos 	static char *		loab;
    851      1.36  christos 	static size_t		loabsize;
    852      1.36  christos 	char const *		ab;
    853      1.17   mlelstv 	time_t			t;
    854      1.17   mlelstv 	struct tm		lotm;
    855      1.17   mlelstv 	struct tm		tm;
    856  1.43.2.1  pgoyette 	bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
    857  1.43.2.1  pgoyette 	bool tm_ok;
    858      1.17   mlelstv 
    859  1.43.2.1  pgoyette 	if (lotm_ok)
    860      1.36  christos 		ab = saveabbr(&loab, &loabsize, &lotm);
    861      1.36  christos 	else
    862      1.36  christos 		ab = NULL;
    863      1.17   mlelstv 	for ( ; ; ) {
    864      1.29  christos 		time_t diff = hit - lot;
    865      1.17   mlelstv 		if (diff < 2)
    866      1.17   mlelstv 			break;
    867      1.17   mlelstv 		t = lot;
    868      1.17   mlelstv 		t += diff / 2;
    869       1.1       jtc 		if (t <= lot)
    870       1.1       jtc 			++t;
    871       1.1       jtc 		else if (t >= hit)
    872       1.1       jtc 			--t;
    873  1.43.2.1  pgoyette 		tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
    874  1.43.2.1  pgoyette 		if (lotm_ok & tm_ok
    875  1.43.2.1  pgoyette 		    ? (delta(&tm, &lotm) == t - lot
    876  1.43.2.1  pgoyette 		       && tm.tm_isdst == lotm.tm_isdst
    877  1.43.2.1  pgoyette 		       && strcmp(abbr(&tm), ab) == 0)
    878  1.43.2.1  pgoyette 		    : lotm_ok == tm_ok) {
    879  1.43.2.1  pgoyette 		  lot = t;
    880  1.43.2.1  pgoyette 		  if (tm_ok)
    881  1.43.2.1  pgoyette 		    lotm = tm;
    882       1.1       jtc 		} else	hit = t;
    883       1.1       jtc 	}
    884       1.1       jtc 	return hit;
    885       1.1       jtc }
    886       1.1       jtc 
    887       1.1       jtc /*
    888      1.17   mlelstv ** Thanks to Paul Eggert for logic used in delta.
    889       1.1       jtc */
    890       1.1       jtc 
    891      1.29  christos static intmax_t
    892      1.26  christos delta(struct tm *newp, struct tm *oldp)
    893       1.1       jtc {
    894      1.29  christos 	intmax_t	result;
    895      1.29  christos 	int		tmy;
    896       1.1       jtc 
    897       1.1       jtc 	if (newp->tm_year < oldp->tm_year)
    898       1.1       jtc 		return -delta(oldp, newp);
    899       1.1       jtc 	result = 0;
    900       1.1       jtc 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
    901      1.17   mlelstv 		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
    902       1.1       jtc 	result += newp->tm_yday - oldp->tm_yday;
    903       1.1       jtc 	result *= HOURSPERDAY;
    904       1.1       jtc 	result += newp->tm_hour - oldp->tm_hour;
    905       1.1       jtc 	result *= MINSPERHOUR;
    906       1.1       jtc 	result += newp->tm_min - oldp->tm_min;
    907       1.1       jtc 	result *= SECSPERMIN;
    908       1.1       jtc 	result += newp->tm_sec - oldp->tm_sec;
    909       1.1       jtc 	return result;
    910       1.1       jtc }
    911       1.1       jtc 
    912      1.36  christos #ifndef TM_GMTOFF
    913      1.36  christos /* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
    914      1.39  christos    Assume A and B differ by at most one year.  */
    915      1.36  christos static int
    916      1.36  christos adjusted_yday(struct tm const *a, struct tm const *b)
    917      1.36  christos {
    918      1.36  christos 	int yday = a->tm_yday;
    919      1.36  christos 	if (b->tm_year < a->tm_year)
    920      1.36  christos 		yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
    921      1.36  christos 	return yday;
    922      1.36  christos }
    923      1.36  christos #endif
    924      1.36  christos 
    925      1.36  christos /* If A is the broken-down local time and B the broken-down UTC for
    926      1.36  christos    the same instant, return A's UTC offset in seconds, where positive
    927  1.43.2.1  pgoyette    offsets are east of Greenwich.  On failure, return LONG_MIN.
    928  1.43.2.1  pgoyette 
    929  1.43.2.1  pgoyette    If T is nonnull, *T is the time stamp that corresponds to A; call
    930  1.43.2.1  pgoyette    my_gmtime_r and use its result instead of B.  Otherwise, B is the
    931  1.43.2.1  pgoyette    possibly nonnull result of an earlier call to my_gmtime_r.  */
    932      1.36  christos static long
    933  1.43.2.1  pgoyette gmtoff(struct tm const *a, time_t *t, struct tm const *b)
    934      1.36  christos {
    935      1.36  christos #ifdef TM_GMTOFF
    936      1.36  christos 	return a->TM_GMTOFF;
    937      1.36  christos #else
    938  1.43.2.1  pgoyette 	struct tm tm;
    939  1.43.2.1  pgoyette 	if (t)
    940  1.43.2.1  pgoyette 		b = my_gmtime_r(t, &tm);
    941      1.36  christos 	if (! b)
    942      1.36  christos 		return LONG_MIN;
    943      1.36  christos 	else {
    944      1.36  christos 		int ayday = adjusted_yday(a, b);
    945      1.36  christos 		int byday = adjusted_yday(b, a);
    946      1.36  christos 		int days = ayday - byday;
    947      1.36  christos 		long hours = a->tm_hour - b->tm_hour + 24 * days;
    948      1.36  christos 		long minutes = a->tm_min - b->tm_min + 60 * hours;
    949      1.36  christos 		long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
    950      1.36  christos 		return seconds;
    951      1.36  christos 	}
    952      1.36  christos #endif
    953      1.36  christos }
    954      1.36  christos 
    955       1.1       jtc static void
    956      1.36  christos show(timezone_t tz, char *zone, time_t t, bool v)
    957       1.1       jtc {
    958      1.26  christos 	struct tm *	tmp;
    959      1.36  christos 	struct tm *	gmtmp;
    960      1.36  christos 	struct tm tm, gmtm;
    961       1.1       jtc 
    962       1.5       jtc 	(void) printf("%-*s  ", (int) longest, zone);
    963       1.1       jtc 	if (v) {
    964      1.36  christos 		gmtmp = my_gmtime_r(&t, &gmtm);
    965      1.36  christos 		if (gmtmp == NULL) {
    966      1.36  christos 			printf(tformat(), t);
    967      1.17   mlelstv 		} else {
    968      1.36  christos 			dumptime(gmtmp);
    969      1.31  christos 			(void) printf(" UT");
    970      1.17   mlelstv 		}
    971      1.17   mlelstv 		(void) printf(" = ");
    972      1.17   mlelstv 	}
    973      1.36  christos 	tmp = my_localtime_rz(tz, &t, &tm);
    974      1.17   mlelstv 	dumptime(tmp);
    975      1.17   mlelstv 	if (tmp != NULL) {
    976      1.17   mlelstv 		if (*abbr(tmp) != '\0')
    977      1.17   mlelstv 			(void) printf(" %s", abbr(tmp));
    978      1.17   mlelstv 		if (v) {
    979  1.43.2.1  pgoyette 			long off = gmtoff(tmp, NULL,  gmtmp);
    980      1.17   mlelstv 			(void) printf(" isdst=%d", tmp->tm_isdst);
    981      1.36  christos 			if (off != LONG_MIN)
    982      1.36  christos 				(void) printf(" gmtoff=%ld", off);
    983      1.17   mlelstv 		}
    984       1.1       jtc 	}
    985       1.1       jtc 	(void) printf("\n");
    986      1.17   mlelstv 	if (tmp != NULL && *abbr(tmp) != '\0')
    987      1.17   mlelstv 		abbrok(abbr(tmp), zone);
    988       1.1       jtc }
    989       1.1       jtc 
    990  1.43.2.1  pgoyette /* Store into BUF, of size SIZE, a formatted local time taken from *TM.
    991  1.43.2.1  pgoyette    Use ISO 8601 format +HH:MM:SS.  Omit :SS if SS is zero, and omit
    992  1.43.2.1  pgoyette    :MM too if MM is also zero.
    993  1.43.2.1  pgoyette 
    994  1.43.2.1  pgoyette    Return the length of the resulting string.  If the string does not
    995  1.43.2.1  pgoyette    fit, return the length that the string would have been if it had
    996  1.43.2.1  pgoyette    fit; do not overrun the output buffer.  */
    997  1.43.2.1  pgoyette static int
    998  1.43.2.1  pgoyette format_local_time(char *buf, size_t size, struct tm const *tm)
    999  1.43.2.1  pgoyette {
   1000  1.43.2.1  pgoyette   int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
   1001  1.43.2.1  pgoyette   return (ss
   1002  1.43.2.1  pgoyette 	  ? snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
   1003  1.43.2.1  pgoyette 	  : mm
   1004  1.43.2.1  pgoyette 	  ? snprintf(buf, size, "%02d:%02d", hh, mm)
   1005  1.43.2.1  pgoyette 	  : snprintf(buf, size, "%02d", hh));
   1006  1.43.2.1  pgoyette }
   1007  1.43.2.1  pgoyette 
   1008  1.43.2.1  pgoyette /* Store into BUF, of size SIZE, a formatted UTC offset for the
   1009  1.43.2.1  pgoyette    localtime *TM corresponding to time T.  Use ISO 8601 format
   1010  1.43.2.1  pgoyette    +HH:MM:SS, or -HH:MM:SS for time stamps west of Greenwich.  Omit
   1011  1.43.2.1  pgoyette    :SS if :SS is zero, and omit :MM too if :MM is also zero.  If the
   1012  1.43.2.1  pgoyette    time stamp represents an unknown UTC offset, use the format -00.
   1013  1.43.2.1  pgoyette 
   1014  1.43.2.1  pgoyette    Return the length of the resulting string, or -1 if the result is
   1015  1.43.2.1  pgoyette    not representable as a string.  If the string does not fit, return
   1016  1.43.2.1  pgoyette    the length that the string would have been if it had fit; do not
   1017  1.43.2.1  pgoyette    overrun the output buffer.  */
   1018  1.43.2.1  pgoyette static int
   1019  1.43.2.1  pgoyette format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
   1020  1.43.2.1  pgoyette {
   1021  1.43.2.1  pgoyette   long off = gmtoff(tm, &t, NULL);
   1022  1.43.2.1  pgoyette   char sign = ((off < 0
   1023  1.43.2.1  pgoyette 		|| (off == 0
   1024  1.43.2.1  pgoyette 		    && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0)))
   1025  1.43.2.1  pgoyette 	       ? '-' : '+');
   1026  1.43.2.1  pgoyette   long hh;
   1027  1.43.2.1  pgoyette   int mm, ss;
   1028  1.43.2.1  pgoyette   if (off < 0)
   1029  1.43.2.1  pgoyette     {
   1030  1.43.2.1  pgoyette       if (off == LONG_MIN)
   1031  1.43.2.1  pgoyette 	return -1;
   1032  1.43.2.1  pgoyette       off = -off;
   1033  1.43.2.1  pgoyette     }
   1034  1.43.2.1  pgoyette   ss = off % 60;
   1035  1.43.2.1  pgoyette   mm = off / 60 % 60;
   1036  1.43.2.1  pgoyette   hh = off / 60 / 60;
   1037  1.43.2.1  pgoyette   return (ss
   1038  1.43.2.1  pgoyette 	  ? snprintf(buf, size, "%c%02ld:%02d:%02d", sign, hh, mm, ss)
   1039  1.43.2.1  pgoyette 	  : mm
   1040  1.43.2.1  pgoyette 	  ? snprintf(buf, size, "%c%02ld:%02d", sign, hh, mm)
   1041  1.43.2.1  pgoyette 	  : snprintf(buf, size, "%c%02ld", sign, hh));
   1042  1.43.2.1  pgoyette }
   1043  1.43.2.1  pgoyette 
   1044  1.43.2.1  pgoyette /* Store into BUF (of size SIZE) a quoted string representation of P.
   1045  1.43.2.1  pgoyette    If the representation's length is less than SIZE, return the
   1046  1.43.2.1  pgoyette    length; the representation is not null terminated.  Otherwise
   1047  1.43.2.1  pgoyette    return SIZE, to indicate that BUF is too small.  */
   1048  1.43.2.1  pgoyette static size_t
   1049  1.43.2.1  pgoyette format_quoted_string(char *buf, size_t size, char const *p)
   1050  1.43.2.1  pgoyette {
   1051  1.43.2.1  pgoyette   char *b = buf;
   1052  1.43.2.1  pgoyette   size_t s = size;
   1053  1.43.2.1  pgoyette   if (!s)
   1054  1.43.2.1  pgoyette     return size;
   1055  1.43.2.1  pgoyette   *b++ = '"', s--;
   1056  1.43.2.1  pgoyette   for (;;) {
   1057  1.43.2.1  pgoyette     char c = *p++;
   1058  1.43.2.1  pgoyette     if (s <= 1)
   1059  1.43.2.1  pgoyette       return size;
   1060  1.43.2.1  pgoyette     switch (c) {
   1061  1.43.2.1  pgoyette     default: *b++ = c, s--; continue;
   1062  1.43.2.1  pgoyette     case '\0': *b++ = '"', s--; return size - s;
   1063  1.43.2.1  pgoyette     case '"': case '\\': break;
   1064  1.43.2.1  pgoyette     case ' ': c = 's'; break;
   1065  1.43.2.1  pgoyette     case '\f': c = 'f'; break;
   1066  1.43.2.1  pgoyette     case '\n': c = 'n'; break;
   1067  1.43.2.1  pgoyette     case '\r': c = 'r'; break;
   1068  1.43.2.1  pgoyette     case '\t': c = 't'; break;
   1069  1.43.2.1  pgoyette     case '\v': c = 'v'; break;
   1070  1.43.2.1  pgoyette     }
   1071  1.43.2.1  pgoyette     *b++ = '\\', *b++ = c, s -= 2;
   1072  1.43.2.1  pgoyette   }
   1073  1.43.2.1  pgoyette }
   1074  1.43.2.1  pgoyette 
   1075  1.43.2.1  pgoyette /* Store into BUF (of size SIZE) a time stamp formatted by TIME_FMT.
   1076  1.43.2.1  pgoyette    TM is the broken-down time, T the seconds count, AB the time zone
   1077  1.43.2.1  pgoyette    abbreviation, and ZONE_NAME the zone name.  Return true if
   1078  1.43.2.1  pgoyette    successful, false if the output would require more than SIZE bytes.
   1079  1.43.2.1  pgoyette    TIME_FMT uses the same format that strftime uses, with these
   1080  1.43.2.1  pgoyette    additions:
   1081  1.43.2.1  pgoyette 
   1082  1.43.2.1  pgoyette    %f zone name
   1083  1.43.2.1  pgoyette    %L local time as per format_local_time
   1084  1.43.2.1  pgoyette    %Q like "U\t%Z\tD" where U is the UTC offset as for format_utc_offset
   1085  1.43.2.1  pgoyette       and D is the isdst flag; except omit D if it is zero, omit %Z if
   1086  1.43.2.1  pgoyette       it equals U, quote and escape %Z if it contains nonalphabetics,
   1087  1.43.2.1  pgoyette       and omit any trailing tabs.  */
   1088  1.43.2.1  pgoyette 
   1089  1.43.2.1  pgoyette static bool
   1090  1.43.2.1  pgoyette istrftime(char *buf, size_t size, char const *time_fmt,
   1091  1.43.2.1  pgoyette 	  struct tm const *tm, time_t t, char const *ab, char const *zone_name)
   1092  1.43.2.1  pgoyette {
   1093  1.43.2.1  pgoyette   char *b = buf;
   1094  1.43.2.1  pgoyette   size_t s = size;
   1095  1.43.2.1  pgoyette   char const *f = time_fmt, *p;
   1096  1.43.2.1  pgoyette 
   1097  1.43.2.1  pgoyette   for (p = f; ; p++)
   1098  1.43.2.1  pgoyette     if (*p == '%' && p[1] == '%')
   1099  1.43.2.1  pgoyette       p++;
   1100  1.43.2.1  pgoyette     else if (!*p
   1101  1.43.2.1  pgoyette 	     || (*p == '%'
   1102  1.43.2.1  pgoyette 		 && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
   1103  1.43.2.1  pgoyette       size_t formatted_len;
   1104  1.43.2.1  pgoyette       size_t f_prefix_len = p - f;
   1105  1.43.2.1  pgoyette       size_t f_prefix_copy_size = p - f + 2;
   1106  1.43.2.1  pgoyette       char fbuf[100];
   1107  1.43.2.1  pgoyette       bool oversized = sizeof fbuf <= f_prefix_copy_size;
   1108  1.43.2.1  pgoyette       char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
   1109  1.43.2.1  pgoyette       memcpy(f_prefix_copy, f, f_prefix_len);
   1110  1.43.2.1  pgoyette       strcpy(f_prefix_copy + f_prefix_len, "X");
   1111  1.43.2.1  pgoyette       formatted_len = strftime(b, s, f_prefix_copy, tm);
   1112  1.43.2.1  pgoyette       if (oversized)
   1113  1.43.2.1  pgoyette 	free(f_prefix_copy);
   1114  1.43.2.1  pgoyette       if (formatted_len == 0)
   1115  1.43.2.1  pgoyette 	return false;
   1116  1.43.2.1  pgoyette       formatted_len--;
   1117  1.43.2.1  pgoyette       b += formatted_len, s -= formatted_len;
   1118  1.43.2.1  pgoyette       if (!*p++)
   1119  1.43.2.1  pgoyette 	break;
   1120  1.43.2.1  pgoyette       switch (*p) {
   1121  1.43.2.1  pgoyette       case 'f':
   1122  1.43.2.1  pgoyette 	formatted_len = format_quoted_string(b, s, zone_name);
   1123  1.43.2.1  pgoyette 	break;
   1124  1.43.2.1  pgoyette       case 'L':
   1125  1.43.2.1  pgoyette 	formatted_len = format_local_time(b, s, tm);
   1126  1.43.2.1  pgoyette 	break;
   1127  1.43.2.1  pgoyette       case 'Q':
   1128  1.43.2.1  pgoyette 	{
   1129  1.43.2.1  pgoyette 	  bool show_abbr;
   1130  1.43.2.1  pgoyette 	  int offlen = format_utc_offset(b, s, tm, t);
   1131  1.43.2.1  pgoyette 	  if (! (0 <= offlen && (size_t)offlen < s))
   1132  1.43.2.1  pgoyette 	    return false;
   1133  1.43.2.1  pgoyette 	  show_abbr = strcmp(b, ab) != 0;
   1134  1.43.2.1  pgoyette 	  b += offlen, s -= offlen;
   1135  1.43.2.1  pgoyette 	  if (show_abbr) {
   1136  1.43.2.1  pgoyette 	    char const *abp;
   1137  1.43.2.1  pgoyette 	    size_t len;
   1138  1.43.2.1  pgoyette 	    if (s <= 1)
   1139  1.43.2.1  pgoyette 	      return false;
   1140  1.43.2.1  pgoyette 	    *b++ = '\t', s--;
   1141  1.43.2.1  pgoyette 	    for (abp = ab; is_alpha(*abp); abp++)
   1142  1.43.2.1  pgoyette 	      continue;
   1143  1.43.2.1  pgoyette 	    len = (!*abp && *ab
   1144  1.43.2.1  pgoyette 		   ? (size_t)snprintf(b, s, "%s", ab)
   1145  1.43.2.1  pgoyette 		   : format_quoted_string(b, s, ab));
   1146  1.43.2.1  pgoyette 	    if (s <= len)
   1147  1.43.2.1  pgoyette 	      return false;
   1148  1.43.2.1  pgoyette 	    b += len, s -= len;
   1149  1.43.2.1  pgoyette 	  }
   1150  1.43.2.1  pgoyette 	  formatted_len = (tm->tm_isdst
   1151  1.43.2.1  pgoyette 			   ? snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
   1152  1.43.2.1  pgoyette 			   : 0);
   1153  1.43.2.1  pgoyette 	}
   1154  1.43.2.1  pgoyette 	break;
   1155  1.43.2.1  pgoyette       }
   1156  1.43.2.1  pgoyette       if (! (formatted_len < s))
   1157  1.43.2.1  pgoyette 	return false;
   1158  1.43.2.1  pgoyette       b += formatted_len, s -= formatted_len;
   1159  1.43.2.1  pgoyette       f = p + 1;
   1160  1.43.2.1  pgoyette     }
   1161  1.43.2.1  pgoyette   *b = '\0';
   1162  1.43.2.1  pgoyette   return true;
   1163  1.43.2.1  pgoyette }
   1164  1.43.2.1  pgoyette 
   1165  1.43.2.1  pgoyette /* Show a time transition.  */
   1166  1.43.2.1  pgoyette static void
   1167  1.43.2.1  pgoyette showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
   1168  1.43.2.1  pgoyette 	  char const *zone_name)
   1169  1.43.2.1  pgoyette {
   1170  1.43.2.1  pgoyette   if (!tm) {
   1171  1.43.2.1  pgoyette     printf(tformat(), t);
   1172  1.43.2.1  pgoyette     putchar('\n');
   1173  1.43.2.1  pgoyette   } else {
   1174  1.43.2.1  pgoyette     char stackbuf[1000];
   1175  1.43.2.1  pgoyette     size_t size = sizeof stackbuf;
   1176  1.43.2.1  pgoyette     char *buf = stackbuf;
   1177  1.43.2.1  pgoyette     char *bufalloc = NULL;
   1178  1.43.2.1  pgoyette     while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
   1179  1.43.2.1  pgoyette       size = sumsize(size, size);
   1180  1.43.2.1  pgoyette       free(bufalloc);
   1181  1.43.2.1  pgoyette       buf = bufalloc = xmalloc(size);
   1182  1.43.2.1  pgoyette     }
   1183  1.43.2.1  pgoyette     puts(buf);
   1184  1.43.2.1  pgoyette     free(bufalloc);
   1185  1.43.2.1  pgoyette   }
   1186  1.43.2.1  pgoyette }
   1187  1.43.2.1  pgoyette 
   1188       1.9   mycroft static const char *
   1189      1.36  christos abbr(struct tm const *tmp)
   1190       1.1       jtc {
   1191      1.36  christos #ifdef TM_ZONE
   1192      1.36  christos 	return tmp->TM_ZONE;
   1193      1.36  christos #else
   1194      1.36  christos 	return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
   1195      1.36  christos 		? tzname[0 < tmp->tm_isdst]
   1196      1.36  christos 		: "");
   1197      1.36  christos #endif
   1198       1.1       jtc }
   1199      1.17   mlelstv 
   1200      1.17   mlelstv /*
   1201      1.17   mlelstv ** The code below can fail on certain theoretical systems;
   1202      1.17   mlelstv ** it works on all known real-world systems as of 2004-12-30.
   1203      1.17   mlelstv */
   1204      1.17   mlelstv 
   1205      1.17   mlelstv static const char *
   1206      1.17   mlelstv tformat(void)
   1207      1.17   mlelstv {
   1208      1.17   mlelstv 	if (0 > (time_t) -1) {		/* signed */
   1209      1.29  christos 		if (sizeof (time_t) == sizeof (intmax_t))
   1210      1.29  christos 			return "%"PRIdMAX;
   1211      1.17   mlelstv 		if (sizeof (time_t) > sizeof (long))
   1212      1.17   mlelstv 			return "%lld";
   1213      1.17   mlelstv 		if (sizeof (time_t) > sizeof (int))
   1214      1.17   mlelstv 			return "%ld";
   1215      1.17   mlelstv 		return "%d";
   1216      1.17   mlelstv 	}
   1217      1.29  christos #ifdef PRIuMAX
   1218      1.29  christos 	if (sizeof (time_t) == sizeof (uintmax_t))
   1219      1.29  christos 		return "%"PRIuMAX;
   1220      1.29  christos #endif
   1221      1.17   mlelstv 	if (sizeof (time_t) > sizeof (unsigned long))
   1222      1.17   mlelstv 		return "%llu";
   1223      1.17   mlelstv 	if (sizeof (time_t) > sizeof (unsigned int))
   1224      1.17   mlelstv 		return "%lu";
   1225      1.17   mlelstv 	return "%u";
   1226      1.17   mlelstv }
   1227      1.17   mlelstv 
   1228      1.17   mlelstv static void
   1229      1.26  christos dumptime(const struct tm *timeptr)
   1230      1.17   mlelstv {
   1231      1.17   mlelstv 	static const char	wday_name[][3] = {
   1232      1.17   mlelstv 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
   1233      1.17   mlelstv 	};
   1234      1.17   mlelstv 	static const char	mon_name[][3] = {
   1235      1.17   mlelstv 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
   1236      1.17   mlelstv 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
   1237      1.17   mlelstv 	};
   1238      1.26  christos 	const char *	wn;
   1239      1.26  christos 	const char *	mn;
   1240      1.26  christos 	int		lead;
   1241      1.26  christos 	int		trail;
   1242      1.17   mlelstv 
   1243      1.17   mlelstv 	if (timeptr == NULL) {
   1244      1.36  christos 		printf("NULL");
   1245      1.17   mlelstv 		return;
   1246      1.17   mlelstv 	}
   1247      1.17   mlelstv 	/*
   1248      1.36  christos 	** The packaged localtime_rz and gmtime_r never put out-of-range
   1249      1.17   mlelstv 	** values in tm_wday or tm_mon, but since this code might be compiled
   1250      1.17   mlelstv 	** with other (perhaps experimental) versions, paranoia is in order.
   1251      1.17   mlelstv 	*/
   1252      1.17   mlelstv 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
   1253      1.17   mlelstv 		(int) (sizeof wday_name / sizeof wday_name[0]))
   1254      1.17   mlelstv 			wn = "???";
   1255      1.17   mlelstv 	else		wn = wday_name[timeptr->tm_wday];
   1256      1.17   mlelstv 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
   1257      1.17   mlelstv 		(int) (sizeof mon_name / sizeof mon_name[0]))
   1258      1.17   mlelstv 			mn = "???";
   1259      1.17   mlelstv 	else		mn = mon_name[timeptr->tm_mon];
   1260      1.36  christos 	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
   1261      1.17   mlelstv 		wn, mn,
   1262      1.17   mlelstv 		timeptr->tm_mday, timeptr->tm_hour,
   1263      1.17   mlelstv 		timeptr->tm_min, timeptr->tm_sec);
   1264      1.17   mlelstv #define DIVISOR	10
   1265      1.17   mlelstv 	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
   1266      1.17   mlelstv 	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
   1267      1.17   mlelstv 		trail / DIVISOR;
   1268      1.17   mlelstv 	trail %= DIVISOR;
   1269      1.17   mlelstv 	if (trail < 0 && lead > 0) {
   1270      1.17   mlelstv 		trail += DIVISOR;
   1271      1.17   mlelstv 		--lead;
   1272      1.17   mlelstv 	} else if (lead < 0 && trail > 0) {
   1273      1.17   mlelstv 		trail -= DIVISOR;
   1274      1.17   mlelstv 		++lead;
   1275      1.17   mlelstv 	}
   1276      1.17   mlelstv 	if (lead == 0)
   1277      1.36  christos 		printf("%d", trail);
   1278      1.36  christos 	else	printf("%d%d", lead, ((trail < 0) ? -trail : trail));
   1279      1.17   mlelstv }
   1280