zdump.c revision 192625
1#ifndef lint
2static const char rcsid[] =
3  "$FreeBSD: head/usr.sbin/zic/zdump.c 192625 2009-05-23 06:31:50Z edwin $";
4static char	elsieid[] = "@(#)zdump.c	8.8";
5#endif /* not lint */
6
7/*
8** This code has been made independent of the rest of the time
9** conversion package to increase confidence in the verification it provides.
10** You can use this code to help in verifying other implementations.
11*/
12
13#include <err.h>
14#include <stdio.h>	/* for stdout, stderr */
15#include <stdlib.h>	/* for exit, malloc, atoi */
16#include <string.h>	/* for strcpy */
17#include <sys/types.h>	/* for time_t */
18#include <time.h>	/* for struct tm */
19#include <unistd.h>
20#include <float.h>	/* for FLT_MAX and DBL_MAX */
21#include <ctype.h>	/* for isalpha et al. */
22#ifndef isascii
23#define isascii(x) 1
24#endif /* !defined isascii */
25
26#ifndef ZDUMP_LO_YEAR
27#define ZDUMP_LO_YEAR	(-500)
28#endif /* !defined ZDUMP_LO_YEAR */
29
30#ifndef ZDUMP_HI_YEAR
31#define ZDUMP_HI_YEAR	2500
32#endif /* !defined ZDUMP_HI_YEAR */
33
34#ifndef MAX_STRING_LENGTH
35#define MAX_STRING_LENGTH	1024
36#endif /* !defined MAX_STRING_LENGTH */
37
38#ifndef TRUE
39#define TRUE		1
40#endif /* !defined TRUE */
41
42#ifndef FALSE
43#define FALSE		0
44#endif /* !defined FALSE */
45
46#ifndef EXIT_SUCCESS
47#define EXIT_SUCCESS	0
48#endif /* !defined EXIT_SUCCESS */
49
50#ifndef EXIT_FAILURE
51#define EXIT_FAILURE	1
52#endif /* !defined EXIT_FAILURE */
53
54#ifndef SECSPERMIN
55#define SECSPERMIN	60
56#endif /* !defined SECSPERMIN */
57
58#ifndef MINSPERHOUR
59#define MINSPERHOUR	60
60#endif /* !defined MINSPERHOUR */
61
62#ifndef SECSPERHOUR
63#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
64#endif /* !defined SECSPERHOUR */
65
66#ifndef HOURSPERDAY
67#define HOURSPERDAY	24
68#endif /* !defined HOURSPERDAY */
69
70#ifndef EPOCH_YEAR
71#define EPOCH_YEAR	1970
72#endif /* !defined EPOCH_YEAR */
73
74#ifndef TM_YEAR_BASE
75#define TM_YEAR_BASE	1900
76#endif /* !defined TM_YEAR_BASE */
77
78#ifndef DAYSPERNYEAR
79#define DAYSPERNYEAR	365
80#endif /* !defined DAYSPERNYEAR */
81
82#ifndef isleap
83#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
84#endif /* !defined isleap */
85
86#ifndef isleap_sum
87/*
88** See tzfile.h for details on isleap_sum.
89*/
90#define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
91#endif /* !defined isleap_sum */
92
93#define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
94#define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
95#define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
96
97#ifndef HAVE_GETTEXT
98#define HAVE_GETTEXT 0
99#endif
100#if HAVE_GETTEXT
101#include "locale.h"	/* for setlocale */
102#include "libintl.h"
103#endif /* HAVE_GETTEXT */
104
105#ifndef GNUC_or_lint
106#ifdef lint
107#define GNUC_or_lint
108#else /* !defined lint */
109#ifdef __GNUC__
110#define GNUC_or_lint
111#endif /* defined __GNUC__ */
112#endif /* !defined lint */
113#endif /* !defined GNUC_or_lint */
114
115#ifndef INITIALIZE
116#ifdef GNUC_or_lint
117#define INITIALIZE(x)	((x) = 0)
118#else /* !defined GNUC_or_lint */
119#define INITIALIZE(x)
120#endif /* !defined GNUC_or_lint */
121#endif /* !defined INITIALIZE */
122
123/*
124** For the benefit of GNU folk...
125** `_(MSGID)' uses the current locale's message library string for MSGID.
126** The default is to use gettext if available, and use MSGID otherwise.
127*/
128
129#ifndef _
130#if HAVE_GETTEXT
131#define _(msgid) gettext(msgid)
132#else /* !(HAVE_GETTEXT) */
133#define _(msgid) msgid
134#endif /* !(HAVE_GETTEXT) */
135#endif /* !defined _ */
136
137#ifndef TZ_DOMAIN
138#define TZ_DOMAIN "tz"
139#endif /* !defined TZ_DOMAIN */
140
141extern char **	environ;
142extern char *	tzname[2];
143
144static time_t	absolute_min_time;
145static time_t	absolute_max_time;
146static size_t	longest;
147static char *	progname;
148static int	warned;
149
150static void	usage(const char *progname, FILE *stream, int status);
151static char *	abbr(struct tm * tmp);
152static void	abbrok(const char * abbrp, const char * zone);
153static long	delta(struct tm * newp, struct tm * oldp);
154static void	dumptime(const struct tm * tmp);
155static time_t	hunt(char * name, time_t lot, time_t	hit);
156static void	setabsolutes(void);
157static void	show(char * zone, time_t t, int v);
158static const char *	tformat(void);
159static time_t	yeartot(long y);
160
161#ifndef TYPECHECK
162#define my_localtime	localtime
163#else /* !defined TYPECHECK */
164static struct tm *
165my_localtime(tp)
166time_t *	tp;
167{
168	register struct tm *	tmp;
169
170	tmp = localtime(tp);
171	if (tp != NULL && tmp != NULL) {
172		struct tm	tm;
173		register time_t	t;
174
175		tm = *tmp;
176		t = mktime(&tm);
177		if (t - *tp >= 1 || *tp - t >= 1) {
178			(void) fflush(stdout);
179			(void) fprintf(stderr, "\n%s: ", progname);
180			(void) fprintf(stderr, tformat(), *tp);
181			(void) fprintf(stderr, " ->");
182			(void) fprintf(stderr, " year=%d", tmp->tm_year);
183			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
184			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
185			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
186			(void) fprintf(stderr, " min=%d", tmp->tm_min);
187			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
188			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
189			(void) fprintf(stderr, " -> ");
190			(void) fprintf(stderr, tformat(), t);
191			(void) fprintf(stderr, "\n");
192		}
193	}
194	return tmp;
195}
196#endif /* !defined TYPECHECK */
197
198static void
199abbrok(abbrp, zone)
200const char * const	abbrp;
201const char * const	zone;
202{
203	register const char *	cp;
204	register char *		wp;
205
206	if (warned)
207		return;
208	cp = abbrp;
209	wp = NULL;
210	while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
211		++cp;
212	if (cp - abbrp == 0)
213		wp = _("lacks alphabetic at start");
214	else if (cp - abbrp < 3)
215		wp = _("has fewer than 3 alphabetics");
216	else if (cp - abbrp > 6)
217		wp = _("has more than 6 alphabetics");
218	if (wp == NULL && (*cp == '+' || *cp == '-')) {
219		++cp;
220		if (isascii((unsigned char) *cp) &&
221			isdigit((unsigned char) *cp))
222				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
223					++cp;
224		if (*cp != '\0')
225			wp = _("differs from POSIX standard");
226	}
227	if (wp == NULL)
228		return;
229	(void) fflush(stdout);
230	(void) fprintf(stderr,
231		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
232		progname, zone, abbrp, wp);
233	warned = TRUE;
234}
235
236int
237main(argc, argv)
238int	argc;
239char *	argv[];
240{
241	register int		i;
242	register int		c;
243	register int		vflag;
244	register char *		cutarg;
245	register long		cutloyear = ZDUMP_LO_YEAR;
246	register long		cuthiyear = ZDUMP_HI_YEAR;
247	register time_t		cutlotime;
248	register time_t		cuthitime;
249	register char **	fakeenv;
250	time_t			now;
251	time_t			t;
252	time_t			newt;
253	struct tm		tm;
254	struct tm		newtm;
255	register struct tm *	tmp;
256	register struct tm *	newtmp;
257
258	INITIALIZE(cutlotime);
259	INITIALIZE(cuthitime);
260#if HAVE_GETTEXT
261	(void) setlocale(LC_MESSAGES, "");
262#ifdef TZ_DOMAINDIR
263	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
264#endif /* TEXTDOMAINDIR */
265	(void) textdomain(TZ_DOMAIN);
266#endif /* HAVE_GETTEXT */
267	for (i = 1; i < argc; ++i)
268		if (strcmp(argv[i], "--version") == 0) {
269			errx(EXIT_SUCCESS, "%s", elsieid);
270		} else if (strcmp(argv[i], "--help") == 0) {
271			usage(progname, stdout, EXIT_SUCCESS);
272		}
273	vflag = 0;
274	cutarg = NULL;
275	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
276		if (c == 'v')
277			vflag = 1;
278		else	cutarg = optarg;
279	if ((c != -1) ||
280		(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
281			usage(progname, stderr, EXIT_FAILURE);
282	}
283	if (vflag) {
284		if (cutarg != NULL) {
285			long	lo;
286			long	hi;
287			char	dummy;
288
289			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
290				cuthiyear = hi;
291			} else if (sscanf(cutarg, "%ld,%ld%c",
292				&lo, &hi, &dummy) == 2) {
293					cutloyear = lo;
294					cuthiyear = hi;
295			} else {
296(void) fprintf(stderr, _("%s: wild -c argument %s\n"),
297					progname, cutarg);
298				exit(EXIT_FAILURE);
299			}
300		}
301		setabsolutes();
302		cutlotime = yeartot(cutloyear);
303		cuthitime = yeartot(cuthiyear);
304	}
305	(void) time(&now);
306	longest = 0;
307	for (i = optind; i < argc; ++i)
308		if (strlen(argv[i]) > longest)
309			longest = strlen(argv[i]);
310	{
311		register int	from;
312		register int	to;
313
314		for (i = 0;  environ[i] != NULL; ++i)
315			continue;
316		fakeenv = (char **) malloc((size_t) ((i + 2) *
317			sizeof *fakeenv));
318		if (fakeenv == NULL ||
319			(fakeenv[0] = (char *) malloc((size_t) (longest +
320				4))) == NULL)
321					errx(EXIT_FAILURE,
322					     _("malloc() failed"));
323		to = 0;
324		(void) strcpy(fakeenv[to++], "TZ=");
325		for (from = 0; environ[from] != NULL; ++from)
326			if (strncmp(environ[from], "TZ=", 3) != 0)
327				fakeenv[to++] = environ[from];
328		fakeenv[to] = NULL;
329		environ = fakeenv;
330	}
331	for (i = optind; i < argc; ++i) {
332		static char	buf[MAX_STRING_LENGTH];
333
334		(void) strcpy(&fakeenv[0][3], argv[i]);
335		if (!vflag) {
336			show(argv[i], now, FALSE);
337			continue;
338		}
339		warned = FALSE;
340		t = absolute_min_time;
341		show(argv[i], t, TRUE);
342		t += SECSPERHOUR * HOURSPERDAY;
343		show(argv[i], t, TRUE);
344		if (t < cutlotime)
345			t = cutlotime;
346		tmp = my_localtime(&t);
347		if (tmp != NULL) {
348			tm = *tmp;
349			(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
350		}
351		for ( ; ; ) {
352			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
353				break;
354			newt = t + SECSPERHOUR * 12;
355			newtmp = localtime(&newt);
356			if (newtmp != NULL)
357				newtm = *newtmp;
358			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
359				(delta(&newtm, &tm) != (newt - t) ||
360				newtm.tm_isdst != tm.tm_isdst ||
361				strcmp(abbr(&newtm), buf) != 0)) {
362					newt = hunt(argv[i], t, newt);
363					newtmp = localtime(&newt);
364					if (newtmp != NULL) {
365						newtm = *newtmp;
366						(void) strncpy(buf,
367							abbr(&newtm),
368							(sizeof buf) - 1);
369					}
370			}
371			t = newt;
372			tm = newtm;
373			tmp = newtmp;
374		}
375		t = absolute_max_time;
376		t -= SECSPERHOUR * HOURSPERDAY;
377		show(argv[i], t, TRUE);
378		t += SECSPERHOUR * HOURSPERDAY;
379		show(argv[i], t, TRUE);
380	}
381	if (fflush(stdout) || ferror(stdout))
382		errx(EXIT_FAILURE, _("error writing standard output"));
383	exit(EXIT_SUCCESS);
384	/* If exit fails to exit... */
385	return(EXIT_FAILURE);
386}
387
388static void
389setabsolutes(void)
390{
391	if (0.5 == (time_t) 0.5) {
392		/*
393		** time_t is floating.
394		*/
395		if (sizeof (time_t) == sizeof (float)) {
396			absolute_min_time = (time_t) -FLT_MAX;
397			absolute_max_time = (time_t) FLT_MAX;
398		} else if (sizeof (time_t) == sizeof (double)) {
399			absolute_min_time = (time_t) -DBL_MAX;
400			absolute_max_time = (time_t) DBL_MAX;
401		} else {
402			(void) fprintf(stderr,
403_("%s: use of -v on system with floating time_t other than float or double\n"),
404				progname);
405			exit(EXIT_FAILURE);
406		}
407	} else if (0 > (time_t) -1) {
408		/*
409		** time_t is signed.  Assume overflow wraps around.
410		*/
411		time_t t = 0;
412		time_t t1 = 1;
413
414		while (t < t1) {
415			t = t1;
416			t1 = 2 * t1 + 1;
417		}
418
419		absolute_max_time = t;
420		t = -t;
421		absolute_min_time = t - 1;
422		if (t < absolute_min_time)
423			absolute_min_time = t;
424	} else {
425		/*
426		** time_t is unsigned.
427		*/
428		absolute_min_time = 0;
429		absolute_max_time = absolute_min_time - 1;
430	}
431}
432
433static time_t
434yeartot(y)
435const long	y;
436{
437	register long	myy;
438	register long	seconds;
439	register time_t	t;
440
441	myy = EPOCH_YEAR;
442	t = 0;
443	while (myy != y) {
444		if (myy < y) {
445			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
446			++myy;
447			if (t > absolute_max_time - seconds) {
448				t = absolute_max_time;
449				break;
450			}
451			t += seconds;
452		} else {
453			--myy;
454			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
455			if (t < absolute_min_time + seconds) {
456				t = absolute_min_time;
457				break;
458			}
459			t -= seconds;
460		}
461	}
462	return t;
463}
464
465static void
466usage(const char *progname, FILE *stream, int status)
467{
468	fprintf(stream,
469_("usage: %s [--version] [-v] [--help] [-c [loyear,]hiyear] zonename ...\n\
470\n\
471Report bugs to tz@elsie.nci.nih.gov.\n"), progname);
472	exit(status);
473}
474
475static time_t
476hunt(char *name, time_t lot, time_t hit)
477{
478	time_t			t;
479	long			diff;
480	struct tm		lotm;
481	register struct tm *	lotmp;
482	struct tm		tm;
483	register struct tm *	tmp;
484	char			loab[MAX_STRING_LENGTH];
485
486	lotmp = my_localtime(&lot);
487	if (lotmp != NULL) {
488		lotm = *lotmp;
489		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
490	}
491	for ( ; ; ) {
492		diff = (long) (hit - lot);
493		if (diff < 2)
494			break;
495		t = lot;
496		t += diff / 2;
497		if (t <= lot)
498			++t;
499		else if (t >= hit)
500			--t;
501		tmp = my_localtime(&t);
502		if (tmp != NULL)
503			tm = *tmp;
504		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
505			(delta(&tm, &lotm) == (t - lot) &&
506			tm.tm_isdst == lotm.tm_isdst &&
507			strcmp(abbr(&tm), loab) == 0)) {
508				lot = t;
509				lotm = tm;
510				lotmp = tmp;
511		} else	hit = t;
512	}
513	show(name, lot, TRUE);
514	show(name, hit, TRUE);
515	return hit;
516}
517
518/*
519** Thanks to Paul Eggert for logic used in delta.
520*/
521
522static long
523delta(newp, oldp)
524struct tm *	newp;
525struct tm *	oldp;
526{
527	register long	result;
528	register int	tmy;
529
530	if (newp->tm_year < oldp->tm_year)
531		return -delta(oldp, newp);
532	result = 0;
533	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
534		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
535	result += newp->tm_yday - oldp->tm_yday;
536	result *= HOURSPERDAY;
537	result += newp->tm_hour - oldp->tm_hour;
538	result *= MINSPERHOUR;
539	result += newp->tm_min - oldp->tm_min;
540	result *= SECSPERMIN;
541	result += newp->tm_sec - oldp->tm_sec;
542	return result;
543}
544
545static void
546show(char *zone, time_t t, int v)
547{
548	register struct tm *	tmp;
549
550	(void) printf("%-*s  ", (int) longest, zone);
551	if (v) {
552		tmp = gmtime(&t);
553		if (tmp == NULL) {
554			(void) printf(tformat(), t);
555		} else {
556			dumptime(tmp);
557			(void) printf(" UTC");
558		}
559		(void) printf(" = ");
560	}
561	tmp = my_localtime(&t);
562	dumptime(tmp);
563	if (tmp != NULL) {
564		if (*abbr(tmp) != '\0')
565			(void) printf(" %s", abbr(tmp));
566		if (v) {
567			(void) printf(" isdst=%d", tmp->tm_isdst);
568#ifdef TM_GMTOFF
569			(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
570#endif /* defined TM_GMTOFF */
571		}
572	}
573	(void) printf("\n");
574	if (tmp != NULL && *abbr(tmp) != '\0')
575		abbrok(abbr(tmp), zone);
576}
577
578static char *
579abbr(tmp)
580struct tm *	tmp;
581{
582	register char *	result;
583	static char	nada;
584
585	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
586		return &nada;
587	result = tzname[tmp->tm_isdst];
588	return (result == NULL) ? &nada : result;
589}
590
591/*
592** The code below can fail on certain theoretical systems;
593** it works on all known real-world systems as of 2004-12-30.
594*/
595
596static const char *
597tformat(void)
598{
599	if (0.5 == (time_t) 0.5) {	/* floating */
600		if (sizeof (time_t) > sizeof (double))
601			return "%Lg";
602		return "%g";
603	}
604	if (0 > (time_t) -1) {		/* signed */
605		if (sizeof (time_t) > sizeof (long))
606			return "%lld";
607		if (sizeof (time_t) > sizeof (int))
608			return "%ld";
609		return "%d";
610	}
611	if (sizeof (time_t) > sizeof (unsigned long))
612		return "%llu";
613	if (sizeof (time_t) > sizeof (unsigned int))
614		return "%lu";
615	return "%u";
616}
617
618static void
619dumptime(timeptr)
620register const struct tm *	timeptr;
621{
622	static const char	wday_name[][3] = {
623		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
624	};
625	static const char	mon_name[][3] = {
626		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
627		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
628	};
629	register const char *	wn;
630	register const char *	mn;
631	register int		lead;
632	register int		trail;
633
634	if (timeptr == NULL) {
635		(void) printf("NULL");
636		return;
637	}
638	/*
639	** The packaged versions of localtime and gmtime never put out-of-range
640	** values in tm_wday or tm_mon, but since this code might be compiled
641	** with other (perhaps experimental) versions, paranoia is in order.
642	*/
643	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
644		(int) (sizeof wday_name / sizeof wday_name[0]))
645			wn = "???";
646	else		wn = wday_name[timeptr->tm_wday];
647	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
648		(int) (sizeof mon_name / sizeof mon_name[0]))
649			mn = "???";
650	else		mn = mon_name[timeptr->tm_mon];
651	(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
652		wn, mn,
653		timeptr->tm_mday, timeptr->tm_hour,
654		timeptr->tm_min, timeptr->tm_sec);
655#define DIVISOR	10
656	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
657	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
658		trail / DIVISOR;
659	trail %= DIVISOR;
660	if (trail < 0 && lead > 0) {
661		trail += DIVISOR;
662		--lead;
663	} else if (lead < 0 && trail > 0) {
664		trail -= DIVISOR;
665		++lead;
666	}
667	if (lead == 0)
668		(void) printf("%d", trail);
669	else	(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
670}
671