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