xref: /illumos-gate/usr/src/cmd/zic/zic.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1 /*
2  * Copyright (c) 1986-1999, by Sun Microsystems, Inc.
3  * All rights reserved.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /* static char	elsieid[] = "@(#)zic.c	7.99"; */
9 
10 /*
11  * #define	LEAPSECOND_SUPPORT
12  */
13 
14 #include "private.h"
15 #include <tzfile.h>			/* this is in system headers at Sun */
16 
17 #include <sys/stat.h>			/* for umask manifest constants */
18 #include <ctype.h>
19 #include <locale.h>
20 #include <stdlib.h>			/* for getopt */
21 
22 struct rule {
23 	const char	*r_filename;
24 	int		r_linenum;
25 	const char	*r_name;
26 
27 	int		r_loyear;	/* for example, 1986 */
28 	int		r_hiyear;	/* for example, 1986 */
29 	const char	*r_yrtype;
30 
31 	int		r_month;	/* 0..11 */
32 
33 	int		r_dycode;	/* see below */
34 	int		r_dayofmonth;
35 	int		r_wday;
36 
37 	long		r_tod;		/* time from midnight */
38 	int		r_todisstd;	/* above is standard time if TRUE */
39 					/* or wall clock time if FALSE */
40 	int		r_todisgmt;	/* above is GMT if TRUE */
41 					/* or local time if FALSE */
42 	long		r_stdoff;	/* offset from standard time */
43 	const char	*r_abbrvar;	/* variable part of abbreviation */
44 
45 	int		r_todo;		/* a rule to do (used in outzone) */
46 	time_t		r_temp;		/* used in outzone */
47 };
48 
49 /*
50  *	r_dycode		r_dayofmonth	r_wday
51  */
52 
53 #define	DC_DOM		0	/* 1..31 */	/* unused */
54 #define	DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */
55 #define	DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */
56 
57 struct zone {
58 	const char	*z_filename;
59 	int		z_linenum;
60 
61 	const char	*z_name;
62 	long		z_gmtoff;
63 	const char	*z_rule;
64 	const char	*z_format;
65 
66 	long		z_stdoff;
67 
68 	struct rule	*z_rules;
69 	int		z_nrules;
70 
71 	struct rule	z_untilrule;
72 	time_t		z_untiltime;
73 };
74 
75 static void	addtt(time_t starttime, int type);
76 static int	addtype(long gmtoff, const char *abbr, int isdst,
77 				int ttisstd, int ttisgmt);
78 #ifdef LEAPSECOND_SUPPORT
79 static void	leapadd(time_t t, int positive, int rolling, int count);
80 static void	adjleap(void);
81 #endif
82 static void	associate(void);
83 static int	ciequal(const char *ap, const char *bp);
84 static void	convert(long val, char *buf);
85 static void	dolink(const char *fromfile, const char *tofile);
86 static void	doabbr(char *abbr, const char *format,
87 			const char *letters, int isdst);
88 static void	eat(const char *name, int num);
89 static void	eats(const char *name, int num,
90 			const char *rname, int rnum);
91 static long	eitol(int i);
92 static void	error(const char *message);
93 static char	**getfields(char *buf);
94 static long	gethms(const char *string, const char *errstrng, int signable);
95 static void	infile(const char *filename);
96 #ifdef LEAPSECOND_SUPPORT
97 static void	inleap(char **fields, int nfields);
98 #endif
99 static void	inlink(char **fields, int nfields);
100 static void	inrule(char **fields, int nfields);
101 static int	inzcont(char **fields, int nfields);
102 static int	inzone(char **fields, int nfields);
103 static int	inzsub(char **fields, int nfields, int iscont);
104 static int	itsabbr(const char *abbr, const char *word);
105 static int	itsdir(const char *name);
106 static int	lowerit(int c);
107 static char	*memcheck(char *tocheck);
108 static int	mkdirs(char *filename);
109 static void	newabbr(const char *abbr);
110 static long	oadd(long t1, long t2);
111 static void	outzone(const struct zone *zp, int ntzones);
112 static void	puttzcode(long code, FILE *fp);
113 static int	rcomp(const void *leftp, const void *rightp);
114 static time_t	rpytime(const struct rule *rp, int wantedy);
115 static void	rulesub(struct rule *rp,
116 			const char *loyearp, const char *hiyearp,
117 			const char *typep, const char *monthp,
118 			const char *dayp, const char *timep);
119 static void	setboundaries(void);
120 static time_t	tadd(time_t t1, long t2);
121 static void	usage(void);
122 static void	writezone(const char *name);
123 static int	yearistype(int year, const char *type);
124 
125 static int		charcnt;
126 static int		errors;
127 static const char	*filename;
128 static int		leapcnt;
129 static int		linenum;
130 static time_t		max_time;
131 static int		max_year;
132 static int		max_year_representable;
133 static time_t		min_time;
134 static int		min_year;
135 static int		min_year_representable;
136 static int		noise;
137 static const char	*rfilename;
138 static int		rlinenum;
139 static const char	*progname;
140 static int		timecnt;
141 static int		typecnt;
142 
143 /*
144  * Line codes.
145  */
146 
147 #define	LC_RULE		0
148 #define	LC_ZONE		1
149 #define	LC_LINK		2
150 #define	LC_LEAP		3
151 
152 /*
153  * Which fields are which on a Zone line.
154  */
155 
156 #define	ZF_NAME		1
157 #define	ZF_GMTOFF	2
158 #define	ZF_RULE		3
159 #define	ZF_FORMAT	4
160 #define	ZF_TILYEAR	5
161 #define	ZF_TILMONTH	6
162 #define	ZF_TILDAY	7
163 #define	ZF_TILTIME	8
164 #define	ZONE_MINFIELDS	5
165 #define	ZONE_MAXFIELDS	9
166 
167 /*
168  * Which fields are which on a Zone continuation line.
169  */
170 
171 #define	ZFC_GMTOFF	0
172 #define	ZFC_RULE	1
173 #define	ZFC_FORMAT	2
174 #define	ZFC_TILYEAR	3
175 #define	ZFC_TILMONTH	4
176 #define	ZFC_TILDAY	5
177 #define	ZFC_TILTIME	6
178 #define	ZONEC_MINFIELDS	3
179 #define	ZONEC_MAXFIELDS	7
180 
181 /*
182  * Which files are which on a Rule line.
183  */
184 
185 #define	RF_NAME		1
186 #define	RF_LOYEAR	2
187 #define	RF_HIYEAR	3
188 #define	RF_COMMAND	4
189 #define	RF_MONTH	5
190 #define	RF_DAY		6
191 #define	RF_TOD		7
192 #define	RF_STDOFF	8
193 #define	RF_ABBRVAR	9
194 #define	RULE_FIELDS	10
195 
196 /*
197  * Which fields are which on a Link line.
198  */
199 
200 #define	LF_FROM		1
201 #define	LF_TO		2
202 #define	LINK_FIELDS	3
203 
204 /*
205  * Which fields are which on a Leap line.
206  */
207 
208 #define	LP_YEAR		1
209 #define	LP_MONTH	2
210 #define	LP_DAY		3
211 #define	LP_TIME		4
212 #define	LP_CORR		5
213 #define	LP_ROLL		6
214 #define	LEAP_FIELDS	7
215 
216 /*
217  * Year synonyms.
218  */
219 
220 #define	YR_MINIMUM	0
221 #define	YR_MAXIMUM	1
222 #define	YR_ONLY		2
223 
224 static struct rule	*rules;
225 static int		nrules;	/* number of rules */
226 
227 static struct zone	*zones;
228 static int		nzones;	/* number of zones */
229 
230 struct link {
231 	const char	*l_filename;
232 	int		l_linenum;
233 	const char	*l_from;
234 	const char	*l_to;
235 };
236 
237 static struct link	*links;
238 static int		nlinks;
239 
240 struct lookup {
241 	const char	*l_word;
242 	const int	l_value;
243 };
244 
245 static struct lookup const *byword(const char *string,
246     const struct lookup *lp);
247 
248 static struct lookup const	line_codes[] = {
249 	{ "Rule",	LC_RULE },
250 	{ "Zone",	LC_ZONE },
251 	{ "Link",	LC_LINK },
252 	{ "Leap",	LC_LEAP },
253 	{ NULL,		0}
254 };
255 
256 static struct lookup const	mon_names[] = {
257 	{ "January",	TM_JANUARY },
258 	{ "February",	TM_FEBRUARY },
259 	{ "March",	TM_MARCH },
260 	{ "April",	TM_APRIL },
261 	{ "May",	TM_MAY },
262 	{ "June",	TM_JUNE },
263 	{ "July",	TM_JULY },
264 	{ "August",	TM_AUGUST },
265 	{ "September",	TM_SEPTEMBER },
266 	{ "October",	TM_OCTOBER },
267 	{ "November",	TM_NOVEMBER },
268 	{ "December",	TM_DECEMBER },
269 	{ NULL,		0 }
270 };
271 
272 static struct lookup const	wday_names[] = {
273 	{ "Sunday",	TM_SUNDAY },
274 	{ "Monday",	TM_MONDAY },
275 	{ "Tuesday",	TM_TUESDAY },
276 	{ "Wednesday",	TM_WEDNESDAY },
277 	{ "Thursday",	TM_THURSDAY },
278 	{ "Friday",	TM_FRIDAY },
279 	{ "Saturday",	TM_SATURDAY },
280 	{ NULL,		0 }
281 };
282 
283 static struct lookup const	lasts[] = {
284 	{ "last-Sunday",	TM_SUNDAY },
285 	{ "last-Monday",	TM_MONDAY },
286 	{ "last-Tuesday",	TM_TUESDAY },
287 	{ "last-Wednesday",	TM_WEDNESDAY },
288 	{ "last-Thursday",	TM_THURSDAY },
289 	{ "last-Friday",	TM_FRIDAY },
290 	{ "last-Saturday",	TM_SATURDAY },
291 	{ NULL,			0 }
292 };
293 
294 static struct lookup const	begin_years[] = {
295 	{ "minimum",	YR_MINIMUM },
296 	{ "maximum",	YR_MAXIMUM },
297 	{ NULL,		0 }
298 };
299 
300 static struct lookup const	end_years[] = {
301 	{ "minimum",	YR_MINIMUM },
302 	{ "maximum",	YR_MAXIMUM },
303 	{ "only",	YR_ONLY },
304 	{ NULL,		0 }
305 };
306 
307 static struct lookup const	leap_types[] = {
308 	{ "Rolling",	TRUE },
309 	{ "Stationary",	FALSE },
310 	{ NULL,		0 }
311 };
312 
313 static const int	len_months[2][MONSPERYEAR] = {
314 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
315 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
316 };
317 
318 static const int	len_years[2] = {
319 	DAYSPERNYEAR, DAYSPERLYEAR
320 };
321 
322 static struct attype {
323 	time_t		at;
324 	unsigned char	type;
325 }			attypes[TZ_MAX_TIMES];
326 static long		gmtoffs[TZ_MAX_TYPES];
327 static char		isdsts[TZ_MAX_TYPES];
328 static unsigned char	abbrinds[TZ_MAX_TYPES];
329 static char		ttisstds[TZ_MAX_TYPES];
330 static char		ttisgmts[TZ_MAX_TYPES];
331 static char		chars[TZ_MAX_CHARS];
332 static time_t		trans[TZ_MAX_LEAPS];
333 static long		corr[TZ_MAX_LEAPS];
334 static char		roll[TZ_MAX_LEAPS];
335 
336 /*
337  * Memory allocation.
338  */
339 
340 static char *
341 memcheck(ptr)
342 char * const	ptr;
343 {
344 	if (ptr == NULL) {
345 		const char *e = strerror(errno);
346 		(void) fprintf(stderr, gettext("%s: Memory exhausted: %s\n"),
347 			progname, e);
348 		(void) exit(EXIT_FAILURE);
349 	}
350 	return (ptr);
351 }
352 
353 #define	emalloc(size)		memcheck(imalloc(size))
354 #define	erealloc(ptr, size)	memcheck(irealloc((ptr), (size)))
355 #define	ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
356 #define	ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
357 
358 /*
359  * Error handling.
360  */
361 
362 static void
363 eats(name, num, rname, rnum)
364 const char * const	name;
365 const int		num;
366 const char * const	rname;
367 const int		rnum;
368 {
369 	filename = name;
370 	linenum = num;
371 	rfilename = rname;
372 	rlinenum = rnum;
373 }
374 
375 static void
376 eat(name, num)
377 const char * const	name;
378 const int		num;
379 {
380 	eats(name, num, (char *)NULL, -1);
381 }
382 
383 static void
384 error(string)
385 const char * const	string;
386 {
387 	/*
388 	 * Match the format of "cc" to allow sh users to
389 	 *	zic ... 2>&1 | error -t "*" -v
390 	 * on BSD systems.
391 	 */
392 	(void) fprintf(stderr, gettext("\"%s\", line %d: %s"),
393 		filename, linenum, string);
394 	if (rfilename != NULL)
395 		(void) fprintf(stderr, gettext(" (rule from \"%s\", line %d)"),
396 			rfilename, rlinenum);
397 	(void) fprintf(stderr, "\n");
398 	++errors;
399 }
400 
401 static void
402 warning(string)
403 const char * const	string;
404 {
405 	char *cp;
406 
407 	cp = ecpyalloc(gettext("warning: "));
408 	cp = ecatalloc(cp, string);
409 	error(cp);
410 	ifree(cp);
411 	--errors;
412 }
413 
414 static void
415 usage(void)
416 {
417 #ifdef LEAPSECOND_SUPPORT
418 	(void) fprintf(stderr, gettext(
419 "usage: %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n"
420 "\t[ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"),
421 		progname);
422 #else /* ! LEAPSECOND_SUPPORT */
423 	(void) fprintf(stderr, gettext(
424 "usage: %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n"
425 "\t[ -y yearistype ] [ filename ... ]\n"),
426 		progname);
427 #endif /* LEAPSECOND_SUPPORT */
428 }
429 
430 static const char	*psxrules;
431 static const char	*lcltime;
432 static const char	*directory;
433 static const char	*leapsec;
434 static const char	*yitcommand;
435 static int		sflag = FALSE;
436 
437 int
438 main(argc, argv)
439 int	argc;
440 char	*argv[];
441 {
442 	register int	i;
443 	register int	j;
444 	register int	c;
445 
446 	(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
447 
448 	(void) setlocale(LC_ALL, "");
449 #if !defined(TEXT_DOMAIN)
450 #define	TEXT_DOMAIN	"SYS_TEST"
451 #endif
452 	(void) textdomain(TEXT_DOMAIN);
453 
454 	progname = argv[0];
455 #ifdef LEAPSECOND_SUPPORT
456 	while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF)
457 #else
458 	while ((c = getopt(argc, argv, "d:l:p:vsy:")) != EOF)
459 #endif
460 		switch (c) {
461 			default:
462 				usage();
463 			case 'd':
464 				if (directory == NULL)
465 					directory = optarg;
466 				else {
467 					(void) fprintf(stderr, gettext(
468 "%s: More than one -d option specified\n"),
469 						progname);
470 					(void) exit(EXIT_FAILURE);
471 				}
472 				break;
473 			case 'l':
474 				if (lcltime == NULL)
475 					lcltime = optarg;
476 				else {
477 					(void) fprintf(stderr, gettext(
478 "%s: More than one -l option specified\n"),
479 						progname);
480 					(void) exit(EXIT_FAILURE);
481 				}
482 				break;
483 			case 'p':
484 				if (psxrules == NULL)
485 					psxrules = optarg;
486 				else {
487 					(void) fprintf(stderr, gettext(
488 "%s: More than one -p option specified\n"),
489 						progname);
490 					(void) exit(EXIT_FAILURE);
491 				}
492 				break;
493 			case 'y':
494 				if (yitcommand == NULL)
495 					yitcommand = optarg;
496 				else {
497 					(void) fprintf(stderr, gettext(
498 "%s: More than one -y option specified\n"),
499 						progname);
500 					(void) exit(EXIT_FAILURE);
501 				}
502 				break;
503 #ifdef LEAPSECOND_SUPPORT
504 			case 'L':
505 				if (leapsec == NULL)
506 					leapsec = optarg;
507 				else {
508 					(void) fprintf(stderr, gettext(
509 "%s: More than one -L option specified\n"),
510 						progname);
511 					(void) exit(EXIT_FAILURE);
512 				}
513 				break;
514 #endif /* LEAPSECOND_SUPPORT */
515 			case 'v':
516 				noise = TRUE;
517 				break;
518 			case 's':
519 				sflag = TRUE;
520 				break;
521 		}
522 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
523 		usage();	/* usage message by request */
524 	if (directory == NULL)
525 		directory = TZDIR;
526 	if (yitcommand == NULL)
527 		yitcommand = "yearistype";
528 
529 	setboundaries();
530 
531 #ifdef LEAPSECOND_SUPPORT
532 	if (optind < argc && leapsec != NULL) {
533 		infile(leapsec);
534 		adjleap();
535 	}
536 #endif /* LEAPSECOND_SUPPORT */
537 
538 	for (i = optind; i < argc; ++i)
539 		infile(argv[i]);
540 	if (errors)
541 		(void) exit(EXIT_FAILURE);
542 	associate();
543 	for (i = 0; i < nzones; i = j) {
544 		/*
545 		 * Find the next non-continuation zone entry.
546 		 */
547 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
548 			continue;
549 		outzone(&zones[i], j - i);
550 	}
551 	/*
552 	 * Make links.
553 	 */
554 	for (i = 0; i < nlinks; ++i) {
555 		eat(links[i].l_filename, links[i].l_linenum);
556 		dolink(links[i].l_from, links[i].l_to);
557 	}
558 	if (lcltime != NULL) {
559 		eat("command line", 1);
560 		dolink(lcltime, TZDEFAULT);
561 	}
562 	if (psxrules != NULL) {
563 		eat("command line", 1);
564 		dolink(psxrules, TZDEFRULES);
565 	}
566 	return ((errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
567 }
568 
569 static void
570 dolink(fromfile, tofile)
571 const char * const	fromfile;
572 const char * const	tofile;
573 {
574 	register char *fromname;
575 	register char *toname;
576 
577 	if (fromfile[0] == '/')
578 		fromname = ecpyalloc(fromfile);
579 	else {
580 		fromname = ecpyalloc(directory);
581 		fromname = ecatalloc(fromname, "/");
582 		fromname = ecatalloc(fromname, fromfile);
583 	}
584 	if (tofile[0] == '/')
585 		toname = ecpyalloc(tofile);
586 	else {
587 		toname = ecpyalloc(directory);
588 		toname = ecatalloc(toname, "/");
589 		toname = ecatalloc(toname, tofile);
590 	}
591 	/*
592 	 * We get to be careful here since
593 	 * there's a fair chance of root running us.
594 	 */
595 	if (!itsdir(toname))
596 		(void) remove(toname);
597 	if (link(fromname, toname) != 0) {
598 		int	result;
599 
600 		if (mkdirs(toname) != 0)
601 			(void) exit(EXIT_FAILURE);
602 
603 		result = link(fromname, toname);
604 
605 		if (result != 0) {
606 			char *s = (char *)tofile;
607 			register char *symlinkcontents = NULL;
608 			while ((s = strchr(s+1, '/')) != NULL)
609 				symlinkcontents = ecatalloc(symlinkcontents,
610 				    "../");
611 			symlinkcontents = ecatalloc(symlinkcontents, fromfile);
612 
613 			result = symlink(symlinkcontents, toname);
614 			if (result == 0)
615 				warning(gettext(
616 				    "hard link failed, symbolic link used"));
617 			ifree(symlinkcontents);
618 		}
619 
620 		if (result != 0) {
621 			const char *e = strerror(errno);
622 
623 			(void) fprintf(stderr, gettext(
624 				"%s: Can't link from %s to %s: %s\n"),
625 				progname, fromname, toname, e);
626 			(void) exit(EXIT_FAILURE);
627 		}
628 	}
629 	ifree(fromname);
630 	ifree(toname);
631 }
632 
633 #ifndef INT_MAX
634 #define	INT_MAX		((int)(((unsigned)~0)>>1))
635 #endif /* !defined INT_MAX */
636 
637 #ifndef INT_MIN
638 #define	INT_MIN		((int)~(((unsigned)~0)>>1))
639 #endif /* !defined INT_MIN */
640 
641 /*
642  * The tz file format currently allows at most 32-bit quantities.
643  * This restriction should be removed before signed 32-bit values
644  * wrap around in 2038, but unfortunately this will require a
645  * change to the tz file format.
646  */
647 
648 #define	MAX_BITS_IN_FILE	32
649 /* CSTYLED */
650 #define	TIME_T_BITS_IN_FILE	((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? \
651 				    TYPE_BIT(time_t): MAX_BITS_IN_FILE)
652 
653 static void
654 setboundaries(void)
655 {
656 	if (TYPE_SIGNED(time_t)) {
657 		min_time = ~ (time_t)0;
658 		min_time <<= TIME_T_BITS_IN_FILE - 1;
659 		max_time = ~ (time_t)0 - min_time;
660 		if (sflag)
661 			min_time = 0;
662 	} else {
663 		min_time = 0;
664 		max_time = 2 - sflag;
665 		max_time <<= TIME_T_BITS_IN_FILE - 1;
666 		--max_time;
667 	}
668 	min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
669 	max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
670 	min_year_representable = min_year;
671 	max_year_representable = max_year;
672 }
673 
674 static int
675 itsdir(name)
676 const char * const	name;
677 {
678 	register char	*myname;
679 	register int	accres;
680 
681 	myname = ecpyalloc(name);
682 	myname = ecatalloc(myname, "/.");
683 	accres = access(myname, F_OK);
684 	ifree(myname);
685 	return (accres == 0);
686 }
687 
688 /*
689  * Associate sets of rules with zones.
690  */
691 
692 /*
693  * Sort by rule name.
694  */
695 
696 static int
697 rcomp(cp1, cp2)
698 const void *	cp1;
699 const void *	cp2;
700 {
701 	return (strcmp(((const struct rule *) cp1)->r_name,
702 		((const struct rule *) cp2)->r_name));
703 }
704 
705 static void
706 associate(void)
707 {
708 	register struct zone	*zp;
709 	register struct rule	*rp;
710 	register int		base, out;
711 	register int		i, j;
712 
713 	if (nrules != 0) {
714 		(void) qsort((void *)rules, (size_t)nrules,
715 			(size_t)sizeof (*rules), rcomp);
716 		for (i = 0; i < nrules - 1; ++i) {
717 			if (strcmp(rules[i].r_name,
718 				rules[i + 1].r_name) != 0)
719 					continue;
720 			if (strcmp(rules[i].r_filename,
721 				rules[i + 1].r_filename) == 0)
722 					continue;
723 			eat(rules[i].r_filename, rules[i].r_linenum);
724 			warning(gettext("same rule name in multiple files"));
725 			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
726 			warning(gettext("same rule name in multiple files"));
727 			for (j = i + 2; j < nrules; ++j) {
728 				if (strcmp(rules[i].r_name,
729 					rules[j].r_name) != 0)
730 						break;
731 				if (strcmp(rules[i].r_filename,
732 					rules[j].r_filename) == 0)
733 						continue;
734 				if (strcmp(rules[i + 1].r_filename,
735 					rules[j].r_filename) == 0)
736 						continue;
737 				break;
738 			}
739 			i = j - 1;
740 		}
741 	}
742 	for (i = 0; i < nzones; ++i) {
743 		zp = &zones[i];
744 		zp->z_rules = NULL;
745 		zp->z_nrules = 0;
746 	}
747 	for (base = 0; base < nrules; base = out) {
748 		rp = &rules[base];
749 		for (out = base + 1; out < nrules; ++out)
750 			if (strcmp(rp->r_name, rules[out].r_name) != 0)
751 				break;
752 		for (i = 0; i < nzones; ++i) {
753 			zp = &zones[i];
754 			if (strcmp(zp->z_rule, rp->r_name) != 0)
755 				continue;
756 			zp->z_rules = rp;
757 			zp->z_nrules = out - base;
758 		}
759 	}
760 	for (i = 0; i < nzones; ++i) {
761 		zp = &zones[i];
762 		if (zp->z_nrules == 0) {
763 			/*
764 			 * Maybe we have a local standard time offset.
765 			 */
766 			eat(zp->z_filename, zp->z_linenum);
767 			zp->z_stdoff = gethms(zp->z_rule,
768 				gettext("unruly zone"), TRUE);
769 			/*
770 			 * Note, though, that if there's no rule,
771 			 * a '%s' in the format is a bad thing.
772 			 */
773 			if (strchr(zp->z_format, '%') != 0)
774 				error(gettext("%s in ruleless zone"));
775 		}
776 	}
777 	if (errors)
778 		(void) exit(EXIT_FAILURE);
779 }
780 
781 static void
782 infile(name)
783 const char *name;
784 {
785 	register FILE			*fp;
786 	register char			**fields;
787 	register char			*cp;
788 	register const struct lookup	*lp;
789 	register int			nfields;
790 	register int			wantcont;
791 	register int			num;
792 	char				buf[BUFSIZ];
793 
794 	if (strcmp(name, "-") == 0) {
795 		name = gettext("standard input");
796 		fp = stdin;
797 	} else if ((fp = fopen(name, "r")) == NULL) {
798 		const char *e = strerror(errno);
799 
800 		(void) fprintf(stderr, gettext("%s: Can't open %s: %s\n"),
801 			progname, name, e);
802 		(void) exit(EXIT_FAILURE);
803 	}
804 	wantcont = FALSE;
805 	for (num = 1; ; ++num) {
806 		eat(name, num);
807 		if (fgets(buf, (int)sizeof (buf), fp) != buf)
808 			break;
809 		cp = strchr(buf, '\n');
810 		if (cp == NULL) {
811 			error(gettext("line too long"));
812 			(void) exit(EXIT_FAILURE);
813 		}
814 		*cp = '\0';
815 		fields = getfields(buf);
816 		nfields = 0;
817 		while (fields[nfields] != NULL) {
818 			static char	nada;
819 
820 			if (strcmp(fields[nfields], "-") == 0)
821 				fields[nfields] = &nada;
822 			++nfields;
823 		}
824 		if (nfields == 0) {
825 			/* nothing to do */
826 		} else if (wantcont) {
827 			wantcont = inzcont(fields, nfields);
828 		} else {
829 			lp = byword(fields[0], line_codes);
830 			if (lp == NULL)
831 				error(gettext("input line of unknown type"));
832 			else switch ((int)(lp->l_value)) {
833 				case LC_RULE:
834 					inrule(fields, nfields);
835 					wantcont = FALSE;
836 					break;
837 				case LC_ZONE:
838 					wantcont = inzone(fields, nfields);
839 					break;
840 				case LC_LINK:
841 					inlink(fields, nfields);
842 					wantcont = FALSE;
843 					break;
844 #ifdef LEAPSECOND_SUPPORT
845 				case LC_LEAP:
846 					if (name != leapsec)
847 						(void) fprintf(stderr, gettext(
848 "%s: Leap line in non leap seconds file %s\n"),
849 							progname, name);
850 					else	inleap(fields, nfields);
851 					wantcont = FALSE;
852 					break;
853 #endif /* LEAPSECOND_SUPPORT */
854 				default:	/* "cannot happen" */
855 					(void) fprintf(stderr, gettext(
856 "%s: panic: Invalid l_value %d\n"),
857 						progname, lp->l_value);
858 					(void) exit(EXIT_FAILURE);
859 			}
860 		}
861 		ifree((char *)fields);
862 	}
863 	if (ferror(fp)) {
864 		(void) fprintf(stderr, gettext("%s: Error reading %s\n"),
865 			progname, filename);
866 		(void) exit(EXIT_FAILURE);
867 	}
868 	if (fp != stdin && fclose(fp)) {
869 		const char *e = strerror(errno);
870 		(void) fprintf(stderr, gettext("%s: Error closing %s: %s\n"),
871 			progname, filename, e);
872 		(void) exit(EXIT_FAILURE);
873 	}
874 	if (wantcont)
875 		error(gettext("expected continuation line not found"));
876 }
877 
878 /*
879  * Convert a string of one of the forms
880  *	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
881  * into a number of seconds.
882  * A null string maps to zero.
883  * Call error with errstring and return zero on errors.
884  */
885 
886 static long
887 gethms(string, errstring, signable)
888 const char		*string;
889 const char * const	errstring;
890 const int		signable;
891 {
892 	int	hh, mm, ss, sign;
893 
894 	if (string == NULL || *string == '\0')
895 		return (0);
896 	if (!signable)
897 		sign = 1;
898 	else if (*string == '-') {
899 		sign = -1;
900 		++string;
901 	} else	sign = 1;
902 	if (sscanf(string, scheck(string, "%d"), &hh) == 1)
903 		mm = ss = 0;
904 	else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
905 		ss = 0;
906 	else if (sscanf(string, scheck(string, "%d:%d:%d"),
907 		&hh, &mm, &ss) != 3) {
908 			error(errstring);
909 			return (0);
910 	}
911 	if ((hh < 0 || hh >= HOURSPERDAY ||
912 		mm < 0 || mm >= MINSPERHOUR ||
913 		ss < 0 || ss > SECSPERMIN) &&
914 		!(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
915 			error(errstring);
916 			return (0);
917 	}
918 	return (eitol(sign) *
919 		(eitol(hh * MINSPERHOUR + mm) *
920 		eitol(SECSPERMIN) + eitol(ss)));
921 }
922 
923 static void
924 inrule(fields, nfields)
925 register char ** const	fields;
926 const int		nfields;
927 {
928 	static struct rule	r;
929 
930 	if (nfields != RULE_FIELDS) {
931 		error(gettext("wrong number of fields on Rule line"));
932 		return;
933 	}
934 	if (*fields[RF_NAME] == '\0') {
935 		error(gettext("nameless rule"));
936 		return;
937 	}
938 	r.r_filename = filename;
939 	r.r_linenum = linenum;
940 	r.r_stdoff = gethms(fields[RF_STDOFF], gettext("invalid saved time"),
941 		TRUE);
942 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
943 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
944 	r.r_name = ecpyalloc(fields[RF_NAME]);
945 	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
946 	rules = (struct rule *)(void *)erealloc((char *)rules,
947 		(int)((nrules + 1) * sizeof (*rules)));
948 	rules[nrules++] = r;
949 }
950 
951 static int
952 inzone(fields, nfields)
953 register char ** const	fields;
954 const int		nfields;
955 {
956 	register int	i;
957 	static char	*buf;
958 
959 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
960 		error(gettext("wrong number of fields on Zone line"));
961 		return (FALSE);
962 	}
963 	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
964 		buf = erealloc(buf, (int)(132 + strlen(TZDEFAULT)));
965 		(void) sprintf(buf,
966 gettext("\"Zone %s\" line and -l option are mutually exclusive"),
967 			TZDEFAULT);
968 		error(buf);
969 		return (FALSE);
970 	}
971 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
972 		buf = erealloc(buf, (int)(132 + strlen(TZDEFRULES)));
973 		(void) sprintf(buf,
974 gettext("\"Zone %s\" line and -p option are mutually exclusive"),
975 			TZDEFRULES);
976 		error(buf);
977 		return (FALSE);
978 	}
979 	for (i = 0; i < nzones; ++i)
980 		if (zones[i].z_name != NULL &&
981 			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
982 				buf = erealloc(buf, (int)(132 +
983 					strlen(fields[ZF_NAME]) +
984 					strlen(zones[i].z_filename)));
985 				(void) sprintf(buf,
986 gettext("duplicate zone name %s (file \"%s\", line %d)"),
987 					fields[ZF_NAME],
988 					zones[i].z_filename,
989 					zones[i].z_linenum);
990 				error(buf);
991 				return (FALSE);
992 		}
993 	return (inzsub(fields, nfields, FALSE));
994 }
995 
996 static int
997 inzcont(fields, nfields)
998 register char ** const	fields;
999 const int		nfields;
1000 {
1001 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
1002 		error(gettext(
1003 		    "wrong number of fields on Zone continuation line"));
1004 		return (FALSE);
1005 	}
1006 	return (inzsub(fields, nfields, TRUE));
1007 }
1008 
1009 static int
1010 inzsub(fields, nfields, iscont)
1011 register char ** const	fields;
1012 const int		nfields;
1013 const int		iscont;
1014 {
1015 	register char		*cp;
1016 	static struct zone	z;
1017 	register int		i_gmtoff, i_rule, i_format;
1018 	register int		i_untilyear, i_untilmonth;
1019 	register int		i_untilday, i_untiltime;
1020 	register int		hasuntil;
1021 
1022 	if (iscont) {
1023 		i_gmtoff = ZFC_GMTOFF;
1024 		i_rule = ZFC_RULE;
1025 		i_format = ZFC_FORMAT;
1026 		i_untilyear = ZFC_TILYEAR;
1027 		i_untilmonth = ZFC_TILMONTH;
1028 		i_untilday = ZFC_TILDAY;
1029 		i_untiltime = ZFC_TILTIME;
1030 		z.z_name = NULL;
1031 	} else {
1032 		i_gmtoff = ZF_GMTOFF;
1033 		i_rule = ZF_RULE;
1034 		i_format = ZF_FORMAT;
1035 		i_untilyear = ZF_TILYEAR;
1036 		i_untilmonth = ZF_TILMONTH;
1037 		i_untilday = ZF_TILDAY;
1038 		i_untiltime = ZF_TILTIME;
1039 		z.z_name = ecpyalloc(fields[ZF_NAME]);
1040 	}
1041 	z.z_filename = filename;
1042 	z.z_linenum = linenum;
1043 	z.z_gmtoff = gethms(fields[i_gmtoff], gettext("invalid UTC offset"),
1044 		TRUE);
1045 	if ((cp = strchr(fields[i_format], '%')) != 0) {
1046 		if (*++cp != 's' || strchr(cp, '%') != 0) {
1047 			error(gettext("invalid abbreviation format"));
1048 			return (FALSE);
1049 		}
1050 	}
1051 	z.z_rule = ecpyalloc(fields[i_rule]);
1052 	z.z_format = ecpyalloc(fields[i_format]);
1053 	hasuntil = nfields > i_untilyear;
1054 	if (hasuntil) {
1055 		z.z_untilrule.r_filename = filename;
1056 		z.z_untilrule.r_linenum = linenum;
1057 		rulesub(&z.z_untilrule,
1058 			fields[i_untilyear],
1059 			"only",
1060 			"",
1061 			(nfields > i_untilmonth) ?
1062 			fields[i_untilmonth] : "Jan",
1063 			(nfields > i_untilday) ? fields[i_untilday] : "1",
1064 			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
1065 		z.z_untiltime = rpytime(&z.z_untilrule,
1066 			z.z_untilrule.r_loyear);
1067 		if (iscont && nzones > 0 &&
1068 			z.z_untiltime > min_time &&
1069 			z.z_untiltime < max_time &&
1070 			zones[nzones - 1].z_untiltime > min_time &&
1071 			zones[nzones - 1].z_untiltime < max_time &&
1072 			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
1073 				error(gettext(
1074 "Zone continuation line end time is not after end time of previous line"));
1075 				return (FALSE);
1076 		}
1077 	}
1078 	zones = (struct zone *)(void *)erealloc((char *)zones,
1079 		(int)((nzones + 1) * sizeof (*zones)));
1080 	zones[nzones++] = z;
1081 	/*
1082 	 * If there was an UNTIL field on this line,
1083 	 * there's more information about the zone on the next line.
1084 	 */
1085 	return (hasuntil);
1086 }
1087 
1088 #ifdef LEAPSECOND_SUPPORT
1089 static void
1090 inleap(fields, nfields)
1091 register char ** const	fields;
1092 const int		nfields;
1093 {
1094 	register const char		*cp;
1095 	register const struct lookup	*lp;
1096 	register int			i, j;
1097 	int				year, month, day;
1098 	long				dayoff, tod;
1099 	time_t				t;
1100 
1101 	if (nfields != LEAP_FIELDS) {
1102 		error(gettext("wrong number of fields on Leap line"));
1103 		return;
1104 	}
1105 	dayoff = 0;
1106 	cp = fields[LP_YEAR];
1107 	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
1108 			/*
1109 			 * Leapin' Lizards!
1110 			 */
1111 			error(gettext("invalid leaping year"));
1112 			return;
1113 	}
1114 	j = EPOCH_YEAR;
1115 	while (j != year) {
1116 		if (year > j) {
1117 			i = len_years[isleap(j)];
1118 			++j;
1119 		} else {
1120 			--j;
1121 			i = -len_years[isleap(j)];
1122 		}
1123 		dayoff = oadd(dayoff, eitol(i));
1124 	}
1125 	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
1126 		error(gettext("invalid month name"));
1127 		return;
1128 	}
1129 	month = lp->l_value;
1130 	j = TM_JANUARY;
1131 	while (j != month) {
1132 		i = len_months[isleap(year)][j];
1133 		dayoff = oadd(dayoff, eitol(i));
1134 		++j;
1135 	}
1136 	cp = fields[LP_DAY];
1137 	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
1138 		day <= 0 || day > len_months[isleap(year)][month]) {
1139 			error(gettext("invalid day of month"));
1140 			return;
1141 	}
1142 	dayoff = oadd(dayoff, eitol(day - 1));
1143 	if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
1144 		error(gettext("time before zero"));
1145 		return;
1146 	}
1147 	t = (time_t)dayoff * SECSPERDAY;
1148 	/*
1149 	 * Cheap overflow check.
1150 	 */
1151 	if (t / SECSPERDAY != dayoff) {
1152 		error(gettext("time overflow"));
1153 		return;
1154 	}
1155 	tod = gethms(fields[LP_TIME], gettext("invalid time of day"), FALSE);
1156 	cp = fields[LP_CORR];
1157 	{
1158 		register int	positive;
1159 		int		count;
1160 
1161 		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
1162 			positive = FALSE;
1163 			count = 1;
1164 		} else if (strcmp(cp, "--") == 0) {
1165 			positive = FALSE;
1166 			count = 2;
1167 		} else if (strcmp(cp, "+") == 0) {
1168 			positive = TRUE;
1169 			count = 1;
1170 		} else if (strcmp(cp, "++") == 0) {
1171 			positive = TRUE;
1172 			count = 2;
1173 		} else {
1174 			error(gettext("illegal CORRECTION field on Leap line"));
1175 			return;
1176 		}
1177 		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
1178 			error(gettext(
1179 "illegal Rolling/Stationary field on Leap line"));
1180 			return;
1181 		}
1182 		leapadd(tadd(t, tod), positive, lp->l_value, count);
1183 	}
1184 }
1185 #endif /* LEAPSECOND_SUPPORT */
1186 
1187 static void
1188 inlink(fields, nfields)
1189 register char ** const	fields;
1190 const int		nfields;
1191 {
1192 	struct link	l;
1193 
1194 	if (nfields != LINK_FIELDS) {
1195 		error(gettext("wrong number of fields on Link line"));
1196 		return;
1197 	}
1198 	if (*fields[LF_FROM] == '\0') {
1199 		error(gettext("blank FROM field on Link line"));
1200 		return;
1201 	}
1202 	if (*fields[LF_TO] == '\0') {
1203 		error(gettext("blank TO field on Link line"));
1204 		return;
1205 	}
1206 	l.l_filename = filename;
1207 	l.l_linenum = linenum;
1208 	l.l_from = ecpyalloc(fields[LF_FROM]);
1209 	l.l_to = ecpyalloc(fields[LF_TO]);
1210 	links = (struct link *)(void *)erealloc((char *)links,
1211 		(int)((nlinks + 1) * sizeof (*links)));
1212 	links[nlinks++] = l;
1213 }
1214 
1215 static void
1216 rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
1217 register struct rule * const	rp;
1218 const char * const		loyearp;
1219 const char * const		hiyearp;
1220 const char * const		typep;
1221 const char * const		monthp;
1222 const char * const		dayp;
1223 const char * const		timep;
1224 {
1225 	register const struct lookup	*lp;
1226 	register const char		*cp;
1227 	register char			*dp;
1228 	register char			*ep;
1229 
1230 	if ((lp = byword(monthp, mon_names)) == NULL) {
1231 		error(gettext("invalid month name"));
1232 		return;
1233 	}
1234 	rp->r_month = lp->l_value;
1235 	rp->r_todisstd = FALSE;
1236 	rp->r_todisgmt = FALSE;
1237 	dp = ecpyalloc(timep);
1238 	if (*dp != '\0') {
1239 		ep = dp + strlen(dp) - 1;
1240 		switch (lowerit(*ep)) {
1241 			case 's':	/* Standard */
1242 				rp->r_todisstd = TRUE;
1243 				rp->r_todisgmt = FALSE;
1244 				*ep = '\0';
1245 				break;
1246 			case 'w':	/* Wall */
1247 				rp->r_todisstd = FALSE;
1248 				rp->r_todisgmt = FALSE;
1249 				*ep = '\0';
1250 				break;
1251 			case 'g':	/* Greenwich */
1252 			case 'u':	/* Universal */
1253 			case 'z':	/* Zulu */
1254 				rp->r_todisstd = TRUE;
1255 				rp->r_todisgmt = TRUE;
1256 				*ep = '\0';
1257 				break;
1258 		}
1259 	}
1260 	rp->r_tod = gethms(dp, gettext("invalid time of day"), FALSE);
1261 	ifree(dp);
1262 	/*
1263 	 * Year work.
1264 	 */
1265 	cp = loyearp;
1266 	lp = byword(cp, begin_years);
1267 	if (lp != NULL) {
1268 	    switch ((int)lp->l_value) {
1269 		case YR_MINIMUM:
1270 			rp->r_loyear = INT_MIN;
1271 			break;
1272 		case YR_MAXIMUM:
1273 			rp->r_loyear = INT_MAX;
1274 			break;
1275 		default:	/* "cannot happen" */
1276 			(void) fprintf(stderr,
1277 				gettext("%s: panic: Invalid l_value %d\n"),
1278 				progname, lp->l_value);
1279 			(void) exit(EXIT_FAILURE);
1280 	    }
1281 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
1282 		error(gettext("invalid starting year"));
1283 		return;
1284 	} else if (noise) {
1285 		if (rp->r_loyear < min_year_representable)
1286 			warning(gettext(
1287 			    "starting year too low to be represented"));
1288 		else if (rp->r_loyear > max_year_representable)
1289 			warning(gettext(
1290 			    "starting year too high to be represented"));
1291 	}
1292 	cp = hiyearp;
1293 	if ((lp = byword(cp, end_years)) != NULL) {
1294 	    switch ((int)lp->l_value) {
1295 		case YR_MINIMUM:
1296 			rp->r_hiyear = INT_MIN;
1297 			break;
1298 		case YR_MAXIMUM:
1299 			rp->r_hiyear = INT_MAX;
1300 			break;
1301 		case YR_ONLY:
1302 			rp->r_hiyear = rp->r_loyear;
1303 			break;
1304 		default:	/* "cannot happen" */
1305 			(void) fprintf(stderr,
1306 				gettext("%s: panic: Invalid l_value %d\n"),
1307 				progname, lp->l_value);
1308 			(void) exit(EXIT_FAILURE);
1309 	    }
1310 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
1311 		error(gettext("invalid ending year"));
1312 		return;
1313 	} else if (noise) {
1314 		if (rp->r_loyear < min_year_representable)
1315 			warning(gettext(
1316 			    "starting year too low to be represented"));
1317 		else if (rp->r_loyear > max_year_representable)
1318 			warning(gettext(
1319 			    "starting year too high to be represented"));
1320 	}
1321 	if (rp->r_loyear > rp->r_hiyear) {
1322 		error(gettext("starting year greater than ending year"));
1323 		return;
1324 	}
1325 	if (*typep == '\0')
1326 		rp->r_yrtype = NULL;
1327 	else {
1328 		if (rp->r_loyear == rp->r_hiyear) {
1329 			error(gettext("typed single year"));
1330 			return;
1331 		}
1332 		rp->r_yrtype = ecpyalloc(typep);
1333 	}
1334 	if (rp->r_loyear < min_year && rp->r_loyear > 0)
1335 		min_year = rp->r_loyear;
1336 	/*
1337 	 * Day work.
1338 	 * Accept things such as:
1339 	 *	1
1340 	 *	last-Sunday
1341 	 *	Sun<=20
1342 	 *	Sun>=7
1343 	 */
1344 	dp = ecpyalloc(dayp);
1345 	if ((lp = byword(dp, lasts)) != NULL) {
1346 		rp->r_dycode = DC_DOWLEQ;
1347 		rp->r_wday = lp->l_value;
1348 		rp->r_dayofmonth = len_months[1][rp->r_month];
1349 	} else {
1350 		if ((ep = strchr(dp, '<')) != 0)
1351 			rp->r_dycode = DC_DOWLEQ;
1352 		else if ((ep = strchr(dp, '>')) != 0)
1353 			rp->r_dycode = DC_DOWGEQ;
1354 		else {
1355 			ep = dp;
1356 			rp->r_dycode = DC_DOM;
1357 		}
1358 		if (rp->r_dycode != DC_DOM) {
1359 			*ep++ = 0;
1360 			if (*ep++ != '=') {
1361 				error(gettext("invalid day of month"));
1362 				ifree(dp);
1363 				return;
1364 			}
1365 			if ((lp = byword(dp, wday_names)) == NULL) {
1366 				error(gettext("invalid weekday name"));
1367 				ifree(dp);
1368 				return;
1369 			}
1370 			rp->r_wday = lp->l_value;
1371 		}
1372 		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
1373 			rp->r_dayofmonth <= 0 ||
1374 			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
1375 				error(gettext("invalid day of month"));
1376 				ifree(dp);
1377 				return;
1378 		}
1379 	}
1380 	ifree(dp);
1381 }
1382 
1383 static void
1384 convert(val, buf)
1385 const long	val;
1386 char * const	buf;
1387 {
1388 	register int	i;
1389 	register long	shift;
1390 
1391 	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
1392 		buf[i] = val >> shift;
1393 }
1394 
1395 static void
1396 puttzcode(val, fp)
1397 const long	val;
1398 FILE * const	fp;
1399 {
1400 	char	buf[4];
1401 
1402 	convert(val, buf);
1403 	(void) fwrite((void *)buf, (size_t)sizeof (buf), (size_t)1, fp);
1404 }
1405 
1406 static int
1407 atcomp(avp, bvp)
1408 const void *avp;
1409 const void *bvp;
1410 {
1411 	if (((struct attype *)avp)->at < ((struct attype *)bvp)->at)
1412 		return (-1);
1413 	else if (((struct attype *)avp)->at > ((struct attype *)bvp)->at)
1414 		return (1);
1415 	else	return (0);
1416 }
1417 
1418 static void
1419 writezone(name)
1420 const char * const	name;
1421 {
1422 	register FILE		*fp;
1423 	register int		i, j;
1424 	static char		*fullname;
1425 	static struct tzhead	tzh;
1426 	time_t			ats[TZ_MAX_TIMES];
1427 	unsigned char		types[TZ_MAX_TIMES];
1428 
1429 	/*
1430 	 * Sort.
1431 	 */
1432 	if (timecnt > 1)
1433 		(void) qsort((void *)attypes, (size_t)timecnt,
1434 			(size_t)sizeof (*attypes), atcomp);
1435 	/*
1436 	 * Optimize.
1437 	 */
1438 	{
1439 		int	fromi;
1440 		int	toi;
1441 
1442 		toi = 0;
1443 		fromi = 0;
1444 		while (fromi < timecnt && attypes[fromi].at < min_time)
1445 			++fromi;
1446 		if (isdsts[0] == 0)
1447 			while (fromi < timecnt && attypes[fromi].type == 0)
1448 				++fromi;	/* handled by default rule */
1449 		for (; fromi < timecnt; ++fromi) {
1450 			if (toi != 0 &&
1451 			    ((attypes[fromi].at +
1452 				gmtoffs[attypes[toi - 1].type]) <=
1453 			    (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
1454 				: attypes[toi - 2].type]))) {
1455 				attypes[toi - 1].type = attypes[fromi].type;
1456 				continue;
1457 			}
1458 			if (toi == 0 ||
1459 				attypes[toi - 1].type != attypes[fromi].type)
1460 					attypes[toi++] = attypes[fromi];
1461 		}
1462 		timecnt = toi;
1463 	}
1464 	/*
1465 	 * Transfer.
1466 	 */
1467 	for (i = 0; i < timecnt; ++i) {
1468 		ats[i] = attypes[i].at;
1469 		types[i] = attypes[i].type;
1470 	}
1471 	fullname = erealloc(fullname,
1472 		(int)(strlen(directory) + 1 + strlen(name) + 1));
1473 	(void) sprintf(fullname, "%s/%s", directory, name);
1474 	/*
1475 	 * Remove old file, if any, to snap links.
1476 	 */
1477 	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
1478 		const char *e = strerror(errno);
1479 
1480 		(void) fprintf(stderr, gettext("%s: Can't remove %s: %s\n"),
1481 			progname, fullname, e);
1482 		(void) exit(EXIT_FAILURE);
1483 	}
1484 	if ((fp = fopen(fullname, "wb")) == NULL) {
1485 		if (mkdirs(fullname) != 0)
1486 			(void) exit(EXIT_FAILURE);
1487 		if ((fp = fopen(fullname, "wb")) == NULL) {
1488 			const char *e = strerror(errno);
1489 			(void) fprintf(stderr, gettext(
1490 				"%s: Can't create %s: %s\n"),
1491 				progname, fullname, e);
1492 			(void) exit(EXIT_FAILURE);
1493 		}
1494 	}
1495 	convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
1496 	convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
1497 	convert(eitol(leapcnt), tzh.tzh_leapcnt);
1498 	convert(eitol(timecnt), tzh.tzh_timecnt);
1499 	convert(eitol(typecnt), tzh.tzh_typecnt);
1500 	convert(eitol(charcnt), tzh.tzh_charcnt);
1501 	(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof (tzh.tzh_magic));
1502 #define	DO(field)	(void) fwrite((void *) tzh.field, \
1503 			(size_t)sizeof (tzh.field), (size_t)1, fp)
1504 	DO(tzh_magic);
1505 	DO(tzh_reserved);
1506 	DO(tzh_ttisgmtcnt);
1507 	DO(tzh_ttisstdcnt);
1508 	DO(tzh_leapcnt);
1509 	DO(tzh_timecnt);
1510 	DO(tzh_typecnt);
1511 	DO(tzh_charcnt);
1512 #undef DO
1513 	for (i = 0; i < timecnt; ++i) {
1514 		j = leapcnt;
1515 		while (--j >= 0)
1516 			if (ats[i] >= trans[j]) {
1517 				ats[i] = tadd(ats[i], corr[j]);
1518 				break;
1519 			}
1520 		puttzcode((long)ats[i], fp);
1521 	}
1522 	if (timecnt > 0)
1523 		(void) fwrite((void *)types, (size_t)sizeof (types[0]),
1524 			(size_t)timecnt, fp);
1525 	for (i = 0; i < typecnt; ++i) {
1526 		puttzcode((long)gmtoffs[i], fp);
1527 		(void) putc(isdsts[i], fp);
1528 		(void) putc(abbrinds[i], fp);
1529 	}
1530 	if (charcnt != 0)
1531 		(void) fwrite((void *)chars, (size_t)sizeof (chars[0]),
1532 			(size_t)charcnt, fp);
1533 	for (i = 0; i < leapcnt; ++i) {
1534 		if (roll[i]) {
1535 			if (timecnt == 0 || trans[i] < ats[0]) {
1536 				j = 0;
1537 				while (isdsts[j])
1538 					if (++j >= typecnt) {
1539 						j = 0;
1540 						break;
1541 					}
1542 			} else {
1543 				j = 1;
1544 				while (j < timecnt && trans[i] >= ats[j])
1545 					++j;
1546 				j = types[j - 1];
1547 			}
1548 			puttzcode((long)tadd(trans[i], -gmtoffs[j]), fp);
1549 		} else	puttzcode((long)trans[i], fp);
1550 		puttzcode((long)corr[i], fp);
1551 	}
1552 	for (i = 0; i < typecnt; ++i)
1553 		(void) putc(ttisstds[i], fp);
1554 	for (i = 0; i < typecnt; ++i)
1555 		(void) putc(ttisgmts[i], fp);
1556 	if (ferror(fp) || fclose(fp)) {
1557 		(void) fprintf(stderr, gettext("%s: Error writing %s\n"),
1558 			progname, fullname);
1559 		(void) exit(EXIT_FAILURE);
1560 	}
1561 }
1562 
1563 static void
1564 doabbr(abbr, format, letters, isdst)
1565 char * const		abbr;
1566 const char * const	format;
1567 const char * const	letters;
1568 const int		isdst;
1569 {
1570 	if (strchr(format, '/') == NULL) {
1571 		if (letters == NULL)
1572 			(void) strcpy(abbr, format);
1573 		else
1574 			(void) sprintf(abbr, format, letters);
1575 	} else if (isdst)
1576 		(void) strcpy(abbr, strchr(format, '/') + 1);
1577 	else {
1578 		(void) strcpy(abbr, format);
1579 		*strchr(abbr, '/') = '\0';
1580 	}
1581 }
1582 
1583 static void
1584 outzone(zpfirst, zonecount)
1585 const struct zone * const	zpfirst;
1586 const int			zonecount;
1587 {
1588 	register const struct zone	*zp;
1589 	register struct rule		*rp;
1590 	register int			i, j;
1591 	register int			usestart, useuntil;
1592 	register time_t			starttime, untiltime;
1593 	register long			gmtoff;
1594 	register long			stdoff;
1595 	register int			year;
1596 	register long			startoff;
1597 	register int			startttisstd;
1598 	register int			startttisgmt;
1599 	register int			type;
1600 	char				startbuf[BUFSIZ];
1601 
1602 	INITIALIZE(untiltime);
1603 	INITIALIZE(starttime);
1604 	/*
1605 	 * Now. . .finally. . .generate some useful data!
1606 	 */
1607 	timecnt = 0;
1608 	typecnt = 0;
1609 	charcnt = 0;
1610 	/*
1611 	 * Thanks to Earl Chew (earl@dnd.icp.nec.com.au)
1612 	 * for noting the need to unconditionally initialize startttisstd.
1613 	 */
1614 	startttisstd = FALSE;
1615 	startttisgmt = FALSE;
1616 	for (i = 0; i < zonecount; ++i) {
1617 		/*
1618 		 * A guess that may well be corrected later.
1619 		 */
1620 		stdoff = 0;
1621 		zp = &zpfirst[i];
1622 		usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
1623 		useuntil = i < (zonecount - 1);
1624 		if (useuntil && zp->z_untiltime <= min_time)
1625 			continue;
1626 		gmtoff = zp->z_gmtoff;
1627 		eat(zp->z_filename, zp->z_linenum);
1628 		*startbuf = '\0';
1629 		startoff = zp->z_gmtoff;
1630 		if (zp->z_nrules == 0) {
1631 			stdoff = zp->z_stdoff;
1632 			doabbr(startbuf, zp->z_format,
1633 				(char *)NULL, stdoff != 0);
1634 			type = addtype(oadd(zp->z_gmtoff, stdoff),
1635 				startbuf, stdoff != 0, startttisstd,
1636 				startttisgmt);
1637 			if (usestart) {
1638 				addtt(starttime, type);
1639 				usestart = FALSE;
1640 			} else {
1641 				if (stdoff != 0)
1642 					addtt(min_time, type);
1643 			}
1644 		} else
1645 		    for (year = min_year; year <= max_year; ++year) {
1646 			if (useuntil && year > zp->z_untilrule.r_hiyear)
1647 				break;
1648 			/*
1649 			 * Mark which rules to do in the current year.
1650 			 * For those to do, calculate rpytime(rp, year);
1651 			 */
1652 			for (j = 0; j < zp->z_nrules; ++j) {
1653 				rp = &zp->z_rules[j];
1654 				eats(zp->z_filename, zp->z_linenum,
1655 					rp->r_filename, rp->r_linenum);
1656 				rp->r_todo = year >= rp->r_loyear &&
1657 						year <= rp->r_hiyear &&
1658 						yearistype(year, rp->r_yrtype);
1659 				if (rp->r_todo)
1660 					rp->r_temp = rpytime(rp, year);
1661 			}
1662 			for (;;) {
1663 				register int	k;
1664 				register time_t	jtime, ktime;
1665 				register long	offset;
1666 				char		buf[BUFSIZ];
1667 
1668 				INITIALIZE(ktime);
1669 				if (useuntil) {
1670 					/*
1671 					 * Turn untiltime into UTC
1672 					 * assuming the current gmtoff and
1673 					 * stdoff values.
1674 					 */
1675 					untiltime = zp->z_untiltime;
1676 					if (!zp->z_untilrule.r_todisgmt)
1677 						untiltime = tadd(untiltime,
1678 							-gmtoff);
1679 					if (!zp->z_untilrule.r_todisstd)
1680 						untiltime = tadd(untiltime,
1681 							-stdoff);
1682 				}
1683 				/*
1684 				 * Find the rule (of those to do, if any)
1685 				 * that takes effect earliest in the year.
1686 				 */
1687 				k = -1;
1688 				for (j = 0; j < zp->z_nrules; ++j) {
1689 					rp = &zp->z_rules[j];
1690 					if (!rp->r_todo)
1691 						continue;
1692 					eats(zp->z_filename, zp->z_linenum,
1693 						rp->r_filename, rp->r_linenum);
1694 					offset = rp->r_todisgmt ? 0 : gmtoff;
1695 					if (!rp->r_todisstd)
1696 						offset = oadd(offset, stdoff);
1697 					jtime = rp->r_temp;
1698 					if (jtime == min_time ||
1699 						jtime == max_time)
1700 							continue;
1701 					jtime = tadd(jtime, -offset);
1702 					if (k < 0 || jtime < ktime) {
1703 						k = j;
1704 						ktime = jtime;
1705 					}
1706 				}
1707 				if (k < 0)
1708 					break;	/* go on to next year */
1709 				rp = &zp->z_rules[k];
1710 				rp->r_todo = FALSE;
1711 				if (useuntil && ktime >= untiltime)
1712 					break;
1713 				stdoff = rp->r_stdoff;
1714 				if (usestart && ktime == starttime)
1715 					usestart = FALSE;
1716 				if (usestart) {
1717 					if (ktime < starttime) {
1718 						startoff = oadd(zp->z_gmtoff,
1719 							stdoff);
1720 						doabbr(startbuf, zp->z_format,
1721 							rp->r_abbrvar,
1722 							rp->r_stdoff != 0);
1723 						continue;
1724 					}
1725 					if (*startbuf == '\0' &&
1726 					    startoff == oadd(zp->z_gmtoff,
1727 					    stdoff)) {
1728 						doabbr(startbuf, zp->z_format,
1729 							rp->r_abbrvar,
1730 							rp->r_stdoff != 0);
1731 					}
1732 				}
1733 				eats(zp->z_filename, zp->z_linenum,
1734 					rp->r_filename, rp->r_linenum);
1735 				doabbr(buf, zp->z_format, rp->r_abbrvar,
1736 					rp->r_stdoff != 0);
1737 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
1738 				type = addtype(offset, buf, rp->r_stdoff != 0,
1739 					rp->r_todisstd, rp->r_todisgmt);
1740 				addtt(ktime, type);
1741 			}
1742 		    }
1743 		if (usestart) {
1744 			if (*startbuf == '\0' &&
1745 				zp->z_format != NULL &&
1746 				strchr(zp->z_format, '%') == NULL &&
1747 				strchr(zp->z_format, '/') == NULL)
1748 					(void) strcpy(startbuf, zp->z_format);
1749 			eat(zp->z_filename, zp->z_linenum);
1750 			if (*startbuf == '\0')
1751 				error(gettext(
1752 "can't determine time zone abbrevation to use just after until time"));
1753 			else	addtt(starttime,
1754 					addtype(startoff, startbuf,
1755 						startoff != zp->z_gmtoff,
1756 						startttisstd,
1757 						startttisgmt));
1758 		}
1759 		/*
1760 		 * Now we may get to set starttime for the next zone line.
1761 		 */
1762 		if (useuntil) {
1763 			startttisstd = zp->z_untilrule.r_todisstd;
1764 			startttisgmt = zp->z_untilrule.r_todisgmt;
1765 			starttime = zp->z_untiltime;
1766 			if (!startttisstd)
1767 				starttime = tadd(starttime, -stdoff);
1768 			if (!startttisgmt)
1769 				starttime = tadd(starttime, -gmtoff);
1770 		}
1771 	}
1772 	writezone(zpfirst->z_name);
1773 }
1774 
1775 static void
1776 addtt(starttime, type)
1777 const time_t	starttime;
1778 int		type;
1779 {
1780 	if (starttime <= min_time ||
1781 		(timecnt == 1 && attypes[0].at < min_time)) {
1782 		gmtoffs[0] = gmtoffs[type];
1783 		isdsts[0] = isdsts[type];
1784 		ttisstds[0] = ttisstds[type];
1785 		ttisgmts[0] = ttisgmts[type];
1786 		if (abbrinds[type] != 0)
1787 			(void) strcpy(chars, &chars[abbrinds[type]]);
1788 		abbrinds[0] = 0;
1789 		charcnt = strlen(chars) + 1;
1790 		typecnt = 1;
1791 		timecnt = 0;
1792 		type = 0;
1793 	}
1794 	if (timecnt >= TZ_MAX_TIMES) {
1795 		error(gettext("too many transitions?!"));
1796 		(void) exit(EXIT_FAILURE);
1797 	}
1798 	attypes[timecnt].at = starttime;
1799 	attypes[timecnt].type = type;
1800 	++timecnt;
1801 }
1802 
1803 static int
1804 addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
1805 const long		gmtoff;
1806 const char * const	abbr;
1807 const int		isdst;
1808 const int		ttisstd;
1809 const int		ttisgmt;
1810 {
1811 	register int	i, j;
1812 
1813 	if (isdst != TRUE && isdst != FALSE) {
1814 		error(gettext(
1815 		    "internal error - addtype called with bad isdst"));
1816 		(void) exit(EXIT_FAILURE);
1817 	}
1818 	if (ttisstd != TRUE && ttisstd != FALSE) {
1819 		error(gettext(
1820 		    "internal error - addtype called with bad ttisstd"));
1821 		(void) exit(EXIT_FAILURE);
1822 	}
1823 	if (ttisgmt != TRUE && ttisgmt != FALSE) {
1824 		error(gettext(
1825 		    "internal error - addtype called with bad ttisgmt"));
1826 		(void) exit(EXIT_FAILURE);
1827 	}
1828 	/*
1829 	 * See if there's already an entry for this zone type.
1830 	 * If so, just return its index.
1831 	 */
1832 	for (i = 0; i < typecnt; ++i) {
1833 		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
1834 			strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
1835 			ttisstd == ttisstds[i] &&
1836 			ttisgmt == ttisgmts[i])
1837 				return (i);
1838 	}
1839 	/*
1840 	 * There isn't one; add a new one, unless there are already too
1841 	 * many.
1842 	 */
1843 	if (typecnt >= TZ_MAX_TYPES) {
1844 		error(gettext("too many local time types"));
1845 		(void) exit(EXIT_FAILURE);
1846 	}
1847 	gmtoffs[i] = gmtoff;
1848 	isdsts[i] = isdst;
1849 	ttisstds[i] = ttisstd;
1850 	ttisgmts[i] = ttisgmt;
1851 
1852 	for (j = 0; j < charcnt; ++j)
1853 		if (strcmp(&chars[j], abbr) == 0)
1854 			break;
1855 	if (j == charcnt)
1856 		newabbr(abbr);
1857 	abbrinds[i] = j;
1858 	++typecnt;
1859 	return (i);
1860 }
1861 
1862 #ifdef LEAPSECOND_SUPPORT
1863 static void
1864 leapadd(t, positive, rolling, count)
1865 const time_t	t;
1866 const int	positive;
1867 const int	rolling;
1868 int		count;
1869 {
1870 	register int	i, j;
1871 
1872 	if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
1873 		error(gettext("too many leap seconds"));
1874 		(void) exit(EXIT_FAILURE);
1875 	}
1876 	for (i = 0; i < leapcnt; ++i)
1877 		if (t <= trans[i]) {
1878 			if (t == trans[i]) {
1879 				error(gettext("repeated leap second moment"));
1880 				(void) exit(EXIT_FAILURE);
1881 			}
1882 			break;
1883 		}
1884 	do {
1885 		for (j = leapcnt; j > i; --j) {
1886 			trans[j] = trans[j - 1];
1887 			corr[j] = corr[j - 1];
1888 			roll[j] = roll[j - 1];
1889 		}
1890 		trans[i] = t;
1891 		corr[i] = positive ? 1L : eitol(-count);
1892 		roll[i] = rolling;
1893 		++leapcnt;
1894 	} while (positive && --count != 0);
1895 }
1896 #endif /* LEAPSECOND_SUPPORT */
1897 
1898 #ifdef LEAPSECOND_SUPPORT
1899 static void
1900 adjleap(void)
1901 {
1902 	register int	i;
1903 	register long	last = 0;
1904 
1905 	/*
1906 	 * propagate leap seconds forward
1907 	 */
1908 	for (i = 0; i < leapcnt; ++i) {
1909 		trans[i] = tadd(trans[i], last);
1910 		last = corr[i] += last;
1911 	}
1912 }
1913 #endif /* LEAPSECOND_SUPPORT */
1914 
1915 static int
1916 yearistype(year, type)
1917 const int		year;
1918 const char * const	type;
1919 {
1920 	static char	*buf;
1921 	int		result;
1922 
1923 	if (type == NULL || *type == '\0')
1924 		return (TRUE);
1925 #if defined(sun)
1926 	if (strcmp(type, "uspres") == 0)
1927 		return ((year % 4) == 0);
1928 	if (strcmp(type, "nonpres") == 0)
1929 		return ((year % 4) != 0);
1930 	if (strcmp(type, "even") == 0)
1931 		return ((year % 2) == 0);
1932 	if (strcmp(type, "odd") == 0)
1933 		return ((year % 2) != 0);
1934 #endif /* defined(sun) */
1935 
1936 	buf = erealloc(buf, (int)(132 + strlen(yitcommand) + strlen(type)));
1937 	(void) sprintf(buf, "%s %d %s", yitcommand, year, type);
1938 	result = system(buf);
1939 	if (result == 0)
1940 		return (TRUE);
1941 	if (result == (1 << 8))
1942 		return (FALSE);
1943 	error(gettext("Wild result from command execution"));
1944 	(void) fprintf(stderr, gettext("%s: command was '%s', result was %d\n"),
1945 		progname, buf, result);
1946 	for (;;)
1947 		(void) exit(EXIT_FAILURE);
1948 }
1949 
1950 static int
1951 lowerit(a)
1952 int	a;
1953 {
1954 	a = (unsigned char) a;
1955 	return ((isascii(a) && isupper(a)) ? tolower(a) : a);
1956 }
1957 
1958 static int
1959 ciequal(ap, bp)		/* case-insensitive equality */
1960 register const char *ap;
1961 register const char *bp;
1962 {
1963 	while (lowerit(*ap) == lowerit(*bp++))
1964 		if (*ap++ == '\0')
1965 			return (TRUE);
1966 	return (FALSE);
1967 }
1968 
1969 static int
1970 itsabbr(abbr, word)
1971 register const char *abbr;
1972 register const char *word;
1973 {
1974 	if (lowerit(*abbr) != lowerit(*word))
1975 		return (FALSE);
1976 	++word;
1977 	while (*++abbr != '\0')
1978 		do {
1979 			if (*word == '\0')
1980 				return (FALSE);
1981 		} while (lowerit(*word++) != lowerit(*abbr));
1982 	return (TRUE);
1983 }
1984 
1985 static const struct lookup *
1986 byword(word, table)
1987 register const char * const		word;
1988 register const struct lookup * const	table;
1989 {
1990 	register const struct lookup *foundlp;
1991 	register const struct lookup *lp;
1992 
1993 	if (word == NULL || table == NULL)
1994 		return (NULL);
1995 	/*
1996 	 * Look for exact match.
1997 	 */
1998 	for (lp = table; lp->l_word != NULL; ++lp)
1999 		if (ciequal(word, lp->l_word))
2000 			return (lp);
2001 	/*
2002 	 * Look for inexact match.
2003 	 */
2004 	foundlp = NULL;
2005 	for (lp = table; lp->l_word != NULL; ++lp)
2006 		if (itsabbr(word, lp->l_word)) {
2007 			if (foundlp == NULL)
2008 				foundlp = lp;
2009 			else	return (NULL);	/* multiple inexact matches */
2010 		}
2011 	return (foundlp);
2012 }
2013 
2014 static char **
2015 getfields(cp)
2016 register char *cp;
2017 {
2018 	register char 	*dp;
2019 	register char	**array;
2020 	register int	nsubs;
2021 
2022 	if (cp == NULL)
2023 		return (NULL);
2024 	array = (char **)(void *)
2025 		emalloc((int)((strlen(cp) + 1) * sizeof (*array)));
2026 	nsubs = 0;
2027 	for (;;) {
2028 		while (isascii(*cp) && isspace((unsigned char) *cp))
2029 			++cp;
2030 		if (*cp == '\0' || *cp == '#')
2031 			break;
2032 		array[nsubs++] = dp = cp;
2033 		do {
2034 			if ((*dp = *cp++) != '"')
2035 				++dp;
2036 			else while ((*dp = *cp++) != '"')
2037 				if (*dp != '\0')
2038 					++dp;
2039 				else
2040 					error(gettext(
2041 					    "Odd number of quotation marks"));
2042 		} while (*cp != '\0' && *cp != '#' &&
2043 			(!isascii(*cp) || !isspace((unsigned char) *cp)));
2044 		if (isascii(*cp) && isspace((unsigned char) *cp))
2045 			++cp;
2046 		*dp = '\0';
2047 	}
2048 	array[nsubs] = NULL;
2049 	return (array);
2050 }
2051 
2052 static long
2053 oadd(t1, t2)
2054 const long	t1;
2055 const long	t2;
2056 {
2057 	register long	t;
2058 
2059 	t = t1 + t2;
2060 	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2061 		error(gettext("time overflow"));
2062 		(void) exit(EXIT_FAILURE);
2063 	}
2064 	return (t);
2065 }
2066 
2067 static time_t
2068 tadd(t1, t2)
2069 const time_t	t1;
2070 const long	t2;
2071 {
2072 	register time_t	t;
2073 
2074 	if (t1 == max_time && t2 > 0)
2075 		return (max_time);
2076 	if (t1 == min_time && t2 < 0)
2077 		return (min_time);
2078 	t = t1 + t2;
2079 	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2080 		error(gettext("time overflow"));
2081 		(void) exit(EXIT_FAILURE);
2082 	}
2083 	return (t);
2084 }
2085 
2086 /*
2087  * Given a rule, and a year, compute the date - in seconds since January 1,
2088  * 1970, 00:00 LOCAL time - in that year that the rule refers to.
2089  */
2090 
2091 static time_t
2092 rpytime(rp, wantedy)
2093 register const struct rule * const	rp;
2094 register const int			wantedy;
2095 {
2096 	register int	y, m, i;
2097 	register long	dayoff;			/* with a nod to Margaret O. */
2098 	register time_t	t;
2099 
2100 	if (wantedy == INT_MIN)
2101 		return (min_time);
2102 	if (wantedy == INT_MAX)
2103 		return (max_time);
2104 	dayoff = 0;
2105 	m = TM_JANUARY;
2106 	y = EPOCH_YEAR;
2107 	while (wantedy != y) {
2108 		if (wantedy > y) {
2109 			i = len_years[isleap(y)];
2110 			++y;
2111 		} else {
2112 			--y;
2113 			i = -len_years[isleap(y)];
2114 		}
2115 		dayoff = oadd(dayoff, eitol(i));
2116 	}
2117 	while (m != rp->r_month) {
2118 		i = len_months[isleap(y)][m];
2119 		dayoff = oadd(dayoff, eitol(i));
2120 		++m;
2121 	}
2122 	i = rp->r_dayofmonth;
2123 	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
2124 		if (rp->r_dycode == DC_DOWLEQ)
2125 			--i;
2126 		else {
2127 			error(gettext("use of 2/29 in non leap-year"));
2128 			(void) exit(EXIT_FAILURE);
2129 		}
2130 	}
2131 	--i;
2132 	dayoff = oadd(dayoff, eitol(i));
2133 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
2134 		register long	wday;
2135 
2136 #define	LDAYSPERWEEK	((long)DAYSPERWEEK)
2137 		wday = eitol(EPOCH_WDAY);
2138 		/*
2139 		 * Don't trust mod of negative numbers.
2140 		 */
2141 		if (dayoff >= 0)
2142 			wday = (wday + dayoff) % LDAYSPERWEEK;
2143 		else {
2144 			wday -= ((-dayoff) % LDAYSPERWEEK);
2145 			if (wday < 0)
2146 				wday += LDAYSPERWEEK;
2147 		}
2148 		while (wday != eitol(rp->r_wday))
2149 			if (rp->r_dycode == DC_DOWGEQ) {
2150 				dayoff = oadd(dayoff, (long)1);
2151 				if (++wday >= LDAYSPERWEEK)
2152 					wday = 0;
2153 				++i;
2154 			} else {
2155 				dayoff = oadd(dayoff, (long)-1);
2156 				if (--wday < 0)
2157 					wday = LDAYSPERWEEK - 1;
2158 				--i;
2159 			}
2160 		if (i < 0 || i >= len_months[isleap(y)][m]) {
2161 			error(gettext("no day in month matches rule"));
2162 			(void) exit(EXIT_FAILURE);
2163 		}
2164 	}
2165 	if (dayoff < 0 && !TYPE_SIGNED(time_t))
2166 		return (min_time);
2167 	t = (time_t)dayoff * SECSPERDAY;
2168 	/*
2169 	 * Cheap overflow check.
2170 	 */
2171 	if (t / SECSPERDAY != dayoff)
2172 		return ((dayoff > 0) ? max_time : min_time);
2173 	return (tadd(t, rp->r_tod));
2174 }
2175 
2176 static void
2177 newabbr(string)
2178 const char * const	string;
2179 {
2180 	register int	i;
2181 
2182 	i = strlen(string) + 1;
2183 	if (charcnt + i > TZ_MAX_CHARS) {
2184 		error(gettext(
2185 		    "too many, or too long, time zone abbreviations"));
2186 		(void) exit(EXIT_FAILURE);
2187 	}
2188 	(void) strcpy(&chars[charcnt], string);
2189 	charcnt += eitol(i);
2190 }
2191 
2192 static int
2193 mkdirs(argname)
2194 char * const	argname;
2195 {
2196 	register char *name;
2197 	register char *cp;
2198 
2199 	if (argname == NULL || *argname == '\0')
2200 		return (0);
2201 	cp = name = ecpyalloc(argname);
2202 	while ((cp = strchr(cp + 1, '/')) != 0) {
2203 		*cp = '\0';
2204 		if (!itsdir(name)) {
2205 			/*
2206 			 * It doesn't seem to exist, so we try to create it.
2207 			 * Creation may fail because of the directory being
2208 			 * created by some other multiprocessor, so we get
2209 			 * to do extra checking.
2210 			 */
2211 			if (mkdir(name,
2212 		S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) != 0) {
2213 				const char *e = strerror(errno);
2214 
2215 				if (errno != EEXIST || !itsdir(name)) {
2216 					(void) fprintf(stderr, gettext(
2217 				    "%s: Can't create directory %s: %s\n"),
2218 						progname, name, e);
2219 					ifree(name);
2220 					return (-1);
2221 				}
2222 			}
2223 		}
2224 		*cp = '/';
2225 	}
2226 	ifree(name);
2227 	return (0);
2228 }
2229 
2230 static long
2231 eitol(i)
2232 const int	i;
2233 {
2234 	long	l;
2235 
2236 	l = i;
2237 	if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
2238 		(void) fprintf(stderr,
2239 			gettext("%s: %d did not sign extend correctly\n"),
2240 			progname, i);
2241 		(void) exit(EXIT_FAILURE);
2242 	}
2243 	return (l);
2244 }
2245 
2246 /*
2247  * UNIX was a registered trademark of UNIX System Laboratories in 1993.
2248  */
2249