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