xref: /minix/lib/libc/time/zdump.c (revision 0a6a1f1d)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: zdump.c,v 1.42 2015/08/13 11:21:18 christos Exp $	*/
22fe8fb19SBen Gras /*
32fe8fb19SBen Gras ** This file is in the public domain, so clarified as of
42fe8fb19SBen Gras ** 2009-05-17 by Arthur David Olson.
52fe8fb19SBen Gras */
62fe8fb19SBen Gras 
72fe8fb19SBen Gras #include <sys/cdefs.h>
82fe8fb19SBen Gras #ifndef lint
9*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: zdump.c,v 1.42 2015/08/13 11:21:18 christos Exp $");
102fe8fb19SBen Gras #endif /* !defined lint */
112fe8fb19SBen Gras 
122fe8fb19SBen Gras /*
132fe8fb19SBen Gras ** This code has been made independent of the rest of the time
142fe8fb19SBen Gras ** conversion package to increase confidence in the verification it provides.
152fe8fb19SBen Gras ** You can use this code to help in verifying other implementations.
1684d9c625SLionel Sambuc **
17*0a6a1f1dSLionel Sambuc ** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
182fe8fb19SBen Gras */
192fe8fb19SBen Gras 
20*0a6a1f1dSLionel Sambuc #ifndef NETBSD_INSPIRED
21*0a6a1f1dSLionel Sambuc # define NETBSD_INSPIRED 1
22*0a6a1f1dSLionel Sambuc #endif
23*0a6a1f1dSLionel Sambuc #ifndef USE_LTZ
24*0a6a1f1dSLionel Sambuc # define USE_LTZ 1
25*0a6a1f1dSLionel Sambuc #endif
26*0a6a1f1dSLionel Sambuc 
27*0a6a1f1dSLionel Sambuc #if USE_LTZ
2884d9c625SLionel Sambuc #include "private.h"
29*0a6a1f1dSLionel Sambuc #endif
30*0a6a1f1dSLionel Sambuc 
31*0a6a1f1dSLionel Sambuc /* Enable tm_gmtoff and tm_zone on GNUish systems.  */
32*0a6a1f1dSLionel Sambuc #define _GNU_SOURCE 1
33*0a6a1f1dSLionel Sambuc /* Enable strtoimax on Solaris 10.  */
34*0a6a1f1dSLionel Sambuc #define __EXTENSIONS__ 1
3584d9c625SLionel Sambuc 
362fe8fb19SBen Gras #include "stdio.h"	/* for stdout, stderr */
372fe8fb19SBen Gras #include "string.h"	/* for strcpy */
382fe8fb19SBen Gras #include "sys/types.h"	/* for time_t */
392fe8fb19SBen Gras #include "time.h"	/* for struct tm */
402fe8fb19SBen Gras #include "stdlib.h"	/* for exit, malloc, atoi */
41*0a6a1f1dSLionel Sambuc #include "limits.h"	/* for CHAR_BIT, LLONG_MAX */
42*0a6a1f1dSLionel Sambuc #include <errno.h>
432fe8fb19SBen Gras #include <err.h>
442fe8fb19SBen Gras 
4584d9c625SLionel Sambuc /*
4684d9c625SLionel Sambuc ** Substitutes for pre-C99 compilers.
4784d9c625SLionel Sambuc ** Much of this section of code is stolen from private.h.
4884d9c625SLionel Sambuc */
4984d9c625SLionel Sambuc 
5084d9c625SLionel Sambuc #ifndef HAVE_STDINT_H
5184d9c625SLionel Sambuc # define HAVE_STDINT_H \
52*0a6a1f1dSLionel Sambuc     (199901 <= __STDC_VERSION__ \
53*0a6a1f1dSLionel Sambuc      || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__)	\
54*0a6a1f1dSLionel Sambuc      || __CYGWIN__)
5584d9c625SLionel Sambuc #endif
5684d9c625SLionel Sambuc #if HAVE_STDINT_H
5784d9c625SLionel Sambuc # include "stdint.h"
5884d9c625SLionel Sambuc #endif
5984d9c625SLionel Sambuc #ifndef HAVE_INTTYPES_H
6084d9c625SLionel Sambuc # define HAVE_INTTYPES_H HAVE_STDINT_H
6184d9c625SLionel Sambuc #endif
6284d9c625SLionel Sambuc #if HAVE_INTTYPES_H
6384d9c625SLionel Sambuc # include <inttypes.h>
6484d9c625SLionel Sambuc #endif
6584d9c625SLionel Sambuc 
6684d9c625SLionel Sambuc #ifndef INT_FAST32_MAX
6784d9c625SLionel Sambuc # if INT_MAX >> 31 == 0
6884d9c625SLionel Sambuc typedef long int_fast32_t;
6984d9c625SLionel Sambuc # else
7084d9c625SLionel Sambuc typedef int int_fast32_t;
7184d9c625SLionel Sambuc # endif
7284d9c625SLionel Sambuc #endif
7384d9c625SLionel Sambuc 
74*0a6a1f1dSLionel Sambuc /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
75*0a6a1f1dSLionel Sambuc #if !defined LLONG_MAX && defined __LONG_LONG_MAX__
76*0a6a1f1dSLionel Sambuc # define LLONG_MAX __LONG_LONG_MAX__
77*0a6a1f1dSLionel Sambuc #endif
78*0a6a1f1dSLionel Sambuc 
7984d9c625SLionel Sambuc #ifndef INTMAX_MAX
8084d9c625SLionel Sambuc # ifdef LLONG_MAX
81*0a6a1f1dSLionel Sambuc typedef long long intmax_t;
82*0a6a1f1dSLionel Sambuc #  define strtoimax strtoll
8384d9c625SLionel Sambuc #  define INTMAX_MAX LLONG_MAX
8484d9c625SLionel Sambuc # else
8584d9c625SLionel Sambuc typedef long intmax_t;
86*0a6a1f1dSLionel Sambuc #  define strtoimax strtol
8784d9c625SLionel Sambuc #  define INTMAX_MAX LONG_MAX
8884d9c625SLionel Sambuc # endif
8984d9c625SLionel Sambuc #endif
90*0a6a1f1dSLionel Sambuc 
91*0a6a1f1dSLionel Sambuc #ifndef PRIdMAX
92*0a6a1f1dSLionel Sambuc # if INTMAX_MAX == LLONG_MAX
93*0a6a1f1dSLionel Sambuc #  define PRIdMAX "lld"
94*0a6a1f1dSLionel Sambuc # else
95*0a6a1f1dSLionel Sambuc #  define PRIdMAX "ld"
96*0a6a1f1dSLionel Sambuc # endif
9784d9c625SLionel Sambuc #endif
9884d9c625SLionel Sambuc 
99*0a6a1f1dSLionel Sambuc /* Infer TM_ZONE on systems where this information is known, but suppress
100*0a6a1f1dSLionel Sambuc    guessing if NO_TM_ZONE is defined.  Similarly for TM_GMTOFF.  */
101*0a6a1f1dSLionel Sambuc #if (defined __GLIBC__ \
102*0a6a1f1dSLionel Sambuc      || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
103*0a6a1f1dSLionel Sambuc      || (defined __APPLE__ && defined __MACH__))
104*0a6a1f1dSLionel Sambuc # if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
105*0a6a1f1dSLionel Sambuc #  define TM_GMTOFF tm_gmtoff
106*0a6a1f1dSLionel Sambuc # endif
107*0a6a1f1dSLionel Sambuc # if !defined TM_ZONE && !defined NO_TM_ZONE
108*0a6a1f1dSLionel Sambuc #  define TM_ZONE tm_zone
109*0a6a1f1dSLionel Sambuc # endif
110*0a6a1f1dSLionel Sambuc #endif
111*0a6a1f1dSLionel Sambuc 
112*0a6a1f1dSLionel Sambuc #ifndef HAVE_LOCALTIME_R
113*0a6a1f1dSLionel Sambuc # define HAVE_LOCALTIME_R 1
114*0a6a1f1dSLionel Sambuc #endif
115*0a6a1f1dSLionel Sambuc 
116*0a6a1f1dSLionel Sambuc #ifndef HAVE_LOCALTIME_RZ
117*0a6a1f1dSLionel Sambuc # ifdef TM_ZONE
118*0a6a1f1dSLionel Sambuc #  define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
119*0a6a1f1dSLionel Sambuc # else
120*0a6a1f1dSLionel Sambuc #  define HAVE_LOCALTIME_RZ 0
121*0a6a1f1dSLionel Sambuc # endif
122*0a6a1f1dSLionel Sambuc #endif
123*0a6a1f1dSLionel Sambuc 
124*0a6a1f1dSLionel Sambuc #ifndef HAVE_TZSET
125*0a6a1f1dSLionel Sambuc # define HAVE_TZSET 1
126*0a6a1f1dSLionel Sambuc #endif
12784d9c625SLionel Sambuc 
1282fe8fb19SBen Gras #ifndef ZDUMP_LO_YEAR
1292fe8fb19SBen Gras #define ZDUMP_LO_YEAR	(-500)
1302fe8fb19SBen Gras #endif /* !defined ZDUMP_LO_YEAR */
1312fe8fb19SBen Gras 
1322fe8fb19SBen Gras #ifndef ZDUMP_HI_YEAR
1332fe8fb19SBen Gras #define ZDUMP_HI_YEAR	2500
1342fe8fb19SBen Gras #endif /* !defined ZDUMP_HI_YEAR */
1352fe8fb19SBen Gras 
1362fe8fb19SBen Gras #ifndef MAX_STRING_LENGTH
1372fe8fb19SBen Gras #define MAX_STRING_LENGTH	1024
1382fe8fb19SBen Gras #endif /* !defined MAX_STRING_LENGTH */
1392fe8fb19SBen Gras 
140*0a6a1f1dSLionel Sambuc #if __STDC_VERSION__ < 199901
141*0a6a1f1dSLionel Sambuc # define true 1
142*0a6a1f1dSLionel Sambuc # define false 0
143*0a6a1f1dSLionel Sambuc # define bool int
144*0a6a1f1dSLionel Sambuc #else
145*0a6a1f1dSLionel Sambuc # include <stdbool.h>
146*0a6a1f1dSLionel Sambuc #endif
1472fe8fb19SBen Gras 
1482fe8fb19SBen Gras #ifndef EXIT_SUCCESS
1492fe8fb19SBen Gras #define EXIT_SUCCESS	0
1502fe8fb19SBen Gras #endif /* !defined EXIT_SUCCESS */
1512fe8fb19SBen Gras 
1522fe8fb19SBen Gras #ifndef EXIT_FAILURE
1532fe8fb19SBen Gras #define EXIT_FAILURE	1
1542fe8fb19SBen Gras #endif /* !defined EXIT_FAILURE */
1552fe8fb19SBen Gras 
1562fe8fb19SBen Gras #ifndef SECSPERMIN
1572fe8fb19SBen Gras #define SECSPERMIN	60
1582fe8fb19SBen Gras #endif /* !defined SECSPERMIN */
1592fe8fb19SBen Gras 
1602fe8fb19SBen Gras #ifndef MINSPERHOUR
1612fe8fb19SBen Gras #define MINSPERHOUR	60
1622fe8fb19SBen Gras #endif /* !defined MINSPERHOUR */
1632fe8fb19SBen Gras 
1642fe8fb19SBen Gras #ifndef SECSPERHOUR
1652fe8fb19SBen Gras #define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
1662fe8fb19SBen Gras #endif /* !defined SECSPERHOUR */
1672fe8fb19SBen Gras 
1682fe8fb19SBen Gras #ifndef HOURSPERDAY
1692fe8fb19SBen Gras #define HOURSPERDAY	24
1702fe8fb19SBen Gras #endif /* !defined HOURSPERDAY */
1712fe8fb19SBen Gras 
1722fe8fb19SBen Gras #ifndef EPOCH_YEAR
1732fe8fb19SBen Gras #define EPOCH_YEAR	1970
1742fe8fb19SBen Gras #endif /* !defined EPOCH_YEAR */
1752fe8fb19SBen Gras 
1762fe8fb19SBen Gras #ifndef TM_YEAR_BASE
1772fe8fb19SBen Gras #define TM_YEAR_BASE	1900
1782fe8fb19SBen Gras #endif /* !defined TM_YEAR_BASE */
1792fe8fb19SBen Gras 
1802fe8fb19SBen Gras #ifndef DAYSPERNYEAR
1812fe8fb19SBen Gras #define DAYSPERNYEAR	365
1822fe8fb19SBen Gras #endif /* !defined DAYSPERNYEAR */
1832fe8fb19SBen Gras 
1842fe8fb19SBen Gras #ifndef isleap
1852fe8fb19SBen Gras #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
1862fe8fb19SBen Gras #endif /* !defined isleap */
1872fe8fb19SBen Gras 
1882fe8fb19SBen Gras #ifndef isleap_sum
1892fe8fb19SBen Gras /*
1902fe8fb19SBen Gras ** See tzfile.h for details on isleap_sum.
1912fe8fb19SBen Gras */
1922fe8fb19SBen Gras #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
1932fe8fb19SBen Gras #endif /* !defined isleap_sum */
1942fe8fb19SBen Gras 
19584d9c625SLionel Sambuc #define SECSPERDAY	((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
1962fe8fb19SBen Gras #define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
1972fe8fb19SBen Gras #define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
19884d9c625SLionel Sambuc #define SECSPER400YEARS	(SECSPERNYEAR * (intmax_t) (300 + 3)	\
19984d9c625SLionel Sambuc 			 + SECSPERLYEAR * (intmax_t) (100 - 3))
20084d9c625SLionel Sambuc 
20184d9c625SLionel Sambuc /*
20284d9c625SLionel Sambuc ** True if SECSPER400YEARS is known to be representable as an
20384d9c625SLionel Sambuc ** intmax_t.  It's OK that SECSPER400YEARS_FITS can in theory be false
20484d9c625SLionel Sambuc ** even if SECSPER400YEARS is representable, because when that happens
20584d9c625SLionel Sambuc ** the code merely runs a bit more slowly, and this slowness doesn't
20684d9c625SLionel Sambuc ** occur on any practical platform.
20784d9c625SLionel Sambuc */
20884d9c625SLionel Sambuc enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
2092fe8fb19SBen Gras 
2102fe8fb19SBen Gras #ifndef HAVE_GETTEXT
2112fe8fb19SBen Gras #define HAVE_GETTEXT 0
2122fe8fb19SBen Gras #endif
2132fe8fb19SBen Gras #if HAVE_GETTEXT
2142fe8fb19SBen Gras #include "locale.h"	/* for setlocale */
2152fe8fb19SBen Gras #include "libintl.h"
2162fe8fb19SBen Gras #endif /* HAVE_GETTEXT */
2172fe8fb19SBen Gras 
21884d9c625SLionel Sambuc #ifndef ATTRIBUTE_PURE
21984d9c625SLionel Sambuc #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
22084d9c625SLionel Sambuc # define ATTRIBUTE_PURE __attribute__ ((ATTRIBUTE_PURE__))
22184d9c625SLionel Sambuc #else
22284d9c625SLionel Sambuc # define ATTRIBUTE_PURE /* empty */
22384d9c625SLionel Sambuc #endif
22484d9c625SLionel Sambuc #endif
22584d9c625SLionel Sambuc 
2262fe8fb19SBen Gras #ifndef INITIALIZE
227*0a6a1f1dSLionel Sambuc #if defined(__GNUC__) || defined(__lint__)
2282fe8fb19SBen Gras #define INITIALIZE(x)	((x) = 0)
229*0a6a1f1dSLionel Sambuc #else /* !defined GNUC || lint */
2302fe8fb19SBen Gras #define INITIALIZE(x)
231*0a6a1f1dSLionel Sambuc #endif /* !defined GNUC || lint */
2322fe8fb19SBen Gras #endif /* !defined INITIALIZE */
2332fe8fb19SBen Gras 
2342fe8fb19SBen Gras /*
2352fe8fb19SBen Gras ** For the benefit of GNU folk...
236*0a6a1f1dSLionel Sambuc ** '_(MSGID)' uses the current locale's message library string for MSGID.
2372fe8fb19SBen Gras ** The default is to use gettext if available, and use MSGID otherwise.
2382fe8fb19SBen Gras */
2392fe8fb19SBen Gras 
2402fe8fb19SBen Gras #ifndef _
2412fe8fb19SBen Gras #if HAVE_GETTEXT
2422fe8fb19SBen Gras #define _(msgid) gettext(msgid)
2432fe8fb19SBen Gras #else /* !HAVE_GETTEXT */
244f14fb602SLionel Sambuc #define _(msgid) msgid
2452fe8fb19SBen Gras #endif /* !HAVE_GETTEXT */
2462fe8fb19SBen Gras #endif /* !defined _ */
2472fe8fb19SBen Gras 
248*0a6a1f1dSLionel Sambuc #if !defined TZ_DOMAIN && defined HAVE_GETTEXT
2492fe8fb19SBen Gras # define TZ_DOMAIN "tz"
250*0a6a1f1dSLionel Sambuc #endif
251*0a6a1f1dSLionel Sambuc 
252*0a6a1f1dSLionel Sambuc #if ! HAVE_LOCALTIME_RZ
253*0a6a1f1dSLionel Sambuc # undef  timezone_t
254*0a6a1f1dSLionel Sambuc # define timezone_t char **
255*0a6a1f1dSLionel Sambuc #endif
2562fe8fb19SBen Gras 
2572fe8fb19SBen Gras extern char **	environ;
2582fe8fb19SBen Gras extern int	getopt(int argc, char * const argv[],
2592fe8fb19SBen Gras 			const char * options);
2602fe8fb19SBen Gras extern char *	optarg;
2612fe8fb19SBen Gras extern int	optind;
2622fe8fb19SBen Gras 
26384d9c625SLionel Sambuc /* The minimum and maximum finite time values.  */
264*0a6a1f1dSLionel Sambuc enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
26584d9c625SLionel Sambuc static time_t	absolute_min_time =
26684d9c625SLionel Sambuc   ((time_t) -1 < 0
267*0a6a1f1dSLionel Sambuc     ? (- ((time_t) ~ (time_t) 0 < 0)
268*0a6a1f1dSLionel Sambuc        - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
26984d9c625SLionel Sambuc     : 0);
27084d9c625SLionel Sambuc static time_t	absolute_max_time =
27184d9c625SLionel Sambuc   ((time_t) -1 < 0
272*0a6a1f1dSLionel Sambuc     ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
27384d9c625SLionel Sambuc    : -1);
2742fe8fb19SBen Gras static size_t	longest;
2752fe8fb19SBen Gras static char *	progname;
276*0a6a1f1dSLionel Sambuc static bool	warned;
277*0a6a1f1dSLionel Sambuc static bool	errout;
2782fe8fb19SBen Gras 
279*0a6a1f1dSLionel Sambuc static char const *abbr(struct tm const *);
280*0a6a1f1dSLionel Sambuc static intmax_t	delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
281*0a6a1f1dSLionel Sambuc static void dumptime(struct tm const *);
282*0a6a1f1dSLionel Sambuc static time_t hunt(timezone_t, char *, time_t, time_t);
283*0a6a1f1dSLionel Sambuc static void show(timezone_t, char *, time_t, bool);
2842fe8fb19SBen Gras static const char *tformat(void);
285*0a6a1f1dSLionel Sambuc static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
286*0a6a1f1dSLionel Sambuc 
287*0a6a1f1dSLionel Sambuc /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
288*0a6a1f1dSLionel Sambuc #define is_digit(c) ((unsigned)(c) - '0' <= 9)
289*0a6a1f1dSLionel Sambuc 
290*0a6a1f1dSLionel Sambuc /* Is A an alphabetic character in the C locale?  */
291*0a6a1f1dSLionel Sambuc static bool
is_alpha(char a)292*0a6a1f1dSLionel Sambuc is_alpha(char a)
293*0a6a1f1dSLionel Sambuc {
294*0a6a1f1dSLionel Sambuc 	switch (a) {
295*0a6a1f1dSLionel Sambuc 	  default:
296*0a6a1f1dSLionel Sambuc 		return false;
297*0a6a1f1dSLionel Sambuc 	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
298*0a6a1f1dSLionel Sambuc 	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
299*0a6a1f1dSLionel Sambuc 	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
300*0a6a1f1dSLionel Sambuc 	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
301*0a6a1f1dSLionel Sambuc 	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
302*0a6a1f1dSLionel Sambuc 	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
303*0a6a1f1dSLionel Sambuc 	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
304*0a6a1f1dSLionel Sambuc 	  case 'v': case 'w': case 'x': case 'y': case 'z':
305*0a6a1f1dSLionel Sambuc 	  	return true;
306*0a6a1f1dSLionel Sambuc 	}
307*0a6a1f1dSLionel Sambuc }
308*0a6a1f1dSLionel Sambuc 
309*0a6a1f1dSLionel Sambuc /* Return A + B, exiting if the result would overflow.  */
310*0a6a1f1dSLionel Sambuc static size_t
sumsize(size_t a,size_t b)311*0a6a1f1dSLionel Sambuc sumsize(size_t a, size_t b)
312*0a6a1f1dSLionel Sambuc {
313*0a6a1f1dSLionel Sambuc 	size_t sum = a + b;
314*0a6a1f1dSLionel Sambuc 	if (sum < a)
315*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "size overflow");
316*0a6a1f1dSLionel Sambuc 	return sum;
317*0a6a1f1dSLionel Sambuc }
318*0a6a1f1dSLionel Sambuc 
319*0a6a1f1dSLionel Sambuc #if ! HAVE_TZSET
320*0a6a1f1dSLionel Sambuc # undef tzset
321*0a6a1f1dSLionel Sambuc # define tzset zdump_tzset
tzset(void)322*0a6a1f1dSLionel Sambuc static void tzset(void) { }
323*0a6a1f1dSLionel Sambuc #endif
324*0a6a1f1dSLionel Sambuc 
325*0a6a1f1dSLionel Sambuc /* Assume gmtime_r works if localtime_r does.
326*0a6a1f1dSLionel Sambuc    A replacement localtime_r is defined below if needed.  */
327*0a6a1f1dSLionel Sambuc #if ! HAVE_LOCALTIME_R
328*0a6a1f1dSLionel Sambuc 
329*0a6a1f1dSLionel Sambuc # undef gmtime_r
330*0a6a1f1dSLionel Sambuc # define gmtime_r zdump_gmtime_r
331*0a6a1f1dSLionel Sambuc 
332*0a6a1f1dSLionel Sambuc static struct tm *
gmtime_r(time_t * tp,struct tm * tmp)333*0a6a1f1dSLionel Sambuc gmtime_r(time_t *tp, struct tm *tmp)
334*0a6a1f1dSLionel Sambuc {
335*0a6a1f1dSLionel Sambuc 	struct tm *r = gmtime(tp);
336*0a6a1f1dSLionel Sambuc 	if (r) {
337*0a6a1f1dSLionel Sambuc 		*tmp = *r;
338*0a6a1f1dSLionel Sambuc 		r = tmp;
339*0a6a1f1dSLionel Sambuc 	}
340*0a6a1f1dSLionel Sambuc 	return r;
341*0a6a1f1dSLionel Sambuc }
342*0a6a1f1dSLionel Sambuc 
343*0a6a1f1dSLionel Sambuc #endif
344*0a6a1f1dSLionel Sambuc 
345*0a6a1f1dSLionel Sambuc /* Platforms with TM_ZONE don't need tzname, so they can use the
346*0a6a1f1dSLionel Sambuc    faster localtime_rz or localtime_r if available.  */
347*0a6a1f1dSLionel Sambuc 
348*0a6a1f1dSLionel Sambuc #if defined TM_ZONE && HAVE_LOCALTIME_RZ
349*0a6a1f1dSLionel Sambuc # define USE_LOCALTIME_RZ true
350*0a6a1f1dSLionel Sambuc #else
351*0a6a1f1dSLionel Sambuc # define USE_LOCALTIME_RZ false
352*0a6a1f1dSLionel Sambuc #endif
353*0a6a1f1dSLionel Sambuc 
354*0a6a1f1dSLionel Sambuc #if ! USE_LOCALTIME_RZ
355*0a6a1f1dSLionel Sambuc 
356*0a6a1f1dSLionel Sambuc # if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
357*0a6a1f1dSLionel Sambuc #  undef localtime_r
358*0a6a1f1dSLionel Sambuc #  define localtime_r zdump_localtime_r
359*0a6a1f1dSLionel Sambuc static struct tm *
localtime_r(time_t * tp,struct tm * tmp)360*0a6a1f1dSLionel Sambuc localtime_r(time_t *tp, struct tm *tmp)
361*0a6a1f1dSLionel Sambuc {
362*0a6a1f1dSLionel Sambuc 	struct tm *r = localtime(tp);
363*0a6a1f1dSLionel Sambuc 	if (r) {
364*0a6a1f1dSLionel Sambuc 		*tmp = *r;
365*0a6a1f1dSLionel Sambuc 		r = tmp;
366*0a6a1f1dSLionel Sambuc 	}
367*0a6a1f1dSLionel Sambuc 	return r;
368*0a6a1f1dSLionel Sambuc }
369*0a6a1f1dSLionel Sambuc # endif
370*0a6a1f1dSLionel Sambuc 
371*0a6a1f1dSLionel Sambuc # undef localtime_rz
372*0a6a1f1dSLionel Sambuc # define localtime_rz zdump_localtime_rz
373*0a6a1f1dSLionel Sambuc static struct tm *
localtime_rz(timezone_t rz,time_t * tp,struct tm * tmp)374*0a6a1f1dSLionel Sambuc localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp)
375*0a6a1f1dSLionel Sambuc {
376*0a6a1f1dSLionel Sambuc 	return localtime_r(tp, tmp);
377*0a6a1f1dSLionel Sambuc }
378*0a6a1f1dSLionel Sambuc 
379*0a6a1f1dSLionel Sambuc # ifdef TYPECHECK
380*0a6a1f1dSLionel Sambuc #  undef mktime_z
381*0a6a1f1dSLionel Sambuc #  define mktime_z zdump_mktime_z
382*0a6a1f1dSLionel Sambuc static time_t
mktime_z(timezone_t tz,struct tm * tmp)383*0a6a1f1dSLionel Sambuc mktime_z(timezone_t tz, struct tm *tmp)
384*0a6a1f1dSLionel Sambuc {
385*0a6a1f1dSLionel Sambuc 	return mktime(tmp);
386*0a6a1f1dSLionel Sambuc }
387*0a6a1f1dSLionel Sambuc # endif
388*0a6a1f1dSLionel Sambuc 
389*0a6a1f1dSLionel Sambuc # undef tzalloc
390*0a6a1f1dSLionel Sambuc # undef tzfree
391*0a6a1f1dSLionel Sambuc # define tzalloc zdump_tzalloc
392*0a6a1f1dSLionel Sambuc # define tzfree zdump_tzfree
393*0a6a1f1dSLionel Sambuc 
394*0a6a1f1dSLionel Sambuc static timezone_t
tzalloc(char const * val)395*0a6a1f1dSLionel Sambuc tzalloc(char const *val)
396*0a6a1f1dSLionel Sambuc {
397*0a6a1f1dSLionel Sambuc 	static char **fakeenv;
398*0a6a1f1dSLionel Sambuc 	char **env = fakeenv;
399*0a6a1f1dSLionel Sambuc 	char *env0;
400*0a6a1f1dSLionel Sambuc 	if (! env) {
401*0a6a1f1dSLionel Sambuc 		char **e = environ;
402*0a6a1f1dSLionel Sambuc 		int to;
403*0a6a1f1dSLionel Sambuc 
404*0a6a1f1dSLionel Sambuc 		while (*e++)
405*0a6a1f1dSLionel Sambuc 			continue;
406*0a6a1f1dSLionel Sambuc 		env = malloc(sumsize(sizeof *environ,
407*0a6a1f1dSLionel Sambuc 		    (e - environ) * sizeof *environ));
408*0a6a1f1dSLionel Sambuc 		if (! env) {
409*0a6a1f1dSLionel Sambuc 			err(EXIT_FAILURE, "malloc");
410*0a6a1f1dSLionel Sambuc 		}
411*0a6a1f1dSLionel Sambuc 		to = 1;
412*0a6a1f1dSLionel Sambuc 		for (e = environ; (env[to] = *e); e++)
413*0a6a1f1dSLionel Sambuc 			to += strncmp(*e, "TZ=", 3) != 0;
414*0a6a1f1dSLionel Sambuc 	}
415*0a6a1f1dSLionel Sambuc 	env0 = malloc(sumsize(sizeof "TZ=", strlen(val)));
416*0a6a1f1dSLionel Sambuc 	if (! env0) {
417*0a6a1f1dSLionel Sambuc 		err(EXIT_FAILURE, "malloc");
418*0a6a1f1dSLionel Sambuc 	}
419*0a6a1f1dSLionel Sambuc 	env[0] = strcat(strcpy(env0, "TZ="), val);
420*0a6a1f1dSLionel Sambuc 	environ = fakeenv = env;
421*0a6a1f1dSLionel Sambuc 	tzset();
422*0a6a1f1dSLionel Sambuc 	return env;
423*0a6a1f1dSLionel Sambuc }
424*0a6a1f1dSLionel Sambuc 
425*0a6a1f1dSLionel Sambuc static void
tzfree(timezone_t env)426*0a6a1f1dSLionel Sambuc tzfree(timezone_t env)
427*0a6a1f1dSLionel Sambuc {
428*0a6a1f1dSLionel Sambuc 	environ = env + 1;
429*0a6a1f1dSLionel Sambuc 	free(env[0]);
430*0a6a1f1dSLionel Sambuc }
431*0a6a1f1dSLionel Sambuc #endif /* ! USE_LOCALTIME_RZ */
432*0a6a1f1dSLionel Sambuc 
433*0a6a1f1dSLionel Sambuc /* A UTC time zone, and its initializer.  */
434*0a6a1f1dSLionel Sambuc static timezone_t gmtz;
435*0a6a1f1dSLionel Sambuc static void
gmtzinit(void)436*0a6a1f1dSLionel Sambuc gmtzinit(void)
437*0a6a1f1dSLionel Sambuc {
438*0a6a1f1dSLionel Sambuc 	if (USE_LOCALTIME_RZ) {
439*0a6a1f1dSLionel Sambuc 		static char const utc[] = "UTC0";
440*0a6a1f1dSLionel Sambuc 		gmtz = tzalloc(utc);
441*0a6a1f1dSLionel Sambuc 		if (!gmtz) {
442*0a6a1f1dSLionel Sambuc 		      err(EXIT_FAILURE, "Cannot create %s", utc);
443*0a6a1f1dSLionel Sambuc 		}
444*0a6a1f1dSLionel Sambuc 	}
445*0a6a1f1dSLionel Sambuc }
446*0a6a1f1dSLionel Sambuc 
447*0a6a1f1dSLionel Sambuc /* Convert *TP to UTC, storing the broken-down time into *TMP.
448*0a6a1f1dSLionel Sambuc    Return TMP if successful, NULL otherwise.  This is like gmtime_r(TP, TMP),
449*0a6a1f1dSLionel Sambuc    except typically faster if USE_LOCALTIME_RZ.  */
450*0a6a1f1dSLionel Sambuc static struct tm *
my_gmtime_r(time_t * tp,struct tm * tmp)451*0a6a1f1dSLionel Sambuc my_gmtime_r(time_t *tp, struct tm *tmp)
452*0a6a1f1dSLionel Sambuc {
453*0a6a1f1dSLionel Sambuc 	return USE_LOCALTIME_RZ ?
454*0a6a1f1dSLionel Sambuc 	    localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
455*0a6a1f1dSLionel Sambuc }
4562fe8fb19SBen Gras 
4572fe8fb19SBen Gras #ifndef TYPECHECK
458*0a6a1f1dSLionel Sambuc #define my_localtime_rz	localtime_rz
4592fe8fb19SBen Gras #else /* !defined TYPECHECK */
4602fe8fb19SBen Gras static struct tm *
my_localtime_rz(timezone_t tz,const time_t * tp,struct tm * tmp)461*0a6a1f1dSLionel Sambuc my_localtime_rz(timezone_t tz, const time_t *tp, struct tm *tmp)
4622fe8fb19SBen Gras {
463*0a6a1f1dSLionel Sambuc 	tmp = localtime_rz(tz, tp, tmp);
464*0a6a1f1dSLionel Sambuc 	if (tmp) {
4652fe8fb19SBen Gras 		struct tm	tm;
46684d9c625SLionel Sambuc 		time_t	t;
4672fe8fb19SBen Gras 
4682fe8fb19SBen Gras 		tm = *tmp;
469*0a6a1f1dSLionel Sambuc 		t = mktime_z(tz, &tm);
47084d9c625SLionel Sambuc 		if (t != *tp) {
4712fe8fb19SBen Gras 			(void) fflush(stdout);
4722fe8fb19SBen Gras 			(void) fprintf(stderr, "\n%s: ", progname);
4732fe8fb19SBen Gras 			(void) fprintf(stderr, tformat(), *tp);
4742fe8fb19SBen Gras 			(void) fprintf(stderr, " ->");
4752fe8fb19SBen Gras 			(void) fprintf(stderr, " year=%d", tmp->tm_year);
4762fe8fb19SBen Gras 			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
4772fe8fb19SBen Gras 			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
4782fe8fb19SBen Gras 			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
4792fe8fb19SBen Gras 			(void) fprintf(stderr, " min=%d", tmp->tm_min);
4802fe8fb19SBen Gras 			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
4812fe8fb19SBen Gras 			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
4822fe8fb19SBen Gras 			(void) fprintf(stderr, " -> ");
4832fe8fb19SBen Gras 			(void) fprintf(stderr, tformat(), t);
4842fe8fb19SBen Gras 			(void) fprintf(stderr, "\n");
485*0a6a1f1dSLionel Sambuc 			errout = true;
4862fe8fb19SBen Gras 		}
4872fe8fb19SBen Gras 	}
4882fe8fb19SBen Gras 	return tmp;
4892fe8fb19SBen Gras }
4902fe8fb19SBen Gras #endif /* !defined TYPECHECK */
4912fe8fb19SBen Gras 
4922fe8fb19SBen Gras static void
abbrok(const char * const abbrp,const char * const zone)49384d9c625SLionel Sambuc abbrok(const char *const abbrp, const char *const zone)
4942fe8fb19SBen Gras {
49584d9c625SLionel Sambuc 	const char *cp;
49684d9c625SLionel Sambuc 	const char *wp;
4972fe8fb19SBen Gras 
4982fe8fb19SBen Gras 	if (warned)
4992fe8fb19SBen Gras 		return;
5002fe8fb19SBen Gras 	cp = abbrp;
501*0a6a1f1dSLionel Sambuc 	while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
5022fe8fb19SBen Gras 		++cp;
503*0a6a1f1dSLionel Sambuc 	if (cp - abbrp < 3)
504*0a6a1f1dSLionel Sambuc 		wp = _("has fewer than 3 characters");
5052fe8fb19SBen Gras 	else if (cp - abbrp > 6)
506*0a6a1f1dSLionel Sambuc 		wp = _("has more than 6 characters");
507*0a6a1f1dSLionel Sambuc 	else if (*cp)
508*0a6a1f1dSLionel Sambuc 		wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
509*0a6a1f1dSLionel Sambuc 	else
5102fe8fb19SBen Gras 		return;
5112fe8fb19SBen Gras 	(void) fflush(stdout);
5122fe8fb19SBen Gras 	(void) fprintf(stderr,
5132fe8fb19SBen Gras 		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
5142fe8fb19SBen Gras 		progname, zone, abbrp, wp);
515*0a6a1f1dSLionel Sambuc 	warned = errout = true;
516*0a6a1f1dSLionel Sambuc }
517*0a6a1f1dSLionel Sambuc 
518*0a6a1f1dSLionel Sambuc /* Return a time zone abbreviation.  If the abbreviation needs to be
519*0a6a1f1dSLionel Sambuc    saved, use *BUF (of size *BUFALLOC) to save it, and return the
520*0a6a1f1dSLionel Sambuc    abbreviation in the possibly-reallocated *BUF.  Otherwise, just
521*0a6a1f1dSLionel Sambuc    return the abbreviation.  Get the abbreviation from TMP.
522*0a6a1f1dSLionel Sambuc    Exit on memory allocation failure.  */
523*0a6a1f1dSLionel Sambuc static char const *
saveabbr(char ** buf,size_t * bufalloc,struct tm const * tmp)524*0a6a1f1dSLionel Sambuc saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp)
525*0a6a1f1dSLionel Sambuc {
526*0a6a1f1dSLionel Sambuc 	char const *ab = abbr(tmp);
527*0a6a1f1dSLionel Sambuc 	if (HAVE_LOCALTIME_RZ)
528*0a6a1f1dSLionel Sambuc 		return ab;
529*0a6a1f1dSLionel Sambuc 	else {
530*0a6a1f1dSLionel Sambuc 		size_t ablen = strlen(ab);
531*0a6a1f1dSLionel Sambuc 		if (*bufalloc <= ablen) {
532*0a6a1f1dSLionel Sambuc 			free(*buf);
533*0a6a1f1dSLionel Sambuc 
534*0a6a1f1dSLionel Sambuc 			/* Make the new buffer at least twice as long as the
535*0a6a1f1dSLionel Sambuc 			   old, to avoid O(N**2) behavior on repeated calls.  */
536*0a6a1f1dSLionel Sambuc 			*bufalloc = sumsize(*bufalloc, ablen + 1);
537*0a6a1f1dSLionel Sambuc 			*buf = malloc(*bufalloc);
538*0a6a1f1dSLionel Sambuc 			if (! *buf) {
539*0a6a1f1dSLionel Sambuc 				err(EXIT_FAILURE, "malloc");
540*0a6a1f1dSLionel Sambuc 			}
541*0a6a1f1dSLionel Sambuc 		}
542*0a6a1f1dSLionel Sambuc 		return strcpy(*buf, ab);
543*0a6a1f1dSLionel Sambuc 	}
544*0a6a1f1dSLionel Sambuc }
545*0a6a1f1dSLionel Sambuc 
546*0a6a1f1dSLionel Sambuc static void
close_file(FILE * stream)547*0a6a1f1dSLionel Sambuc close_file(FILE *stream)
548*0a6a1f1dSLionel Sambuc {
549*0a6a1f1dSLionel Sambuc 	char const *e = (ferror(stream) ? _("I/O error")
550*0a6a1f1dSLionel Sambuc 	    : fclose(stream) != 0 ? strerror(errno) : NULL);
551*0a6a1f1dSLionel Sambuc 	if (e) {
552*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "%s", e);
553*0a6a1f1dSLionel Sambuc 	}
5542fe8fb19SBen Gras }
5552fe8fb19SBen Gras 
556f14fb602SLionel Sambuc __dead static void
usage(FILE * const stream,const int status)55784d9c625SLionel Sambuc usage(FILE *const stream, const int status)
5582fe8fb19SBen Gras {
5592fe8fb19SBen Gras 	(void) fprintf(stream,
56084d9c625SLionel Sambuc _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
56184d9c625SLionel Sambuc   "\n"
56284d9c625SLionel Sambuc   "Report bugs to %s.\n"),
56384d9c625SLionel Sambuc 		       progname, progname, REPORT_BUGS_TO);
564*0a6a1f1dSLionel Sambuc 	if (status == EXIT_SUCCESS)
565*0a6a1f1dSLionel Sambuc 		close_file(stream);
5662fe8fb19SBen Gras 	exit(status);
5672fe8fb19SBen Gras }
5682fe8fb19SBen Gras 
5692fe8fb19SBen Gras int
main(int argc,char * argv[])57084d9c625SLionel Sambuc main(int argc, char *argv[])
5712fe8fb19SBen Gras {
572*0a6a1f1dSLionel Sambuc 	/* These are static so that they're initially zero.  */
573*0a6a1f1dSLionel Sambuc 	static char *		abbrev;
574*0a6a1f1dSLionel Sambuc 	static size_t		abbrevsize;
575*0a6a1f1dSLionel Sambuc 	static struct tm	newtm;
576*0a6a1f1dSLionel Sambuc 
57784d9c625SLionel Sambuc 	int		i;
578*0a6a1f1dSLionel Sambuc 	bool		vflag;
579*0a6a1f1dSLionel Sambuc 	bool		Vflag;
58084d9c625SLionel Sambuc 	char *		cutarg;
58184d9c625SLionel Sambuc 	char *		cuttimes;
58284d9c625SLionel Sambuc 	time_t		cutlotime;
58384d9c625SLionel Sambuc 	time_t		cuthitime;
5842fe8fb19SBen Gras 	time_t		now;
5852fe8fb19SBen Gras 	time_t		t;
5862fe8fb19SBen Gras 	time_t		newt;
5872fe8fb19SBen Gras 	struct tm	tm;
58884d9c625SLionel Sambuc 	struct tm *	tmp;
58984d9c625SLionel Sambuc 	struct tm *	newtmp;
5902fe8fb19SBen Gras 
59184d9c625SLionel Sambuc 	cutlotime = absolute_min_time;
59284d9c625SLionel Sambuc 	cuthitime = absolute_max_time;
5932fe8fb19SBen Gras #if HAVE_GETTEXT
5942fe8fb19SBen Gras 	(void) setlocale(LC_ALL, "");
5952fe8fb19SBen Gras #ifdef TZ_DOMAINDIR
5962fe8fb19SBen Gras 	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
5972fe8fb19SBen Gras #endif /* defined TEXTDOMAINDIR */
5982fe8fb19SBen Gras 	(void) textdomain(TZ_DOMAIN);
5992fe8fb19SBen Gras #endif /* HAVE_GETTEXT */
6002fe8fb19SBen Gras 	progname = argv[0];
6012fe8fb19SBen Gras 	for (i = 1; i < argc; ++i)
6022fe8fb19SBen Gras 		if (strcmp(argv[i], "--version") == 0) {
60384d9c625SLionel Sambuc 			(void) printf("zdump %s%s\n", PKGVERSION, TZVERSION);
604*0a6a1f1dSLionel Sambuc 			return EXIT_SUCCESS;
6052fe8fb19SBen Gras 		} else if (strcmp(argv[i], "--help") == 0) {
606f14fb602SLionel Sambuc 			usage(stdout, EXIT_SUCCESS);
6072fe8fb19SBen Gras 		}
608*0a6a1f1dSLionel Sambuc 	vflag = Vflag = false;
60984d9c625SLionel Sambuc 	cutarg = cuttimes = NULL;
61084d9c625SLionel Sambuc 	for (;;)
61184d9c625SLionel Sambuc 	  switch (getopt(argc, argv, "c:t:vV")) {
61284d9c625SLionel Sambuc 	  case 'c': cutarg = optarg; break;
61384d9c625SLionel Sambuc 	  case 't': cuttimes = optarg; break;
614*0a6a1f1dSLionel Sambuc 	  case 'v': vflag = true; break;
615*0a6a1f1dSLionel Sambuc 	  case 'V': Vflag = true; break;
61684d9c625SLionel Sambuc 	  case -1:
61784d9c625SLionel Sambuc 	    if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
61884d9c625SLionel Sambuc 	      goto arg_processing_done;
61984d9c625SLionel Sambuc 	    /* Fall through.  */
62084d9c625SLionel Sambuc 	  default:
621f14fb602SLionel Sambuc 	    usage(stderr, EXIT_FAILURE);
6222fe8fb19SBen Gras 	  }
62384d9c625SLionel Sambuc  arg_processing_done:;
6242fe8fb19SBen Gras 
62584d9c625SLionel Sambuc 	if (vflag | Vflag) {
62684d9c625SLionel Sambuc 		intmax_t	lo;
62784d9c625SLionel Sambuc 		intmax_t	hi;
628*0a6a1f1dSLionel Sambuc 		char *loend, *hiend;
62984d9c625SLionel Sambuc 		intmax_t cutloyear = ZDUMP_LO_YEAR;
63084d9c625SLionel Sambuc 		intmax_t cuthiyear = ZDUMP_HI_YEAR;
63184d9c625SLionel Sambuc 		if (cutarg != NULL) {
632*0a6a1f1dSLionel Sambuc 			lo = strtoimax(cutarg, &loend, 10);
633*0a6a1f1dSLionel Sambuc 			if (cutarg != loend && !*loend) {
634*0a6a1f1dSLionel Sambuc 				hi = lo;
6352fe8fb19SBen Gras 				cuthiyear = hi;
636*0a6a1f1dSLionel Sambuc 			} else if (cutarg != loend && *loend == ','
637*0a6a1f1dSLionel Sambuc 				   && (hi = strtoimax(loend + 1, &hiend, 10),
638*0a6a1f1dSLionel Sambuc 				       loend + 1 != hiend && !*hiend)) {
6392fe8fb19SBen Gras 				cutloyear = lo;
6402fe8fb19SBen Gras 				cuthiyear = hi;
6412fe8fb19SBen Gras 			} else {
642*0a6a1f1dSLionel Sambuc 				fprintf(stderr, _("%s: wild -c argument %s\n"),
6432fe8fb19SBen Gras 					progname, cutarg);
644*0a6a1f1dSLionel Sambuc 				return EXIT_FAILURE;
6452fe8fb19SBen Gras 			}
6462fe8fb19SBen Gras 		}
64784d9c625SLionel Sambuc 		if (cutarg != NULL || cuttimes == NULL) {
6482fe8fb19SBen Gras 			cutlotime = yeartot(cutloyear);
6492fe8fb19SBen Gras 			cuthitime = yeartot(cuthiyear);
6502fe8fb19SBen Gras 		}
65184d9c625SLionel Sambuc 		if (cuttimes != NULL) {
652*0a6a1f1dSLionel Sambuc 			lo = strtoimax(cuttimes, &loend, 10);
653*0a6a1f1dSLionel Sambuc 			if (cuttimes != loend && !*loend) {
654*0a6a1f1dSLionel Sambuc 				hi = lo;
65584d9c625SLionel Sambuc 				if (hi < cuthitime) {
65684d9c625SLionel Sambuc 					if (hi < absolute_min_time)
65784d9c625SLionel Sambuc 						hi = absolute_min_time;
65884d9c625SLionel Sambuc 					cuthitime = hi;
65984d9c625SLionel Sambuc 				}
660*0a6a1f1dSLionel Sambuc 			} else if (cuttimes != loend && *loend == ','
661*0a6a1f1dSLionel Sambuc 				   && (hi = strtoimax(loend + 1, &hiend, 10),
662*0a6a1f1dSLionel Sambuc 				       loend + 1 != hiend && !*hiend)) {
66384d9c625SLionel Sambuc 				if (cutlotime < lo) {
66484d9c625SLionel Sambuc 					if (absolute_max_time < lo)
66584d9c625SLionel Sambuc 						lo = absolute_max_time;
66684d9c625SLionel Sambuc 					cutlotime = lo;
66784d9c625SLionel Sambuc 				}
66884d9c625SLionel Sambuc 				if (hi < cuthitime) {
66984d9c625SLionel Sambuc 					if (hi < absolute_min_time)
67084d9c625SLionel Sambuc 						hi = absolute_min_time;
67184d9c625SLionel Sambuc 					cuthitime = hi;
67284d9c625SLionel Sambuc 				}
67384d9c625SLionel Sambuc 			} else {
67484d9c625SLionel Sambuc 				(void) fprintf(stderr,
67584d9c625SLionel Sambuc 					_("%s: wild -t argument %s\n"),
67684d9c625SLionel Sambuc 					progname, cuttimes);
677*0a6a1f1dSLionel Sambuc 				return EXIT_FAILURE;
67884d9c625SLionel Sambuc 			}
67984d9c625SLionel Sambuc 		}
68084d9c625SLionel Sambuc 	}
681*0a6a1f1dSLionel Sambuc 	gmtzinit();
682*0a6a1f1dSLionel Sambuc 	now = time(NULL);
6832fe8fb19SBen Gras 	longest = 0;
684*0a6a1f1dSLionel Sambuc 	for (i = optind; i < argc; i++) {
685*0a6a1f1dSLionel Sambuc 		size_t arglen = strlen(argv[i]);
686*0a6a1f1dSLionel Sambuc 		if (longest < arglen)
687*0a6a1f1dSLionel Sambuc 			longest = arglen < INT_MAX ? arglen : INT_MAX;
688*0a6a1f1dSLionel Sambuc 	}
6892fe8fb19SBen Gras 
6902fe8fb19SBen Gras 	for (i = optind; i < argc; ++i) {
691*0a6a1f1dSLionel Sambuc 		timezone_t tz = tzalloc(argv[i]);
692*0a6a1f1dSLionel Sambuc 		char const *ab;
693*0a6a1f1dSLionel Sambuc 		if (!tz) {
694*0a6a1f1dSLionel Sambuc 			errx(EXIT_FAILURE, "%s", argv[i]);
695*0a6a1f1dSLionel Sambuc 		}
69684d9c625SLionel Sambuc 		if (! (vflag | Vflag)) {
697*0a6a1f1dSLionel Sambuc 			show(tz, argv[i], now, false);
698*0a6a1f1dSLionel Sambuc 			tzfree(tz);
6992fe8fb19SBen Gras 			continue;
7002fe8fb19SBen Gras 		}
701*0a6a1f1dSLionel Sambuc 		warned = false;
7022fe8fb19SBen Gras 		t = absolute_min_time;
70384d9c625SLionel Sambuc 		if (!Vflag) {
704*0a6a1f1dSLionel Sambuc 			show(tz, argv[i], t, true);
70584d9c625SLionel Sambuc 			t += SECSPERDAY;
706*0a6a1f1dSLionel Sambuc 			show(tz, argv[i], t, true);
70784d9c625SLionel Sambuc 		}
7082fe8fb19SBen Gras 		if (t < cutlotime)
7092fe8fb19SBen Gras 			t = cutlotime;
710*0a6a1f1dSLionel Sambuc 		tmp = my_localtime_rz(tz, &t, &tm);
711*0a6a1f1dSLionel Sambuc 		if (tmp)
712*0a6a1f1dSLionel Sambuc 			ab = saveabbr(&abbrev, &abbrevsize, &tm);
713*0a6a1f1dSLionel Sambuc 		else
714*0a6a1f1dSLionel Sambuc 			ab = NULL;
715*0a6a1f1dSLionel Sambuc 		while (t < cuthitime) {
716*0a6a1f1dSLionel Sambuc 			newt = ((t < absolute_max_time - SECSPERDAY / 2
717*0a6a1f1dSLionel Sambuc 				&& t + SECSPERDAY / 2 < cuthitime)
71884d9c625SLionel Sambuc 				? t + SECSPERDAY / 2
719*0a6a1f1dSLionel Sambuc 				: cuthitime);
720*0a6a1f1dSLionel Sambuc 			newtmp = localtime_rz(tz, &newt, &newtm);
7212fe8fb19SBen Gras 			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
7222fe8fb19SBen Gras 			    (delta(&newtm, &tm) != (newt - t) ||
7232fe8fb19SBen Gras 			    newtm.tm_isdst != tm.tm_isdst ||
724*0a6a1f1dSLionel Sambuc 			    strcmp(abbr(&newtm), ab) != 0)) {
725*0a6a1f1dSLionel Sambuc 				newt = hunt(tz, argv[i], t, newt);
726*0a6a1f1dSLionel Sambuc 				newtmp = localtime_rz(tz, &newt, &newtm);
727*0a6a1f1dSLionel Sambuc 				if (newtmp)
728*0a6a1f1dSLionel Sambuc 					  ab = saveabbr(&abbrev, &abbrevsize,
729*0a6a1f1dSLionel Sambuc 							&newtm);
7302fe8fb19SBen Gras 			}
7312fe8fb19SBen Gras 			t = newt;
7322fe8fb19SBen Gras 			tm = newtm;
7332fe8fb19SBen Gras 			tmp = newtmp;
7342fe8fb19SBen Gras 		}
73584d9c625SLionel Sambuc 		if (!Vflag) {
7362fe8fb19SBen Gras 			t = absolute_max_time;
73784d9c625SLionel Sambuc 			t -= SECSPERDAY;
738*0a6a1f1dSLionel Sambuc 			show(tz, argv[i], t, true);
73984d9c625SLionel Sambuc 			t += SECSPERDAY;
740*0a6a1f1dSLionel Sambuc 			show(tz, argv[i], t, true);
7412fe8fb19SBen Gras 		}
742*0a6a1f1dSLionel Sambuc 		tzfree(tz);
74384d9c625SLionel Sambuc 	}
744*0a6a1f1dSLionel Sambuc 	close_file(stdout);
745*0a6a1f1dSLionel Sambuc 	if (errout && (ferror(stderr) || fclose(stderr) != 0))
7462fe8fb19SBen Gras 		return EXIT_FAILURE;
747*0a6a1f1dSLionel Sambuc 	return EXIT_SUCCESS;
7482fe8fb19SBen Gras }
7492fe8fb19SBen Gras 
750f14fb602SLionel Sambuc static time_t
yeartot(intmax_t y)751*0a6a1f1dSLionel Sambuc yeartot(intmax_t y)
752f14fb602SLionel Sambuc {
75384d9c625SLionel Sambuc 	intmax_t	myy, seconds, years;
75484d9c625SLionel Sambuc 	time_t		t;
7552fe8fb19SBen Gras 
7562fe8fb19SBen Gras 	myy = EPOCH_YEAR;
7572fe8fb19SBen Gras 	t = 0;
75884d9c625SLionel Sambuc 	while (myy < y) {
75984d9c625SLionel Sambuc 		if (SECSPER400YEARS_FITS && 400 <= y - myy) {
76084d9c625SLionel Sambuc 			intmax_t diff400 = (y - myy) / 400;
76184d9c625SLionel Sambuc 			if (INTMAX_MAX / SECSPER400YEARS < diff400)
76284d9c625SLionel Sambuc 				return absolute_max_time;
76384d9c625SLionel Sambuc 			seconds = diff400 * SECSPER400YEARS;
76484d9c625SLionel Sambuc 			years = diff400 * 400;
7652fe8fb19SBen Gras                 } else {
7662fe8fb19SBen Gras 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
76784d9c625SLionel Sambuc 			years = 1;
7682fe8fb19SBen Gras 		}
76984d9c625SLionel Sambuc 		myy += years;
77084d9c625SLionel Sambuc 		if (t > absolute_max_time - seconds)
77184d9c625SLionel Sambuc 			return absolute_max_time;
77284d9c625SLionel Sambuc 		t += seconds;
77384d9c625SLionel Sambuc 	}
77484d9c625SLionel Sambuc 	while (y < myy) {
77584d9c625SLionel Sambuc 		if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
77684d9c625SLionel Sambuc 			intmax_t diff400 = (myy - y) / 400;
77784d9c625SLionel Sambuc 			if (INTMAX_MAX / SECSPER400YEARS < diff400)
77884d9c625SLionel Sambuc 				return absolute_min_time;
77984d9c625SLionel Sambuc 			seconds = diff400 * SECSPER400YEARS;
78084d9c625SLionel Sambuc 			years = diff400 * 400;
78184d9c625SLionel Sambuc 		} else {
78284d9c625SLionel Sambuc 			seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
78384d9c625SLionel Sambuc 			years = 1;
78484d9c625SLionel Sambuc 		}
78584d9c625SLionel Sambuc 		myy -= years;
78684d9c625SLionel Sambuc 		if (t < absolute_min_time + seconds)
78784d9c625SLionel Sambuc 			return absolute_min_time;
7882fe8fb19SBen Gras 		t -= seconds;
7892fe8fb19SBen Gras 	}
7902fe8fb19SBen Gras 	return t;
7912fe8fb19SBen Gras }
7922fe8fb19SBen Gras 
7932fe8fb19SBen Gras static time_t
hunt(timezone_t tz,char * name,time_t lot,time_t hit)794*0a6a1f1dSLionel Sambuc hunt(timezone_t tz, char *name, time_t lot, time_t hit)
7952fe8fb19SBen Gras {
796*0a6a1f1dSLionel Sambuc 	static char *		loab;
797*0a6a1f1dSLionel Sambuc 	static size_t		loabsize;
798*0a6a1f1dSLionel Sambuc 	char const *		ab;
7992fe8fb19SBen Gras 	time_t			t;
8002fe8fb19SBen Gras 	struct tm		lotm;
80184d9c625SLionel Sambuc 	struct tm *	lotmp;
8022fe8fb19SBen Gras 	struct tm		tm;
80384d9c625SLionel Sambuc 	struct tm *	tmp;
8042fe8fb19SBen Gras 
805*0a6a1f1dSLionel Sambuc 	lotmp = my_localtime_rz(tz, &lot, &lotm);
806*0a6a1f1dSLionel Sambuc 	if (lotmp)
807*0a6a1f1dSLionel Sambuc 		ab = saveabbr(&loab, &loabsize, &lotm);
808*0a6a1f1dSLionel Sambuc 	else
809*0a6a1f1dSLionel Sambuc 		ab = NULL;
8102fe8fb19SBen Gras 	for ( ; ; ) {
81184d9c625SLionel Sambuc 		time_t diff = hit - lot;
8122fe8fb19SBen Gras 		if (diff < 2)
8132fe8fb19SBen Gras 			break;
8142fe8fb19SBen Gras 		t = lot;
8152fe8fb19SBen Gras 		t += diff / 2;
8162fe8fb19SBen Gras 		if (t <= lot)
8172fe8fb19SBen Gras 			++t;
8182fe8fb19SBen Gras 		else if (t >= hit)
8192fe8fb19SBen Gras 			--t;
820*0a6a1f1dSLionel Sambuc 		tmp = my_localtime_rz(tz, &t, &tm);
8212fe8fb19SBen Gras 		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
8222fe8fb19SBen Gras 			(delta(&tm, &lotm) == (t - lot) &&
8232fe8fb19SBen Gras 			tm.tm_isdst == lotm.tm_isdst &&
824*0a6a1f1dSLionel Sambuc 			strcmp(abbr(&tm), ab) == 0)) {
8252fe8fb19SBen Gras 				lot = t;
8262fe8fb19SBen Gras 				lotm = tm;
8272fe8fb19SBen Gras 				lotmp = tmp;
8282fe8fb19SBen Gras 		} else	hit = t;
8292fe8fb19SBen Gras 	}
830*0a6a1f1dSLionel Sambuc 	show(tz, name, lot, true);
831*0a6a1f1dSLionel Sambuc 	show(tz, name, hit, true);
8322fe8fb19SBen Gras 	return hit;
8332fe8fb19SBen Gras }
8342fe8fb19SBen Gras 
8352fe8fb19SBen Gras /*
8362fe8fb19SBen Gras ** Thanks to Paul Eggert for logic used in delta.
8372fe8fb19SBen Gras */
8382fe8fb19SBen Gras 
83984d9c625SLionel Sambuc static intmax_t
delta(struct tm * newp,struct tm * oldp)84084d9c625SLionel Sambuc delta(struct tm *newp, struct tm *oldp)
8412fe8fb19SBen Gras {
84284d9c625SLionel Sambuc 	intmax_t	result;
84384d9c625SLionel Sambuc 	int		tmy;
8442fe8fb19SBen Gras 
8452fe8fb19SBen Gras 	if (newp->tm_year < oldp->tm_year)
8462fe8fb19SBen Gras 		return -delta(oldp, newp);
8472fe8fb19SBen Gras 	result = 0;
8482fe8fb19SBen Gras 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
8492fe8fb19SBen Gras 		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
8502fe8fb19SBen Gras 	result += newp->tm_yday - oldp->tm_yday;
8512fe8fb19SBen Gras 	result *= HOURSPERDAY;
8522fe8fb19SBen Gras 	result += newp->tm_hour - oldp->tm_hour;
8532fe8fb19SBen Gras 	result *= MINSPERHOUR;
8542fe8fb19SBen Gras 	result += newp->tm_min - oldp->tm_min;
8552fe8fb19SBen Gras 	result *= SECSPERMIN;
8562fe8fb19SBen Gras 	result += newp->tm_sec - oldp->tm_sec;
8572fe8fb19SBen Gras 	return result;
8582fe8fb19SBen Gras }
8592fe8fb19SBen Gras 
860*0a6a1f1dSLionel Sambuc #ifndef TM_GMTOFF
861*0a6a1f1dSLionel Sambuc /* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
862*0a6a1f1dSLionel Sambuc    Assume A and B differ by at most one year.  */
863*0a6a1f1dSLionel Sambuc static int
adjusted_yday(struct tm const * a,struct tm const * b)864*0a6a1f1dSLionel Sambuc adjusted_yday(struct tm const *a, struct tm const *b)
865*0a6a1f1dSLionel Sambuc {
866*0a6a1f1dSLionel Sambuc 	int yday = a->tm_yday;
867*0a6a1f1dSLionel Sambuc 	if (b->tm_year < a->tm_year)
868*0a6a1f1dSLionel Sambuc 		yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
869*0a6a1f1dSLionel Sambuc 	return yday;
870*0a6a1f1dSLionel Sambuc }
871*0a6a1f1dSLionel Sambuc #endif
872*0a6a1f1dSLionel Sambuc 
873*0a6a1f1dSLionel Sambuc /* If A is the broken-down local time and B the broken-down UTC for
874*0a6a1f1dSLionel Sambuc    the same instant, return A's UTC offset in seconds, where positive
875*0a6a1f1dSLionel Sambuc    offsets are east of Greenwich.  On failure, return LONG_MIN.  */
876*0a6a1f1dSLionel Sambuc static long
gmtoff(struct tm const * a,struct tm const * b)877*0a6a1f1dSLionel Sambuc gmtoff(struct tm const *a, struct tm const *b)
878*0a6a1f1dSLionel Sambuc {
879*0a6a1f1dSLionel Sambuc #ifdef TM_GMTOFF
880*0a6a1f1dSLionel Sambuc 	return a->TM_GMTOFF;
881*0a6a1f1dSLionel Sambuc #else
882*0a6a1f1dSLionel Sambuc 	if (! b)
883*0a6a1f1dSLionel Sambuc 		return LONG_MIN;
884*0a6a1f1dSLionel Sambuc 	else {
885*0a6a1f1dSLionel Sambuc 		int ayday = adjusted_yday(a, b);
886*0a6a1f1dSLionel Sambuc 		int byday = adjusted_yday(b, a);
887*0a6a1f1dSLionel Sambuc 		int days = ayday - byday;
888*0a6a1f1dSLionel Sambuc 		long hours = a->tm_hour - b->tm_hour + 24 * days;
889*0a6a1f1dSLionel Sambuc 		long minutes = a->tm_min - b->tm_min + 60 * hours;
890*0a6a1f1dSLionel Sambuc 		long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
891*0a6a1f1dSLionel Sambuc 		return seconds;
892*0a6a1f1dSLionel Sambuc 	}
893*0a6a1f1dSLionel Sambuc #endif
894*0a6a1f1dSLionel Sambuc }
895*0a6a1f1dSLionel Sambuc 
8962fe8fb19SBen Gras static void
show(timezone_t tz,char * zone,time_t t,bool v)897*0a6a1f1dSLionel Sambuc show(timezone_t tz, char *zone, time_t t, bool v)
8982fe8fb19SBen Gras {
89984d9c625SLionel Sambuc 	struct tm *	tmp;
900*0a6a1f1dSLionel Sambuc 	struct tm *	gmtmp;
901*0a6a1f1dSLionel Sambuc 	struct tm tm, gmtm;
9022fe8fb19SBen Gras 
9032fe8fb19SBen Gras 	(void) printf("%-*s  ", (int) longest, zone);
9042fe8fb19SBen Gras 	if (v) {
905*0a6a1f1dSLionel Sambuc 		gmtmp = my_gmtime_r(&t, &gmtm);
906*0a6a1f1dSLionel Sambuc 		if (gmtmp == NULL) {
907*0a6a1f1dSLionel Sambuc 			printf(tformat(), t);
9082fe8fb19SBen Gras 		} else {
909*0a6a1f1dSLionel Sambuc 			dumptime(gmtmp);
91084d9c625SLionel Sambuc 			(void) printf(" UT");
9112fe8fb19SBen Gras 		}
9122fe8fb19SBen Gras 		(void) printf(" = ");
9132fe8fb19SBen Gras 	}
914*0a6a1f1dSLionel Sambuc 	tmp = my_localtime_rz(tz, &t, &tm);
9152fe8fb19SBen Gras 	dumptime(tmp);
9162fe8fb19SBen Gras 	if (tmp != NULL) {
9172fe8fb19SBen Gras 		if (*abbr(tmp) != '\0')
9182fe8fb19SBen Gras 			(void) printf(" %s", abbr(tmp));
9192fe8fb19SBen Gras 		if (v) {
920*0a6a1f1dSLionel Sambuc 			long off = gmtoff(tmp, gmtmp);
9212fe8fb19SBen Gras 			(void) printf(" isdst=%d", tmp->tm_isdst);
922*0a6a1f1dSLionel Sambuc 			if (off != LONG_MIN)
923*0a6a1f1dSLionel Sambuc 				(void) printf(" gmtoff=%ld", off);
9242fe8fb19SBen Gras 		}
9252fe8fb19SBen Gras 	}
9262fe8fb19SBen Gras 	(void) printf("\n");
9272fe8fb19SBen Gras 	if (tmp != NULL && *abbr(tmp) != '\0')
9282fe8fb19SBen Gras 		abbrok(abbr(tmp), zone);
9292fe8fb19SBen Gras }
9302fe8fb19SBen Gras 
9312fe8fb19SBen Gras static const char *
abbr(struct tm const * tmp)932*0a6a1f1dSLionel Sambuc abbr(struct tm const *tmp)
9332fe8fb19SBen Gras {
934*0a6a1f1dSLionel Sambuc #ifdef TM_ZONE
935*0a6a1f1dSLionel Sambuc 	return tmp->TM_ZONE;
936*0a6a1f1dSLionel Sambuc #else
937*0a6a1f1dSLionel Sambuc 	return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
938*0a6a1f1dSLionel Sambuc 		? tzname[0 < tmp->tm_isdst]
939*0a6a1f1dSLionel Sambuc 		: "");
940*0a6a1f1dSLionel Sambuc #endif
9412fe8fb19SBen Gras }
9422fe8fb19SBen Gras 
9432fe8fb19SBen Gras /*
9442fe8fb19SBen Gras ** The code below can fail on certain theoretical systems;
9452fe8fb19SBen Gras ** it works on all known real-world systems as of 2004-12-30.
9462fe8fb19SBen Gras */
9472fe8fb19SBen Gras 
9482fe8fb19SBen Gras static const char *
tformat(void)9492fe8fb19SBen Gras tformat(void)
9502fe8fb19SBen Gras {
9512fe8fb19SBen Gras 	if (0 > (time_t) -1) {		/* signed */
95284d9c625SLionel Sambuc 		if (sizeof (time_t) == sizeof (intmax_t))
95384d9c625SLionel Sambuc 			return "%"PRIdMAX;
9542fe8fb19SBen Gras 		if (sizeof (time_t) > sizeof (long))
9552fe8fb19SBen Gras 			return "%lld";
9562fe8fb19SBen Gras 		if (sizeof (time_t) > sizeof (int))
9572fe8fb19SBen Gras 			return "%ld";
9582fe8fb19SBen Gras 		return "%d";
9592fe8fb19SBen Gras 	}
96084d9c625SLionel Sambuc #ifdef PRIuMAX
96184d9c625SLionel Sambuc 	if (sizeof (time_t) == sizeof (uintmax_t))
96284d9c625SLionel Sambuc 		return "%"PRIuMAX;
96384d9c625SLionel Sambuc #endif
9642fe8fb19SBen Gras 	if (sizeof (time_t) > sizeof (unsigned long))
9652fe8fb19SBen Gras 		return "%llu";
9662fe8fb19SBen Gras 	if (sizeof (time_t) > sizeof (unsigned int))
9672fe8fb19SBen Gras 		return "%lu";
9682fe8fb19SBen Gras 	return "%u";
9692fe8fb19SBen Gras }
9702fe8fb19SBen Gras 
9712fe8fb19SBen Gras static void
dumptime(const struct tm * timeptr)97284d9c625SLionel Sambuc dumptime(const struct tm *timeptr)
9732fe8fb19SBen Gras {
9742fe8fb19SBen Gras 	static const char	wday_name[][3] = {
9752fe8fb19SBen Gras 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
9762fe8fb19SBen Gras 	};
9772fe8fb19SBen Gras 	static const char	mon_name[][3] = {
9782fe8fb19SBen Gras 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
9792fe8fb19SBen Gras 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
9802fe8fb19SBen Gras 	};
98184d9c625SLionel Sambuc 	const char *	wn;
98284d9c625SLionel Sambuc 	const char *	mn;
98384d9c625SLionel Sambuc 	int		lead;
98484d9c625SLionel Sambuc 	int		trail;
9852fe8fb19SBen Gras 
9862fe8fb19SBen Gras 	if (timeptr == NULL) {
987*0a6a1f1dSLionel Sambuc 		printf("NULL");
9882fe8fb19SBen Gras 		return;
9892fe8fb19SBen Gras 	}
9902fe8fb19SBen Gras 	/*
991*0a6a1f1dSLionel Sambuc 	** The packaged localtime_rz and gmtime_r never put out-of-range
9922fe8fb19SBen Gras 	** values in tm_wday or tm_mon, but since this code might be compiled
9932fe8fb19SBen Gras 	** with other (perhaps experimental) versions, paranoia is in order.
9942fe8fb19SBen Gras 	*/
9952fe8fb19SBen Gras 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
9962fe8fb19SBen Gras 		(int) (sizeof wday_name / sizeof wday_name[0]))
9972fe8fb19SBen Gras 			wn = "???";
9982fe8fb19SBen Gras 	else		wn = wday_name[timeptr->tm_wday];
9992fe8fb19SBen Gras 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
10002fe8fb19SBen Gras 		(int) (sizeof mon_name / sizeof mon_name[0]))
10012fe8fb19SBen Gras 			mn = "???";
10022fe8fb19SBen Gras 	else		mn = mon_name[timeptr->tm_mon];
1003*0a6a1f1dSLionel Sambuc 	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
10042fe8fb19SBen Gras 		wn, mn,
10052fe8fb19SBen Gras 		timeptr->tm_mday, timeptr->tm_hour,
10062fe8fb19SBen Gras 		timeptr->tm_min, timeptr->tm_sec);
10072fe8fb19SBen Gras #define DIVISOR	10
10082fe8fb19SBen Gras 	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
10092fe8fb19SBen Gras 	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
10102fe8fb19SBen Gras 		trail / DIVISOR;
10112fe8fb19SBen Gras 	trail %= DIVISOR;
10122fe8fb19SBen Gras 	if (trail < 0 && lead > 0) {
10132fe8fb19SBen Gras 		trail += DIVISOR;
10142fe8fb19SBen Gras 		--lead;
10152fe8fb19SBen Gras 	} else if (lead < 0 && trail > 0) {
10162fe8fb19SBen Gras 		trail -= DIVISOR;
10172fe8fb19SBen Gras 		++lead;
10182fe8fb19SBen Gras 	}
10192fe8fb19SBen Gras 	if (lead == 0)
1020*0a6a1f1dSLionel Sambuc 		printf("%d", trail);
1021*0a6a1f1dSLionel Sambuc 	else	printf("%d%d", lead, ((trail < 0) ? -trail : trail));
10222fe8fb19SBen Gras }
1023