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