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