• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 **
13 ** However, include private.h when debugging, so that it overrides
14 ** time_t consistently with the rest of the package.
15 */
16 
17 #ifdef time_tz
18 # include "private.h"
19 #endif
20 
21 #include <stdbool.h>
22 
23 #include "stdio.h"	/* for stdout, stderr, perror */
24 #include "string.h"	/* for strcpy */
25 #include "sys/types.h"	/* for time_t */
26 #include "time.h"	/* for struct tm */
27 #include "stdlib.h"	/* for exit, malloc, atoi */
28 #include "limits.h"	/* for CHAR_BIT, LLONG_MAX */
29 #include "ctype.h"	/* for isalpha et al. */
30 
31 /* Enable extensions and modifications for ICU. */
32 #define ICU
33 
34 #ifdef ICU
35 #include "tzdir.h"
36 #include "dirent.h"
37 #include "sys/stat.h"
38 #endif
39 
40 #ifndef isascii
41 #define isascii(x) 1
42 #endif /* !defined isascii */
43 
44 /*
45 ** Substitutes for pre-C99 compilers.
46 ** Much of this section of code is stolen from private.h.
47 */
48 
49 #ifndef HAVE_STDINT_H
50 # define HAVE_STDINT_H \
51     (199901 <= __STDC_VERSION__ || 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
52 #endif
53 #if HAVE_STDINT_H
54 # include "stdint.h"
55 #endif
56 #ifndef HAVE_INTTYPES_H
57 # define HAVE_INTTYPES_H HAVE_STDINT_H
58 #endif
59 #if HAVE_INTTYPES_H
60 # include <inttypes.h>
61 #endif
62 
63 #ifndef INT_FAST32_MAX
64 # if INT_MAX >> 31 == 0
65 typedef long int_fast32_t;
66 # else
67 typedef int int_fast32_t;
68 # endif
69 #endif
70 
71 #ifndef INTMAX_MAX
72 # if defined LLONG_MAX || defined __LONG_LONG_MAX__
73 typedef long long intmax_t;
74 #  define strtoimax strtoll
75 #  define PRIdMAX "lld"
76 #  ifdef LLONG_MAX
77 #   define INTMAX_MAX LLONG_MAX
78 #  else
79 #   define INTMAX_MAX __LONG_LONG_MAX__
80 #  endif
81 # else
82 typedef long intmax_t;
83 #  define strtoimax strtol
84 #  define PRIdMAX "ld"
85 #  define INTMAX_MAX LONG_MAX
86 # endif
87 #endif
88 
89 
90 #ifndef ZDUMP_LO_YEAR
91 #define ZDUMP_LO_YEAR	(-500)
92 #endif /* !defined ZDUMP_LO_YEAR */
93 
94 #ifndef ZDUMP_HI_YEAR
95 #define ZDUMP_HI_YEAR	2500
96 #endif /* !defined ZDUMP_HI_YEAR */
97 
98 #ifndef MAX_STRING_LENGTH
99 #define MAX_STRING_LENGTH	1024
100 #endif /* !defined MAX_STRING_LENGTH */
101 
102 #ifndef EXIT_SUCCESS
103 #define EXIT_SUCCESS	0
104 #endif /* !defined EXIT_SUCCESS */
105 
106 #ifndef EXIT_FAILURE
107 #define EXIT_FAILURE	1
108 #endif /* !defined EXIT_FAILURE */
109 
110 #ifndef SECSPERMIN
111 #define SECSPERMIN	60
112 #endif /* !defined SECSPERMIN */
113 
114 #ifndef MINSPERHOUR
115 #define MINSPERHOUR	60
116 #endif /* !defined MINSPERHOUR */
117 
118 #ifndef SECSPERHOUR
119 #define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
120 #endif /* !defined SECSPERHOUR */
121 
122 #ifndef HOURSPERDAY
123 #define HOURSPERDAY	24
124 #endif /* !defined HOURSPERDAY */
125 
126 #ifndef EPOCH_YEAR
127 #define EPOCH_YEAR	1970
128 #endif /* !defined EPOCH_YEAR */
129 
130 #ifndef TM_YEAR_BASE
131 #define TM_YEAR_BASE	1900
132 #endif /* !defined TM_YEAR_BASE */
133 
134 #ifndef DAYSPERNYEAR
135 #define DAYSPERNYEAR	365
136 #endif /* !defined DAYSPERNYEAR */
137 
138 #ifndef isleap
139 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
140 #endif /* !defined isleap */
141 
142 #ifndef isleap_sum
143 /*
144 ** See tzfile.h for details on isleap_sum.
145 */
146 #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
147 #endif /* !defined isleap_sum */
148 
149 #define SECSPERDAY	((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
150 #define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
151 #define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
152 #define SECSPER400YEARS	(SECSPERNYEAR * (intmax_t) (300 + 3)	\
153 			 + SECSPERLYEAR * (intmax_t) (100 - 3))
154 
155 /*
156 ** True if SECSPER400YEARS is known to be representable as an
157 ** intmax_t.  It's OK that SECSPER400YEARS_FITS can in theory be false
158 ** even if SECSPER400YEARS is representable, because when that happens
159 ** the code merely runs a bit more slowly, and this slowness doesn't
160 ** occur on any practical platform.
161 */
162 enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
163 
164 #ifndef HAVE_GETTEXT
165 #define HAVE_GETTEXT 0
166 #endif
167 #if HAVE_GETTEXT
168 #include "locale.h"	/* for setlocale */
169 #include "libintl.h"
170 #endif /* HAVE_GETTEXT */
171 
172 #ifndef GNUC_or_lint
173 #ifdef lint
174 #define GNUC_or_lint
175 #else /* !defined lint */
176 #ifdef __GNUC__
177 #define GNUC_or_lint
178 #endif /* defined __GNUC__ */
179 #endif /* !defined lint */
180 #endif /* !defined GNUC_or_lint */
181 
182 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
183 # define ATTRIBUTE_PURE __attribute__ ((__pure__))
184 #else
185 # define ATTRIBUTE_PURE /* empty */
186 #endif
187 
188 /*
189 ** For the benefit of GNU folk...
190 ** `_(MSGID)' uses the current locale's message library string for MSGID.
191 ** The default is to use gettext if available, and use MSGID otherwise.
192 */
193 
194 #ifndef _
195 #if HAVE_GETTEXT
196 #define _(msgid) gettext(msgid)
197 #else /* !HAVE_GETTEXT */
198 #define _(msgid) msgid
199 #endif /* !HAVE_GETTEXT */
200 #endif /* !defined _ */
201 
202 #ifndef TZ_DOMAIN
203 #define TZ_DOMAIN "tz"
204 #endif /* !defined TZ_DOMAIN */
205 
206 extern char **	environ;
207 extern int	getopt(int argc, char * const argv[],
208 			const char * options);
209 extern char *	optarg;
210 extern int	optind;
211 extern char *	tzname[2];
212 
213 /* The minimum and maximum finite time values.  */
214 static time_t const absolute_min_time =
215   ((time_t) -1 < 0
216    ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
217    : 0);
218 static time_t const absolute_max_time =
219   ((time_t) -1 < 0
220    ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
221    : -1);
222 static size_t	longest;
223 static char *	progname;
224 static int	warned;
225 
226 static char *	abbr(struct tm * tmp);
227 static void	abbrok(const char * abbrp, const char * zone);
228 static intmax_t	delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE;
229 static void	dumptime(const struct tm * tmp);
230 static time_t	hunt(char * name, time_t lot, time_t	hit);
231 static void	show(char * zone, time_t t, int v);
232 static const char *	tformat(void);
233 static time_t	yeartot(intmax_t y) ATTRIBUTE_PURE;
234 #ifdef ICU
235 typedef struct listentry {
236 	char *		name;
237 	struct listentry *	next;
238 } listentry;
239 
240 static time_t	huntICU(char * name, time_t lot, time_t	hit, FILE *fp);
241 static void	dumptimeICU(FILE * fp, time_t t);
242 static void	showICU(FILE * fp, char * zone, time_t t1, time_t t2);
243 static int	getall(struct listentry ** namelist);
244 static void getzones(char * basedir, char * subdir, struct listentry ** last, int * count);
245 #endif
246 
247 #ifndef TYPECHECK
248 #define my_localtime	localtime
249 #else /* !defined TYPECHECK */
250 static struct tm *
my_localtime(time_t * tp)251 my_localtime(time_t *tp)
252 {
253 	register struct tm *	tmp;
254 
255 	tmp = localtime(tp);
256 	if (tp != NULL && tmp != NULL) {
257 		struct tm	tm;
258 		register time_t	t;
259 
260 		tm = *tmp;
261 		t = mktime(&tm);
262 		if (t != *tp) {
263 			(void) fflush(stdout);
264 			(void) fprintf(stderr, "\n%s: ", progname);
265 			(void) fprintf(stderr, tformat(), *tp);
266 			(void) fprintf(stderr, " ->");
267 			(void) fprintf(stderr, " year=%d", tmp->tm_year);
268 			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
269 			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
270 			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
271 			(void) fprintf(stderr, " min=%d", tmp->tm_min);
272 			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
273 			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
274 			(void) fprintf(stderr, " -> ");
275 			(void) fprintf(stderr, tformat(), t);
276 			(void) fprintf(stderr, "\n");
277 		}
278 	}
279 	return tmp;
280 }
281 #endif /* !defined TYPECHECK */
282 
283 static void
abbrok(const char * const abbrp,const char * const zone)284 abbrok(const char *const abbrp, const char *const zone)
285 {
286 	register const char *	cp;
287 	register const char *	wp;
288 
289 	if (warned)
290 		return;
291 	cp = abbrp;
292 	wp = NULL;
293 	while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
294 		++cp;
295 	if (cp - abbrp == 0)
296 		wp = _("lacks alphabetic at start");
297 	else if (cp - abbrp < 3)
298 		wp = _("has fewer than 3 alphabetics");
299 	else if (cp - abbrp > 6)
300 		wp = _("has more than 6 alphabetics");
301 	if (wp == NULL && (*cp == '+' || *cp == '-')) {
302 		++cp;
303 		if (isascii((unsigned char) *cp) &&
304 			isdigit((unsigned char) *cp))
305 				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
306 					++cp;
307 		if (*cp != '\0')
308 			wp = _("differs from POSIX standard");
309 	}
310 	if (wp == NULL)
311 		return;
312 	(void) fflush(stdout);
313 	(void) fprintf(stderr,
314 		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
315 		progname, zone, abbrp, wp);
316 	warned = true;
317 }
318 
319 static void
usage(FILE * const stream,const int status)320 usage(FILE * const stream, const int status)
321 {
322 	(void) fprintf(stream,
323 _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
324   "\n"
325   "Report bugs to %s.\n"),
326 		       progname, progname, REPORT_BUGS_TO);
327 	exit(status);
328 }
329 
330 int
main(int argc,char * argv[])331 main(int argc, char *argv[])
332 {
333 	register int		i;
334 	register int		vflag;
335 	register int		Vflag;
336 	register char *		cutarg;
337 	register char *		cuttimes;
338 	register time_t		cutlotime;
339 	register time_t		cuthitime;
340 	register char **	fakeenv;
341 	time_t			now;
342 	time_t			t;
343 	time_t			newt;
344 	struct tm		tm;
345 	struct tm		newtm;
346 	register struct tm *	tmp;
347 	register struct tm *	newtmp;
348 #ifdef ICU
349 	int		nextopt;
350 	char *	dirarg;
351 	int		aflag;
352 	int		iflag;
353 	listentry *	namelist = NULL;
354 	FILE *	fp = stdout;
355 #endif
356 
357 	cutlotime = absolute_min_time;
358 	cuthitime = absolute_max_time;
359 #if HAVE_GETTEXT
360 	(void) setlocale(LC_ALL, "");
361 #ifdef TZ_DOMAINDIR
362 	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
363 #endif /* defined TEXTDOMAINDIR */
364 	(void) textdomain(TZ_DOMAIN);
365 #endif /* HAVE_GETTEXT */
366 	progname = argv[0];
367 	for (i = 1; i < argc; ++i)
368 		if (strcmp(argv[i], "--version") == 0) {
369 			(void) printf("zdump %s%s\n", PKGVERSION, TZVERSION);
370 			exit(EXIT_SUCCESS);
371 		} else if (strcmp(argv[i], "--help") == 0) {
372 			usage(stdout, EXIT_SUCCESS);
373 		}
374 	vflag = Vflag = 0;
375 	cutarg = cuttimes = NULL;
376 #ifdef ICU
377 	aflag = 0;
378 	iflag = 0;
379 	dirarg = NULL;
380 	for (;;)
381 		switch(getopt(argc, argv, "ac:d:it:vV")) {
382 		case 'a': aflag = 1; break;
383 		case 'c': cutarg = optarg; break;
384 		case 'd': dirarg = optarg; break;
385 		case 'i': iflag = 1; break;
386 		case 't': cuttimes = optarg; break;
387 		case 'v': vflag = 1; break;
388 		case 'V': Vflag = 1; break;
389 		case -1:
390 			if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
391 				goto arg_processing_done;
392 		/* Fall through.  */
393 		default:
394 			(void) fprintf(stderr,
395 				_("%s: usage is %s [ --version ] [ -a ] [ -v ] [ -V ] [ -i ] [ -c [loyear,]hiyear ] [ -t [lotime,]hitime] ][ -d dir ] [ zonename ... ]\n"),
396 				progname, progname);
397 			exit(EXIT_FAILURE);
398 		}
399 #else
400 	for (;;)
401 	  switch (getopt(argc, argv, "c:t:vV")) {
402 	  case 'c': cutarg = optarg; break;
403 	  case 't': cuttimes = optarg; break;
404 	  case 'v': vflag = 1; break;
405 	  case 'V': Vflag = 1; break;
406 	  case -1:
407 	    if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
408 	      goto arg_processing_done;
409 	    /* Fall through.  */
410 	  default:
411 	    usage(stderr, EXIT_FAILURE);
412 	  }
413 #endif
414  arg_processing_done:;
415 
416 #ifdef ICU
417 	if (dirarg != NULL) {
418 		DIR *	dp;
419 		/* create the output directory */
420 		mkdir(dirarg, 0777);
421 		if ((dp = opendir(dirarg)) == NULL) {
422 			fprintf(stderr, "cannot create the target directory");
423 			exit(EXIT_FAILURE);
424 		}
425 		closedir(dp);
426 	}
427 #endif
428 
429 	if (vflag | Vflag) {
430 		intmax_t	lo;
431 		intmax_t	hi;
432 		char *loend, *hiend;
433 		register intmax_t cutloyear = ZDUMP_LO_YEAR;
434 		register intmax_t cuthiyear = ZDUMP_HI_YEAR;
435 		if (cutarg != NULL) {
436 			lo = strtoimax(cutarg, &loend, 10);
437 			if (cutarg != loend && !*loend) {
438 				hi = lo;
439 				cuthiyear = hi;
440 			} else if (cutarg != loend && *loend == ','
441 				   && (hi = strtoimax(loend + 1, &hiend, 10),
442 				       loend + 1 != hiend && !*hiend)) {
443 				cutloyear = lo;
444 				cuthiyear = hi;
445 			} else {
446 (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
447 					progname, cutarg);
448 				exit(EXIT_FAILURE);
449 			}
450 		}
451 		if (cutarg != NULL || cuttimes == NULL) {
452 			cutlotime = yeartot(cutloyear);
453 			cuthitime = yeartot(cuthiyear);
454 		}
455 		if (cuttimes != NULL) {
456 			lo = strtoimax(cuttimes, &loend, 10);
457 			if (cuttimes != loend && !*loend) {
458 				hi = lo;
459 				if (hi < cuthitime) {
460 					if (hi < absolute_min_time)
461 						hi = absolute_min_time;
462 					cuthitime = hi;
463 				}
464 			} else if (cuttimes != loend && *loend == ','
465 				   && (hi = strtoimax(loend + 1, &hiend, 10),
466 				       loend + 1 != hiend && !*hiend)) {
467 				if (cutlotime < lo) {
468 					if (absolute_max_time < lo)
469 						lo = absolute_max_time;
470 					cutlotime = lo;
471 				}
472 				if (hi < cuthitime) {
473 					if (hi < absolute_min_time)
474 						hi = absolute_min_time;
475 					cuthitime = hi;
476 				}
477 			} else {
478 				(void) fprintf(stderr,
479 					_("%s: wild -t argument %s\n"),
480 					progname, cuttimes);
481 				exit(EXIT_FAILURE);
482 			}
483 		}
484 	}
485 
486 #ifdef ICU
487 	if (aflag) {
488 		/* get all available zones */
489 		char ** fakeargv;
490 		int i;
491 		int count;
492 
493 		count = getall(&namelist);
494 		fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv);
495 		/*
496 		if ((fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv)) == NULL) {
497 			exit(EXIT_FAILURE);
498 		}
499 		*/
500 		for (i = 0; i < argc; i++) {
501 			fakeargv[i] = argv[i];
502 		}
503 		for (i = 0; i < count; i++) {
504 			fakeargv[i + argc] = namelist->name;
505 			namelist = namelist->next;
506 		}
507 		argv = fakeargv;
508 		argc += count;
509 	}
510 #endif
511 	(void) time(&now);
512 	longest = 0;
513 	for (i = optind; i < argc; ++i)
514 		if (strlen(argv[i]) > longest)
515 			longest = strlen(argv[i]);
516 	{
517 		register int	from;
518 		register int	to;
519 
520 		for (i = 0; environ[i] != NULL; ++i)
521 			continue;
522 		fakeenv = malloc((i + 2) * sizeof *fakeenv);
523 		if (fakeenv == NULL
524 		    || (fakeenv[0] = malloc(longest + 4)) == NULL) {
525 					(void) perror(progname);
526 					exit(EXIT_FAILURE);
527 		}
528 		to = 0;
529 		(void) strcpy(fakeenv[to++], "TZ=");
530 		for (from = 0; environ[from] != NULL; ++from)
531 			if (strncmp(environ[from], "TZ=", 3) != 0)
532 				fakeenv[to++] = environ[from];
533 		fakeenv[to] = NULL;
534 		environ = fakeenv;
535 	}
536 	for (i = optind; i < argc; ++i) {
537 		static char	buf[MAX_STRING_LENGTH];
538 
539 		(void) strcpy(&fakeenv[0][3], argv[i]);
540 		if (! (vflag | Vflag)) {
541 			show(argv[i], now, false);
542 			continue;
543 		}
544 #ifdef ICU
545 		fp = NULL;
546 		if (iflag) {
547 			if (dirarg == NULL) {
548 				/* we want to display a zone name here */
549 				if (i != optind) {
550 					printf("\n");
551 				}
552 				printf("ZONE: %s\n", argv[i]);
553 			} else {
554 				int		zstart;
555 				char	path[FILENAME_MAX + 1];
556 				strcpy(path, dirarg);
557 				strcat(path, "/");
558 				zstart = strlen(path);
559 				strcat(path, argv[i]);
560 				/* replace '/' with '-' */
561 				while(path[++zstart] != 0) {
562 					if (path[zstart] == '/') {
563 						path[zstart] = '-';
564 					}
565 				}
566 				if ((fp = fopen(path, "w")) == NULL) {
567 					fprintf(stderr, "cannot create output file %s\n", path);
568 					exit(EXIT_FAILURE);
569 				}
570 			}
571 		}
572 #endif
573 		warned = false;
574 		t = absolute_min_time;
575 #ifdef ICU
576 		/* skip displaying info for the lowest time, which is actually not
577 		 * a transition when -i option is set */
578 		if (!iflag) {
579 #endif
580 		if (!Vflag) {
581 			show(argv[i], t, true);
582 			t += SECSPERDAY;
583 			show(argv[i], t, true);
584 		}
585 #ifdef ICU
586 		}
587 #endif
588 		if (t < cutlotime)
589 			t = cutlotime;
590 		tmp = my_localtime(&t);
591 		if (tmp != NULL) {
592 			tm = *tmp;
593 			(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
594 		}
595 		for ( ; ; ) {
596 			newt = (t < absolute_max_time - SECSPERDAY / 2
597 				? t + SECSPERDAY / 2
598 				: absolute_max_time);
599 			if (cuthitime <= newt)
600 				break;
601 			newtmp = localtime(&newt);
602 			if (newtmp != NULL)
603 				newtm = *newtmp;
604 #ifdef ICU
605 			if (iflag) {
606 				/* We  do not want to capture transitions just for
607 				 * abbreviated zone name changes */
608 				if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
609 					(delta(&newtm, &tm) != (newt - t) ||
610 					newtm.tm_isdst != tm.tm_isdst)) {
611 						newt = huntICU(argv[i], t, newt, fp);
612 						newtmp = localtime(&newt);
613 						if (newtmp != NULL) {
614 							newtm = *newtmp;
615 							(void) strncpy(buf,
616 								abbr(&newtm),
617 								(sizeof buf) - 1);
618 						}
619 				}
620 			} else {
621 #endif
622 			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
623 				(delta(&newtm, &tm) != (newt - t) ||
624 				newtm.tm_isdst != tm.tm_isdst ||
625 				strcmp(abbr(&newtm), buf) != 0)) {
626 					newt = hunt(argv[i], t, newt);
627 					newtmp = localtime(&newt);
628 					if (newtmp != NULL) {
629 						newtm = *newtmp;
630 						(void) strncpy(buf,
631 							abbr(&newtm),
632 							(sizeof buf) - 1);
633 					}
634 			}
635 #ifdef ICU
636 			}
637 #endif
638 			t = newt;
639 			tm = newtm;
640 			tmp = newtmp;
641 		}
642 #ifdef ICU
643 		if (!iflag) {
644 		/* skip displaying info for the highest time, which is actually not
645 		 * a transition when -i option is used*/
646 #endif
647 		if (!Vflag) {
648 			t = absolute_max_time;
649 			t -= SECSPERDAY;
650 			show(argv[i], t, true);
651 			t += SECSPERDAY;
652 			show(argv[i], t, true);
653 		}
654 #ifdef ICU
655 		}
656 		/* close file */
657 		if (fp != NULL) {
658 			fclose(fp);
659 		}
660 #endif
661 	}
662 	if (fflush(stdout) || ferror(stdout)) {
663 		(void) fprintf(stderr, "%s: ", progname);
664 		(void) perror(_("Error writing to standard output"));
665 		exit(EXIT_FAILURE);
666 	}
667 #ifdef ICU
668 	if (aflag) {
669 		struct listentry *	entry = namelist;
670 		struct listentry *	next;
671 		while (entry != NULL) {
672 			free(entry->name);
673 			next = entry->next;
674 			free(entry);
675 			entry = next;
676 		}
677 	}
678 #endif
679 	exit(EXIT_SUCCESS);
680 	/* If exit fails to exit... */
681 	return EXIT_FAILURE;
682 }
683 
684 static time_t
yeartot(const intmax_t y)685 yeartot(const intmax_t y)
686 {
687 	register intmax_t	myy, seconds, years;
688 	register time_t		t;
689 
690 	myy = EPOCH_YEAR;
691 	t = 0;
692 	while (myy < y) {
693 		if (SECSPER400YEARS_FITS && 400 <= y - myy) {
694 			intmax_t diff400 = (y - myy) / 400;
695 			if (INTMAX_MAX / SECSPER400YEARS < diff400)
696 				return absolute_max_time;
697 			seconds = diff400 * SECSPER400YEARS;
698 			years = diff400 * 400;
699                 } else {
700 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
701 			years = 1;
702 		}
703 		myy += years;
704 		if (t > absolute_max_time - seconds)
705 			return absolute_max_time;
706 		t += seconds;
707 	}
708 	while (y < myy) {
709 		if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
710 			intmax_t diff400 = (myy - y) / 400;
711 			if (INTMAX_MAX / SECSPER400YEARS < diff400)
712 				return absolute_min_time;
713 			seconds = diff400 * SECSPER400YEARS;
714 			years = diff400 * 400;
715 		} else {
716 			seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
717 			years = 1;
718 		}
719 		myy -= years;
720 		if (t < absolute_min_time + seconds)
721 			return absolute_min_time;
722 		t -= seconds;
723 	}
724 	return t;
725 }
726 
727 static time_t
hunt(char * name,time_t lot,time_t hit)728 hunt(char *name, time_t lot, time_t hit)
729 {
730 	time_t			t;
731 	struct tm		lotm;
732 	register struct tm *	lotmp;
733 	struct tm		tm;
734 	register struct tm *	tmp;
735 	char			loab[MAX_STRING_LENGTH];
736 
737 	lotmp = my_localtime(&lot);
738 	if (lotmp != NULL) {
739 		lotm = *lotmp;
740 		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
741 	}
742 	for ( ; ; ) {
743 		time_t diff = hit - lot;
744 		if (diff < 2)
745 			break;
746 		t = lot;
747 		t += diff / 2;
748 		if (t <= lot)
749 			++t;
750 		else if (t >= hit)
751 			--t;
752 		tmp = my_localtime(&t);
753 		if (tmp != NULL)
754 			tm = *tmp;
755 		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
756 			(delta(&tm, &lotm) == (t - lot) &&
757 			tm.tm_isdst == lotm.tm_isdst &&
758 			strcmp(abbr(&tm), loab) == 0)) {
759 				lot = t;
760 				lotm = tm;
761 				lotmp = tmp;
762 		} else	hit = t;
763 	}
764 	show(name, lot, true);
765 	show(name, hit, true);
766 	return hit;
767 }
768 
769 /*
770 ** Thanks to Paul Eggert for logic used in delta.
771 */
772 
773 static intmax_t
delta(struct tm * newp,struct tm * oldp)774 delta(struct tm * newp, struct tm *oldp)
775 {
776 	register intmax_t	result;
777 	register int		tmy;
778 
779 	if (newp->tm_year < oldp->tm_year)
780 		return -delta(oldp, newp);
781 	result = 0;
782 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
783 		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
784 	result += newp->tm_yday - oldp->tm_yday;
785 	result *= HOURSPERDAY;
786 	result += newp->tm_hour - oldp->tm_hour;
787 	result *= MINSPERHOUR;
788 	result += newp->tm_min - oldp->tm_min;
789 	result *= SECSPERMIN;
790 	result += newp->tm_sec - oldp->tm_sec;
791 	return result;
792 }
793 
794 static void
show(char * zone,time_t t,int v)795 show(char *zone, time_t t, int v)
796 {
797 	register struct tm *	tmp;
798 
799 	(void) printf("%-*s  ", (int) longest, zone);
800 	if (v) {
801 		tmp = gmtime(&t);
802 		if (tmp == NULL) {
803 			(void) printf(tformat(), t);
804 		} else {
805 			dumptime(tmp);
806 			(void) printf(" UT");
807 		}
808 		(void) printf(" = ");
809 	}
810 	tmp = my_localtime(&t);
811 	dumptime(tmp);
812 	if (tmp != NULL) {
813 		if (*abbr(tmp) != '\0')
814 			(void) printf(" %s", abbr(tmp));
815 		if (v) {
816 			(void) printf(" isdst=%d", tmp->tm_isdst);
817 #ifdef TM_GMTOFF
818 			(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
819 #endif /* defined TM_GMTOFF */
820 		}
821 	}
822 	(void) printf("\n");
823 	if (tmp != NULL && *abbr(tmp) != '\0')
824 		abbrok(abbr(tmp), zone);
825 }
826 
827 static char *
abbr(struct tm * tmp)828 abbr(struct tm *tmp)
829 {
830 	register char *	result;
831 	static char	nada;
832 
833 	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
834 		return &nada;
835 	result = tzname[tmp->tm_isdst];
836 	return (result == NULL) ? &nada : result;
837 }
838 
839 /*
840 ** The code below can fail on certain theoretical systems;
841 ** it works on all known real-world systems as of 2004-12-30.
842 */
843 
844 static const char *
tformat(void)845 tformat(void)
846 {
847 	if (0 > (time_t) -1) {		/* signed */
848 		if (sizeof (time_t) == sizeof (intmax_t))
849 			return "%"PRIdMAX;
850 		if (sizeof (time_t) > sizeof (long))
851 			return "%lld";
852 		if (sizeof (time_t) > sizeof (int))
853 			return "%ld";
854 		return "%d";
855 	}
856 #ifdef PRIuMAX
857 	if (sizeof (time_t) == sizeof (uintmax_t))
858 		return "%"PRIuMAX;
859 #endif
860 	if (sizeof (time_t) > sizeof (unsigned long))
861 		return "%llu";
862 	if (sizeof (time_t) > sizeof (unsigned int))
863 		return "%lu";
864 	return "%u";
865 }
866 
867 static void
dumptime(register const struct tm * timeptr)868 dumptime(register const struct tm *timeptr)
869 {
870 	static const char	wday_name[][3] = {
871 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
872 	};
873 	static const char	mon_name[][3] = {
874 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
875 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
876 	};
877 	register const char *	wn;
878 	register const char *	mn;
879 	register int		lead;
880 	register int		trail;
881 
882 	if (timeptr == NULL) {
883 		(void) printf("NULL");
884 		return;
885 	}
886 	/*
887 	** The packaged versions of localtime and gmtime never put out-of-range
888 	** values in tm_wday or tm_mon, but since this code might be compiled
889 	** with other (perhaps experimental) versions, paranoia is in order.
890 	*/
891 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
892 		(int) (sizeof wday_name / sizeof wday_name[0]))
893 			wn = "???";
894 	else		wn = wday_name[timeptr->tm_wday];
895 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
896 		(int) (sizeof mon_name / sizeof mon_name[0]))
897 			mn = "???";
898 	else		mn = mon_name[timeptr->tm_mon];
899 	(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
900 		wn, mn,
901 		timeptr->tm_mday, timeptr->tm_hour,
902 		timeptr->tm_min, timeptr->tm_sec);
903 #define DIVISOR	10
904 	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
905 	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
906 		trail / DIVISOR;
907 	trail %= DIVISOR;
908 	if (trail < 0 && lead > 0) {
909 		trail += DIVISOR;
910 		--lead;
911 	} else if (lead < 0 && trail > 0) {
912 		trail -= DIVISOR;
913 		++lead;
914 	}
915 	if (lead == 0)
916 		(void) printf("%d", trail);
917 	else	(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
918 }
919 
920 #ifdef ICU
921 static time_t
huntICU(char * name,time_t lot,time_t hit,FILE * fp)922 huntICU(char *name, time_t lot, time_t hit, FILE * fp)
923 {
924 	time_t			t;
925 	long			diff;
926 	struct tm		lotm;
927 	register struct tm *	lotmp;
928 	struct tm		tm;
929 	register struct tm *	tmp;
930 	char			loab[MAX_STRING_LENGTH];
931 
932 	lotmp = my_localtime(&lot);
933 	if (lotmp != NULL) {
934 		lotm = *lotmp;
935 		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
936 	}
937 	for ( ; ; ) {
938 		diff = (long) (hit - lot);
939 		if (diff < 2)
940 			break;
941 		t = lot;
942 		t += diff / 2;
943 		if (t <= lot)
944 			++t;
945 		else if (t >= hit)
946 			--t;
947 		tmp = my_localtime(&t);
948 		if (tmp != NULL)
949 			tm = *tmp;
950 		/* We  do not want to capture transitions just for
951 		 * abbreviated zone name changes */
952 		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
953 			(delta(&tm, &lotm) == (t - lot) &&
954 			tm.tm_isdst == lotm.tm_isdst)) {
955 				lot = t;
956 				lotm = tm;
957 				lotmp = tmp;
958 		} else	hit = t;
959 	}
960 	showICU(fp, name, lot, hit);
961 	return hit;
962 }
963 
showICU(FILE * fp,char * zone,time_t t1,time_t t2)964 static void showICU(FILE * fp, char *zone, time_t t1, time_t t2)
965 {
966 	if (fp == NULL) {
967 		fp = stdout;
968 	}
969 	dumptimeICU(fp, t1);
970 	fprintf(fp, " > ");
971 	dumptimeICU(fp, t2);
972 	fprintf(fp, "\n");
973 }
974 
dumptimeICU(FILE * fp,time_t t)975 static void dumptimeICU(FILE * fp, time_t t)
976 {
977 	static const char	wday_name[][3] = {
978 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
979 	};
980 	struct tm gmt;
981 	struct tm loc;
982 	register int		lead;
983 	register int		trail;
984 	long	offset;
985 	long	hour, min, sec;
986 
987 	loc = *my_localtime(&t);
988 
989 	trail = loc.tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
990 	lead = loc.tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + trail / DIVISOR;
991 	trail %= DIVISOR;
992 	if (trail < 0 && lead > 0) {
993 		trail += DIVISOR;
994 		--lead;
995 	} else if (lead < 0 && trail > 0) {
996 		trail -= DIVISOR;
997 		++lead;
998 	}
999 
1000 	fprintf(fp, "%04d-%02d-%02d", lead * DIVISOR + trail, loc.tm_mon + 1, loc.tm_mday);
1001 	fprintf(fp, " %.3s ", wday_name[loc.tm_wday]);
1002 	fprintf(fp, "%02d:%02d:%02d", loc.tm_hour, loc.tm_min, loc.tm_sec);
1003 
1004 	gmt = *gmtime(&t);
1005 	offset = delta(&loc, &gmt);
1006 	if (offset < 0) {
1007 		offset = -offset;
1008 		fprintf(fp, "-");
1009 	} else {
1010 		fprintf(fp, "+");
1011 	}
1012 
1013 	sec = offset % 60;
1014 	offset = (offset - sec) / 60;
1015 	min = offset % 60;
1016 	hour = offset / 60;
1017 
1018 	fprintf(fp, "%02ld", hour);
1019 	fprintf(fp, "%02ld", min);
1020 	fprintf(fp, "%02ld", sec);
1021 	fprintf(fp, "[DST=%d]", loc.tm_isdst);
1022 }
1023 
getall(struct listentry ** namelist)1024 static int getall(struct listentry ** namelist) {
1025 	int count = 0;
1026 	struct listentry dummyentry;
1027 	struct listentry *	last = &dummyentry;
1028 
1029 	getzones(TZDIR, NULL, &last, &count);
1030 	if (count > 0) {
1031 		*namelist = dummyentry.next;
1032 	}
1033 
1034 	return count;
1035 }
1036 
getzones(char * basedir,char * relpath,struct listentry ** last,int * count)1037 static void getzones(char * basedir, char * relpath, struct listentry ** last, int * count) {
1038 	char	path[FILENAME_MAX + 1];
1039 	struct	dirent *	dir;
1040 	DIR *	dp;
1041 
1042 	strcpy(path, basedir);
1043 	if (relpath != NULL) {
1044 		strcat(path, "/");
1045 		strcat(path, relpath);
1046 	}
1047 
1048 	if ((dp = opendir(path)) == NULL) {
1049 		/* file */
1050 		if (strstr(relpath, ".tab") == NULL && strcmp(relpath, "Etc/Unknown") != 0) {
1051 			char *		pzonename;
1052 			listentry *	pentry;
1053 
1054 			if ((pzonename = malloc(strlen(relpath) + 1)) == NULL) {
1055 				exit(EXIT_FAILURE);
1056 			}
1057 			strcpy(pzonename, relpath);
1058 
1059 			if ((pentry = malloc(sizeof(listentry))) == NULL) {
1060 				exit(EXIT_FAILURE);
1061 			}
1062 
1063 			pentry->name = pzonename;
1064 			pentry->next = NULL;
1065 			(*last)->next = pentry;
1066 			*last = pentry;
1067 			(*count)++;
1068 		}
1069 	} else {
1070 		/* directory */
1071 		while ((dir = readdir(dp)) != NULL) {
1072 			char	subpath[FILENAME_MAX + 1];
1073 
1074 			if (strcmp(dir->d_name, ".") == 0
1075 				|| strcmp(dir->d_name, "..") == 0) {
1076 				continue;
1077 			}
1078 			if (relpath != NULL) {
1079 				strcpy(subpath, relpath);
1080 				strcat(subpath, "/");
1081 				strcat(subpath, dir->d_name);
1082 			} else {
1083 				strcpy(subpath, dir->d_name);
1084 			}
1085 			getzones(basedir, subpath, last, count);
1086 		}
1087 		closedir(dp);
1088 	}
1089 }
1090 #endif
1091