tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

zdump.c (25923B)


      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 *
    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
    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
    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
    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
    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
    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
    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
    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 *
    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 *
    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
    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
    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 
    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 
    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 
   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 
   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