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