zdump.c revision 192625
130829Scharnier#ifndef lint
230829Scharnierstatic const char rcsid[] =
350479Speter  "$FreeBSD: head/usr.sbin/zic/zdump.c 192625 2009-05-23 06:31:50Z edwin $";
4192625Sedwinstatic char	elsieid[] = "@(#)zdump.c	8.8";
530829Scharnier#endif /* not lint */
630829Scharnier
72702Swollman/*
82702Swollman** This code has been made independent of the rest of the time
92702Swollman** conversion package to increase confidence in the verification it provides.
102702Swollman** You can use this code to help in verifying other implementations.
112702Swollman*/
122702Swollman
1330829Scharnier#include <err.h>
1430829Scharnier#include <stdio.h>	/* for stdout, stderr */
1530829Scharnier#include <stdlib.h>	/* for exit, malloc, atoi */
1630829Scharnier#include <string.h>	/* for strcpy */
1730829Scharnier#include <sys/types.h>	/* for time_t */
1830829Scharnier#include <time.h>	/* for struct tm */
1930829Scharnier#include <unistd.h>
20192625Sedwin#include <float.h>	/* for FLT_MAX and DBL_MAX */
21192625Sedwin#include <ctype.h>	/* for isalpha et al. */
22192625Sedwin#ifndef isascii
23192625Sedwin#define isascii(x) 1
24192625Sedwin#endif /* !defined isascii */
252702Swollman
26192625Sedwin#ifndef ZDUMP_LO_YEAR
27192625Sedwin#define ZDUMP_LO_YEAR	(-500)
28192625Sedwin#endif /* !defined ZDUMP_LO_YEAR */
29192625Sedwin
30192625Sedwin#ifndef ZDUMP_HI_YEAR
31192625Sedwin#define ZDUMP_HI_YEAR	2500
32192625Sedwin#endif /* !defined ZDUMP_HI_YEAR */
33192625Sedwin
342702Swollman#ifndef MAX_STRING_LENGTH
352702Swollman#define MAX_STRING_LENGTH	1024
362702Swollman#endif /* !defined MAX_STRING_LENGTH */
372702Swollman
382702Swollman#ifndef TRUE
392702Swollman#define TRUE		1
402702Swollman#endif /* !defined TRUE */
412702Swollman
422702Swollman#ifndef FALSE
432702Swollman#define FALSE		0
442702Swollman#endif /* !defined FALSE */
452702Swollman
462702Swollman#ifndef EXIT_SUCCESS
472702Swollman#define EXIT_SUCCESS	0
482702Swollman#endif /* !defined EXIT_SUCCESS */
492702Swollman
502702Swollman#ifndef EXIT_FAILURE
512702Swollman#define EXIT_FAILURE	1
522702Swollman#endif /* !defined EXIT_FAILURE */
532702Swollman
542702Swollman#ifndef SECSPERMIN
552702Swollman#define SECSPERMIN	60
562702Swollman#endif /* !defined SECSPERMIN */
572702Swollman
582702Swollman#ifndef MINSPERHOUR
592702Swollman#define MINSPERHOUR	60
602702Swollman#endif /* !defined MINSPERHOUR */
612702Swollman
622702Swollman#ifndef SECSPERHOUR
632702Swollman#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
642702Swollman#endif /* !defined SECSPERHOUR */
652702Swollman
662702Swollman#ifndef HOURSPERDAY
672702Swollman#define HOURSPERDAY	24
682702Swollman#endif /* !defined HOURSPERDAY */
692702Swollman
702702Swollman#ifndef EPOCH_YEAR
712702Swollman#define EPOCH_YEAR	1970
722702Swollman#endif /* !defined EPOCH_YEAR */
732702Swollman
742702Swollman#ifndef TM_YEAR_BASE
752702Swollman#define TM_YEAR_BASE	1900
762702Swollman#endif /* !defined TM_YEAR_BASE */
772702Swollman
782702Swollman#ifndef DAYSPERNYEAR
792702Swollman#define DAYSPERNYEAR	365
802702Swollman#endif /* !defined DAYSPERNYEAR */
812702Swollman
822702Swollman#ifndef isleap
83192625Sedwin#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
842702Swollman#endif /* !defined isleap */
852702Swollman
86192625Sedwin#ifndef isleap_sum
87192625Sedwin/*
88192625Sedwin** See tzfile.h for details on isleap_sum.
89192625Sedwin*/
90192625Sedwin#define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
91192625Sedwin#endif /* !defined isleap_sum */
92192625Sedwin
93192625Sedwin#define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
94192625Sedwin#define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
95192625Sedwin#define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
96192625Sedwin
97192625Sedwin#ifndef HAVE_GETTEXT
98192625Sedwin#define HAVE_GETTEXT 0
99192625Sedwin#endif
100192625Sedwin#if HAVE_GETTEXT
10117214Swollman#include "locale.h"	/* for setlocale */
10217214Swollman#include "libintl.h"
103192625Sedwin#endif /* HAVE_GETTEXT */
10417214Swollman
1059937Swollman#ifndef GNUC_or_lint
1069937Swollman#ifdef lint
1079937Swollman#define GNUC_or_lint
108192625Sedwin#else /* !defined lint */
1099937Swollman#ifdef __GNUC__
1109937Swollman#define GNUC_or_lint
1119937Swollman#endif /* defined __GNUC__ */
1129937Swollman#endif /* !defined lint */
1139937Swollman#endif /* !defined GNUC_or_lint */
1149937Swollman
1159937Swollman#ifndef INITIALIZE
1169937Swollman#ifdef GNUC_or_lint
1179937Swollman#define INITIALIZE(x)	((x) = 0)
118192625Sedwin#else /* !defined GNUC_or_lint */
1199937Swollman#define INITIALIZE(x)
1209937Swollman#endif /* !defined GNUC_or_lint */
1219937Swollman#endif /* !defined INITIALIZE */
1229937Swollman
12317214Swollman/*
12417214Swollman** For the benefit of GNU folk...
12517214Swollman** `_(MSGID)' uses the current locale's message library string for MSGID.
12617214Swollman** The default is to use gettext if available, and use MSGID otherwise.
12717214Swollman*/
12817214Swollman
12917214Swollman#ifndef _
130192625Sedwin#if HAVE_GETTEXT
13117214Swollman#define _(msgid) gettext(msgid)
132192625Sedwin#else /* !(HAVE_GETTEXT) */
13317214Swollman#define _(msgid) msgid
134192625Sedwin#endif /* !(HAVE_GETTEXT) */
13517214Swollman#endif /* !defined _ */
13617214Swollman
13717214Swollman#ifndef TZ_DOMAIN
13817214Swollman#define TZ_DOMAIN "tz"
13917214Swollman#endif /* !defined TZ_DOMAIN */
14017214Swollman
1412702Swollmanextern char **	environ;
1422702Swollmanextern char *	tzname[2];
1432702Swollman
144192625Sedwinstatic time_t	absolute_min_time;
145192625Sedwinstatic time_t	absolute_max_time;
14642997Swollmanstatic size_t	longest;
147192625Sedwinstatic char *	progname;
148192625Sedwinstatic int	warned;
1492702Swollman
150192625Sedwinstatic void	usage(const char *progname, FILE *stream, int status);
151192625Sedwinstatic char *	abbr(struct tm * tmp);
152192625Sedwinstatic void	abbrok(const char * abbrp, const char * zone);
153192625Sedwinstatic long	delta(struct tm * newp, struct tm * oldp);
154192625Sedwinstatic void	dumptime(const struct tm * tmp);
155192625Sedwinstatic time_t	hunt(char * name, time_t lot, time_t	hit);
156192625Sedwinstatic void	setabsolutes(void);
157192625Sedwinstatic void	show(char * zone, time_t t, int v);
158192625Sedwinstatic const char *	tformat(void);
159192625Sedwinstatic time_t	yeartot(long y);
160192625Sedwin
161192625Sedwin#ifndef TYPECHECK
162192625Sedwin#define my_localtime	localtime
163192625Sedwin#else /* !defined TYPECHECK */
164192625Sedwinstatic struct tm *
165192625Sedwinmy_localtime(tp)
166192625Sedwintime_t *	tp;
167192625Sedwin{
168192625Sedwin	register struct tm *	tmp;
169192625Sedwin
170192625Sedwin	tmp = localtime(tp);
171192625Sedwin	if (tp != NULL && tmp != NULL) {
172192625Sedwin		struct tm	tm;
173192625Sedwin		register time_t	t;
174192625Sedwin
175192625Sedwin		tm = *tmp;
176192625Sedwin		t = mktime(&tm);
177192625Sedwin		if (t - *tp >= 1 || *tp - t >= 1) {
178192625Sedwin			(void) fflush(stdout);
179192625Sedwin			(void) fprintf(stderr, "\n%s: ", progname);
180192625Sedwin			(void) fprintf(stderr, tformat(), *tp);
181192625Sedwin			(void) fprintf(stderr, " ->");
182192625Sedwin			(void) fprintf(stderr, " year=%d", tmp->tm_year);
183192625Sedwin			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
184192625Sedwin			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
185192625Sedwin			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
186192625Sedwin			(void) fprintf(stderr, " min=%d", tmp->tm_min);
187192625Sedwin			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
188192625Sedwin			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
189192625Sedwin			(void) fprintf(stderr, " -> ");
190192625Sedwin			(void) fprintf(stderr, tformat(), t);
191192625Sedwin			(void) fprintf(stderr, "\n");
192192625Sedwin		}
193192625Sedwin	}
194192625Sedwin	return tmp;
195192625Sedwin}
196192625Sedwin#endif /* !defined TYPECHECK */
197192625Sedwin
198192625Sedwinstatic void
199192625Sedwinabbrok(abbrp, zone)
200192625Sedwinconst char * const	abbrp;
201192625Sedwinconst char * const	zone;
202192625Sedwin{
203192625Sedwin	register const char *	cp;
204192625Sedwin	register char *		wp;
205192625Sedwin
206192625Sedwin	if (warned)
207192625Sedwin		return;
208192625Sedwin	cp = abbrp;
209192625Sedwin	wp = NULL;
210192625Sedwin	while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
211192625Sedwin		++cp;
212192625Sedwin	if (cp - abbrp == 0)
213192625Sedwin		wp = _("lacks alphabetic at start");
214192625Sedwin	else if (cp - abbrp < 3)
215192625Sedwin		wp = _("has fewer than 3 alphabetics");
216192625Sedwin	else if (cp - abbrp > 6)
217192625Sedwin		wp = _("has more than 6 alphabetics");
218192625Sedwin	if (wp == NULL && (*cp == '+' || *cp == '-')) {
219192625Sedwin		++cp;
220192625Sedwin		if (isascii((unsigned char) *cp) &&
221192625Sedwin			isdigit((unsigned char) *cp))
222192625Sedwin				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
223192625Sedwin					++cp;
224192625Sedwin		if (*cp != '\0')
225192625Sedwin			wp = _("differs from POSIX standard");
226192625Sedwin	}
227192625Sedwin	if (wp == NULL)
228192625Sedwin		return;
229192625Sedwin	(void) fflush(stdout);
230192625Sedwin	(void) fprintf(stderr,
231192625Sedwin		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
232192625Sedwin		progname, zone, abbrp, wp);
233192625Sedwin	warned = TRUE;
234192625Sedwin}
235192625Sedwin
2362702Swollmanint
2372702Swollmanmain(argc, argv)
2382702Swollmanint	argc;
2392702Swollmanchar *	argv[];
2402702Swollman{
2419937Swollman	register int		i;
2429937Swollman	register int		c;
2439937Swollman	register int		vflag;
244192625Sedwin	register char *		cutarg;
245192625Sedwin	register long		cutloyear = ZDUMP_LO_YEAR;
246192625Sedwin	register long		cuthiyear = ZDUMP_HI_YEAR;
247192625Sedwin	register time_t		cutlotime;
248192625Sedwin	register time_t		cuthitime;
249192625Sedwin	register char **	fakeenv;
2509937Swollman	time_t			now;
2519937Swollman	time_t			t;
2529937Swollman	time_t			newt;
2539937Swollman	struct tm		tm;
2549937Swollman	struct tm		newtm;
255192625Sedwin	register struct tm *	tmp;
256192625Sedwin	register struct tm *	newtmp;
2572702Swollman
258192625Sedwin	INITIALIZE(cutlotime);
259192625Sedwin	INITIALIZE(cuthitime);
260192625Sedwin#if HAVE_GETTEXT
26117214Swollman	(void) setlocale(LC_MESSAGES, "");
26217214Swollman#ifdef TZ_DOMAINDIR
26317214Swollman	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
264192625Sedwin#endif /* TEXTDOMAINDIR */
26517214Swollman	(void) textdomain(TZ_DOMAIN);
266192625Sedwin#endif /* HAVE_GETTEXT */
267130819Sstefanf	for (i = 1; i < argc; ++i)
268130819Sstefanf		if (strcmp(argv[i], "--version") == 0) {
269130819Sstefanf			errx(EXIT_SUCCESS, "%s", elsieid);
270192625Sedwin		} else if (strcmp(argv[i], "--help") == 0) {
271192625Sedwin			usage(progname, stdout, EXIT_SUCCESS);
272130819Sstefanf		}
2732702Swollman	vflag = 0;
274192625Sedwin	cutarg = NULL;
2752702Swollman	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
2762702Swollman		if (c == 'v')
2772702Swollman			vflag = 1;
278192625Sedwin		else	cutarg = optarg;
279176407Sru	if ((c != -1) ||
2802702Swollman		(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
281192625Sedwin			usage(progname, stderr, EXIT_FAILURE);
2822702Swollman	}
283192625Sedwin	if (vflag) {
284192625Sedwin		if (cutarg != NULL) {
285192625Sedwin			long	lo;
286192625Sedwin			long	hi;
287192625Sedwin			char	dummy;
2882702Swollman
289192625Sedwin			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
290192625Sedwin				cuthiyear = hi;
291192625Sedwin			} else if (sscanf(cutarg, "%ld,%ld%c",
292192625Sedwin				&lo, &hi, &dummy) == 2) {
293192625Sedwin					cutloyear = lo;
294192625Sedwin					cuthiyear = hi;
295192625Sedwin			} else {
296192625Sedwin(void) fprintf(stderr, _("%s: wild -c argument %s\n"),
297192625Sedwin					progname, cutarg);
298192625Sedwin				exit(EXIT_FAILURE);
299192625Sedwin			}
300192625Sedwin		}
301192625Sedwin		setabsolutes();
302192625Sedwin		cutlotime = yeartot(cutloyear);
303192625Sedwin		cuthitime = yeartot(cuthiyear);
3042702Swollman	}
3052702Swollman	(void) time(&now);
3062702Swollman	longest = 0;
3072702Swollman	for (i = optind; i < argc; ++i)
3082702Swollman		if (strlen(argv[i]) > longest)
3092702Swollman			longest = strlen(argv[i]);
3109937Swollman	{
3119937Swollman		register int	from;
3129937Swollman		register int	to;
3132702Swollman
314192625Sedwin		for (i = 0;  environ[i] != NULL; ++i)
3159937Swollman			continue;
3169937Swollman		fakeenv = (char **) malloc((size_t) ((i + 2) *
3179937Swollman			sizeof *fakeenv));
3189937Swollman		if (fakeenv == NULL ||
3199937Swollman			(fakeenv[0] = (char *) malloc((size_t) (longest +
32030829Scharnier				4))) == NULL)
32130829Scharnier					errx(EXIT_FAILURE,
32230829Scharnier					     _("malloc() failed"));
3239937Swollman		to = 0;
3249937Swollman		(void) strcpy(fakeenv[to++], "TZ=");
3259937Swollman		for (from = 0; environ[from] != NULL; ++from)
3269937Swollman			if (strncmp(environ[from], "TZ=", 3) != 0)
3279937Swollman				fakeenv[to++] = environ[from];
3289937Swollman		fakeenv[to] = NULL;
3292702Swollman		environ = fakeenv;
3309937Swollman	}
3319937Swollman	for (i = optind; i < argc; ++i) {
3329937Swollman		static char	buf[MAX_STRING_LENGTH];
3339937Swollman
3349937Swollman		(void) strcpy(&fakeenv[0][3], argv[i]);
33517214Swollman		if (!vflag) {
33617214Swollman			show(argv[i], now, FALSE);
3372702Swollman			continue;
33817214Swollman		}
339192625Sedwin		warned = FALSE;
340192625Sedwin		t = absolute_min_time;
3412702Swollman		show(argv[i], t, TRUE);
3422702Swollman		t += SECSPERHOUR * HOURSPERDAY;
3432702Swollman		show(argv[i], t, TRUE);
344192625Sedwin		if (t < cutlotime)
345192625Sedwin			t = cutlotime;
346192625Sedwin		tmp = my_localtime(&t);
347192625Sedwin		if (tmp != NULL) {
348192625Sedwin			tm = *tmp;
349192625Sedwin			(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
350192625Sedwin		}
3512702Swollman		for ( ; ; ) {
352192625Sedwin			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
3532702Swollman				break;
3542702Swollman			newt = t + SECSPERHOUR * 12;
355192625Sedwin			newtmp = localtime(&newt);
356192625Sedwin			if (newtmp != NULL)
357192625Sedwin				newtm = *newtmp;
358192625Sedwin			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
359192625Sedwin				(delta(&newtm, &tm) != (newt - t) ||
3602702Swollman				newtm.tm_isdst != tm.tm_isdst ||
361192625Sedwin				strcmp(abbr(&newtm), buf) != 0)) {
3622702Swollman					newt = hunt(argv[i], t, newt);
363192625Sedwin					newtmp = localtime(&newt);
364192625Sedwin					if (newtmp != NULL) {
365192625Sedwin						newtm = *newtmp;
366192625Sedwin						(void) strncpy(buf,
367192625Sedwin							abbr(&newtm),
368192625Sedwin							(sizeof buf) - 1);
369192625Sedwin					}
3702702Swollman			}
3712702Swollman			t = newt;
3722702Swollman			tm = newtm;
373192625Sedwin			tmp = newtmp;
3742702Swollman		}
375192625Sedwin		t = absolute_max_time;
3762702Swollman		t -= SECSPERHOUR * HOURSPERDAY;
3772702Swollman		show(argv[i], t, TRUE);
3782702Swollman		t += SECSPERHOUR * HOURSPERDAY;
3792702Swollman		show(argv[i], t, TRUE);
3802702Swollman	}
38130829Scharnier	if (fflush(stdout) || ferror(stdout))
38230829Scharnier		errx(EXIT_FAILURE, _("error writing standard output"));
3832702Swollman	exit(EXIT_SUCCESS);
384192625Sedwin	/* If exit fails to exit... */
385192625Sedwin	return(EXIT_FAILURE);
386192625Sedwin}
3872702Swollman
388192625Sedwinstatic void
389192625Sedwinsetabsolutes(void)
390192625Sedwin{
391192625Sedwin	if (0.5 == (time_t) 0.5) {
392192625Sedwin		/*
393192625Sedwin		** time_t is floating.
394192625Sedwin		*/
395192625Sedwin		if (sizeof (time_t) == sizeof (float)) {
396192625Sedwin			absolute_min_time = (time_t) -FLT_MAX;
397192625Sedwin			absolute_max_time = (time_t) FLT_MAX;
398192625Sedwin		} else if (sizeof (time_t) == sizeof (double)) {
399192625Sedwin			absolute_min_time = (time_t) -DBL_MAX;
400192625Sedwin			absolute_max_time = (time_t) DBL_MAX;
401192625Sedwin		} else {
402192625Sedwin			(void) fprintf(stderr,
403192625Sedwin_("%s: use of -v on system with floating time_t other than float or double\n"),
404192625Sedwin				progname);
405192625Sedwin			exit(EXIT_FAILURE);
406192625Sedwin		}
407192625Sedwin	} else if (0 > (time_t) -1) {
408192625Sedwin		/*
409192625Sedwin		** time_t is signed.  Assume overflow wraps around.
410192625Sedwin		*/
411192625Sedwin		time_t t = 0;
412192625Sedwin		time_t t1 = 1;
413192625Sedwin
414192625Sedwin		while (t < t1) {
415192625Sedwin			t = t1;
416192625Sedwin			t1 = 2 * t1 + 1;
417192625Sedwin		}
418192625Sedwin
419192625Sedwin		absolute_max_time = t;
420192625Sedwin		t = -t;
421192625Sedwin		absolute_min_time = t - 1;
422192625Sedwin		if (t < absolute_min_time)
423192625Sedwin			absolute_min_time = t;
424192625Sedwin	} else {
425192625Sedwin		/*
426192625Sedwin		** time_t is unsigned.
427192625Sedwin		*/
428192625Sedwin		absolute_min_time = 0;
429192625Sedwin		absolute_max_time = absolute_min_time - 1;
430192625Sedwin	}
4312702Swollman}
4322702Swollman
433192625Sedwinstatic time_t
434192625Sedwinyeartot(y)
435192625Sedwinconst long	y;
436192625Sedwin{
437192625Sedwin	register long	myy;
438192625Sedwin	register long	seconds;
439192625Sedwin	register time_t	t;
440192625Sedwin
441192625Sedwin	myy = EPOCH_YEAR;
442192625Sedwin	t = 0;
443192625Sedwin	while (myy != y) {
444192625Sedwin		if (myy < y) {
445192625Sedwin			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
446192625Sedwin			++myy;
447192625Sedwin			if (t > absolute_max_time - seconds) {
448192625Sedwin				t = absolute_max_time;
449192625Sedwin				break;
450192625Sedwin			}
451192625Sedwin			t += seconds;
452192625Sedwin		} else {
453192625Sedwin			--myy;
454192625Sedwin			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
455192625Sedwin			if (t < absolute_min_time + seconds) {
456192625Sedwin				t = absolute_min_time;
457192625Sedwin				break;
458192625Sedwin			}
459192625Sedwin			t -= seconds;
460192625Sedwin		}
461192625Sedwin	}
462192625Sedwin	return t;
463192625Sedwin}
464192625Sedwin
46530829Scharnierstatic void
466192625Sedwinusage(const char *progname, FILE *stream, int status)
46730829Scharnier{
468192625Sedwin	fprintf(stream,
469192625Sedwin_("usage: %s [--version] [-v] [--help] [-c [loyear,]hiyear] zonename ...\n\
470192625Sedwin\n\
471192625SedwinReport bugs to tz@elsie.nci.nih.gov.\n"), progname);
472192625Sedwin	exit(status);
47330829Scharnier}
47430829Scharnier
4752702Swollmanstatic time_t
476192625Sedwinhunt(char *name, time_t lot, time_t hit)
4772702Swollman{
478192625Sedwin	time_t			t;
479192625Sedwin	long			diff;
480192625Sedwin	struct tm		lotm;
481192625Sedwin	register struct tm *	lotmp;
482192625Sedwin	struct tm		tm;
483192625Sedwin	register struct tm *	tmp;
484192625Sedwin	char			loab[MAX_STRING_LENGTH];
4852702Swollman
486192625Sedwin	lotmp = my_localtime(&lot);
487192625Sedwin	if (lotmp != NULL) {
488192625Sedwin		lotm = *lotmp;
489192625Sedwin		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
490192625Sedwin	}
491192625Sedwin	for ( ; ; ) {
492192625Sedwin		diff = (long) (hit - lot);
493192625Sedwin		if (diff < 2)
494192625Sedwin			break;
495192625Sedwin		t = lot;
496192625Sedwin		t += diff / 2;
4972702Swollman		if (t <= lot)
4982702Swollman			++t;
4992702Swollman		else if (t >= hit)
5002702Swollman			--t;
501192625Sedwin		tmp = my_localtime(&t);
502192625Sedwin		if (tmp != NULL)
503192625Sedwin			tm = *tmp;
504192625Sedwin		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
505192625Sedwin			(delta(&tm, &lotm) == (t - lot) &&
5062702Swollman			tm.tm_isdst == lotm.tm_isdst &&
507192625Sedwin			strcmp(abbr(&tm), loab) == 0)) {
5082702Swollman				lot = t;
5092702Swollman				lotm = tm;
510192625Sedwin				lotmp = tmp;
5112702Swollman		} else	hit = t;
5122702Swollman	}
5132702Swollman	show(name, lot, TRUE);
5142702Swollman	show(name, hit, TRUE);
5152702Swollman	return hit;
5162702Swollman}
5172702Swollman
5182702Swollman/*
519192625Sedwin** Thanks to Paul Eggert for logic used in delta.
5202702Swollman*/
5212702Swollman
5222702Swollmanstatic long
5232702Swollmandelta(newp, oldp)
5242702Swollmanstruct tm *	newp;
5252702Swollmanstruct tm *	oldp;
5262702Swollman{
527192625Sedwin	register long	result;
528192625Sedwin	register int	tmy;
5292702Swollman
5302702Swollman	if (newp->tm_year < oldp->tm_year)
5312702Swollman		return -delta(oldp, newp);
5322702Swollman	result = 0;
5332702Swollman	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
534192625Sedwin		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
5352702Swollman	result += newp->tm_yday - oldp->tm_yday;
5362702Swollman	result *= HOURSPERDAY;
5372702Swollman	result += newp->tm_hour - oldp->tm_hour;
5382702Swollman	result *= MINSPERHOUR;
5392702Swollman	result += newp->tm_min - oldp->tm_min;
5402702Swollman	result *= SECSPERMIN;
5412702Swollman	result += newp->tm_sec - oldp->tm_sec;
5422702Swollman	return result;
5432702Swollman}
5442702Swollman
5452702Swollmanstatic void
546192625Sedwinshow(char *zone, time_t t, int v)
5472702Swollman{
548192625Sedwin	register struct tm *	tmp;
5492702Swollman
55042997Swollman	(void) printf("%-*s  ", (int) longest, zone);
5512702Swollman	if (v) {
552192625Sedwin		tmp = gmtime(&t);
553192625Sedwin		if (tmp == NULL) {
554192625Sedwin			(void) printf(tformat(), t);
555192625Sedwin		} else {
556192625Sedwin			dumptime(tmp);
557192625Sedwin			(void) printf(" UTC");
558192625Sedwin		}
559192625Sedwin		(void) printf(" = ");
560192625Sedwin	}
561192625Sedwin	tmp = my_localtime(&t);
562192625Sedwin	dumptime(tmp);
563192625Sedwin	if (tmp != NULL) {
564192625Sedwin		if (*abbr(tmp) != '\0')
565192625Sedwin			(void) printf(" %s", abbr(tmp));
566192625Sedwin		if (v) {
567192625Sedwin			(void) printf(" isdst=%d", tmp->tm_isdst);
5682702Swollman#ifdef TM_GMTOFF
569192625Sedwin			(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
5702702Swollman#endif /* defined TM_GMTOFF */
571192625Sedwin		}
5722702Swollman	}
5732702Swollman	(void) printf("\n");
574192625Sedwin	if (tmp != NULL && *abbr(tmp) != '\0')
575192625Sedwin		abbrok(abbr(tmp), zone);
5762702Swollman}
5772702Swollman
5782702Swollmanstatic char *
5792702Swollmanabbr(tmp)
5802702Swollmanstruct tm *	tmp;
5812702Swollman{
5822702Swollman	register char *	result;
5839937Swollman	static char	nada;
5842702Swollman
5852702Swollman	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
5869937Swollman		return &nada;
5872702Swollman	result = tzname[tmp->tm_isdst];
5889937Swollman	return (result == NULL) ? &nada : result;
5892702Swollman}
590192625Sedwin
591192625Sedwin/*
592192625Sedwin** The code below can fail on certain theoretical systems;
593192625Sedwin** it works on all known real-world systems as of 2004-12-30.
594192625Sedwin*/
595192625Sedwin
596192625Sedwinstatic const char *
597192625Sedwintformat(void)
598192625Sedwin{
599192625Sedwin	if (0.5 == (time_t) 0.5) {	/* floating */
600192625Sedwin		if (sizeof (time_t) > sizeof (double))
601192625Sedwin			return "%Lg";
602192625Sedwin		return "%g";
603192625Sedwin	}
604192625Sedwin	if (0 > (time_t) -1) {		/* signed */
605192625Sedwin		if (sizeof (time_t) > sizeof (long))
606192625Sedwin			return "%lld";
607192625Sedwin		if (sizeof (time_t) > sizeof (int))
608192625Sedwin			return "%ld";
609192625Sedwin		return "%d";
610192625Sedwin	}
611192625Sedwin	if (sizeof (time_t) > sizeof (unsigned long))
612192625Sedwin		return "%llu";
613192625Sedwin	if (sizeof (time_t) > sizeof (unsigned int))
614192625Sedwin		return "%lu";
615192625Sedwin	return "%u";
616192625Sedwin}
617192625Sedwin
618192625Sedwinstatic void
619192625Sedwindumptime(timeptr)
620192625Sedwinregister const struct tm *	timeptr;
621192625Sedwin{
622192625Sedwin	static const char	wday_name[][3] = {
623192625Sedwin		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
624192625Sedwin	};
625192625Sedwin	static const char	mon_name[][3] = {
626192625Sedwin		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
627192625Sedwin		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
628192625Sedwin	};
629192625Sedwin	register const char *	wn;
630192625Sedwin	register const char *	mn;
631192625Sedwin	register int		lead;
632192625Sedwin	register int		trail;
633192625Sedwin
634192625Sedwin	if (timeptr == NULL) {
635192625Sedwin		(void) printf("NULL");
636192625Sedwin		return;
637192625Sedwin	}
638192625Sedwin	/*
639192625Sedwin	** The packaged versions of localtime and gmtime never put out-of-range
640192625Sedwin	** values in tm_wday or tm_mon, but since this code might be compiled
641192625Sedwin	** with other (perhaps experimental) versions, paranoia is in order.
642192625Sedwin	*/
643192625Sedwin	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
644192625Sedwin		(int) (sizeof wday_name / sizeof wday_name[0]))
645192625Sedwin			wn = "???";
646192625Sedwin	else		wn = wday_name[timeptr->tm_wday];
647192625Sedwin	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
648192625Sedwin		(int) (sizeof mon_name / sizeof mon_name[0]))
649192625Sedwin			mn = "???";
650192625Sedwin	else		mn = mon_name[timeptr->tm_mon];
651192625Sedwin	(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
652192625Sedwin		wn, mn,
653192625Sedwin		timeptr->tm_mday, timeptr->tm_hour,
654192625Sedwin		timeptr->tm_min, timeptr->tm_sec);
655192625Sedwin#define DIVISOR	10
656192625Sedwin	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
657192625Sedwin	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
658192625Sedwin		trail / DIVISOR;
659192625Sedwin	trail %= DIVISOR;
660192625Sedwin	if (trail < 0 && lead > 0) {
661192625Sedwin		trail += DIVISOR;
662192625Sedwin		--lead;
663192625Sedwin	} else if (lead < 0 && trail > 0) {
664192625Sedwin		trail -= DIVISOR;
665192625Sedwin		++lead;
666192625Sedwin	}
667192625Sedwin	if (lead == 0)
668192625Sedwin		(void) printf("%d", trail);
669192625Sedwin	else	(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
670192625Sedwin}
671