xref: /illumos-gate/usr/src/cmd/zdump/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 
18 static 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 
88 static time_t	absolute_min_time;
89 static time_t	absolute_max_time;
90 static size_t	longest;
91 static char	*progname;
92 static int	warned;
93 
94 static char	*abbr(struct tm *);
95 static void	abbrok(const char *, const char *);
96 static long	delta(struct tm *, struct tm *);
97 static void	dumptime(const struct tm *);
98 static time_t	hunt(char *, time_t, time_t);
99 static void	setabsolutes(void);
100 static void	show(char *, time_t, int);
101 static void	usage(void);
102 static const char	*tformat(void);
103 static time_t	yeartot(long y);
104 
105 #ifndef TYPECHECK
106 #define	my_localtime	localtime
107 #else /* !defined TYPECHECK */
108 static struct tm *
109 my_localtime(tp)
110 time_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 
142 static void
143 abbrok(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 
174 int
175 main(argc, argv)
176 int	argc;
177 char	*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 
340 static void
341 setabsolutes()
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 
385 static time_t
386 yeartot(y)
387 const 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 
417 static time_t
418 hunt(name, lot, hit)
419 char	*name;
420 time_t	lot;
421 time_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 
467 static long
468 delta(newp, oldp)
469 struct tm	*newp;
470 struct 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 
490 static void
491 show(zone, t, v)
492 char	*zone;
493 time_t	t;
494 int	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 
526 static char *
527 abbr(tmp)
528 struct 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 
544 static const char *
545 tformat()
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 
578 static void
579 dumptime(timeptr)
580 register 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 
633 static void
634 usage()
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