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