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