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