xref: /csrg-svn/share/zoneinfo/zic.c (revision 46793)
139991Sbostic #ifndef lint
239991Sbostic #ifndef NOID
339991Sbostic static char	elsieid[] = "@(#)zic.c	4.12";
439991Sbostic #endif /* !defined NOID */
539991Sbostic #endif /* !defined lint */
639991Sbostic 
7*46793Sbostic #include <sys/types.h>
8*46793Sbostic #include <sys/cdefs.h>
9*46793Sbostic #include <sys/stat.h>
10*46793Sbostic #include <time.h>
11*46793Sbostic #include <tzfile.h>
12*46793Sbostic #include <stdio.h>
13*46793Sbostic #include <ctype.h>
14*46793Sbostic #include <string.h>
15*46793Sbostic #include <stdlib.h>
1639991Sbostic 
1739991Sbostic #ifndef TRUE
1839991Sbostic #define TRUE	1
1939991Sbostic #define FALSE	0
2039991Sbostic #endif /* !defined TRUE */
2139991Sbostic 
2239991Sbostic struct rule {
2339991Sbostic 	const char *	r_filename;
2439991Sbostic 	int		r_linenum;
2539991Sbostic 	const char *	r_name;
2639991Sbostic 
2739991Sbostic 	int		r_loyear;	/* for example, 1986 */
2839991Sbostic 	int		r_hiyear;	/* for example, 1986 */
2939991Sbostic 	const char *	r_yrtype;
3039991Sbostic 
3139991Sbostic 	int		r_month;	/* 0..11 */
3239991Sbostic 
3339991Sbostic 	int		r_dycode;	/* see below */
3439991Sbostic 	int		r_dayofmonth;
3539991Sbostic 	int		r_wday;
3639991Sbostic 
3739991Sbostic 	long		r_tod;		/* time from midnight */
3839991Sbostic 	int		r_todisstd;	/* above is standard time if TRUE */
3939991Sbostic 					/* or wall clock time if FALSE */
4039991Sbostic 	long		r_stdoff;	/* offset from standard time */
4139991Sbostic 	const char *	r_abbrvar;	/* variable part of abbreviation */
4239991Sbostic 
4339991Sbostic 	int		r_todo;		/* a rule to do (used in outzone) */
4439991Sbostic 	time_t		r_temp;		/* used in outzone */
4539991Sbostic };
4639991Sbostic 
4739991Sbostic /*
4839991Sbostic **	r_dycode		r_dayofmonth	r_wday
4939991Sbostic */
5039991Sbostic 
5139991Sbostic #define DC_DOM		0	/* 1..31 */	/* unused */
5239991Sbostic #define DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */
5339991Sbostic #define DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */
5439991Sbostic 
5539991Sbostic struct zone {
5639991Sbostic 	const char *	z_filename;
5739991Sbostic 	int		z_linenum;
5839991Sbostic 
5939991Sbostic 	const char *	z_name;
6039991Sbostic 	long		z_gmtoff;
6139991Sbostic 	const char *	z_rule;
6239991Sbostic 	const char *	z_format;
6339991Sbostic 
6439991Sbostic 	long		z_stdoff;
6539991Sbostic 
6639991Sbostic 	struct rule *	z_rules;
6739991Sbostic 	int		z_nrules;
6839991Sbostic 
6939991Sbostic 	struct rule	z_untilrule;
7039991Sbostic 	time_t		z_untiltime;
7139991Sbostic };
7239991Sbostic 
73*46793Sbostic extern char *	icatalloc __P((char * old, const char * new));
74*46793Sbostic extern char *	icpyalloc __P((const char * string));
75*46793Sbostic extern void	ifree __P((char * p));
76*46793Sbostic extern char *	imalloc __P((int n));
77*46793Sbostic extern char *	irealloc __P((char * old, int n));
78*46793Sbostic extern int	link __P((const char * fromname, const char * toname));
7939991Sbostic extern char *	optarg;
8039991Sbostic extern int	optind;
81*46793Sbostic extern void	perror __P((const char * string));
82*46793Sbostic extern char *	scheck __P((const char * string, const char * format));
83*46793Sbostic static void	addtt __P((time_t starttime, int type));
84*46793Sbostic static int	addtype
85*46793Sbostic 		    __P((long gmtoff, const char * abbr, int isdst,
86*46793Sbostic 		    int ttisstd));
87*46793Sbostic static void	addleap __P((time_t t, int positive, int rolling));
88*46793Sbostic static void	adjleap __P((void));
89*46793Sbostic static void	associate __P((void));
90*46793Sbostic static int	ciequal __P((const char * ap, const char * bp));
91*46793Sbostic static void	convert __P((long val, char * buf));
92*46793Sbostic static void	dolink __P((const char * fromfile, const char * tofile));
93*46793Sbostic static void	eat __P((const char * name, int num));
94*46793Sbostic static void	eats __P((const char * name, int num,
95*46793Sbostic 		    const char * rname, int rnum));
96*46793Sbostic static long	eitol __P((int i));
97*46793Sbostic static void	error __P((const char * message));
98*46793Sbostic static char **	getfields __P((char * buf));
99*46793Sbostic static long	gethms __P((char * string, const char * errstrng,
100*46793Sbostic 		    int signable));
101*46793Sbostic static void	infile __P((const char * filename));
102*46793Sbostic static void	inleap __P((char ** fields, int nfields));
103*46793Sbostic static void	inlink __P((char ** fields, int nfields));
104*46793Sbostic static void	inrule __P((char ** fields, int nfields));
105*46793Sbostic static int	inzcont __P((char ** fields, int nfields));
106*46793Sbostic static int	inzone __P((char ** fields, int nfields));
107*46793Sbostic static int	inzsub __P((char ** fields, int nfields, int iscont));
108*46793Sbostic static int	itsabbr __P((const char * abbr, const char * word));
109*46793Sbostic static int	itsdir __P((const char * name));
110*46793Sbostic static int	lowerit __P((int c));
111*46793Sbostic static char *	memcheck __P((char * tocheck));
112*46793Sbostic static int	mkdirs __P((char * filename));
113*46793Sbostic static void	newabbr __P((const char * abbr));
114*46793Sbostic static long	oadd __P((long t1, long t2));
115*46793Sbostic static void	outzone __P((const struct zone * zp, int ntzones));
116*46793Sbostic static void	puttzcode __P((long code, FILE * fp));
117*46793Sbostic static int	rcomp __P((const void *leftp, const void *rightp));
118*46793Sbostic static time_t	rpytime __P((const struct rule * rp, int wantedy));
119*46793Sbostic static void	rulesub __P((struct rule * rp, char * loyearp, char * hiyearp,
120*46793Sbostic 		char * typep, char * monthp, char * dayp, char * timep));
121*46793Sbostic static void	setboundaries __P((void));
122*46793Sbostic static time_t	tadd __P((time_t t1, long t2));
123*46793Sbostic static void	usage __P((void));
124*46793Sbostic static void	writezone __P((const char * name));
125*46793Sbostic static int	yearistype __P((int year, const char * type));
12639991Sbostic 
12739991Sbostic static int		charcnt;
12839991Sbostic static int		errors;
12939991Sbostic static const char *	filename;
13039991Sbostic static int		leapcnt;
13139991Sbostic static int		linenum;
13239991Sbostic static time_t		max_time;
13339991Sbostic static int		max_year;
13439991Sbostic static time_t		min_time;
13539991Sbostic static int		min_year;
13639991Sbostic static int		noise;
13739991Sbostic static const char *	rfilename;
13839991Sbostic static int		rlinenum;
13939991Sbostic static const char *	progname;
14039991Sbostic static int		timecnt;
14139991Sbostic static int		typecnt;
14239991Sbostic static int		tt_signed;
14339991Sbostic 
14439991Sbostic /*
14539991Sbostic ** Line codes.
14639991Sbostic */
14739991Sbostic 
14839991Sbostic #define LC_RULE		0
14939991Sbostic #define LC_ZONE		1
15039991Sbostic #define LC_LINK		2
15139991Sbostic #define LC_LEAP		3
15239991Sbostic 
15339991Sbostic /*
15439991Sbostic ** Which fields are which on a Zone line.
15539991Sbostic */
15639991Sbostic 
15739991Sbostic #define ZF_NAME		1
15839991Sbostic #define ZF_GMTOFF	2
15939991Sbostic #define ZF_RULE		3
16039991Sbostic #define ZF_FORMAT	4
16139991Sbostic #define ZF_TILYEAR	5
16239991Sbostic #define ZF_TILMONTH	6
16339991Sbostic #define ZF_TILDAY	7
16439991Sbostic #define ZF_TILTIME	8
16539991Sbostic #define ZONE_MINFIELDS	5
16639991Sbostic #define ZONE_MAXFIELDS	9
16739991Sbostic 
16839991Sbostic /*
16939991Sbostic ** Which fields are which on a Zone continuation line.
17039991Sbostic */
17139991Sbostic 
17239991Sbostic #define ZFC_GMTOFF	0
17339991Sbostic #define ZFC_RULE	1
17439991Sbostic #define ZFC_FORMAT	2
17539991Sbostic #define ZFC_TILYEAR	3
17639991Sbostic #define ZFC_TILMONTH	4
17739991Sbostic #define ZFC_TILDAY	5
17839991Sbostic #define ZFC_TILTIME	6
17939991Sbostic #define ZONEC_MINFIELDS	3
18039991Sbostic #define ZONEC_MAXFIELDS	7
18139991Sbostic 
18239991Sbostic /*
18339991Sbostic ** Which files are which on a Rule line.
18439991Sbostic */
18539991Sbostic 
18639991Sbostic #define RF_NAME		1
18739991Sbostic #define RF_LOYEAR	2
18839991Sbostic #define RF_HIYEAR	3
18939991Sbostic #define RF_COMMAND	4
19039991Sbostic #define RF_MONTH	5
19139991Sbostic #define RF_DAY		6
19239991Sbostic #define RF_TOD		7
19339991Sbostic #define RF_STDOFF	8
19439991Sbostic #define RF_ABBRVAR	9
19539991Sbostic #define RULE_FIELDS	10
19639991Sbostic 
19739991Sbostic /*
19839991Sbostic ** Which fields are which on a Link line.
19939991Sbostic */
20039991Sbostic 
20139991Sbostic #define LF_FROM		1
20239991Sbostic #define LF_TO		2
20339991Sbostic #define LINK_FIELDS	3
20439991Sbostic 
20539991Sbostic /*
20639991Sbostic ** Which fields are which on a Leap line.
20739991Sbostic */
20839991Sbostic 
20939991Sbostic #define LP_YEAR		1
21039991Sbostic #define LP_MONTH	2
21139991Sbostic #define LP_DAY		3
21239991Sbostic #define LP_TIME		4
21339991Sbostic #define LP_CORR		5
21439991Sbostic #define LP_ROLL		6
21539991Sbostic #define LEAP_FIELDS	7
21639991Sbostic 
21739991Sbostic /*
21839991Sbostic ** Year synonyms.
21939991Sbostic */
22039991Sbostic 
22139991Sbostic #define YR_MINIMUM	0
22239991Sbostic #define YR_MAXIMUM	1
22339991Sbostic #define YR_ONLY		2
22439991Sbostic 
22539991Sbostic static struct rule *	rules;
22639991Sbostic static int		nrules;	/* number of rules */
22739991Sbostic 
22839991Sbostic static struct zone *	zones;
22939991Sbostic static int		nzones;	/* number of zones */
23039991Sbostic 
23139991Sbostic struct link {
23239991Sbostic 	const char *	l_filename;
23339991Sbostic 	int		l_linenum;
23439991Sbostic 	const char *	l_from;
23539991Sbostic 	const char *	l_to;
23639991Sbostic };
23739991Sbostic 
23839991Sbostic static struct link *	links;
23939991Sbostic static int		nlinks;
24039991Sbostic 
24139991Sbostic struct lookup {
24239991Sbostic 	const char *	l_word;
24339991Sbostic 	const int	l_value;
24439991Sbostic };
24539991Sbostic 
246*46793Sbostic static struct lookup const *	byword __P((const char * string,
24739991Sbostic 					const struct lookup * lp));
24839991Sbostic 
24939991Sbostic static struct lookup const	line_codes[] = {
25039991Sbostic 	"Rule",		LC_RULE,
25139991Sbostic 	"Zone",		LC_ZONE,
25239991Sbostic 	"Link",		LC_LINK,
25339991Sbostic 	"Leap",		LC_LEAP,
25439991Sbostic 	NULL,		0
25539991Sbostic };
25639991Sbostic 
25739991Sbostic static struct lookup const	mon_names[] = {
25839991Sbostic 	"January",	TM_JANUARY,
25939991Sbostic 	"February",	TM_FEBRUARY,
26039991Sbostic 	"March",	TM_MARCH,
26139991Sbostic 	"April",	TM_APRIL,
26239991Sbostic 	"May",		TM_MAY,
26339991Sbostic 	"June",		TM_JUNE,
26439991Sbostic 	"July",		TM_JULY,
26539991Sbostic 	"August",	TM_AUGUST,
26639991Sbostic 	"September",	TM_SEPTEMBER,
26739991Sbostic 	"October",	TM_OCTOBER,
26839991Sbostic 	"November",	TM_NOVEMBER,
26939991Sbostic 	"December",	TM_DECEMBER,
27039991Sbostic 	NULL,		0
27139991Sbostic };
27239991Sbostic 
27339991Sbostic static struct lookup const	wday_names[] = {
27439991Sbostic 	"Sunday",	TM_SUNDAY,
27539991Sbostic 	"Monday",	TM_MONDAY,
27639991Sbostic 	"Tuesday",	TM_TUESDAY,
27739991Sbostic 	"Wednesday",	TM_WEDNESDAY,
27839991Sbostic 	"Thursday",	TM_THURSDAY,
27939991Sbostic 	"Friday",	TM_FRIDAY,
28039991Sbostic 	"Saturday",	TM_SATURDAY,
28139991Sbostic 	NULL,		0
28239991Sbostic };
28339991Sbostic 
28439991Sbostic static struct lookup const	lasts[] = {
28539991Sbostic 	"last-Sunday",		TM_SUNDAY,
28639991Sbostic 	"last-Monday",		TM_MONDAY,
28739991Sbostic 	"last-Tuesday",		TM_TUESDAY,
28839991Sbostic 	"last-Wednesday",	TM_WEDNESDAY,
28939991Sbostic 	"last-Thursday",	TM_THURSDAY,
29039991Sbostic 	"last-Friday",		TM_FRIDAY,
29139991Sbostic 	"last-Saturday",	TM_SATURDAY,
29239991Sbostic 	NULL,			0
29339991Sbostic };
29439991Sbostic 
29539991Sbostic static struct lookup const	begin_years[] = {
29639991Sbostic 	"minimum",		YR_MINIMUM,
29739991Sbostic 	"maximum",		YR_MAXIMUM,
29839991Sbostic 	NULL,			0
29939991Sbostic };
30039991Sbostic 
30139991Sbostic static struct lookup const	end_years[] = {
30239991Sbostic 	"minimum",		YR_MINIMUM,
30339991Sbostic 	"maximum",		YR_MAXIMUM,
30439991Sbostic 	"only",			YR_ONLY,
30539991Sbostic 	NULL,			0
30639991Sbostic };
30739991Sbostic 
30839991Sbostic static struct lookup const	leap_types[] = {
30939991Sbostic 	"Rolling",		TRUE,
31039991Sbostic 	"Stationary",		FALSE,
31139991Sbostic 	NULL,			0
31239991Sbostic };
31339991Sbostic 
31439991Sbostic static const int	len_months[2][MONSPERYEAR] = {
31539991Sbostic 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
31639991Sbostic 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
31739991Sbostic };
31839991Sbostic 
31939991Sbostic static const int	len_years[2] = {
32039991Sbostic 	DAYSPERNYEAR, DAYSPERLYEAR
32139991Sbostic };
32239991Sbostic 
32339991Sbostic static time_t		ats[TZ_MAX_TIMES];
32439991Sbostic static unsigned char	types[TZ_MAX_TIMES];
32539991Sbostic static long		gmtoffs[TZ_MAX_TYPES];
32639991Sbostic static char		isdsts[TZ_MAX_TYPES];
32739991Sbostic static char		abbrinds[TZ_MAX_TYPES];
32839991Sbostic static char		ttisstds[TZ_MAX_TYPES];
32939991Sbostic static char		chars[TZ_MAX_CHARS];
33039991Sbostic static time_t		trans[TZ_MAX_LEAPS];
33139991Sbostic static long		corr[TZ_MAX_LEAPS];
33239991Sbostic static char		roll[TZ_MAX_LEAPS];
33339991Sbostic 
33439991Sbostic /*
33539991Sbostic ** Memory allocation.
33639991Sbostic */
33739991Sbostic 
33839991Sbostic static char *
33939991Sbostic memcheck(ptr)
34039991Sbostic char * const	ptr;
34139991Sbostic {
34239991Sbostic 	if (ptr == NULL) {
34339991Sbostic 		(void) perror(progname);
34439991Sbostic 		(void) exit(EXIT_FAILURE);
34539991Sbostic 	}
34639991Sbostic 	return ptr;
34739991Sbostic }
34839991Sbostic 
34939991Sbostic #define emalloc(size)		memcheck(imalloc(size))
35039991Sbostic #define erealloc(ptr, size)	memcheck(irealloc(ptr, size))
35139991Sbostic #define ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
35239991Sbostic #define ecatalloc(oldp, newp)	memcheck(icatalloc(oldp, newp))
35339991Sbostic 
35439991Sbostic /*
35539991Sbostic ** Error handling.
35639991Sbostic */
35739991Sbostic 
35839991Sbostic static void
35939991Sbostic eats(name, num, rname, rnum)
36039991Sbostic const char * const	name;
36139991Sbostic const int		num;
36239991Sbostic const char * const	rname;
36339991Sbostic const int		rnum;
36439991Sbostic {
36539991Sbostic 	filename = name;
36639991Sbostic 	linenum = num;
36739991Sbostic 	rfilename = rname;
36839991Sbostic 	rlinenum = rnum;
36939991Sbostic }
37039991Sbostic 
37139991Sbostic static void
37239991Sbostic eat(name, num)
37339991Sbostic const char * const	name;
37439991Sbostic const int		num;
37539991Sbostic {
37639991Sbostic 	eats(name, num, (char *) NULL, -1);
37739991Sbostic }
37839991Sbostic 
37939991Sbostic static void
38039991Sbostic error(string)
38139991Sbostic const char * const	string;
38239991Sbostic {
38339991Sbostic 	/*
38439991Sbostic 	** Match the format of "cc" to allow sh users to
38539991Sbostic 	** 	zic ... 2>&1 | error -t "*" -v
38639991Sbostic 	** on BSD systems.
38739991Sbostic 	*/
38839991Sbostic 	(void) fprintf(stderr, "\"%s\", line %d: %s",
38939991Sbostic 		filename, linenum, string);
39039991Sbostic 	if (rfilename != NULL)
39139991Sbostic 		(void) fprintf(stderr, " (rule from \"%s\", line %d)",
39239991Sbostic 			rfilename, rlinenum);
39339991Sbostic 	(void) fprintf(stderr, "\n");
39439991Sbostic 	++errors;
39539991Sbostic }
39639991Sbostic 
39739991Sbostic static void
39839991Sbostic usage()
39939991Sbostic {
40039991Sbostic 	(void) fprintf(stderr,
40139991Sbostic "%s: usage is %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n\
40239991Sbostic \t[ -L leapseconds ] [ filename ... ]\n",
40339991Sbostic 		progname, progname);
40439991Sbostic 	(void) exit(EXIT_FAILURE);
40539991Sbostic }
40639991Sbostic 
40739991Sbostic static const char *	psxrules = NULL;
40839991Sbostic static const char *	lcltime = NULL;
40939991Sbostic static const char *	directory = NULL;
41039991Sbostic static const char *	leapsec = NULL;
41139991Sbostic static int		sflag = FALSE;
41239991Sbostic 
41339991Sbostic int
41439991Sbostic main(argc, argv)
41539991Sbostic int	argc;
41639991Sbostic char *	argv[];
41739991Sbostic {
41839991Sbostic 	register int	i, j;
41939991Sbostic 	register int	c;
42039991Sbostic 
42139991Sbostic 	(void) umask(umask(022) | 022);
42239991Sbostic 	progname = argv[0];
42339991Sbostic 	while ((c = getopt(argc, argv, "d:l:p:L:vs")) != EOF)
42439991Sbostic 		switch (c) {
42539991Sbostic 			default:
42639991Sbostic 				usage();
42739991Sbostic 			case 'd':
42839991Sbostic 				if (directory == NULL)
42939991Sbostic 					directory = optarg;
43039991Sbostic 				else {
43139991Sbostic 					(void) fprintf(stderr,
43239991Sbostic "%s: More than one -d option specified\n",
43339991Sbostic 						progname);
43439991Sbostic 					(void) exit(EXIT_FAILURE);
43539991Sbostic 				}
43639991Sbostic 				break;
43739991Sbostic 			case 'l':
43839991Sbostic 				if (lcltime == NULL)
43939991Sbostic 					lcltime = optarg;
44039991Sbostic 				else {
44139991Sbostic 					(void) fprintf(stderr,
44239991Sbostic "%s: More than one -l option specified\n",
44339991Sbostic 						progname);
44439991Sbostic 					(void) exit(EXIT_FAILURE);
44539991Sbostic 				}
44639991Sbostic 				break;
44739991Sbostic 			case 'p':
44839991Sbostic 				if (psxrules == NULL)
44939991Sbostic 					psxrules = optarg;
45039991Sbostic 				else {
45139991Sbostic 					(void) fprintf(stderr,
45239991Sbostic "%s: More than one -p option specified\n",
45339991Sbostic 						progname);
45439991Sbostic 					(void) exit(EXIT_FAILURE);
45539991Sbostic 				}
45639991Sbostic 				break;
45739991Sbostic 			case 'L':
45839991Sbostic 				if (leapsec == NULL)
45939991Sbostic 					leapsec = optarg;
46039991Sbostic 				else {
46139991Sbostic 					(void) fprintf(stderr,
46239991Sbostic "%s: More than one -L option specified\n",
46339991Sbostic 						progname);
46439991Sbostic 					(void) exit(EXIT_FAILURE);
46539991Sbostic 				}
46639991Sbostic 				break;
46739991Sbostic 			case 'v':
46839991Sbostic 				noise = TRUE;
46939991Sbostic 				break;
47039991Sbostic 			case 's':
47139991Sbostic 				sflag = TRUE;
47239991Sbostic 				break;
47339991Sbostic 		}
47439991Sbostic 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
47539991Sbostic 		usage();	/* usage message by request */
47639991Sbostic 	if (directory == NULL)
47739991Sbostic 		directory = TZDIR;
47839991Sbostic 
47939991Sbostic 	setboundaries();
48039991Sbostic 
48139991Sbostic 	if (optind < argc && leapsec != NULL) {
48239991Sbostic 		infile(leapsec);
48339991Sbostic 		adjleap();
48439991Sbostic 	}
48539991Sbostic 
48639991Sbostic 	zones = (struct zone *) emalloc(0);
48739991Sbostic 	rules = (struct rule *) emalloc(0);
48839991Sbostic 	links = (struct link *) emalloc(0);
48939991Sbostic 	for (i = optind; i < argc; ++i)
49039991Sbostic 		infile(argv[i]);
49139991Sbostic 	if (errors)
49239991Sbostic 		(void) exit(EXIT_FAILURE);
49339991Sbostic 	associate();
49439991Sbostic 	for (i = 0; i < nzones; i = j) {
49539991Sbostic 		/*
49639991Sbostic 		** Find the next non-continuation zone entry.
49739991Sbostic 		*/
49839991Sbostic 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
49939991Sbostic 			;
50039991Sbostic 		outzone(&zones[i], j - i);
50139991Sbostic 	}
50239991Sbostic 	/*
50339991Sbostic 	** Make links.
50439991Sbostic 	*/
50539991Sbostic 	for (i = 0; i < nlinks; ++i)
50639991Sbostic 		dolink(links[i].l_from, links[i].l_to);
50739991Sbostic 	if (lcltime != NULL)
50839991Sbostic 		dolink(lcltime, TZDEFAULT);
50939991Sbostic 	if (psxrules != NULL)
51039991Sbostic 		dolink(psxrules, TZDEFRULES);
51139991Sbostic 	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
51239991Sbostic }
51339991Sbostic 
51439991Sbostic static void
51539991Sbostic dolink(fromfile, tofile)
51639991Sbostic const char * const	fromfile;
51739991Sbostic const char * const	tofile;
51839991Sbostic {
51939991Sbostic 	register char *	fromname;
52039991Sbostic 	register char *	toname;
52139991Sbostic 
52239991Sbostic 	fromname = ecpyalloc(directory);
52339991Sbostic 	fromname = ecatalloc(fromname, "/");
52439991Sbostic 	fromname = ecatalloc(fromname, fromfile);
52539991Sbostic 	toname = ecpyalloc(directory);
52639991Sbostic 	toname = ecatalloc(toname, "/");
52739991Sbostic 	toname = ecatalloc(toname, tofile);
52839991Sbostic 	/*
52939991Sbostic 	** We get to be careful here since
53039991Sbostic 	** there's a fair chance of root running us.
53139991Sbostic 	*/
53239991Sbostic 	if (!itsdir(toname))
53339991Sbostic 		(void) remove(toname);
53439991Sbostic 	if (link(fromname, toname) != 0) {
53539991Sbostic 		(void) fprintf(stderr, "%s: Can't link from %s to ",
53639991Sbostic 			progname, fromname);
53739991Sbostic 		(void) perror(toname);
53839991Sbostic 		(void) exit(EXIT_FAILURE);
53939991Sbostic 	}
54039991Sbostic 	ifree(fromname);
54139991Sbostic 	ifree(toname);
54239991Sbostic }
54339991Sbostic 
54439991Sbostic static void
54539991Sbostic setboundaries()
54639991Sbostic {
54739991Sbostic 	register time_t	bit;
54839991Sbostic 
54939991Sbostic 	for (bit = 1; bit > 0; bit <<= 1)
55039991Sbostic 		;
55139991Sbostic 	if (bit == 0) {		/* time_t is an unsigned type */
55239991Sbostic 		tt_signed = FALSE;
55339991Sbostic 		min_time = 0;
55439991Sbostic 		max_time = ~(time_t) 0;
55539991Sbostic 		if (sflag)
55639991Sbostic 			max_time >>= 1;
55739991Sbostic 	} else {
55839991Sbostic 		tt_signed = TRUE;
55939991Sbostic 		min_time = bit;
56039991Sbostic 		max_time = bit;
56139991Sbostic 		++max_time;
56239991Sbostic 		max_time = -max_time;
56339991Sbostic 		if (sflag)
56439991Sbostic 			min_time = 0;
56539991Sbostic 	}
56639991Sbostic 	min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
56739991Sbostic 	max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
56839991Sbostic }
56939991Sbostic 
57039991Sbostic static int
57139991Sbostic itsdir(name)
57239991Sbostic const char * const	name;
57339991Sbostic {
57439991Sbostic 	struct stat	s;
57539991Sbostic 
576*46793Sbostic 	return (stat(name, &s) == 0 && S_ISDIR(s.st_mode));
57739991Sbostic }
57839991Sbostic 
57939991Sbostic /*
58039991Sbostic ** Associate sets of rules with zones.
58139991Sbostic */
58239991Sbostic 
58339991Sbostic /*
58439991Sbostic ** Sort by rule name.
58539991Sbostic */
58639991Sbostic 
58739991Sbostic static int
58839991Sbostic rcomp(cp1, cp2)
589*46793Sbostic const void *	cp1;
590*46793Sbostic const void *	cp2;
59139991Sbostic {
59239991Sbostic 	return strcmp(((struct rule *) cp1)->r_name,
59339991Sbostic 		((struct rule *) cp2)->r_name);
59439991Sbostic }
59539991Sbostic 
59639991Sbostic static void
59739991Sbostic associate()
59839991Sbostic {
59939991Sbostic 	register struct zone *	zp;
60039991Sbostic 	register struct rule *	rp;
60139991Sbostic 	register int		base, out;
60239991Sbostic 	register int		i;
60339991Sbostic 
60439991Sbostic 	if (nrules != 0)
605*46793Sbostic 		(void) qsort((void *) rules, (size_t) nrules,
606*46793Sbostic 		     (size_t) sizeof *rules, rcomp);
60739991Sbostic 	for (i = 0; i < nzones; ++i) {
60839991Sbostic 		zp = &zones[i];
60939991Sbostic 		zp->z_rules = NULL;
61039991Sbostic 		zp->z_nrules = 0;
61139991Sbostic 	}
61239991Sbostic 	for (base = 0; base < nrules; base = out) {
61339991Sbostic 		rp = &rules[base];
61439991Sbostic 		for (out = base + 1; out < nrules; ++out)
61539991Sbostic 			if (strcmp(rp->r_name, rules[out].r_name) != 0)
61639991Sbostic 				break;
61739991Sbostic 		for (i = 0; i < nzones; ++i) {
61839991Sbostic 			zp = &zones[i];
61939991Sbostic 			if (strcmp(zp->z_rule, rp->r_name) != 0)
62039991Sbostic 				continue;
62139991Sbostic 			zp->z_rules = rp;
62239991Sbostic 			zp->z_nrules = out - base;
62339991Sbostic 		}
62439991Sbostic 	}
62539991Sbostic 	for (i = 0; i < nzones; ++i) {
62639991Sbostic 		zp = &zones[i];
62739991Sbostic 		if (zp->z_nrules == 0) {
62839991Sbostic 			/*
62939991Sbostic 			** Maybe we have a local standard time offset.
63039991Sbostic 			*/
63139991Sbostic 			eat(zp->z_filename, zp->z_linenum);
632*46793Sbostic 			zp->z_stdoff =
633*46793Sbostic 			    gethms((char *)zp->z_rule, "unruly zone", TRUE);
63439991Sbostic 			/*
63539991Sbostic 			** Note, though, that if there's no rule,
63639991Sbostic 			** a '%s' in the format is a bad thing.
63739991Sbostic 			*/
63839991Sbostic 			if (strchr(zp->z_format, '%') != 0)
63939991Sbostic 				error("%s in ruleless zone");
64039991Sbostic 		}
64139991Sbostic 	}
64239991Sbostic 	if (errors)
64339991Sbostic 		(void) exit(EXIT_FAILURE);
64439991Sbostic }
64539991Sbostic 
64639991Sbostic static void
64739991Sbostic infile(name)
64839991Sbostic const char *	name;
64939991Sbostic {
65039991Sbostic 	register FILE *			fp;
65139991Sbostic 	register char **		fields;
65239991Sbostic 	register char *			cp;
65339991Sbostic 	register const struct lookup *	lp;
65439991Sbostic 	register int			nfields;
65539991Sbostic 	register int			wantcont;
65639991Sbostic 	register int			num;
65739991Sbostic 	char				buf[BUFSIZ];
65839991Sbostic 
65939991Sbostic 	if (strcmp(name, "-") == 0) {
66039991Sbostic 		name = "standard input";
66139991Sbostic 		fp = stdin;
66239991Sbostic 	} else if ((fp = fopen(name, "r")) == NULL) {
66339991Sbostic 		(void) fprintf(stderr, "%s: Can't open ", progname);
66439991Sbostic 		(void) perror(name);
66539991Sbostic 		(void) exit(EXIT_FAILURE);
66639991Sbostic 	}
66739991Sbostic 	wantcont = FALSE;
66839991Sbostic 	for (num = 1; ; ++num) {
66939991Sbostic 		eat(name, num);
67039991Sbostic 		if (fgets(buf, (int) sizeof buf, fp) != buf)
67139991Sbostic 			break;
67239991Sbostic 		cp = strchr(buf, '\n');
67339991Sbostic 		if (cp == NULL) {
67439991Sbostic 			error("line too long");
67539991Sbostic 			(void) exit(EXIT_FAILURE);
67639991Sbostic 		}
67739991Sbostic 		*cp = '\0';
67839991Sbostic 		fields = getfields(buf);
67939991Sbostic 		nfields = 0;
68039991Sbostic 		while (fields[nfields] != NULL) {
68139991Sbostic 			if (ciequal(fields[nfields], "-"))
68239991Sbostic 				fields[nfields] = "";
68339991Sbostic 			++nfields;
68439991Sbostic 		}
68539991Sbostic 		if (nfields == 0) {
68639991Sbostic 			/* nothing to do */
68739991Sbostic 		} else if (wantcont) {
68839991Sbostic 			wantcont = inzcont(fields, nfields);
68939991Sbostic 		} else {
69039991Sbostic 			lp = byword(fields[0], line_codes);
69139991Sbostic 			if (lp == NULL)
69239991Sbostic 				error("input line of unknown type");
69339991Sbostic 			else switch ((int) (lp->l_value)) {
69439991Sbostic 				case LC_RULE:
69539991Sbostic 					inrule(fields, nfields);
69639991Sbostic 					wantcont = FALSE;
69739991Sbostic 					break;
69839991Sbostic 				case LC_ZONE:
69939991Sbostic 					wantcont = inzone(fields, nfields);
70039991Sbostic 					break;
70139991Sbostic 				case LC_LINK:
70239991Sbostic 					inlink(fields, nfields);
70339991Sbostic 					wantcont = FALSE;
70439991Sbostic 					break;
70539991Sbostic 				case LC_LEAP:
70639991Sbostic 					if (name != leapsec)
70739991Sbostic 						(void) fprintf(stderr,
70839991Sbostic "%s: Leap line in non leap seconds file %s\n",
70939991Sbostic 							progname, name);
71039991Sbostic 					else	inleap(fields, nfields);
71139991Sbostic 					wantcont = FALSE;
71239991Sbostic 					break;
71339991Sbostic 				default:	/* "cannot happen" */
71439991Sbostic 					(void) fprintf(stderr,
71539991Sbostic "%s: panic: Invalid l_value %d\n",
71639991Sbostic 						progname, lp->l_value);
71739991Sbostic 					(void) exit(EXIT_FAILURE);
71839991Sbostic 			}
71939991Sbostic 		}
72039991Sbostic 		ifree((char *) fields);
72139991Sbostic 	}
72239991Sbostic 	if (ferror(fp)) {
72339991Sbostic 		(void) fprintf(stderr, "%s: Error reading ", progname);
72439991Sbostic 		(void) perror(filename);
72539991Sbostic 		(void) exit(EXIT_FAILURE);
72639991Sbostic 	}
72739991Sbostic 	if (fp != stdin && fclose(fp)) {
72839991Sbostic 		(void) fprintf(stderr, "%s: Error closing ", progname);
72939991Sbostic 		(void) perror(filename);
73039991Sbostic 		(void) exit(EXIT_FAILURE);
73139991Sbostic 	}
73239991Sbostic 	if (wantcont)
73339991Sbostic 		error("expected continuation line not found");
73439991Sbostic }
73539991Sbostic 
73639991Sbostic /*
73739991Sbostic ** Convert a string of one of the forms
73839991Sbostic **	h	-h 	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
73939991Sbostic ** into a number of seconds.
74039991Sbostic ** A null string maps to zero.
74139991Sbostic ** Call error with errstring and return zero on errors.
74239991Sbostic */
74339991Sbostic 
74439991Sbostic static long
74539991Sbostic gethms(string, errstring, signable)
746*46793Sbostic char *		string;
74739991Sbostic const char * const	errstring;
74839991Sbostic const int		signable;
74939991Sbostic {
75039991Sbostic 	int	hh, mm, ss, sign;
75139991Sbostic 
75239991Sbostic 	if (string == NULL || *string == '\0')
75339991Sbostic 		return 0;
75439991Sbostic 	if (!signable)
75539991Sbostic 		sign = 1;
75639991Sbostic 	else if (*string == '-') {
75739991Sbostic 		sign = -1;
75839991Sbostic 		++string;
75939991Sbostic 	} else	sign = 1;
76039991Sbostic 	if (sscanf(string, scheck(string, "%d"), &hh) == 1)
76139991Sbostic 		mm = ss = 0;
76239991Sbostic 	else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
76339991Sbostic 		ss = 0;
76439991Sbostic 	else if (sscanf(string, scheck(string, "%d:%d:%d"),
76539991Sbostic 		&hh, &mm, &ss) != 3) {
76639991Sbostic 			error(errstring);
76739991Sbostic 			return 0;
76839991Sbostic 	}
76939991Sbostic 	if (hh < 0 || hh >= HOURSPERDAY ||
77039991Sbostic 		mm < 0 || mm >= MINSPERHOUR ||
77139991Sbostic 		ss < 0 || ss > SECSPERMIN) {
77239991Sbostic 			error(errstring);
77339991Sbostic 			return 0;
77439991Sbostic 	}
77539991Sbostic 	return eitol(sign) *
77639991Sbostic 		(eitol(hh * MINSPERHOUR + mm) *
77739991Sbostic 		eitol(SECSPERMIN) + eitol(ss));
77839991Sbostic }
77939991Sbostic 
78039991Sbostic static void
78139991Sbostic inrule(fields, nfields)
78239991Sbostic register char ** const	fields;
78339991Sbostic const int		nfields;
78439991Sbostic {
78539991Sbostic 	static struct rule	r;
78639991Sbostic 
78739991Sbostic 	if (nfields != RULE_FIELDS) {
78839991Sbostic 		error("wrong number of fields on Rule line");
78939991Sbostic 		return;
79039991Sbostic 	}
79139991Sbostic 	if (*fields[RF_NAME] == '\0') {
79239991Sbostic 		error("nameless rule");
79339991Sbostic 		return;
79439991Sbostic 	}
79539991Sbostic 	r.r_filename = filename;
79639991Sbostic 	r.r_linenum = linenum;
79739991Sbostic 	r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE);
79839991Sbostic 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
79939991Sbostic 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
80039991Sbostic 	r.r_name = ecpyalloc(fields[RF_NAME]);
80139991Sbostic 	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
80239991Sbostic 	rules = (struct rule *) erealloc((char *) rules,
80339991Sbostic 		(int) ((nrules + 1) * sizeof *rules));
80439991Sbostic 	rules[nrules++] = r;
80539991Sbostic }
80639991Sbostic 
80739991Sbostic static int
80839991Sbostic inzone(fields, nfields)
80939991Sbostic register char ** const	fields;
81039991Sbostic const int		nfields;
81139991Sbostic {
81239991Sbostic 	register int	i;
81339991Sbostic 	char		buf[132];
81439991Sbostic 
81539991Sbostic 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
81639991Sbostic 		error("wrong number of fields on Zone line");
81739991Sbostic 		return FALSE;
81839991Sbostic 	}
81939991Sbostic 	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
82039991Sbostic 		(void) sprintf(buf,
82139991Sbostic 			"\"Zone %s\" line and -l option are mutually exclusive",
82239991Sbostic 			TZDEFAULT);
82339991Sbostic 		error(buf);
82439991Sbostic 		return FALSE;
82539991Sbostic 	}
82639991Sbostic 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
82739991Sbostic 		(void) sprintf(buf,
82839991Sbostic 			"\"Zone %s\" line and -p option are mutually exclusive",
82939991Sbostic 			TZDEFRULES);
83039991Sbostic 		error(buf);
83139991Sbostic 		return FALSE;
83239991Sbostic 	}
83339991Sbostic 	for (i = 0; i < nzones; ++i)
83439991Sbostic 		if (zones[i].z_name != NULL &&
83539991Sbostic 			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
83639991Sbostic 				(void) sprintf(buf,
83739991Sbostic "duplicate zone name %s (file \"%s\", line %d)",
83839991Sbostic 					fields[ZF_NAME],
83939991Sbostic 					zones[i].z_filename,
84039991Sbostic 					zones[i].z_linenum);
84139991Sbostic 				error(buf);
84239991Sbostic 				return FALSE;
84339991Sbostic 		}
84439991Sbostic 	return inzsub(fields, nfields, FALSE);
84539991Sbostic }
84639991Sbostic 
84739991Sbostic static int
84839991Sbostic inzcont(fields, nfields)
84939991Sbostic register char ** const	fields;
85039991Sbostic const int		nfields;
85139991Sbostic {
85239991Sbostic 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
85339991Sbostic 		error("wrong number of fields on Zone continuation line");
85439991Sbostic 		return FALSE;
85539991Sbostic 	}
85639991Sbostic 	return inzsub(fields, nfields, TRUE);
85739991Sbostic }
85839991Sbostic 
85939991Sbostic static int
86039991Sbostic inzsub(fields, nfields, iscont)
86139991Sbostic register char ** const	fields;
86239991Sbostic const int		nfields;
86339991Sbostic const int		iscont;
86439991Sbostic {
86539991Sbostic 	register char *		cp;
86639991Sbostic 	static struct zone	z;
86739991Sbostic 	register int		i_gmtoff, i_rule, i_format;
86839991Sbostic 	register int		i_untilyear, i_untilmonth;
86939991Sbostic 	register int		i_untilday, i_untiltime;
87039991Sbostic 	register int		hasuntil;
87139991Sbostic 
87239991Sbostic 	if (iscont) {
87339991Sbostic 		i_gmtoff = ZFC_GMTOFF;
87439991Sbostic 		i_rule = ZFC_RULE;
87539991Sbostic 		i_format = ZFC_FORMAT;
87639991Sbostic 		i_untilyear = ZFC_TILYEAR;
87739991Sbostic 		i_untilmonth = ZFC_TILMONTH;
87839991Sbostic 		i_untilday = ZFC_TILDAY;
87939991Sbostic 		i_untiltime = ZFC_TILTIME;
88039991Sbostic 		z.z_name = NULL;
88139991Sbostic 	} else {
88239991Sbostic 		i_gmtoff = ZF_GMTOFF;
88339991Sbostic 		i_rule = ZF_RULE;
88439991Sbostic 		i_format = ZF_FORMAT;
88539991Sbostic 		i_untilyear = ZF_TILYEAR;
88639991Sbostic 		i_untilmonth = ZF_TILMONTH;
88739991Sbostic 		i_untilday = ZF_TILDAY;
88839991Sbostic 		i_untiltime = ZF_TILTIME;
88939991Sbostic 		z.z_name = ecpyalloc(fields[ZF_NAME]);
89039991Sbostic 	}
89139991Sbostic 	z.z_filename = filename;
89239991Sbostic 	z.z_linenum = linenum;
89339991Sbostic 	z.z_gmtoff = gethms(fields[i_gmtoff], "invalid GMT offset", TRUE);
89439991Sbostic 	if ((cp = strchr(fields[i_format], '%')) != 0) {
89539991Sbostic 		if (*++cp != 's' || strchr(cp, '%') != 0) {
89639991Sbostic 			error("invalid abbreviation format");
89739991Sbostic 			return FALSE;
89839991Sbostic 		}
89939991Sbostic 	}
90039991Sbostic 	z.z_rule = ecpyalloc(fields[i_rule]);
90139991Sbostic 	z.z_format = ecpyalloc(fields[i_format]);
90239991Sbostic 	hasuntil = nfields > i_untilyear;
90339991Sbostic 	if (hasuntil) {
90439991Sbostic 		z.z_untilrule.r_filename = filename;
90539991Sbostic 		z.z_untilrule.r_linenum = linenum;
90639991Sbostic 		rulesub(&z.z_untilrule,
90739991Sbostic 			fields[i_untilyear],
90839991Sbostic 			"only",
90939991Sbostic 			"",
91039991Sbostic 			(nfields > i_untilmonth) ? fields[i_untilmonth] : "Jan",
91139991Sbostic 			(nfields > i_untilday) ? fields[i_untilday] : "1",
91239991Sbostic 			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
91339991Sbostic 		z.z_untiltime = rpytime(&z.z_untilrule, z.z_untilrule.r_loyear);
91439991Sbostic 		if (iscont && nzones > 0 && z.z_untiltime < max_time &&
91539991Sbostic 			z.z_untiltime > min_time &&
91639991Sbostic 			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
91739991Sbostic error("Zone continuation line end time is not after end time of previous line");
91839991Sbostic 			return FALSE;
91939991Sbostic 		}
92039991Sbostic 	}
92139991Sbostic 	zones = (struct zone *) erealloc((char *) zones,
92239991Sbostic 		(int) ((nzones + 1) * sizeof *zones));
92339991Sbostic 	zones[nzones++] = z;
92439991Sbostic 	/*
92539991Sbostic 	** If there was an UNTIL field on this line,
92639991Sbostic 	** there's more information about the zone on the next line.
92739991Sbostic 	*/
92839991Sbostic 	return hasuntil;
92939991Sbostic }
93039991Sbostic 
93139991Sbostic static void
93239991Sbostic inleap(fields, nfields)
93339991Sbostic register char ** const	fields;
93439991Sbostic const int		nfields;
93539991Sbostic {
93639991Sbostic 	register const char *		cp;
93739991Sbostic 	register const struct lookup *	lp;
93839991Sbostic 	register int			i, j;
93939991Sbostic 	int				year, month, day;
94039991Sbostic 	long				dayoff, tod;
94139991Sbostic 	time_t				t;
94239991Sbostic 
94339991Sbostic 	if (nfields != LEAP_FIELDS) {
94439991Sbostic 		error("wrong number of fields on Leap line");
94539991Sbostic 		return;
94639991Sbostic 	}
94739991Sbostic 	dayoff = 0;
94839991Sbostic 	cp = fields[LP_YEAR];
949*46793Sbostic 	if (sscanf((char *)cp, scheck(cp, "%d"), &year) != 1 ||
95039991Sbostic 		year < min_year || year > max_year) {
95139991Sbostic 			/*
95239991Sbostic 			 * Leapin' Lizards!
95339991Sbostic 			 */
95439991Sbostic 			error("invalid leaping year");
95539991Sbostic 			return;
95639991Sbostic 	}
95739991Sbostic 	j = EPOCH_YEAR;
95839991Sbostic 	while (j != year) {
95939991Sbostic 		if (year > j) {
96039991Sbostic 			i = len_years[isleap(j)];
96139991Sbostic 			++j;
96239991Sbostic 		} else {
96339991Sbostic 			--j;
96439991Sbostic 			i = -len_years[isleap(j)];
96539991Sbostic 		}
96639991Sbostic 		dayoff = oadd(dayoff, eitol(i));
96739991Sbostic 	}
96839991Sbostic 	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
96939991Sbostic 		error("invalid month name");
97039991Sbostic 		return;
97139991Sbostic 	}
97239991Sbostic 	month = lp->l_value;
97339991Sbostic 	j = TM_JANUARY;
97439991Sbostic 	while (j != month) {
97539991Sbostic 		i = len_months[isleap(year)][j];
97639991Sbostic 		dayoff = oadd(dayoff, eitol(i));
97739991Sbostic 		++j;
97839991Sbostic 	}
97939991Sbostic 	cp = fields[LP_DAY];
980*46793Sbostic 	if (sscanf((char *)cp, scheck(cp, "%d"), &day) != 1 ||
98139991Sbostic 		day <= 0 || day > len_months[isleap(year)][month]) {
98239991Sbostic 			error("invalid day of month");
98339991Sbostic 			return;
98439991Sbostic 	}
98539991Sbostic 	dayoff = oadd(dayoff, eitol(day - 1));
98639991Sbostic 	if (dayoff < 0 && !tt_signed) {
98739991Sbostic 		error("time before zero");
98839991Sbostic 		return;
98939991Sbostic 	}
99039991Sbostic 	t = (time_t) dayoff * SECSPERDAY;
99139991Sbostic 	/*
99239991Sbostic 	** Cheap overflow check.
99339991Sbostic 	*/
99439991Sbostic 	if (t / SECSPERDAY != dayoff) {
99539991Sbostic 		error("time overflow");
99639991Sbostic 		return;
99739991Sbostic 	}
99839991Sbostic 	tod = gethms(fields[LP_TIME], "invalid time of day", FALSE);
99939991Sbostic 	cp = fields[LP_CORR];
100039991Sbostic 	if (strcmp(cp, "+") != 0 && strcmp(cp, "") != 0) {
100139991Sbostic 		/* infile() turned "-" into "" */
100239991Sbostic 		error("illegal CORRECTION field on Leap line");
100339991Sbostic 		return;
100439991Sbostic 	}
100539991Sbostic 	if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
100639991Sbostic 		error("illegal Rolling/Stationary field on Leap line");
100739991Sbostic 		return;
100839991Sbostic 	}
100939991Sbostic 	addleap(tadd(t, tod), *cp == '+', lp->l_value);
101039991Sbostic }
101139991Sbostic 
101239991Sbostic static void
101339991Sbostic inlink(fields, nfields)
101439991Sbostic register char ** const	fields;
101539991Sbostic const int		nfields;
101639991Sbostic {
101739991Sbostic 	struct link	l;
101839991Sbostic 
101939991Sbostic 	if (nfields != LINK_FIELDS) {
102039991Sbostic 		error("wrong number of fields on Link line");
102139991Sbostic 		return;
102239991Sbostic 	}
102339991Sbostic 	if (*fields[LF_FROM] == '\0') {
102439991Sbostic 		error("blank FROM field on Link line");
102539991Sbostic 		return;
102639991Sbostic 	}
102739991Sbostic 	if (*fields[LF_TO] == '\0') {
102839991Sbostic 		error("blank TO field on Link line");
102939991Sbostic 		return;
103039991Sbostic 	}
103139991Sbostic 	l.l_filename = filename;
103239991Sbostic 	l.l_linenum = linenum;
103339991Sbostic 	l.l_from = ecpyalloc(fields[LF_FROM]);
103439991Sbostic 	l.l_to = ecpyalloc(fields[LF_TO]);
103539991Sbostic 	links = (struct link *) erealloc((char *) links,
103639991Sbostic 		(int) ((nlinks + 1) * sizeof *links));
103739991Sbostic 	links[nlinks++] = l;
103839991Sbostic }
103939991Sbostic 
104039991Sbostic static void
104139991Sbostic rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
104239991Sbostic register struct rule * const	rp;
104339991Sbostic char * const			loyearp;
104439991Sbostic char * const			hiyearp;
104539991Sbostic char * const			typep;
104639991Sbostic char * const			monthp;
104739991Sbostic char * const			dayp;
104839991Sbostic char * const			timep;
104939991Sbostic {
105039991Sbostic 	register struct lookup const *	lp;
105139991Sbostic 	register char *			cp;
105239991Sbostic 
105339991Sbostic 	if ((lp = byword(monthp, mon_names)) == NULL) {
105439991Sbostic 		error("invalid month name");
105539991Sbostic 		return;
105639991Sbostic 	}
105739991Sbostic 	rp->r_month = lp->l_value;
105839991Sbostic 	rp->r_todisstd = FALSE;
105939991Sbostic 	cp = timep;
106039991Sbostic 	if (*cp != '\0') {
106139991Sbostic 		cp += strlen(cp) - 1;
106239991Sbostic 		switch (lowerit(*cp)) {
106339991Sbostic 			case 's':
106439991Sbostic 				rp->r_todisstd = TRUE;
106539991Sbostic 				*cp = '\0';
106639991Sbostic 				break;
106739991Sbostic 			case 'w':
106839991Sbostic 				rp->r_todisstd = FALSE;
106939991Sbostic 				*cp = '\0';
107039991Sbostic 				break;
107139991Sbostic 		}
107239991Sbostic 	}
107339991Sbostic 	rp->r_tod = gethms(timep, "invalid time of day", FALSE);
107439991Sbostic 	/*
107539991Sbostic 	** Year work.
107639991Sbostic 	*/
107739991Sbostic 	cp = loyearp;
107839991Sbostic 	if ((lp = byword(cp, begin_years)) != NULL) switch ((int) lp->l_value) {
107939991Sbostic 		case YR_MINIMUM:
108039991Sbostic 			rp->r_loyear = min_year;
108139991Sbostic 			break;
108239991Sbostic 		case YR_MAXIMUM:
108339991Sbostic 			rp->r_loyear = max_year;
108439991Sbostic 			break;
108539991Sbostic 		default:	/* "cannot happen" */
108639991Sbostic 			(void) fprintf(stderr,
108739991Sbostic 				"%s: panic: Invalid l_value %d\n",
108839991Sbostic 				progname, lp->l_value);
108939991Sbostic 			(void) exit(EXIT_FAILURE);
109039991Sbostic 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1 ||
109139991Sbostic 		rp->r_loyear < min_year || rp->r_loyear > max_year) {
109239991Sbostic 			if (noise)
109339991Sbostic 				error("invalid starting year");
109439991Sbostic 			if (rp->r_loyear > max_year)
109539991Sbostic 				return;
109639991Sbostic 	}
109739991Sbostic 	cp = hiyearp;
109839991Sbostic 	if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
109939991Sbostic 		case YR_MINIMUM:
110039991Sbostic 			rp->r_hiyear = min_year;
110139991Sbostic 			break;
110239991Sbostic 		case YR_MAXIMUM:
110339991Sbostic 			rp->r_hiyear = max_year;
110439991Sbostic 			break;
110539991Sbostic 		case YR_ONLY:
110639991Sbostic 			rp->r_hiyear = rp->r_loyear;
110739991Sbostic 			break;
110839991Sbostic 		default:	/* "cannot happen" */
110939991Sbostic 			(void) fprintf(stderr,
111039991Sbostic 				"%s: panic: Invalid l_value %d\n",
111139991Sbostic 				progname, lp->l_value);
111239991Sbostic 			(void) exit(EXIT_FAILURE);
111339991Sbostic 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1 ||
111439991Sbostic 		rp->r_hiyear < min_year || rp->r_hiyear > max_year) {
111539991Sbostic 			if (noise)
111639991Sbostic 				error("invalid ending year");
111739991Sbostic 			if (rp->r_hiyear < min_year)
111839991Sbostic 				return;
111939991Sbostic 	}
112039991Sbostic 	if (rp->r_hiyear < min_year)
112139991Sbostic  		return;
112239991Sbostic  	if (rp->r_loyear < min_year)
112339991Sbostic  		rp->r_loyear = min_year;
112439991Sbostic  	if (rp->r_hiyear > max_year)
112539991Sbostic  		rp->r_hiyear = max_year;
112639991Sbostic 	if (rp->r_loyear > rp->r_hiyear) {
112739991Sbostic 		error("starting year greater than ending year");
112839991Sbostic 		return;
112939991Sbostic 	}
113039991Sbostic 	if (*typep == '\0')
113139991Sbostic 		rp->r_yrtype = NULL;
113239991Sbostic 	else {
113339991Sbostic 		if (rp->r_loyear == rp->r_hiyear) {
113439991Sbostic 			error("typed single year");
113539991Sbostic 			return;
113639991Sbostic 		}
113739991Sbostic 		rp->r_yrtype = ecpyalloc(typep);
113839991Sbostic 	}
113939991Sbostic 	/*
114039991Sbostic 	** Day work.
114139991Sbostic 	** Accept things such as:
114239991Sbostic 	**	1
114339991Sbostic 	**	last-Sunday
114439991Sbostic 	**	Sun<=20
114539991Sbostic 	**	Sun>=7
114639991Sbostic 	*/
114739991Sbostic 	if ((lp = byword(dayp, lasts)) != NULL) {
114839991Sbostic 		rp->r_dycode = DC_DOWLEQ;
114939991Sbostic 		rp->r_wday = lp->l_value;
115039991Sbostic 		rp->r_dayofmonth = len_months[1][rp->r_month];
115139991Sbostic 	} else {
115239991Sbostic 		if ((cp = strchr(dayp, '<')) != 0)
115339991Sbostic 			rp->r_dycode = DC_DOWLEQ;
115439991Sbostic 		else if ((cp = strchr(dayp, '>')) != 0)
115539991Sbostic 			rp->r_dycode = DC_DOWGEQ;
115639991Sbostic 		else {
115739991Sbostic 			cp = dayp;
115839991Sbostic 			rp->r_dycode = DC_DOM;
115939991Sbostic 		}
116039991Sbostic 		if (rp->r_dycode != DC_DOM) {
116139991Sbostic 			*cp++ = 0;
116239991Sbostic 			if (*cp++ != '=') {
116339991Sbostic 				error("invalid day of month");
116439991Sbostic 				return;
116539991Sbostic 			}
116639991Sbostic 			if ((lp = byword(dayp, wday_names)) == NULL) {
116739991Sbostic 				error("invalid weekday name");
116839991Sbostic 				return;
116939991Sbostic 			}
117039991Sbostic 			rp->r_wday = lp->l_value;
117139991Sbostic 		}
117239991Sbostic 		if (sscanf(cp, scheck(cp, "%d"), &rp->r_dayofmonth) != 1 ||
117339991Sbostic 			rp->r_dayofmonth <= 0 ||
117439991Sbostic 			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
117539991Sbostic 				error("invalid day of month");
117639991Sbostic 				return;
117739991Sbostic 		}
117839991Sbostic 	}
117939991Sbostic }
118039991Sbostic 
118139991Sbostic static void
118239991Sbostic convert(val, buf)
118339991Sbostic const long	val;
118439991Sbostic char * const	buf;
118539991Sbostic {
118639991Sbostic 	register int	i;
118739991Sbostic 	register long	shift;
118839991Sbostic 
118939991Sbostic 	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
119039991Sbostic 		buf[i] = val >> shift;
119139991Sbostic }
119239991Sbostic 
119339991Sbostic static void
119439991Sbostic puttzcode(val, fp)
119539991Sbostic const long	val;
119639991Sbostic FILE * const	fp;
119739991Sbostic {
119839991Sbostic 	char	buf[4];
119939991Sbostic 
120039991Sbostic 	convert(val, buf);
1201*46793Sbostic 	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
120239991Sbostic }
120339991Sbostic 
120439991Sbostic static void
120539991Sbostic writezone(name)
120639991Sbostic const char * const	name;
120739991Sbostic {
120839991Sbostic 	register FILE *		fp;
120939991Sbostic 	register int		i, j;
121039991Sbostic 	char			fullname[BUFSIZ];
121139991Sbostic 	static struct tzhead	tzh;
121239991Sbostic 
121339991Sbostic 	if (strlen(directory) + 1 + strlen(name) >= sizeof fullname) {
121439991Sbostic 		(void) fprintf(stderr,
121539991Sbostic 			"%s: File name %s/%s too long\n", progname,
121639991Sbostic 			directory, name);
121739991Sbostic 		(void) exit(EXIT_FAILURE);
121839991Sbostic 	}
121939991Sbostic 	(void) sprintf(fullname, "%s/%s", directory, name);
122039991Sbostic 	if ((fp = fopen(fullname, "wb")) == NULL) {
122139991Sbostic 		if (mkdirs(fullname) != 0)
122239991Sbostic 			(void) exit(EXIT_FAILURE);
122339991Sbostic 		if ((fp = fopen(fullname, "wb")) == NULL) {
122439991Sbostic 			(void) fprintf(stderr, "%s: Can't create ", progname);
122539991Sbostic 			(void) perror(fullname);
122639991Sbostic 			(void) exit(EXIT_FAILURE);
122739991Sbostic 		}
122839991Sbostic 	}
122939991Sbostic 	convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
123039991Sbostic 	convert(eitol(leapcnt), tzh.tzh_leapcnt);
123139991Sbostic 	convert(eitol(timecnt), tzh.tzh_timecnt);
123239991Sbostic 	convert(eitol(typecnt), tzh.tzh_typecnt);
123339991Sbostic 	convert(eitol(charcnt), tzh.tzh_charcnt);
1234*46793Sbostic 	(void) fwrite((void *) &tzh, (size_t) sizeof tzh, (size_t) 1, fp);
123539991Sbostic 	for (i = 0; i < timecnt; ++i) {
123639991Sbostic 		j = leapcnt;
123739991Sbostic 		while (--j >= 0)
123839991Sbostic 			if (ats[i] >= trans[j]) {
123939991Sbostic 				ats[i] = tadd(ats[i], corr[j]);
124039991Sbostic 				break;
124139991Sbostic 			}
124239991Sbostic 		puttzcode((long) ats[i], fp);
124339991Sbostic 	}
124439991Sbostic 	if (timecnt > 0)
1245*46793Sbostic 		(void) fwrite((void *) types, (size_t) sizeof types[0],
1246*46793Sbostic 		    (size_t) timecnt, fp);
124739991Sbostic 	for (i = 0; i < typecnt; ++i) {
124839991Sbostic 		puttzcode((long) gmtoffs[i], fp);
124939991Sbostic 		(void) putc(isdsts[i], fp);
125039991Sbostic 		(void) putc(abbrinds[i], fp);
125139991Sbostic 	}
125239991Sbostic 	if (charcnt != 0)
1253*46793Sbostic 		(void) fwrite((void *) chars, (size_t) sizeof chars[0],
1254*46793Sbostic 			(size_t) charcnt, fp);
125539991Sbostic 	for (i = 0; i < leapcnt; ++i) {
125639991Sbostic 		if (roll[i]) {
125739991Sbostic 			if (timecnt == 0 || trans[i] < ats[0]) {
125839991Sbostic 				j = 0;
125939991Sbostic 				while (isdsts[j])
126039991Sbostic 					if (++j >= typecnt) {
126139991Sbostic 						j = 0;
126239991Sbostic 						break;
126339991Sbostic 					}
126439991Sbostic 			} else {
126539991Sbostic 				j = 1;
126639991Sbostic 				while (j < timecnt && trans[i] >= ats[j])
126739991Sbostic 					++j;
126839991Sbostic 				j = types[j - 1];
126939991Sbostic 			}
127039991Sbostic 			puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
127139991Sbostic 		} else	puttzcode((long) trans[i], fp);
127239991Sbostic 		puttzcode((long) corr[i], fp);
127339991Sbostic 	}
127439991Sbostic 	for (i = 0; i < typecnt; ++i)
127539991Sbostic 		(void) putc(ttisstds[i], fp);
127639991Sbostic 	if (ferror(fp) || fclose(fp)) {
127739991Sbostic 		(void) fprintf(stderr, "%s: Write error on ", progname);
127839991Sbostic 		(void) perror(fullname);
127939991Sbostic 		(void) exit(EXIT_FAILURE);
128039991Sbostic 	}
128139991Sbostic }
128239991Sbostic 
128339991Sbostic static void
128439991Sbostic outzone(zpfirst, zonecount)
128539991Sbostic const struct zone * const	zpfirst;
128639991Sbostic const int			zonecount;
128739991Sbostic {
128839991Sbostic 	register const struct zone *	zp;
128939991Sbostic 	register struct rule *		rp;
129039991Sbostic 	register int			i, j;
129139991Sbostic 	register int			usestart, useuntil;
129239991Sbostic 	register time_t			starttime, untiltime;
129339991Sbostic 	register long			gmtoff;
129439991Sbostic 	register long			stdoff;
129539991Sbostic 	register int			year;
129639991Sbostic 	register long			startoff;
129739991Sbostic 	register int			startisdst;
129839991Sbostic 	register int			startttisstd;
129939991Sbostic 	register int			type;
130039991Sbostic 	char				startbuf[BUFSIZ];
130139991Sbostic 
130239991Sbostic 	/*
130339991Sbostic 	** Now. . .finally. . .generate some useful data!
130439991Sbostic 	*/
130539991Sbostic 	timecnt = 0;
130639991Sbostic 	typecnt = 0;
130739991Sbostic 	charcnt = 0;
130839991Sbostic 	/*
130939991Sbostic 	** Two guesses. . .the second may well be corrected later.
131039991Sbostic 	*/
131139991Sbostic 	gmtoff = zpfirst->z_gmtoff;
131239991Sbostic 	stdoff = 0;
131339991Sbostic #ifdef lint
131439991Sbostic 	starttime = 0;
131539991Sbostic 	startttisstd = FALSE;
131639991Sbostic #endif /* defined lint */
131739991Sbostic 	for (i = 0; i < zonecount; ++i) {
131839991Sbostic 		usestart = i > 0;
131939991Sbostic 		useuntil = i < (zonecount - 1);
132039991Sbostic 		zp = &zpfirst[i];
132139991Sbostic 		eat(zp->z_filename, zp->z_linenum);
132239991Sbostic 		startisdst = -1;
132339991Sbostic 		if (zp->z_nrules == 0) {
132439991Sbostic 			type = addtype(oadd(zp->z_gmtoff, zp->z_stdoff),
132539991Sbostic 				zp->z_format, zp->z_stdoff != 0,
132639991Sbostic 				startttisstd);
132739991Sbostic 			if (usestart)
132839991Sbostic 				addtt(starttime, type);
132939991Sbostic 			gmtoff = zp->z_gmtoff;
133039991Sbostic 			stdoff = zp->z_stdoff;
133139991Sbostic 		} else for (year = min_year; year <= max_year; ++year) {
133239991Sbostic 			if (useuntil && year > zp->z_untilrule.r_hiyear)
133339991Sbostic 				break;
133439991Sbostic 			/*
133539991Sbostic 			** Mark which rules to do in the current year.
133639991Sbostic 			** For those to do, calculate rpytime(rp, year);
133739991Sbostic 			*/
133839991Sbostic 			for (j = 0; j < zp->z_nrules; ++j) {
133939991Sbostic 				rp = &zp->z_rules[j];
134039991Sbostic 				eats(zp->z_filename, zp->z_linenum,
134139991Sbostic 					rp->r_filename, rp->r_linenum);
134239991Sbostic 				rp->r_todo = year >= rp->r_loyear &&
134339991Sbostic 						year <= rp->r_hiyear &&
134439991Sbostic 						yearistype(year, rp->r_yrtype);
134539991Sbostic 				if (rp->r_todo)
134639991Sbostic 					rp->r_temp = rpytime(rp, year);
134739991Sbostic 			}
134839991Sbostic 			for ( ; ; ) {
134939991Sbostic 				register int	k;
135039991Sbostic 				register time_t	jtime, ktime;
135139991Sbostic 				register long	offset;
135239991Sbostic 				char		buf[BUFSIZ];
135339991Sbostic 
135439991Sbostic 				if (useuntil) {
135539991Sbostic 					/*
135639991Sbostic 					** Turn untiltime into GMT
135739991Sbostic 					** assuming the current gmtoff and
135839991Sbostic 					** stdoff values.
135939991Sbostic 					*/
136039991Sbostic 					offset = gmtoff;
136139991Sbostic 					if (!zp->z_untilrule.r_todisstd)
136239991Sbostic 						offset = oadd(offset, stdoff);
136339991Sbostic 					untiltime = tadd(zp->z_untiltime,
136439991Sbostic 						-offset);
136539991Sbostic 				}
136639991Sbostic 				/*
136739991Sbostic 				** Find the rule (of those to do, if any)
136839991Sbostic 				** that takes effect earliest in the year.
136939991Sbostic 				*/
137039991Sbostic 				k = -1;
137139991Sbostic #ifdef lint
137239991Sbostic 				ktime = 0;
137339991Sbostic #endif /* defined lint */
137439991Sbostic 				for (j = 0; j < zp->z_nrules; ++j) {
137539991Sbostic 					rp = &zp->z_rules[j];
137639991Sbostic 					if (!rp->r_todo)
137739991Sbostic 						continue;
137839991Sbostic 					eats(zp->z_filename, zp->z_linenum,
137939991Sbostic 						rp->r_filename, rp->r_linenum);
138039991Sbostic 					offset = gmtoff;
138139991Sbostic 					if (!rp->r_todisstd)
138239991Sbostic 						offset = oadd(offset, stdoff);
138339991Sbostic 					jtime = rp->r_temp;
138439991Sbostic 					if (jtime == min_time ||
138539991Sbostic 						jtime == max_time)
138639991Sbostic 							continue;
138739991Sbostic 					jtime = tadd(jtime, -offset);
138839991Sbostic 					if (k < 0 || jtime < ktime) {
138939991Sbostic 						k = j;
139039991Sbostic 						ktime = jtime;
139139991Sbostic 					}
139239991Sbostic 				}
139339991Sbostic 				if (k < 0)
139439991Sbostic 					break;	/* go on to next year */
139539991Sbostic 				rp = &zp->z_rules[k];
139639991Sbostic 				rp->r_todo = FALSE;
139739991Sbostic 				if (useuntil && ktime >= untiltime)
139839991Sbostic 					break;
139939991Sbostic 				if (usestart) {
140039991Sbostic 					if (ktime < starttime) {
140139991Sbostic 						stdoff = rp->r_stdoff;
140239991Sbostic 						startoff = oadd(zp->z_gmtoff,
140339991Sbostic 							rp->r_stdoff);
140439991Sbostic 						(void) sprintf(startbuf,
140539991Sbostic 							zp->z_format,
140639991Sbostic 							rp->r_abbrvar);
140739991Sbostic 						startisdst =
140839991Sbostic 							rp->r_stdoff != 0;
140939991Sbostic 						continue;
141039991Sbostic 					}
141139991Sbostic 					if (ktime != starttime &&
141239991Sbostic 						startisdst >= 0)
141339991Sbostic addtt(starttime, addtype(startoff, startbuf, startisdst, startttisstd));
141439991Sbostic 					usestart = FALSE;
141539991Sbostic 				}
141639991Sbostic 				eats(zp->z_filename, zp->z_linenum,
141739991Sbostic 					rp->r_filename, rp->r_linenum);
141839991Sbostic 				(void) sprintf(buf, zp->z_format,
141939991Sbostic 					rp->r_abbrvar);
142039991Sbostic 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
142139991Sbostic 				type = addtype(offset, buf, rp->r_stdoff != 0,
142239991Sbostic 					rp->r_todisstd);
142339991Sbostic 				if (timecnt != 0 || rp->r_stdoff != 0)
142439991Sbostic 					addtt(ktime, type);
142539991Sbostic 				gmtoff = zp->z_gmtoff;
142639991Sbostic 				stdoff = rp->r_stdoff;
142739991Sbostic 			}
142839991Sbostic 		}
142939991Sbostic 		/*
143039991Sbostic 		** Now we may get to set starttime for the next zone line.
143139991Sbostic 		*/
143239991Sbostic 		if (useuntil) {
143339991Sbostic 			starttime = tadd(zp->z_untiltime,
143439991Sbostic 				-gmtoffs[types[timecnt - 1]]);
143539991Sbostic 			startttisstd = zp->z_untilrule.r_todisstd;
143639991Sbostic 		}
143739991Sbostic 	}
143839991Sbostic 	writezone(zpfirst->z_name);
143939991Sbostic }
144039991Sbostic 
144139991Sbostic static void
144239991Sbostic addtt(starttime, type)
144339991Sbostic const time_t	starttime;
144439991Sbostic const int	type;
144539991Sbostic {
144639991Sbostic 	if (timecnt != 0 && type == types[timecnt - 1])
144739991Sbostic 		return;	/* easy enough! */
144839991Sbostic 	if (timecnt >= TZ_MAX_TIMES) {
144939991Sbostic 		error("too many transitions?!");
145039991Sbostic 		(void) exit(EXIT_FAILURE);
145139991Sbostic 	}
145239991Sbostic 	ats[timecnt] = starttime;
145339991Sbostic 	types[timecnt] = type;
145439991Sbostic 	++timecnt;
145539991Sbostic }
145639991Sbostic 
145739991Sbostic static int
145839991Sbostic addtype(gmtoff, abbr, isdst, ttisstd)
145939991Sbostic const long		gmtoff;
146039991Sbostic const char * const	abbr;
146139991Sbostic const int		isdst;
146239991Sbostic const int		ttisstd;
146339991Sbostic {
146439991Sbostic 	register int	i, j;
146539991Sbostic 
146639991Sbostic 	/*
146739991Sbostic 	** See if there's already an entry for this zone type.
146839991Sbostic 	** If so, just return its index.
146939991Sbostic 	*/
147039991Sbostic 	for (i = 0; i < typecnt; ++i) {
147139991Sbostic 		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
147239991Sbostic 			strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
147339991Sbostic 			ttisstd == ttisstds[i])
147439991Sbostic 				return i;
147539991Sbostic 	}
147639991Sbostic 	/*
147739991Sbostic 	** There isn't one; add a new one, unless there are already too
147839991Sbostic 	** many.
147939991Sbostic 	*/
148039991Sbostic 	if (typecnt >= TZ_MAX_TYPES) {
148139991Sbostic 		error("too many local time types");
148239991Sbostic 		(void) exit(EXIT_FAILURE);
148339991Sbostic 	}
148439991Sbostic 	gmtoffs[i] = gmtoff;
148539991Sbostic 	isdsts[i] = isdst;
148639991Sbostic 	ttisstds[i] = ttisstd;
148739991Sbostic 
148839991Sbostic 	for (j = 0; j < charcnt; ++j)
148939991Sbostic 		if (strcmp(&chars[j], abbr) == 0)
149039991Sbostic 			break;
149139991Sbostic 	if (j == charcnt)
149239991Sbostic 		newabbr(abbr);
149339991Sbostic 	abbrinds[i] = j;
149439991Sbostic 	++typecnt;
149539991Sbostic 	return i;
149639991Sbostic }
149739991Sbostic 
149839991Sbostic static void
149939991Sbostic addleap(t, positive, rolling)
150039991Sbostic const time_t	t;
150139991Sbostic const int	positive;
150239991Sbostic const int	rolling;
150339991Sbostic {
150439991Sbostic 	register int	i, j;
150539991Sbostic 
150639991Sbostic 	if (leapcnt >= TZ_MAX_LEAPS) {
150739991Sbostic 		error("too many leap seconds");
150839991Sbostic 		(void) exit(EXIT_FAILURE);
150939991Sbostic 	}
151039991Sbostic 	for (i = 0; i < leapcnt; ++i)
151139991Sbostic 		if (t <= trans[i]) {
151239991Sbostic 			if (t == trans[i]) {
151339991Sbostic 				error("repeated leap second moment");
151439991Sbostic 				(void) exit(EXIT_FAILURE);
151539991Sbostic 			}
151639991Sbostic 			break;
151739991Sbostic 		}
151839991Sbostic 	for (j = leapcnt; j > i; --j) {
151939991Sbostic 		trans[j] = trans[j-1];
152039991Sbostic 		corr[j] = corr[j-1];
152139991Sbostic 		roll[j] = roll[j-1];
152239991Sbostic 	}
152339991Sbostic 	trans[i] = t;
152439991Sbostic 	corr[i] = (positive ? 1L : -1L);
152539991Sbostic 	roll[i] = rolling;
152639991Sbostic 	++leapcnt;
152739991Sbostic }
152839991Sbostic 
152939991Sbostic static void
153039991Sbostic adjleap()
153139991Sbostic {
153239991Sbostic 	register int	i;
153339991Sbostic 	register long	last = 0;
153439991Sbostic 
153539991Sbostic 	/*
153639991Sbostic 	** propagate leap seconds forward
153739991Sbostic 	*/
153839991Sbostic 	for (i = 0; i < leapcnt; ++i) {
153939991Sbostic 		trans[i] = tadd(trans[i], last);
154039991Sbostic 		last = corr[i] += last;
154139991Sbostic 	}
154239991Sbostic }
154339991Sbostic 
154439991Sbostic static int
154539991Sbostic yearistype(year, type)
154639991Sbostic const int		year;
154739991Sbostic const char * const	type;
154839991Sbostic {
154939991Sbostic 	char	buf[BUFSIZ];
155039991Sbostic 	int	result;
155139991Sbostic 
155239991Sbostic 	if (type == NULL || *type == '\0')
155339991Sbostic 		return TRUE;
155439991Sbostic 	if (strcmp(type, "uspres") == 0)
155539991Sbostic 		return (year % 4) == 0;
155639991Sbostic 	if (strcmp(type, "nonpres") == 0)
155739991Sbostic 		return (year % 4) != 0;
155839991Sbostic 	(void) sprintf(buf, "yearistype %d %s", year, type);
155939991Sbostic 	result = system(buf);
156039991Sbostic 	if (result == 0)
156139991Sbostic 		return TRUE;
156239991Sbostic 	if (result == (1 << 8))
156339991Sbostic 		return FALSE;
156439991Sbostic 	error("Wild result from command execution");
156539991Sbostic 	(void) fprintf(stderr, "%s: command was '%s', result was %d\n",
156639991Sbostic 		progname, buf, result);
156739991Sbostic 	for ( ; ; )
156839991Sbostic 		(void) exit(EXIT_FAILURE);
156939991Sbostic }
157039991Sbostic 
157139991Sbostic static int
157239991Sbostic lowerit(a)
157339991Sbostic const int	a;
157439991Sbostic {
157539991Sbostic 	return (isascii(a) && isupper(a)) ? tolower(a) : a;
157639991Sbostic }
157739991Sbostic 
157839991Sbostic static int
157939991Sbostic ciequal(ap, bp)		/* case-insensitive equality */
158039991Sbostic register const char *	ap;
158139991Sbostic register const char *	bp;
158239991Sbostic {
158339991Sbostic 	while (lowerit(*ap) == lowerit(*bp++))
158439991Sbostic 		if (*ap++ == '\0')
158539991Sbostic 			return TRUE;
158639991Sbostic 	return FALSE;
158739991Sbostic }
158839991Sbostic 
158939991Sbostic static int
159039991Sbostic itsabbr(abbr, word)
159139991Sbostic register const char *	abbr;
159239991Sbostic register const char *	word;
159339991Sbostic {
159439991Sbostic 	if (lowerit(*abbr) != lowerit(*word))
159539991Sbostic 		return FALSE;
159639991Sbostic 	++word;
159739991Sbostic 	while (*++abbr != '\0')
159839991Sbostic 		do if (*word == '\0')
159939991Sbostic 			return FALSE;
160039991Sbostic 				while (lowerit(*word++) != lowerit(*abbr));
160139991Sbostic 	return TRUE;
160239991Sbostic }
160339991Sbostic 
160439991Sbostic static const struct lookup *
160539991Sbostic byword(word, table)
160639991Sbostic register const char * const		word;
160739991Sbostic register const struct lookup * const	table;
160839991Sbostic {
160939991Sbostic 	register const struct lookup *	foundlp;
161039991Sbostic 	register const struct lookup *	lp;
161139991Sbostic 
161239991Sbostic 	if (word == NULL || table == NULL)
161339991Sbostic 		return NULL;
161439991Sbostic 	/*
161539991Sbostic 	** Look for exact match.
161639991Sbostic 	*/
161739991Sbostic 	for (lp = table; lp->l_word != NULL; ++lp)
161839991Sbostic 		if (ciequal(word, lp->l_word))
161939991Sbostic 			return lp;
162039991Sbostic 	/*
162139991Sbostic 	** Look for inexact match.
162239991Sbostic 	*/
162339991Sbostic 	foundlp = NULL;
162439991Sbostic 	for (lp = table; lp->l_word != NULL; ++lp)
162539991Sbostic 		if (itsabbr(word, lp->l_word))
162639991Sbostic 			if (foundlp == NULL)
162739991Sbostic 				foundlp = lp;
162839991Sbostic 			else	return NULL;	/* multiple inexact matches */
162939991Sbostic 	return foundlp;
163039991Sbostic }
163139991Sbostic 
163239991Sbostic static char **
163339991Sbostic getfields(cp)
163439991Sbostic register char *	cp;
163539991Sbostic {
163639991Sbostic 	register char *		dp;
163739991Sbostic 	register char **	array;
163839991Sbostic 	register int		nsubs;
163939991Sbostic 
164039991Sbostic 	if (cp == NULL)
164139991Sbostic 		return NULL;
164239991Sbostic 	array = (char **) emalloc((int) ((strlen(cp) + 1) * sizeof *array));
164339991Sbostic 	nsubs = 0;
164439991Sbostic 	for ( ; ; ) {
164539991Sbostic 		while (isascii(*cp) && isspace(*cp))
164639991Sbostic 			++cp;
164739991Sbostic 		if (*cp == '\0' || *cp == '#')
164839991Sbostic 			break;
164939991Sbostic 		array[nsubs++] = dp = cp;
165039991Sbostic 		do {
165139991Sbostic 			if ((*dp = *cp++) != '"')
165239991Sbostic 				++dp;
165339991Sbostic 			else while ((*dp = *cp++) != '"')
165439991Sbostic 				if (*dp != '\0')
165539991Sbostic 					++dp;
165639991Sbostic 				else	error("Odd number of quotation marks");
165739991Sbostic 		} while (*cp != '\0' && *cp != '#' &&
165839991Sbostic 			(!isascii(*cp) || !isspace(*cp)));
165939991Sbostic 		if (isascii(*cp) && isspace(*cp))
166039991Sbostic 			++cp;
166139991Sbostic 		*dp = '\0';
166239991Sbostic 	}
166339991Sbostic 	array[nsubs] = NULL;
166439991Sbostic 	return array;
166539991Sbostic }
166639991Sbostic 
166739991Sbostic static long
166839991Sbostic oadd(t1, t2)
166939991Sbostic const long	t1;
167039991Sbostic const long	t2;
167139991Sbostic {
167239991Sbostic 	register long	t;
167339991Sbostic 
167439991Sbostic 	t = t1 + t2;
167539991Sbostic 	if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) {
167639991Sbostic 		error("time overflow");
167739991Sbostic 		(void) exit(EXIT_FAILURE);
167839991Sbostic 	}
167939991Sbostic 	return t;
168039991Sbostic }
168139991Sbostic 
168239991Sbostic static time_t
168339991Sbostic tadd(t1, t2)
168439991Sbostic const time_t	t1;
168539991Sbostic const long	t2;
168639991Sbostic {
168739991Sbostic 	register time_t	t;
168839991Sbostic 
168939991Sbostic 	if (t1 == max_time && t2 > 0)
169039991Sbostic 		return max_time;
169139991Sbostic 	if (t1 == min_time && t2 < 0)
169239991Sbostic 		return min_time;
169339991Sbostic 	t = t1 + t2;
169439991Sbostic 	if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) {
169539991Sbostic 		error("time overflow");
169639991Sbostic 		(void) exit(EXIT_FAILURE);
169739991Sbostic 	}
169839991Sbostic 	return t;
169939991Sbostic }
170039991Sbostic 
170139991Sbostic /*
170239991Sbostic ** Given a rule, and a year, compute the date - in seconds since January 1,
170339991Sbostic ** 1970, 00:00 LOCAL time - in that year that the rule refers to.
170439991Sbostic */
170539991Sbostic 
170639991Sbostic static time_t
170739991Sbostic rpytime(rp, wantedy)
170839991Sbostic register const struct rule * const	rp;
170939991Sbostic register const int			wantedy;
171039991Sbostic {
171139991Sbostic 	register int	y, m, i;
171239991Sbostic 	register long	dayoff;			/* with a nod to Margaret O. */
171339991Sbostic 	register time_t	t;
171439991Sbostic 
171539991Sbostic 	dayoff = 0;
171639991Sbostic 	m = TM_JANUARY;
171739991Sbostic 	y = EPOCH_YEAR;
171839991Sbostic 	while (wantedy != y) {
171939991Sbostic 		if (wantedy > y) {
172039991Sbostic 			i = len_years[isleap(y)];
172139991Sbostic 			++y;
172239991Sbostic 		} else {
172339991Sbostic 			--y;
172439991Sbostic 			i = -len_years[isleap(y)];
172539991Sbostic 		}
172639991Sbostic 		dayoff = oadd(dayoff, eitol(i));
172739991Sbostic 	}
172839991Sbostic 	while (m != rp->r_month) {
172939991Sbostic 		i = len_months[isleap(y)][m];
173039991Sbostic 		dayoff = oadd(dayoff, eitol(i));
173139991Sbostic 		++m;
173239991Sbostic 	}
173339991Sbostic 	i = rp->r_dayofmonth;
173439991Sbostic 	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
173539991Sbostic 		if (rp->r_dycode == DC_DOWLEQ)
173639991Sbostic 			--i;
173739991Sbostic 		else {
173839991Sbostic 			error("use of 2/29 in non leap-year");
173939991Sbostic 			(void) exit(EXIT_FAILURE);
174039991Sbostic 		}
174139991Sbostic 	}
174239991Sbostic 	--i;
174339991Sbostic 	dayoff = oadd(dayoff, eitol(i));
174439991Sbostic 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
174539991Sbostic 		register long	wday;
174639991Sbostic 
174739991Sbostic #define LDAYSPERWEEK	((long) DAYSPERWEEK)
174839991Sbostic 		wday = eitol(EPOCH_WDAY);
174939991Sbostic 		/*
175039991Sbostic 		** Don't trust mod of negative numbers.
175139991Sbostic 		*/
175239991Sbostic 		if (dayoff >= 0)
175339991Sbostic 			wday = (wday + dayoff) % LDAYSPERWEEK;
175439991Sbostic 		else {
175539991Sbostic 			wday -= ((-dayoff) % LDAYSPERWEEK);
175639991Sbostic 			if (wday < 0)
175739991Sbostic 				wday += LDAYSPERWEEK;
175839991Sbostic 		}
175939991Sbostic 		while (wday != eitol(rp->r_wday))
176039991Sbostic 			if (rp->r_dycode == DC_DOWGEQ) {
176139991Sbostic 				dayoff = oadd(dayoff, (long) 1);
176239991Sbostic 				if (++wday >= LDAYSPERWEEK)
176339991Sbostic 					wday = 0;
176439991Sbostic 				++i;
176539991Sbostic 			} else {
176639991Sbostic 				dayoff = oadd(dayoff, (long) -1);
176739991Sbostic 				if (--wday < 0)
176839991Sbostic 					wday = LDAYSPERWEEK;
176939991Sbostic 				--i;
177039991Sbostic 			}
177139991Sbostic 		if (i < 0 || i >= len_months[isleap(y)][m]) {
177239991Sbostic 			error("no day in month matches rule");
177339991Sbostic 			(void) exit(EXIT_FAILURE);
177439991Sbostic 		}
177539991Sbostic 	}
177639991Sbostic 	if (dayoff < 0 && !tt_signed) {
177739991Sbostic 		if (wantedy == rp->r_loyear)
177839991Sbostic 			return min_time;
177939991Sbostic 		error("time before zero");
178039991Sbostic 		(void) exit(EXIT_FAILURE);
178139991Sbostic 	}
178239991Sbostic 	t = (time_t) dayoff * SECSPERDAY;
178339991Sbostic 	/*
178439991Sbostic 	** Cheap overflow check.
178539991Sbostic 	*/
178639991Sbostic 	if (t / SECSPERDAY != dayoff) {
178739991Sbostic 		if (wantedy == rp->r_hiyear)
178839991Sbostic 			return max_time;
178939991Sbostic 		if (wantedy == rp->r_loyear)
179039991Sbostic 			return min_time;
179139991Sbostic 		error("time overflow");
179239991Sbostic 		(void) exit(EXIT_FAILURE);
179339991Sbostic 	}
179439991Sbostic 	return tadd(t, rp->r_tod);
179539991Sbostic }
179639991Sbostic 
179739991Sbostic static void
179839991Sbostic newabbr(string)
179939991Sbostic const char * const	string;
180039991Sbostic {
180139991Sbostic 	register int	i;
180239991Sbostic 
180339991Sbostic 	i = strlen(string) + 1;
180439991Sbostic 	if (charcnt + i >= TZ_MAX_CHARS) {
180539991Sbostic 		error("too many, or too long, time zone abbreviations");
180639991Sbostic 		(void) exit(EXIT_FAILURE);
180739991Sbostic 	}
180839991Sbostic 	(void) strcpy(&chars[charcnt], string);
180939991Sbostic 	charcnt += eitol(i);
181039991Sbostic }
181139991Sbostic 
181239991Sbostic static int
181339991Sbostic mkdirs(name)
181439991Sbostic char * const	name;
181539991Sbostic {
181639991Sbostic 	register char *	cp;
181739991Sbostic 
181839991Sbostic 	if ((cp = name) == NULL || *cp == '\0')
181939991Sbostic 		return 0;
182039991Sbostic 	while ((cp = strchr(cp + 1, '/')) != 0) {
182139991Sbostic 		*cp = '\0';
182239991Sbostic 		if (!itsdir(name)) {
182339991Sbostic 			/*
182439991Sbostic 			** It doesn't seem to exist, so we try to create it.
182539991Sbostic 			*/
1826*46793Sbostic 			if (mkdir(name, 0755) != 0) {
182739991Sbostic 				(void) fprintf(stderr,
182839991Sbostic 					"%s: Can't create directory ",
182939991Sbostic 					progname);
183039991Sbostic 				(void) perror(name);
183139991Sbostic 				return -1;
183239991Sbostic 			}
183339991Sbostic 		}
183439991Sbostic 		*cp = '/';
183539991Sbostic 	}
183639991Sbostic 	return 0;
183739991Sbostic }
183839991Sbostic 
183939991Sbostic static long
184039991Sbostic eitol(i)
184139991Sbostic const int	i;
184239991Sbostic {
184339991Sbostic 	long	l;
184439991Sbostic 
184539991Sbostic 	l = i;
184639991Sbostic 	if (i < 0 && l >= 0 || i == 0 && l != 0 || i > 0 && l <= 0) {
184739991Sbostic 		(void) fprintf(stderr, "%s: %d did not sign extend correctly\n",
184839991Sbostic 			progname, i);
184939991Sbostic 		(void) exit(EXIT_FAILURE);
185039991Sbostic 	}
185139991Sbostic 	return l;
185239991Sbostic }
185339991Sbostic 
185439991Sbostic /*
185539991Sbostic ** UNIX is a registered trademark of AT&T.
185639991Sbostic */
1857