xref: /openbsd-src/usr.sbin/zic/zic.c (revision 6a4024dc43a99c7d5c4c59432f9b665d1e8e5f6f)
1*6a4024dcSmillert /*	$OpenBSD: zic.c,v 1.27 2024/09/18 17:05:50 millert Exp $	*/
22059035fStedu /*
32059035fStedu ** This file is in the public domain, so clarified as of
42059035fStedu ** 2006-07-17 by Arthur David Olson.
52059035fStedu */
62059035fStedu 
7f0dc9fbaSderaadt #include <sys/types.h>
8f0dc9fbaSderaadt #include <sys/wait.h>
9784122ecStedu #include <sys/stat.h>
10784122ecStedu #include <ctype.h>
114dc71ee2Stedu #include <err.h>
12f0dc9fbaSderaadt #include <errno.h>
13f0dc9fbaSderaadt #include <limits.h>
14f0dc9fbaSderaadt #include <stdio.h>
15f0dc9fbaSderaadt #include <stdlib.h>
16f0dc9fbaSderaadt #include <string.h>
17f0dc9fbaSderaadt #include <stdint.h>
18f0dc9fbaSderaadt #include <unistd.h>
19f0dc9fbaSderaadt #include <time.h>
20784122ecStedu 
217341579fStedu #include "tzfile.h"
22f0dc9fbaSderaadt 
23f0dc9fbaSderaadt #define TRUE	1
24f0dc9fbaSderaadt #define FALSE	0
25f0dc9fbaSderaadt 
26f0dc9fbaSderaadt #define TYPE_SIGNED(type) (((type) -1) < 0)
27f0dc9fbaSderaadt 
28f0dc9fbaSderaadt #define YEARSPERREPEAT	400	/* years before a Gregorian repeat */
29f0dc9fbaSderaadt 
30f0dc9fbaSderaadt #define GRANDPARENTED   "Local time zone must be set--see zic manual page"
312059035fStedu 
322059035fStedu #define	ZIC_VERSION	'2'
332059035fStedu 
342059035fStedu typedef int_fast64_t	zic_t;
352059035fStedu 
362059035fStedu #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
372059035fStedu #define ZIC_MAX_ABBR_LEN_WO_WARN	6
382059035fStedu #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
392059035fStedu 
402059035fStedu #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
412059035fStedu 
422059035fStedu #define OFFSET_STRLEN_MAXIMUM	(7 + INT_STRLEN_MAXIMUM(long))
432059035fStedu #define RULE_STRLEN_MAXIMUM	8	/* "Mdd.dd.d" */
442059035fStedu 
452059035fStedu #define end(cp, n)	(memchr((cp), '\0', (n)))
462059035fStedu 
472059035fStedu struct rule {
482059035fStedu 	const char	*r_filename;
492059035fStedu 	int		r_linenum;
502059035fStedu 	const char	*r_name;
512059035fStedu 
522059035fStedu 	int		r_loyear;	/* for example, 1986 */
532059035fStedu 	int		r_hiyear;	/* for example, 1986 */
542059035fStedu 	int		r_lowasnum;
552059035fStedu 	int		r_hiwasnum;
562059035fStedu 
572059035fStedu 	int		r_month;	/* 0..11 */
582059035fStedu 
592059035fStedu 	int		r_dycode;	/* see below */
602059035fStedu 	int		r_dayofmonth;
612059035fStedu 	int		r_wday;
622059035fStedu 
632059035fStedu 	long		r_tod;		/* time from midnight */
642059035fStedu 	int		r_todisstd;	/* above is standard time if TRUE */
652059035fStedu 					/* or wall clock time if FALSE */
662059035fStedu 	int		r_todisgmt;	/* above is GMT if TRUE */
672059035fStedu 					/* or local time if FALSE */
682059035fStedu 	long		r_stdoff;	/* offset from standard time */
692059035fStedu 	const char	*r_abbrvar;	/* variable part of abbreviation */
702059035fStedu 
712059035fStedu 	int		r_todo;		/* a rule to do (used in outzone) */
722059035fStedu 	zic_t		r_temp;		/* used in outzone */
732059035fStedu };
742059035fStedu 
752059035fStedu /*
762059035fStedu **	r_dycode		r_dayofmonth	r_wday
772059035fStedu */
782059035fStedu 
792059035fStedu #define DC_DOM		0	/* 1..31 */	/* unused */
802059035fStedu #define DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */
812059035fStedu #define DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */
822059035fStedu 
832059035fStedu struct zone {
842059035fStedu 	const char	*z_filename;
852059035fStedu 	int		z_linenum;
862059035fStedu 
872059035fStedu 	const char	*z_name;
882059035fStedu 	long		z_gmtoff;
892059035fStedu 	const char	*z_rule;
902059035fStedu 	const char	*z_format;
91*6a4024dcSmillert 	char		z_format_specifier;
922059035fStedu 
932059035fStedu 	long		z_stdoff;
942059035fStedu 
952059035fStedu 	struct rule	*z_rules;
962059035fStedu 	int		z_nrules;
972059035fStedu 
982059035fStedu 	struct rule	z_untilrule;
992059035fStedu 	zic_t		z_untiltime;
1002059035fStedu };
1012059035fStedu 
1022059035fStedu static void	addtt(zic_t starttime, int type);
1032059035fStedu static int	addtype(long gmtoff, const char *abbr, int isdst,
1042059035fStedu 	    int ttisstd, int ttisgmt);
1052059035fStedu static void	leapadd(zic_t t, int positive, int rolling, int count);
1062059035fStedu static void	adjleap(void);
1072059035fStedu static void	associate(void);
1082059035fStedu static void	convert(long val, char *buf);
1092059035fStedu static void	convert64(zic_t val, char *buf);
1102059035fStedu static void	dolink(const char *fromfield, const char *tofield);
111*6a4024dcSmillert static void	doabbr(char *abbr, size_t size, struct zone const *zp,
1122059035fStedu 	    const char *letters, int isdst, int doquotes);
1132059035fStedu static void	eat(const char *name, int num);
114f0dc9fbaSderaadt static void	eats(const char *name, int num, const char *rname, int rnum);
1152059035fStedu static long	eitol(int i);
1162059035fStedu static void	error(const char *message);
1172059035fStedu static char	**getfields(char *buf);
118f0dc9fbaSderaadt static long	gethms(const char *string, const char *errstrng, int signable);
1192059035fStedu static void	infile(const char *filename);
1202059035fStedu static void	inleap(char **fields, int nfields);
1212059035fStedu static void	inlink(char **fields, int nfields);
1222059035fStedu static void	inrule(char **fields, int nfields);
1232059035fStedu static int	inzcont(char **fields, int nfields);
1242059035fStedu static int	inzone(char **fields, int nfields);
1252059035fStedu static int	inzsub(char **fields, int nfields, int iscont);
1262059035fStedu static int	is32(zic_t x);
12738cd2fb1Stedu static int	itsabbr(const char *abbr, const char *word);
1282059035fStedu static int	itsdir(const char *name);
1292059035fStedu static int	mkdirs(char *filename);
1302059035fStedu static void	newabbr(const char *abbr);
1312059035fStedu static long	oadd(long t1, long t2);
1322059035fStedu static void	outzone(const struct zone *zp, int ntzones);
1332059035fStedu static void	puttzcode(long code, FILE *fp);
1342059035fStedu static void	puttzcode64(zic_t code, FILE *fp);
1352059035fStedu static int	rcomp(const void *leftp, const void *rightp);
1362059035fStedu static zic_t	rpytime(const struct rule *rp, int wantedy);
137f0dc9fbaSderaadt static void	rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
1382059035fStedu 	    const char *typep, const char *monthp,
1392059035fStedu 	    const char *dayp, const char *timep);
1402059035fStedu static int	stringoffset(char *result, size_t size, long offset);
1412059035fStedu static int	stringrule(char *result, size_t size, const struct rule *rp,
1422059035fStedu 	    long dstoff, long gmtoff);
1432059035fStedu static void	stringzone(char *result, size_t size,
1442059035fStedu 	    const struct zone *zp, int ntzones);
1452059035fStedu static void	setboundaries(void);
1462059035fStedu static zic_t	tadd(zic_t t1, long t2);
147b3cc38c2Stedu static void	usage(void);
1482059035fStedu static void	writezone(const char *name, const char *string);
1492059035fStedu 
150f0dc9fbaSderaadt extern char	*__progname;
151f0dc9fbaSderaadt 
152*6a4024dcSmillert /* Bound on length of what %z can expand to.  */
153*6a4024dcSmillert enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
154*6a4024dcSmillert 
1552059035fStedu static int		charcnt;
1562059035fStedu static int		errors;
1572059035fStedu static const char	*filename;
1582059035fStedu static int		leapcnt;
1592059035fStedu static int		leapseen;
1602059035fStedu static int		leapminyear;
1612059035fStedu static int		leapmaxyear;
1622059035fStedu static int		linenum;
163*6a4024dcSmillert static int		max_abbrvar_len = PERCENT_Z_LEN_BOUND;
1642059035fStedu static int		max_format_len;
1652059035fStedu static zic_t		max_time;
1662059035fStedu static int		max_year;
1672059035fStedu static zic_t		min_time;
1682059035fStedu static int		min_year;
1692059035fStedu static int		noise;
1702059035fStedu static const char	*rfilename;
1712059035fStedu static int		rlinenum;
1722059035fStedu static int		timecnt;
1732059035fStedu static int		typecnt;
1742059035fStedu 
1752059035fStedu /*
1762059035fStedu ** Line codes.
1772059035fStedu */
1782059035fStedu 
1792059035fStedu #define LC_RULE		0
1802059035fStedu #define LC_ZONE		1
1812059035fStedu #define LC_LINK		2
1822059035fStedu #define LC_LEAP		3
1832059035fStedu 
1842059035fStedu /*
1852059035fStedu ** Which fields are which on a Zone line.
1862059035fStedu */
1872059035fStedu 
1882059035fStedu #define ZF_NAME		1
1892059035fStedu #define ZF_GMTOFF	2
1902059035fStedu #define ZF_RULE		3
1912059035fStedu #define ZF_FORMAT	4
1922059035fStedu #define ZF_TILYEAR	5
1932059035fStedu #define ZF_TILMONTH	6
1942059035fStedu #define ZF_TILDAY	7
1952059035fStedu #define ZF_TILTIME	8
1962059035fStedu #define ZONE_MINFIELDS	5
1972059035fStedu #define ZONE_MAXFIELDS	9
1982059035fStedu 
1992059035fStedu /*
2002059035fStedu ** Which fields are which on a Zone continuation line.
2012059035fStedu */
2022059035fStedu 
2032059035fStedu #define ZFC_GMTOFF	0
2042059035fStedu #define ZFC_RULE	1
2052059035fStedu #define ZFC_FORMAT	2
2062059035fStedu #define ZFC_TILYEAR	3
2072059035fStedu #define ZFC_TILMONTH	4
2082059035fStedu #define ZFC_TILDAY	5
2092059035fStedu #define ZFC_TILTIME	6
2102059035fStedu #define ZONEC_MINFIELDS	3
2112059035fStedu #define ZONEC_MAXFIELDS	7
2122059035fStedu 
2132059035fStedu /*
2142059035fStedu ** Which files are which on a Rule line.
2152059035fStedu */
2162059035fStedu 
2172059035fStedu #define RF_NAME		1
2182059035fStedu #define RF_LOYEAR	2
2192059035fStedu #define RF_HIYEAR	3
2202059035fStedu #define RF_COMMAND	4
2212059035fStedu #define RF_MONTH	5
2222059035fStedu #define RF_DAY		6
2232059035fStedu #define RF_TOD		7
2242059035fStedu #define RF_STDOFF	8
2252059035fStedu #define RF_ABBRVAR	9
2262059035fStedu #define RULE_FIELDS	10
2272059035fStedu 
2282059035fStedu /*
2292059035fStedu ** Which fields are which on a Link line.
2302059035fStedu */
2312059035fStedu 
2322059035fStedu #define LF_FROM		1
2332059035fStedu #define LF_TO		2
2342059035fStedu #define LINK_FIELDS	3
2352059035fStedu 
2362059035fStedu /*
2372059035fStedu ** Which fields are which on a Leap line.
2382059035fStedu */
2392059035fStedu 
2402059035fStedu #define LP_YEAR		1
2412059035fStedu #define LP_MONTH	2
2422059035fStedu #define LP_DAY		3
2432059035fStedu #define LP_TIME		4
2442059035fStedu #define LP_CORR		5
2452059035fStedu #define LP_ROLL		6
2462059035fStedu #define LEAP_FIELDS	7
2472059035fStedu 
2482059035fStedu /*
2492059035fStedu ** Year synonyms.
2502059035fStedu */
2512059035fStedu 
2522059035fStedu #define YR_MINIMUM	0
2532059035fStedu #define YR_MAXIMUM	1
2542059035fStedu #define YR_ONLY		2
2552059035fStedu 
2562059035fStedu static struct rule	*rules;
2572059035fStedu static int		nrules;	/* number of rules */
2582059035fStedu 
2592059035fStedu static struct zone	*zones;
2602059035fStedu static int		nzones;	/* number of zones */
2612059035fStedu 
2622059035fStedu struct link {
2632059035fStedu 	const char	*l_filename;
2642059035fStedu 	int		l_linenum;
2652059035fStedu 	const char	*l_from;
2662059035fStedu 	const char	*l_to;
2672059035fStedu };
2682059035fStedu 
2692059035fStedu static struct link	*links;
2702059035fStedu static int		nlinks;
2712059035fStedu 
2722059035fStedu struct lookup {
2732059035fStedu 	const char	*l_word;
2742059035fStedu 	const int	l_value;
2752059035fStedu };
2762059035fStedu 
277f0dc9fbaSderaadt static struct lookup const	*byword(const char *string, const struct lookup *lp);
2782059035fStedu 
2792059035fStedu static struct lookup const	line_codes[] = {
2802059035fStedu 	{ "Rule",	LC_RULE },
2812059035fStedu 	{ "Zone",	LC_ZONE },
2822059035fStedu 	{ "Link",	LC_LINK },
2832059035fStedu 	{ "Leap",	LC_LEAP },
2842059035fStedu 	{ NULL,		0}
2852059035fStedu };
2862059035fStedu 
2872059035fStedu static struct lookup const	mon_names[] = {
2882059035fStedu 	{ "January",	TM_JANUARY },
2892059035fStedu 	{ "February",	TM_FEBRUARY },
2902059035fStedu 	{ "March",	TM_MARCH },
2912059035fStedu 	{ "April",	TM_APRIL },
2922059035fStedu 	{ "May",	TM_MAY },
2932059035fStedu 	{ "June",	TM_JUNE },
2942059035fStedu 	{ "July",	TM_JULY },
2952059035fStedu 	{ "August",	TM_AUGUST },
2962059035fStedu 	{ "September",	TM_SEPTEMBER },
2972059035fStedu 	{ "October",	TM_OCTOBER },
2982059035fStedu 	{ "November",	TM_NOVEMBER },
2992059035fStedu 	{ "December",	TM_DECEMBER },
3002059035fStedu 	{ NULL,		0 }
3012059035fStedu };
3022059035fStedu 
3032059035fStedu static struct lookup const	wday_names[] = {
3042059035fStedu 	{ "Sunday",	TM_SUNDAY },
3052059035fStedu 	{ "Monday",	TM_MONDAY },
3062059035fStedu 	{ "Tuesday",	TM_TUESDAY },
3072059035fStedu 	{ "Wednesday",	TM_WEDNESDAY },
3082059035fStedu 	{ "Thursday",	TM_THURSDAY },
3092059035fStedu 	{ "Friday",	TM_FRIDAY },
3102059035fStedu 	{ "Saturday",	TM_SATURDAY },
3112059035fStedu 	{ NULL,		0 }
3122059035fStedu };
3132059035fStedu 
3142059035fStedu static struct lookup const	lasts[] = {
3152059035fStedu 	{ "last-Sunday",	TM_SUNDAY },
3162059035fStedu 	{ "last-Monday",	TM_MONDAY },
3172059035fStedu 	{ "last-Tuesday",	TM_TUESDAY },
3182059035fStedu 	{ "last-Wednesday",	TM_WEDNESDAY },
3192059035fStedu 	{ "last-Thursday",	TM_THURSDAY },
3202059035fStedu 	{ "last-Friday",	TM_FRIDAY },
3212059035fStedu 	{ "last-Saturday",	TM_SATURDAY },
3222059035fStedu 	{ NULL,			0 }
3232059035fStedu };
3242059035fStedu 
3252059035fStedu static struct lookup const	begin_years[] = {
3262059035fStedu 	{ "minimum",	YR_MINIMUM },
3272059035fStedu 	{ "maximum",	YR_MAXIMUM },
3282059035fStedu 	{ NULL,		0 }
3292059035fStedu };
3302059035fStedu 
3312059035fStedu static struct lookup const	end_years[] = {
3322059035fStedu 	{ "minimum",	YR_MINIMUM },
3332059035fStedu 	{ "maximum",	YR_MAXIMUM },
3342059035fStedu 	{ "only",	YR_ONLY },
3352059035fStedu 	{ NULL,		0 }
3362059035fStedu };
3372059035fStedu 
3382059035fStedu static struct lookup const	leap_types[] = {
3392059035fStedu 	{ "Rolling",	TRUE },
3402059035fStedu 	{ "Stationary",	FALSE },
3412059035fStedu 	{ NULL,		0 }
3422059035fStedu };
3432059035fStedu 
3442059035fStedu static const int	len_months[2][MONSPERYEAR] = {
3452059035fStedu 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
3462059035fStedu 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
3472059035fStedu };
3482059035fStedu 
3492059035fStedu static const int	len_years[2] = {
3502059035fStedu 	DAYSPERNYEAR, DAYSPERLYEAR
3512059035fStedu };
3522059035fStedu 
3532059035fStedu static struct attype {
3542059035fStedu 	zic_t		at;
3552059035fStedu 	unsigned char	type;
3562059035fStedu }			attypes[TZ_MAX_TIMES];
357f0dc9fbaSderaadt 
3582059035fStedu static long		gmtoffs[TZ_MAX_TYPES];
3592059035fStedu static char		isdsts[TZ_MAX_TYPES];
3602059035fStedu static unsigned char	abbrinds[TZ_MAX_TYPES];
3612059035fStedu static char		ttisstds[TZ_MAX_TYPES];
3622059035fStedu static char		ttisgmts[TZ_MAX_TYPES];
3632059035fStedu static char		chars[TZ_MAX_CHARS];
3642059035fStedu static zic_t		trans[TZ_MAX_LEAPS];
3652059035fStedu static long		corr[TZ_MAX_LEAPS];
3662059035fStedu static char		roll[TZ_MAX_LEAPS];
3672059035fStedu 
3682059035fStedu /*
3692059035fStedu ** Memory allocation.
3702059035fStedu */
3712059035fStedu 
37275b1bb69Stedu static void *
373738fad2fStedu memcheck(void *ptr)
3742059035fStedu {
375f0dc9fbaSderaadt 	if (ptr == NULL)
3764dc71ee2Stedu 		err(1, "Memory exhausted");
3772059035fStedu 	return ptr;
3782059035fStedu }
3792059035fStedu 
3802059035fStedu static char *
3812059035fStedu ecatalloc(char *start, const char *tail)
3822059035fStedu {
3832059035fStedu 	size_t len;
3842059035fStedu 	char *str;
3852059035fStedu 
3862059035fStedu 	len = strlen(start) + strlen(tail) + 1;
3872059035fStedu 	str = memcheck(realloc(start, len));
3882059035fStedu 	strlcat(str, tail, len);
3892059035fStedu 	return str;
3902059035fStedu }
3912059035fStedu 
3922059035fStedu #define emalloc(size)		memcheck(malloc(size))
393cc917873Sderaadt #define ereallocarray(ptr, nmemb, size)		memcheck(reallocarray(ptr, nmemb, size))
3942059035fStedu #define erealloc(ptr, size)	memcheck(realloc((ptr), (size)))
3952059035fStedu #define ecpyalloc(ptr)		memcheck(strdup(ptr))
3962059035fStedu 
3972059035fStedu /*
3982059035fStedu ** Error handling.
3992059035fStedu */
4002059035fStedu 
4012059035fStedu static void
402738fad2fStedu eats(const char *name, int num, const char *rname, int rnum)
4032059035fStedu {
4042059035fStedu 	filename = name;
4052059035fStedu 	linenum = num;
4062059035fStedu 	rfilename = rname;
4072059035fStedu 	rlinenum = rnum;
4082059035fStedu }
4092059035fStedu 
4102059035fStedu static void
411738fad2fStedu eat(const char *name, int num)
4122059035fStedu {
41375b1bb69Stedu 	eats(name, num, NULL, -1);
4142059035fStedu }
4152059035fStedu 
4162059035fStedu static void
417738fad2fStedu error(const char *string)
4182059035fStedu {
4192059035fStedu 	/*
4202059035fStedu 	** Match the format of "cc" to allow sh users to
4212059035fStedu 	**	zic ... 2>&1 | error -t "*" -v
4222059035fStedu 	** on BSD systems.
4232059035fStedu 	*/
424f0dc9fbaSderaadt 	fprintf(stderr, "\"%s\", line %d: %s",
4252059035fStedu 		filename, linenum, string);
4262059035fStedu 	if (rfilename != NULL)
427f0dc9fbaSderaadt 		fprintf(stderr, " (rule from \"%s\", line %d)",
4282059035fStedu 			rfilename, rlinenum);
42975b1bb69Stedu 	fprintf(stderr, "\n");
4302059035fStedu 	++errors;
4312059035fStedu }
4322059035fStedu 
4332059035fStedu static void
434738fad2fStedu warning(const char *string)
4352059035fStedu {
4362059035fStedu 	char	*cp;
4372059035fStedu 
438f0dc9fbaSderaadt 	cp = ecpyalloc("warning: ");
4392059035fStedu 	cp = ecatalloc(cp, string);
4402059035fStedu 	error(cp);
4412059035fStedu 	free(cp);
4422059035fStedu 	--errors;
4432059035fStedu }
4442059035fStedu 
4456d42e4cdStedu 
4466d42e4cdStedu static const char *
4476d42e4cdStedu scheck(const char *string, const char *format)
4486d42e4cdStedu {
449f0dc9fbaSderaadt 	const char	*fp, *result;
450f0dc9fbaSderaadt 	char		*fbuf, *tp, dummy;
4516d42e4cdStedu 	int		c;
4526d42e4cdStedu 
4536d42e4cdStedu 	result = "";
4546d42e4cdStedu 	if (string == NULL || format == NULL)
4556d42e4cdStedu 		return result;
456cc917873Sderaadt 	fbuf = reallocarray(NULL, strlen(format) + 2, 2);
4576d42e4cdStedu 	if (fbuf == NULL)
4586d42e4cdStedu 		return result;
4596d42e4cdStedu 	fp = format;
4606d42e4cdStedu 	tp = fbuf;
4616d42e4cdStedu 	while ((*tp++ = c = *fp++) != '\0') {
4626d42e4cdStedu 		if (c != '%')
4636d42e4cdStedu 			continue;
4646d42e4cdStedu 		if (*fp == '%') {
4656d42e4cdStedu 			*tp++ = *fp++;
4666d42e4cdStedu 			continue;
4676d42e4cdStedu 		}
4686d42e4cdStedu 		*tp++ = '*';
4696d42e4cdStedu 		if (*fp == '*')
4706d42e4cdStedu 			++fp;
4716d42e4cdStedu 		while (isdigit((unsigned char)*fp))
4726d42e4cdStedu 			*tp++ = *fp++;
4736d42e4cdStedu 		if (*fp == 'l' || *fp == 'h')
4746d42e4cdStedu 			*tp++ = *fp++;
4756d42e4cdStedu 		else if (*fp == '[')
476f0dc9fbaSderaadt 			do {
477f0dc9fbaSderaadt 				*tp++ = *fp++;
478f0dc9fbaSderaadt 			} while (*fp != '\0' && *fp != ']');
4796d42e4cdStedu 		if ((*tp++ = *fp++) == '\0')
4806d42e4cdStedu 			break;
4816d42e4cdStedu 	}
4826d42e4cdStedu 	*(tp - 1) = '%';
4836d42e4cdStedu 	*tp++ = 'c';
4846d42e4cdStedu 	*tp = '\0';
4856d42e4cdStedu 	if (sscanf(string, fbuf, &dummy) != 1)
4866d42e4cdStedu 		result = format;
4876d42e4cdStedu 	free(fbuf);
4886d42e4cdStedu 	return result;
4896d42e4cdStedu }
4906d42e4cdStedu 
4912059035fStedu static void
492b3cc38c2Stedu usage(void)
4932059035fStedu {
494f0dc9fbaSderaadt 	fprintf(stderr,
495f0dc9fbaSderaadt 	    "usage: %s [-v] [-d directory] [-L leapsecondfilename] [-l timezone]\n"
496ce87e856Smillert 	    "\t\t[-p timezone] [filename ...]\n",
497f0dc9fbaSderaadt 		__progname);
498b3cc38c2Stedu 	exit(EXIT_FAILURE);
4992059035fStedu }
5002059035fStedu 
5012059035fStedu static const char	*psxrules;
5022059035fStedu static const char	*lcltime;
5032059035fStedu static const char	*directory;
5042059035fStedu static const char	*leapsec;
5052059035fStedu 
5062059035fStedu int
507738fad2fStedu main(int argc, char **argv)
5082059035fStedu {
509f0dc9fbaSderaadt 	int	i, j, c;
5102059035fStedu 
5111e80dbeeSderaadt 	if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1)
5121e80dbeeSderaadt 		err(1, "pledge");
5131e80dbeeSderaadt 
51475b1bb69Stedu 	umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
5152059035fStedu 	while ((c = getopt(argc, argv, "d:l:p:L:vy:")) != -1)
5162059035fStedu 		switch (c) {
5172059035fStedu 			default:
518b3cc38c2Stedu 				usage();
5192059035fStedu 			case 'd':
5202059035fStedu 				if (directory == NULL)
5212059035fStedu 					directory = optarg;
522f0dc9fbaSderaadt 				else
5234dc71ee2Stedu 					errx(1, "More than one -d option specified");
5242059035fStedu 				break;
5252059035fStedu 			case 'l':
5262059035fStedu 				if (lcltime == NULL)
5272059035fStedu 					lcltime = optarg;
528f0dc9fbaSderaadt 				else
5294dc71ee2Stedu 					errx(1, "More than one -l option specified");
5302059035fStedu 				break;
5312059035fStedu 			case 'p':
5322059035fStedu 				if (psxrules == NULL)
5332059035fStedu 					psxrules = optarg;
534f0dc9fbaSderaadt 				else
5354dc71ee2Stedu 					errx(1, "More than one -p option specified");
5362059035fStedu 				break;
5372059035fStedu 			case 'y':
538a188d4d1Smillert 				warning("ignoring obsolescent option -y");
5392059035fStedu 				break;
5402059035fStedu 			case 'L':
5412059035fStedu 				if (leapsec == NULL)
5422059035fStedu 					leapsec = optarg;
543f0dc9fbaSderaadt 				else
5444dc71ee2Stedu 					errx(1, "More than one -L option specified");
5452059035fStedu 				break;
5462059035fStedu 			case 'v':
5472059035fStedu 				noise = TRUE;
5482059035fStedu 				break;
5492059035fStedu 		}
5502059035fStedu 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
551b3cc38c2Stedu 		usage();	/* usage message by request */
5522059035fStedu 	if (directory == NULL)
5532059035fStedu 		directory = TZDIR;
5542059035fStedu 
5552059035fStedu 	setboundaries();
5562059035fStedu 
5572059035fStedu 	if (optind < argc && leapsec != NULL) {
5582059035fStedu 		infile(leapsec);
5592059035fStedu 		adjleap();
5602059035fStedu 	}
5612059035fStedu 
5622059035fStedu 	for (i = optind; i < argc; ++i)
5632059035fStedu 		infile(argv[i]);
5642059035fStedu 	if (errors)
5652059035fStedu 		exit(EXIT_FAILURE);
5662059035fStedu 	associate();
5672059035fStedu 	for (i = 0; i < nzones; i = j) {
5682059035fStedu 		/*
5692059035fStedu 		** Find the next non-continuation zone entry.
5702059035fStedu 		*/
5712059035fStedu 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
5722059035fStedu 			continue;
5732059035fStedu 		outzone(&zones[i], j - i);
5742059035fStedu 	}
5752059035fStedu 	/*
5762059035fStedu 	** Make links.
5772059035fStedu 	*/
5782059035fStedu 	for (i = 0; i < nlinks; ++i) {
5792059035fStedu 		eat(links[i].l_filename, links[i].l_linenum);
5802059035fStedu 		dolink(links[i].l_from, links[i].l_to);
5812059035fStedu 		if (noise)
5822059035fStedu 			for (j = 0; j < nlinks; ++j)
5832059035fStedu 				if (strcmp(links[i].l_to,
5842059035fStedu 					links[j].l_from) == 0)
585f0dc9fbaSderaadt 						warning("link to link");
5862059035fStedu 	}
5872059035fStedu 	if (lcltime != NULL) {
5882059035fStedu 		eat("command line", 1);
5892059035fStedu 		dolink(lcltime, TZDEFAULT);
5902059035fStedu 	}
5912059035fStedu 	if (psxrules != NULL) {
5922059035fStedu 		eat("command line", 1);
5932059035fStedu 		dolink(psxrules, TZDEFRULES);
5942059035fStedu 	}
5952059035fStedu 	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
5962059035fStedu }
5972059035fStedu 
5982059035fStedu static void
599738fad2fStedu dolink(const char *fromfield, const char *tofield)
6002059035fStedu {
601f0dc9fbaSderaadt 	char	*fromname, *toname;
6022059035fStedu 
6032059035fStedu 	if (fromfield[0] == '/')
6042059035fStedu 		fromname = ecpyalloc(fromfield);
6052059035fStedu 	else {
6062059035fStedu 		fromname = ecpyalloc(directory);
6072059035fStedu 		fromname = ecatalloc(fromname, "/");
6082059035fStedu 		fromname = ecatalloc(fromname, fromfield);
6092059035fStedu 	}
6102059035fStedu 	if (tofield[0] == '/')
6112059035fStedu 		toname = ecpyalloc(tofield);
6122059035fStedu 	else {
6132059035fStedu 		toname = ecpyalloc(directory);
6142059035fStedu 		toname = ecatalloc(toname, "/");
6152059035fStedu 		toname = ecatalloc(toname, tofield);
6162059035fStedu 	}
6172059035fStedu 	/*
6182059035fStedu 	** We get to be careful here since
6192059035fStedu 	** there's a fair chance of root running us.
6202059035fStedu 	*/
6212059035fStedu 	if (!itsdir(toname))
62275b1bb69Stedu 		remove(toname);
6232059035fStedu 	if (link(fromname, toname) != 0) {
6242059035fStedu 		int	result;
6252059035fStedu 
6262059035fStedu 		if (mkdirs(toname) != 0)
6272059035fStedu 			exit(EXIT_FAILURE);
6282059035fStedu 
6292059035fStedu 		result = link(fromname, toname);
6302059035fStedu 		if (result != 0 && errno == EXDEV)
6312059035fStedu 			result = symlink(fromname, toname);
632f0dc9fbaSderaadt 		if (result != 0)
6334dc71ee2Stedu 			err(1, "Can't link from %s to %s", fromname, toname);
6342059035fStedu 	}
6352059035fStedu 	free(fromname);
6362059035fStedu 	free(toname);
6372059035fStedu }
6382059035fStedu 
6392059035fStedu #define TIME_T_BITS_IN_FILE	64
6402059035fStedu 
6412059035fStedu static void
6422059035fStedu setboundaries(void)
6432059035fStedu {
64466e58127Stedu 	int	i;
6452059035fStedu 
6462059035fStedu 	min_time = -1;
6472059035fStedu 	for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
6482059035fStedu 		min_time *= 2;
6492059035fStedu 	max_time = -(min_time + 1);
6502059035fStedu }
6512059035fStedu 
6522059035fStedu static int
653738fad2fStedu itsdir(const char *name)
6542059035fStedu {
65566e58127Stedu 	char	*myname;
65666e58127Stedu 	int	accres;
6572059035fStedu 
6582059035fStedu 	myname = ecpyalloc(name);
6592059035fStedu 	myname = ecatalloc(myname, "/.");
6602059035fStedu 	accres = access(myname, F_OK);
6612059035fStedu 	free(myname);
6622059035fStedu 	return accres == 0;
6632059035fStedu }
6642059035fStedu 
6652059035fStedu /*
6662059035fStedu ** Associate sets of rules with zones.
6672059035fStedu */
6682059035fStedu 
6692059035fStedu /*
6702059035fStedu ** Sort by rule name.
6712059035fStedu */
6722059035fStedu 
6732059035fStedu static int
674738fad2fStedu rcomp(const void *cp1, const void *cp2)
6752059035fStedu {
6762059035fStedu 	return strcmp(((const struct rule *) cp1)->r_name,
6772059035fStedu 		((const struct rule *) cp2)->r_name);
6782059035fStedu }
6792059035fStedu 
6802059035fStedu static void
6812059035fStedu associate(void)
6822059035fStedu {
68366e58127Stedu 	struct zone	*zp;
68466e58127Stedu 	struct rule	*rp;
685f0dc9fbaSderaadt 	int		base, out, i, j;
6862059035fStedu 
6872059035fStedu 	if (nrules != 0) {
68875b1bb69Stedu 		qsort(rules, nrules, sizeof *rules, rcomp);
6892059035fStedu 		for (i = 0; i < nrules - 1; ++i) {
6902059035fStedu 			if (strcmp(rules[i].r_name,
6912059035fStedu 				rules[i + 1].r_name) != 0)
6922059035fStedu 					continue;
6932059035fStedu 			if (strcmp(rules[i].r_filename,
6942059035fStedu 				rules[i + 1].r_filename) == 0)
6952059035fStedu 					continue;
6962059035fStedu 			eat(rules[i].r_filename, rules[i].r_linenum);
697f0dc9fbaSderaadt 			warning("same rule name in multiple files");
6982059035fStedu 			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
699f0dc9fbaSderaadt 			warning("same rule name in multiple files");
7002059035fStedu 			for (j = i + 2; j < nrules; ++j) {
7012059035fStedu 				if (strcmp(rules[i].r_name,
7022059035fStedu 					rules[j].r_name) != 0)
7032059035fStedu 						break;
7042059035fStedu 				if (strcmp(rules[i].r_filename,
7052059035fStedu 					rules[j].r_filename) == 0)
7062059035fStedu 						continue;
7072059035fStedu 				if (strcmp(rules[i + 1].r_filename,
7082059035fStedu 					rules[j].r_filename) == 0)
7092059035fStedu 						continue;
7102059035fStedu 				break;
7112059035fStedu 			}
7122059035fStedu 			i = j - 1;
7132059035fStedu 		}
7142059035fStedu 	}
7152059035fStedu 	for (i = 0; i < nzones; ++i) {
7162059035fStedu 		zp = &zones[i];
7172059035fStedu 		zp->z_rules = NULL;
7182059035fStedu 		zp->z_nrules = 0;
7192059035fStedu 	}
7202059035fStedu 	for (base = 0; base < nrules; base = out) {
7212059035fStedu 		rp = &rules[base];
7222059035fStedu 		for (out = base + 1; out < nrules; ++out)
7232059035fStedu 			if (strcmp(rp->r_name, rules[out].r_name) != 0)
7242059035fStedu 				break;
7252059035fStedu 		for (i = 0; i < nzones; ++i) {
7262059035fStedu 			zp = &zones[i];
7272059035fStedu 			if (strcmp(zp->z_rule, rp->r_name) != 0)
7282059035fStedu 				continue;
7292059035fStedu 			zp->z_rules = rp;
7302059035fStedu 			zp->z_nrules = out - base;
7312059035fStedu 		}
7322059035fStedu 	}
7332059035fStedu 	for (i = 0; i < nzones; ++i) {
7342059035fStedu 		zp = &zones[i];
7352059035fStedu 		if (zp->z_nrules == 0) {
7362059035fStedu 			/*
7372059035fStedu 			** Maybe we have a local standard time offset.
7382059035fStedu 			*/
7392059035fStedu 			eat(zp->z_filename, zp->z_linenum);
740f0dc9fbaSderaadt 			zp->z_stdoff = gethms(zp->z_rule, "unruly zone",
7412059035fStedu 				TRUE);
7422059035fStedu 			/*
7432059035fStedu 			** Note, though, that if there's no rule,
7442059035fStedu 			** a '%s' in the format is a bad thing.
7452059035fStedu 			*/
746*6a4024dcSmillert 			if (zp->z_format_specifier == 's')
747f0dc9fbaSderaadt 				error("%s in ruleless zone");
7482059035fStedu 		}
7492059035fStedu 	}
7502059035fStedu 	if (errors)
7512059035fStedu 		exit(EXIT_FAILURE);
7522059035fStedu }
7532059035fStedu 
7542059035fStedu static void
755738fad2fStedu infile(const char *name)
7562059035fStedu {
75766e58127Stedu 	FILE			*fp;
758f0dc9fbaSderaadt 	char			**fields, *cp;
75966e58127Stedu 	const struct lookup	*lp;
760f0dc9fbaSderaadt 	int			nfields, wantcont, num;
7612059035fStedu 	char			buf[BUFSIZ];
7622059035fStedu 
7632059035fStedu 	if (strcmp(name, "-") == 0) {
764f0dc9fbaSderaadt 		name = "standard input";
7652059035fStedu 		fp = stdin;
766f0dc9fbaSderaadt 	} else if ((fp = fopen(name, "r")) == NULL)
7674dc71ee2Stedu 		err(1, "Can't open %s", name);
7682059035fStedu 	wantcont = FALSE;
7692059035fStedu 	for (num = 1; ; ++num) {
7702059035fStedu 		eat(name, num);
77175b1bb69Stedu 		if (fgets(buf, sizeof buf, fp) != buf)
7722059035fStedu 			break;
7732059035fStedu 		cp = strchr(buf, '\n');
7742059035fStedu 		if (cp == NULL) {
775f0dc9fbaSderaadt 			error("line too long");
7762059035fStedu 			exit(EXIT_FAILURE);
7772059035fStedu 		}
7782059035fStedu 		*cp = '\0';
7792059035fStedu 		fields = getfields(buf);
7802059035fStedu 		nfields = 0;
7812059035fStedu 		while (fields[nfields] != NULL) {
7822059035fStedu 			static char	nada;
7832059035fStedu 
7842059035fStedu 			if (strcmp(fields[nfields], "-") == 0)
7852059035fStedu 				fields[nfields] = &nada;
7862059035fStedu 			++nfields;
7872059035fStedu 		}
7882059035fStedu 		if (nfields == 0) {
7892059035fStedu 			/* nothing to do */
7902059035fStedu 		} else if (wantcont) {
7912059035fStedu 			wantcont = inzcont(fields, nfields);
7922059035fStedu 		} else {
7932059035fStedu 			lp = byword(fields[0], line_codes);
7942059035fStedu 			if (lp == NULL)
795f0dc9fbaSderaadt 				error("input line of unknown type");
7962059035fStedu 			else switch ((int) (lp->l_value)) {
7972059035fStedu 				case LC_RULE:
7982059035fStedu 					inrule(fields, nfields);
7992059035fStedu 					wantcont = FALSE;
8002059035fStedu 					break;
8012059035fStedu 				case LC_ZONE:
8022059035fStedu 					wantcont = inzone(fields, nfields);
8032059035fStedu 					break;
8042059035fStedu 				case LC_LINK:
8052059035fStedu 					inlink(fields, nfields);
8062059035fStedu 					wantcont = FALSE;
8072059035fStedu 					break;
8082059035fStedu 				case LC_LEAP:
8092059035fStedu 					if (name != leapsec)
81075b1bb69Stedu 						fprintf(stderr,
811f0dc9fbaSderaadt 						    "%s: Leap line in non leap seconds file %s\n",
812f0dc9fbaSderaadt 							__progname, name);
8134dc71ee2Stedu 						/* no exit? */
8144dc71ee2Stedu 					else
8154dc71ee2Stedu 						inleap(fields, nfields);
8162059035fStedu 					wantcont = FALSE;
8172059035fStedu 					break;
8182059035fStedu 				default:	/* "cannot happen" */
8194dc71ee2Stedu 					errx(1, "panic: Invalid l_value %d", lp->l_value);
8202059035fStedu 			}
8212059035fStedu 		}
82275b1bb69Stedu 		free(fields);
8232059035fStedu 	}
824f0dc9fbaSderaadt 	if (ferror(fp))
8254dc71ee2Stedu 		errx(1, "Error reading %s", filename);
826f0dc9fbaSderaadt 	if (fp != stdin && fclose(fp))
8274dc71ee2Stedu 		err(1, "Error closing %s", filename);
8282059035fStedu 	if (wantcont)
829f0dc9fbaSderaadt 		error("expected continuation line not found");
8302059035fStedu }
8312059035fStedu 
8322059035fStedu /*
8332059035fStedu ** Convert a string of one of the forms
8342059035fStedu **	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
8352059035fStedu ** into a number of seconds.
8362059035fStedu ** A null string maps to zero.
8372059035fStedu ** Call error with errstring and return zero on errors.
8382059035fStedu */
8392059035fStedu 
8402059035fStedu static long
841738fad2fStedu gethms(const char *string, const char *errstring, int signable)
8422059035fStedu {
8432059035fStedu 	long	hh;
8442059035fStedu 	int	mm, ss, sign;
8452059035fStedu 
8462059035fStedu 	if (string == NULL || *string == '\0')
8472059035fStedu 		return 0;
8482059035fStedu 	if (!signable)
8492059035fStedu 		sign = 1;
8502059035fStedu 	else if (*string == '-') {
8512059035fStedu 		sign = -1;
8522059035fStedu 		++string;
853f0dc9fbaSderaadt 	} else
854f0dc9fbaSderaadt 		sign = 1;
8552059035fStedu 	if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
8562059035fStedu 		mm = ss = 0;
8572059035fStedu 	else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
8582059035fStedu 		ss = 0;
8592059035fStedu 	else if (sscanf(string, scheck(string, "%ld:%d:%d"),
8602059035fStedu 	    &hh, &mm, &ss) != 3) {
8612059035fStedu 		error(errstring);
8622059035fStedu 		return 0;
8632059035fStedu 	}
8642059035fStedu 	if (hh < 0 ||
8652059035fStedu 	    mm < 0 || mm >= MINSPERHOUR ||
8662059035fStedu 	    ss < 0 || ss > SECSPERMIN) {
8672059035fStedu 		error(errstring);
8682059035fStedu 		return 0;
8692059035fStedu 	}
8702059035fStedu 	if (LONG_MAX / SECSPERHOUR < hh) {
871f0dc9fbaSderaadt 		error("time overflow");
8722059035fStedu 		return 0;
8732059035fStedu 	}
8742059035fStedu 	return oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
8752059035fStedu 	    eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)));
8762059035fStedu }
8772059035fStedu 
8782059035fStedu static void
879738fad2fStedu inrule(char **fields, int nfields)
8802059035fStedu {
8812059035fStedu 	static struct rule	r;
8822059035fStedu 
8832059035fStedu 	if (nfields != RULE_FIELDS) {
884f0dc9fbaSderaadt 		error("wrong number of fields on Rule line");
8852059035fStedu 		return;
8862059035fStedu 	}
8872059035fStedu 	if (*fields[RF_NAME] == '\0') {
888f0dc9fbaSderaadt 		error("nameless rule");
8892059035fStedu 		return;
8902059035fStedu 	}
8912059035fStedu 	r.r_filename = filename;
8922059035fStedu 	r.r_linenum = linenum;
893f0dc9fbaSderaadt 	r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE);
8942059035fStedu 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
8952059035fStedu 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
8962059035fStedu 	r.r_name = ecpyalloc(fields[RF_NAME]);
8972059035fStedu 	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
8982059035fStedu 	if (max_abbrvar_len < strlen(r.r_abbrvar))
8992059035fStedu 		max_abbrvar_len = strlen(r.r_abbrvar);
900cc917873Sderaadt 	rules = ereallocarray(rules, nrules + 1, sizeof *rules);
9012059035fStedu 	rules[nrules++] = r;
9022059035fStedu }
9032059035fStedu 
9042059035fStedu static int
905738fad2fStedu inzone(char **fields, int nfields)
9062059035fStedu {
90766e58127Stedu 	int	i;
9082059035fStedu 	static char	*buf;
9092059035fStedu 	size_t		len;
9102059035fStedu 
9112059035fStedu 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
912f0dc9fbaSderaadt 		error("wrong number of fields on Zone line");
9132059035fStedu 		return FALSE;
9142059035fStedu 	}
9152059035fStedu 	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
9162059035fStedu 		len = 132 + strlen(TZDEFAULT);
9172059035fStedu 		buf = erealloc(buf, len);
918f0dc9fbaSderaadt 		snprintf(buf, len,
919f0dc9fbaSderaadt 		    "\"Zone %s\" line and -l option are mutually exclusive",
9202059035fStedu 		    TZDEFAULT);
9212059035fStedu 		error(buf);
9222059035fStedu 		return FALSE;
9232059035fStedu 	}
9242059035fStedu 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
9252059035fStedu 		len = 132 + strlen(TZDEFRULES);
9262059035fStedu 		buf = erealloc(buf, len);
927f0dc9fbaSderaadt 		snprintf(buf, len,
928f0dc9fbaSderaadt 		    "\"Zone %s\" line and -p option are mutually exclusive",
9292059035fStedu 		    TZDEFRULES);
9302059035fStedu 		error(buf);
9312059035fStedu 		return FALSE;
9322059035fStedu 	}
9332059035fStedu 	for (i = 0; i < nzones; ++i)
9342059035fStedu 		if (zones[i].z_name != NULL &&
9352059035fStedu 		    strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
9362059035fStedu 			len = 132 + strlen(fields[ZF_NAME]) +
9372059035fStedu 			    strlen(zones[i].z_filename);
9382059035fStedu 			buf = erealloc(buf, len);
93975b1bb69Stedu 			snprintf(buf, len,
940f0dc9fbaSderaadt 			    "duplicate zone name %s (file \"%s\", line %d)",
9412059035fStedu 			    fields[ZF_NAME],
9422059035fStedu 			    zones[i].z_filename,
9432059035fStedu 			    zones[i].z_linenum);
9442059035fStedu 			error(buf);
9452059035fStedu 			return FALSE;
9462059035fStedu 		}
9472059035fStedu 	return inzsub(fields, nfields, FALSE);
9482059035fStedu }
9492059035fStedu 
9502059035fStedu static int
951738fad2fStedu inzcont(char **fields, int nfields)
9522059035fStedu {
9532059035fStedu 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
954f0dc9fbaSderaadt 		error("wrong number of fields on Zone continuation line");
9552059035fStedu 		return FALSE;
9562059035fStedu 	}
9572059035fStedu 	return inzsub(fields, nfields, TRUE);
9582059035fStedu }
9592059035fStedu 
9602059035fStedu static int
961738fad2fStedu inzsub(char **fields, int nfields, int iscont)
9622059035fStedu {
96366e58127Stedu 	char		*cp;
964*6a4024dcSmillert 	char		*cp1;
9652059035fStedu 	static struct zone	z;
96666e58127Stedu 	int		i_gmtoff, i_rule, i_format;
96766e58127Stedu 	int		i_untilyear, i_untilmonth;
96866e58127Stedu 	int		i_untilday, i_untiltime;
96966e58127Stedu 	int		hasuntil;
9702059035fStedu 
9712059035fStedu 	if (iscont) {
9722059035fStedu 		i_gmtoff = ZFC_GMTOFF;
9732059035fStedu 		i_rule = ZFC_RULE;
9742059035fStedu 		i_format = ZFC_FORMAT;
9752059035fStedu 		i_untilyear = ZFC_TILYEAR;
9762059035fStedu 		i_untilmonth = ZFC_TILMONTH;
9772059035fStedu 		i_untilday = ZFC_TILDAY;
9782059035fStedu 		i_untiltime = ZFC_TILTIME;
9792059035fStedu 		z.z_name = NULL;
9802059035fStedu 	} else {
9812059035fStedu 		i_gmtoff = ZF_GMTOFF;
9822059035fStedu 		i_rule = ZF_RULE;
9832059035fStedu 		i_format = ZF_FORMAT;
9842059035fStedu 		i_untilyear = ZF_TILYEAR;
9852059035fStedu 		i_untilmonth = ZF_TILMONTH;
9862059035fStedu 		i_untilday = ZF_TILDAY;
9872059035fStedu 		i_untiltime = ZF_TILTIME;
9882059035fStedu 		z.z_name = ecpyalloc(fields[ZF_NAME]);
9892059035fStedu 	}
9902059035fStedu 	z.z_filename = filename;
9912059035fStedu 	z.z_linenum = linenum;
992f0dc9fbaSderaadt 	z.z_gmtoff = gethms(fields[i_gmtoff], "invalid UTC offset", TRUE);
9932059035fStedu 	if ((cp = strchr(fields[i_format], '%')) != 0) {
994*6a4024dcSmillert 		if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
995*6a4024dcSmillert 		    || strchr(fields[i_format], '/')) {
996f0dc9fbaSderaadt 			error("invalid abbreviation format");
9972059035fStedu 			return FALSE;
9982059035fStedu 		}
9992059035fStedu 	}
10002059035fStedu 	z.z_rule = ecpyalloc(fields[i_rule]);
1001*6a4024dcSmillert 	z.z_format = cp1 = ecpyalloc(fields[i_format]);
1002*6a4024dcSmillert 	z.z_format_specifier = cp ? *cp : '\0';
1003*6a4024dcSmillert 	if (z.z_format_specifier == 'z') {
1004*6a4024dcSmillert 		if (noise) {
1005*6a4024dcSmillert 			warning("format '%%z' not handled by pre-2015 versions "
1006*6a4024dcSmillert 			    "of zic");
1007*6a4024dcSmillert 		}
1008*6a4024dcSmillert 		cp1[cp - fields[i_format]] = 's';
1009*6a4024dcSmillert 	}
10102059035fStedu 	if (max_format_len < strlen(z.z_format))
10112059035fStedu 		max_format_len = strlen(z.z_format);
10122059035fStedu 	hasuntil = nfields > i_untilyear;
10132059035fStedu 	if (hasuntil) {
10142059035fStedu 		z.z_untilrule.r_filename = filename;
10152059035fStedu 		z.z_untilrule.r_linenum = linenum;
10162059035fStedu 		rulesub(&z.z_untilrule,
10172059035fStedu 			fields[i_untilyear],
10182059035fStedu 			"only",
10192059035fStedu 			"",
10202059035fStedu 			(nfields > i_untilmonth) ?
10212059035fStedu 			fields[i_untilmonth] : "Jan",
10222059035fStedu 			(nfields > i_untilday) ? fields[i_untilday] : "1",
10232059035fStedu 			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
10242059035fStedu 		z.z_untiltime = rpytime(&z.z_untilrule,
10252059035fStedu 			z.z_untilrule.r_loyear);
10262059035fStedu 		if (iscont && nzones > 0 &&
10272059035fStedu 		    z.z_untiltime > min_time &&
10282059035fStedu 		    z.z_untiltime < max_time &&
10292059035fStedu 		    zones[nzones - 1].z_untiltime > min_time &&
10302059035fStedu 		    zones[nzones - 1].z_untiltime < max_time &&
10312059035fStedu 		    zones[nzones - 1].z_untiltime >= z.z_untiltime) {
1032f0dc9fbaSderaadt 			error("Zone continuation line end time is not after end time of previous line");
10332059035fStedu 			return FALSE;
10342059035fStedu 		}
10352059035fStedu 	}
1036cc917873Sderaadt 	zones = ereallocarray(zones, nzones + 1, sizeof *zones);
10372059035fStedu 	zones[nzones++] = z;
10382059035fStedu 	/*
10392059035fStedu 	** If there was an UNTIL field on this line,
10402059035fStedu 	** there's more information about the zone on the next line.
10412059035fStedu 	*/
10422059035fStedu 	return hasuntil;
10432059035fStedu }
10442059035fStedu 
10452059035fStedu static void
1046738fad2fStedu inleap(char **fields, int nfields)
10472059035fStedu {
104866e58127Stedu 	const char		*cp;
104966e58127Stedu 	const struct lookup	*lp;
105066e58127Stedu 	int			i, j;
10512059035fStedu 	int			year, month, day;
10522059035fStedu 	long			dayoff, tod;
10532059035fStedu 	zic_t			t;
10542059035fStedu 
10552059035fStedu 	if (nfields != LEAP_FIELDS) {
1056f0dc9fbaSderaadt 		error("wrong number of fields on Leap line");
10572059035fStedu 		return;
10582059035fStedu 	}
10592059035fStedu 	dayoff = 0;
10602059035fStedu 	cp = fields[LP_YEAR];
10612059035fStedu 	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
10622059035fStedu 		/*
10632059035fStedu 		** Leapin' Lizards!
10642059035fStedu 		*/
1065f0dc9fbaSderaadt 		error("invalid leaping year");
10662059035fStedu 		return;
10672059035fStedu 	}
10682059035fStedu 	if (!leapseen || leapmaxyear < year)
10692059035fStedu 		leapmaxyear = year;
10702059035fStedu 	if (!leapseen || leapminyear > year)
10712059035fStedu 		leapminyear = year;
10722059035fStedu 	leapseen = TRUE;
10732059035fStedu 	j = EPOCH_YEAR;
10742059035fStedu 	while (j != year) {
10752059035fStedu 		if (year > j) {
10762059035fStedu 			i = len_years[isleap(j)];
10772059035fStedu 			++j;
10782059035fStedu 		} else {
10792059035fStedu 			--j;
10802059035fStedu 			i = -len_years[isleap(j)];
10812059035fStedu 		}
10822059035fStedu 		dayoff = oadd(dayoff, eitol(i));
10832059035fStedu 	}
10842059035fStedu 	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
1085f0dc9fbaSderaadt 		error("invalid month name");
10862059035fStedu 		return;
10872059035fStedu 	}
10882059035fStedu 	month = lp->l_value;
10892059035fStedu 	j = TM_JANUARY;
10902059035fStedu 	while (j != month) {
10912059035fStedu 		i = len_months[isleap(year)][j];
10922059035fStedu 		dayoff = oadd(dayoff, eitol(i));
10932059035fStedu 		++j;
10942059035fStedu 	}
10952059035fStedu 	cp = fields[LP_DAY];
10962059035fStedu 	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
10972059035fStedu 	    day <= 0 || day > len_months[isleap(year)][month]) {
1098f0dc9fbaSderaadt 		error("invalid day of month");
10992059035fStedu 		return;
11002059035fStedu 	}
11012059035fStedu 	dayoff = oadd(dayoff, eitol(day - 1));
11022059035fStedu 	if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
1103f0dc9fbaSderaadt 		error("time before zero");
11042059035fStedu 		return;
11052059035fStedu 	}
11062059035fStedu 	if (dayoff < min_time / SECSPERDAY) {
1107f0dc9fbaSderaadt 		error("time too small");
11082059035fStedu 		return;
11092059035fStedu 	}
11102059035fStedu 	if (dayoff > max_time / SECSPERDAY) {
1111f0dc9fbaSderaadt 		error("time too large");
11122059035fStedu 		return;
11132059035fStedu 	}
11142059035fStedu 	t = (zic_t) dayoff * SECSPERDAY;
1115f0dc9fbaSderaadt 	tod = gethms(fields[LP_TIME], "invalid time of day", FALSE);
11162059035fStedu 	cp = fields[LP_CORR];
11172059035fStedu 	{
111866e58127Stedu 		int	positive;
11192059035fStedu 		int		count;
11202059035fStedu 
11212059035fStedu 		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
11222059035fStedu 			positive = FALSE;
11232059035fStedu 			count = 1;
11242059035fStedu 		} else if (strcmp(cp, "--") == 0) {
11252059035fStedu 			positive = FALSE;
11262059035fStedu 			count = 2;
11272059035fStedu 		} else if (strcmp(cp, "+") == 0) {
11282059035fStedu 			positive = TRUE;
11292059035fStedu 			count = 1;
11302059035fStedu 		} else if (strcmp(cp, "++") == 0) {
11312059035fStedu 			positive = TRUE;
11322059035fStedu 			count = 2;
11332059035fStedu 		} else {
1134f0dc9fbaSderaadt 			error("illegal CORRECTION field on Leap line");
11352059035fStedu 			return;
11362059035fStedu 		}
11372059035fStedu 		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
1138f0dc9fbaSderaadt 			error("illegal Rolling/Stationary field on Leap line");
11392059035fStedu 			return;
11402059035fStedu 		}
11412059035fStedu 		leapadd(tadd(t, tod), positive, lp->l_value, count);
11422059035fStedu 	}
11432059035fStedu }
11442059035fStedu 
11452059035fStedu static void
1146738fad2fStedu inlink(char **fields, int nfields)
11472059035fStedu {
11482059035fStedu 	struct link	l;
11492059035fStedu 
11502059035fStedu 	if (nfields != LINK_FIELDS) {
1151f0dc9fbaSderaadt 		error("wrong number of fields on Link line");
11522059035fStedu 		return;
11532059035fStedu 	}
11542059035fStedu 	if (*fields[LF_FROM] == '\0') {
1155f0dc9fbaSderaadt 		error("blank FROM field on Link line");
11562059035fStedu 		return;
11572059035fStedu 	}
11582059035fStedu 	if (*fields[LF_TO] == '\0') {
1159f0dc9fbaSderaadt 		error("blank TO field on Link line");
11602059035fStedu 		return;
11612059035fStedu 	}
11622059035fStedu 	l.l_filename = filename;
11632059035fStedu 	l.l_linenum = linenum;
11642059035fStedu 	l.l_from = ecpyalloc(fields[LF_FROM]);
11652059035fStedu 	l.l_to = ecpyalloc(fields[LF_TO]);
1166cc917873Sderaadt 	links = ereallocarray(links, nlinks + 1, sizeof *links);
11672059035fStedu 	links[nlinks++] = l;
11682059035fStedu }
11692059035fStedu 
11702059035fStedu static void
1171f0dc9fbaSderaadt rulesub(struct rule * const rp, const char * const loyearp,
1172f0dc9fbaSderaadt     const char * const hiyearp, const char * const typep,
1173f0dc9fbaSderaadt     const char * const monthp, const char * const dayp,
1174f0dc9fbaSderaadt     const char * const timep)
11752059035fStedu {
117666e58127Stedu 	const struct lookup	*lp;
117766e58127Stedu 	const char		*cp;
1178f0dc9fbaSderaadt 	char			*dp, *ep;
11792059035fStedu 
11802059035fStedu 	if ((lp = byword(monthp, mon_names)) == NULL) {
1181f0dc9fbaSderaadt 		error("invalid month name");
11822059035fStedu 		return;
11832059035fStedu 	}
11842059035fStedu 	rp->r_month = lp->l_value;
11852059035fStedu 	rp->r_todisstd = FALSE;
11862059035fStedu 	rp->r_todisgmt = FALSE;
11872059035fStedu 	dp = ecpyalloc(timep);
11882059035fStedu 	if (*dp != '\0') {
11892059035fStedu 		ep = dp + strlen(dp) - 1;
1190fb356290Stedu 		switch (tolower((unsigned char)*ep)) {
11912059035fStedu 		case 's':	/* Standard */
11922059035fStedu 			rp->r_todisstd = TRUE;
11932059035fStedu 			rp->r_todisgmt = FALSE;
11942059035fStedu 			*ep = '\0';
11952059035fStedu 			break;
11962059035fStedu 		case 'w':	/* Wall */
11972059035fStedu 			rp->r_todisstd = FALSE;
11982059035fStedu 			rp->r_todisgmt = FALSE;
11992059035fStedu 			*ep = '\0';
12002059035fStedu 			break;
12012059035fStedu 		case 'g':	/* Greenwich */
12022059035fStedu 		case 'u':	/* Universal */
12032059035fStedu 		case 'z':	/* Zulu */
12042059035fStedu 			rp->r_todisstd = TRUE;
12052059035fStedu 			rp->r_todisgmt = TRUE;
12062059035fStedu 			*ep = '\0';
12072059035fStedu 			break;
12082059035fStedu 		}
12092059035fStedu 	}
1210f0dc9fbaSderaadt 	rp->r_tod = gethms(dp, "invalid time of day", FALSE);
12112059035fStedu 	free(dp);
12122059035fStedu 	/*
12132059035fStedu 	** Year work.
12142059035fStedu 	*/
12152059035fStedu 	cp = loyearp;
12162059035fStedu 	lp = byword(cp, begin_years);
12172059035fStedu 	rp->r_lowasnum = lp == NULL;
12182059035fStedu 	if (!rp->r_lowasnum) switch ((int) lp->l_value) {
12192059035fStedu 		case YR_MINIMUM:
12202059035fStedu 			rp->r_loyear = INT_MIN;
12212059035fStedu 			break;
12222059035fStedu 		case YR_MAXIMUM:
12232059035fStedu 			rp->r_loyear = INT_MAX;
12242059035fStedu 			break;
12252059035fStedu 		default:	/* "cannot happen" */
12264dc71ee2Stedu 			errx(1, "panic: Invalid l_value %d", lp->l_value);
12272059035fStedu 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
1228f0dc9fbaSderaadt 		error("invalid starting year");
12292059035fStedu 		return;
12302059035fStedu 	}
12312059035fStedu 	cp = hiyearp;
12322059035fStedu 	lp = byword(cp, end_years);
12332059035fStedu 	rp->r_hiwasnum = lp == NULL;
12342059035fStedu 	if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
12352059035fStedu 		case YR_MINIMUM:
12362059035fStedu 			rp->r_hiyear = INT_MIN;
12372059035fStedu 			break;
12382059035fStedu 		case YR_MAXIMUM:
12392059035fStedu 			rp->r_hiyear = INT_MAX;
12402059035fStedu 			break;
12412059035fStedu 		case YR_ONLY:
12422059035fStedu 			rp->r_hiyear = rp->r_loyear;
12432059035fStedu 			break;
12442059035fStedu 		default:	/* "cannot happen" */
12454dc71ee2Stedu 			errx(1, "panic: Invalid l_value %d", lp->l_value);
12462059035fStedu 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
1247f0dc9fbaSderaadt 		error("invalid ending year");
12482059035fStedu 		return;
12492059035fStedu 	}
12502059035fStedu 	if (rp->r_loyear > rp->r_hiyear) {
1251f0dc9fbaSderaadt 		error("starting year greater than ending year");
12522059035fStedu 		return;
12532059035fStedu 	}
1254a188d4d1Smillert 	if (*typep != '\0') {
12552059035fStedu 		if (rp->r_loyear == rp->r_hiyear) {
1256f0dc9fbaSderaadt 			error("typed single year");
12572059035fStedu 			return;
12582059035fStedu 		}
1259a188d4d1Smillert 		warning("year type is obsolete; use \"-\" instead");
12602059035fStedu 	}
12612059035fStedu 	/*
12622059035fStedu 	** Day work.
12632059035fStedu 	** Accept things such as:
12642059035fStedu 	**	1
12652059035fStedu 	**	last-Sunday
12662059035fStedu 	**	Sun<=20
12672059035fStedu 	**	Sun>=7
12682059035fStedu 	*/
12692059035fStedu 	dp = ecpyalloc(dayp);
12702059035fStedu 	if ((lp = byword(dp, lasts)) != NULL) {
12712059035fStedu 		rp->r_dycode = DC_DOWLEQ;
12722059035fStedu 		rp->r_wday = lp->l_value;
12732059035fStedu 		rp->r_dayofmonth = len_months[1][rp->r_month];
12742059035fStedu 	} else {
12752059035fStedu 		if ((ep = strchr(dp, '<')) != 0)
12762059035fStedu 			rp->r_dycode = DC_DOWLEQ;
12772059035fStedu 		else if ((ep = strchr(dp, '>')) != 0)
12782059035fStedu 			rp->r_dycode = DC_DOWGEQ;
12792059035fStedu 		else {
12802059035fStedu 			ep = dp;
12812059035fStedu 			rp->r_dycode = DC_DOM;
12822059035fStedu 		}
12832059035fStedu 		if (rp->r_dycode != DC_DOM) {
12842059035fStedu 			*ep++ = 0;
12852059035fStedu 			if (*ep++ != '=') {
1286f0dc9fbaSderaadt 				error("invalid day of month");
12872059035fStedu 				free(dp);
12882059035fStedu 				return;
12892059035fStedu 			}
12902059035fStedu 			if ((lp = byword(dp, wday_names)) == NULL) {
1291f0dc9fbaSderaadt 				error("invalid weekday name");
12922059035fStedu 				free(dp);
12932059035fStedu 				return;
12942059035fStedu 			}
12952059035fStedu 			rp->r_wday = lp->l_value;
12962059035fStedu 		}
12972059035fStedu 		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
12982059035fStedu 		    rp->r_dayofmonth <= 0 ||
12992059035fStedu 		    (rp->r_dayofmonth > len_months[1][rp->r_month])) {
1300f0dc9fbaSderaadt 			error("invalid day of month");
13012059035fStedu 			free(dp);
13022059035fStedu 			return;
13032059035fStedu 		}
13042059035fStedu 	}
13052059035fStedu 	free(dp);
13062059035fStedu }
13072059035fStedu 
13082059035fStedu static void
1309738fad2fStedu convert(long val, char *buf)
13102059035fStedu {
131166e58127Stedu 	int	i;
131266e58127Stedu 	int	shift;
13132059035fStedu 
13142059035fStedu 	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
13152059035fStedu 		buf[i] = val >> shift;
13162059035fStedu }
13172059035fStedu 
13182059035fStedu static void
1319738fad2fStedu convert64(zic_t val, char *buf)
13202059035fStedu {
132166e58127Stedu 	int	i;
132266e58127Stedu 	int	shift;
13232059035fStedu 
13242059035fStedu 	for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
13252059035fStedu 		buf[i] = val >> shift;
13262059035fStedu }
13272059035fStedu 
13282059035fStedu static void
1329738fad2fStedu puttzcode(long val, FILE *fp)
13302059035fStedu {
13312059035fStedu 	char	buf[4];
13322059035fStedu 
13332059035fStedu 	convert(val, buf);
133475b1bb69Stedu 	fwrite(buf, sizeof buf, 1, fp);
13352059035fStedu }
13362059035fStedu 
13372059035fStedu static void
1338738fad2fStedu puttzcode64(zic_t val, FILE *fp)
13392059035fStedu {
13402059035fStedu 	char	buf[8];
13412059035fStedu 
13422059035fStedu 	convert64(val, buf);
134375b1bb69Stedu 	fwrite(buf, sizeof buf, 1, fp);
13442059035fStedu }
13452059035fStedu 
13462059035fStedu static int
1347738fad2fStedu atcomp(const void *avp, const void *bvp)
13482059035fStedu {
13492059035fStedu 	const zic_t	a = ((const struct attype *) avp)->at;
13502059035fStedu 	const zic_t	b = ((const struct attype *) bvp)->at;
13512059035fStedu 
13522059035fStedu 	return (a < b) ? -1 : (a > b);
13532059035fStedu }
13542059035fStedu 
13552059035fStedu static int
1356738fad2fStedu is32(zic_t x)
13572059035fStedu {
13582059035fStedu 	return INT32_MIN <= x && x <= INT32_MAX;
13592059035fStedu }
13602059035fStedu 
13612059035fStedu static void
1362738fad2fStedu writezone(const char *name, const char *string)
13632059035fStedu {
136466e58127Stedu 	FILE			*fp;
136566e58127Stedu 	int			i, j;
136666e58127Stedu 	int			leapcnt32, leapi32;
136766e58127Stedu 	int			timecnt32, timei32;
136866e58127Stedu 	int			pass;
13692059035fStedu 	static char		*fullname;
13702059035fStedu 	static const struct tzhead	tzh0;
13712059035fStedu 	static struct tzhead	tzh;
13722059035fStedu 	zic_t			ats[TZ_MAX_TIMES];
13732059035fStedu 	unsigned char		types[TZ_MAX_TIMES];
13742059035fStedu 	size_t			len;
13752059035fStedu 
13762059035fStedu 	/*
13772059035fStedu 	** Sort.
13782059035fStedu 	*/
13792059035fStedu 	if (timecnt > 1)
138075b1bb69Stedu 		qsort(attypes, timecnt, sizeof *attypes, atcomp);
13812059035fStedu 	/*
13822059035fStedu 	** Optimize.
13832059035fStedu 	*/
13842059035fStedu 	{
13852059035fStedu 		int	fromi;
13862059035fStedu 		int	toi;
13872059035fStedu 
13882059035fStedu 		toi = 0;
13892059035fStedu 		fromi = 0;
13902059035fStedu 		while (fromi < timecnt && attypes[fromi].at < min_time)
13912059035fStedu 			++fromi;
13922059035fStedu 		if (isdsts[0] == 0)
13932059035fStedu 			while (fromi < timecnt && attypes[fromi].type == 0)
13942059035fStedu 				++fromi;	/* handled by default rule */
13952059035fStedu 		for ( ; fromi < timecnt; ++fromi) {
13962059035fStedu 			if (toi != 0 && ((attypes[fromi].at +
13972059035fStedu 			    gmtoffs[attypes[toi - 1].type]) <=
1398f0dc9fbaSderaadt 			    (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 :
1399f0dc9fbaSderaadt 			    attypes[toi - 2].type]))) {
1400f0dc9fbaSderaadt 				attypes[toi - 1].type = attypes[fromi].type;
14012059035fStedu 				continue;
14022059035fStedu 			}
14032059035fStedu 			if (toi == 0 ||
14042059035fStedu 			    attypes[toi - 1].type != attypes[fromi].type)
14052059035fStedu 				attypes[toi++] = attypes[fromi];
14062059035fStedu 		}
14072059035fStedu 		timecnt = toi;
14082059035fStedu 	}
14092059035fStedu 	/*
14102059035fStedu 	** Transfer.
14112059035fStedu 	*/
14122059035fStedu 	for (i = 0; i < timecnt; ++i) {
14132059035fStedu 		ats[i] = attypes[i].at;
14142059035fStedu 		types[i] = attypes[i].type;
14152059035fStedu 	}
14162059035fStedu 	/*
14172059035fStedu 	** Correct for leap seconds.
14182059035fStedu 	*/
14192059035fStedu 	for (i = 0; i < timecnt; ++i) {
14202059035fStedu 		j = leapcnt;
14212059035fStedu 		while (--j >= 0)
14222059035fStedu 			if (ats[i] > trans[j] - corr[j]) {
14232059035fStedu 				ats[i] = tadd(ats[i], corr[j]);
14242059035fStedu 				break;
14252059035fStedu 			}
14262059035fStedu 	}
14272059035fStedu 	/*
14282059035fStedu 	** Figure out 32-bit-limited starts and counts.
14292059035fStedu 	*/
14302059035fStedu 	timecnt32 = timecnt;
14312059035fStedu 	timei32 = 0;
14322059035fStedu 	leapcnt32 = leapcnt;
14332059035fStedu 	leapi32 = 0;
14342059035fStedu 	while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
14352059035fStedu 		--timecnt32;
14362059035fStedu 	while (timecnt32 > 0 && !is32(ats[timei32])) {
14372059035fStedu 		--timecnt32;
14382059035fStedu 		++timei32;
14392059035fStedu 	}
14402059035fStedu 	while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
14412059035fStedu 		--leapcnt32;
14422059035fStedu 	while (leapcnt32 > 0 && !is32(trans[leapi32])) {
14432059035fStedu 		--leapcnt32;
14442059035fStedu 		++leapi32;
14452059035fStedu 	}
14462059035fStedu 	len = strlen(directory) + 1 + strlen(name) + 1;
14472059035fStedu 	fullname = erealloc(fullname, len);
144875b1bb69Stedu 	snprintf(fullname, len, "%s/%s", directory, name);
14492059035fStedu 	/*
14502059035fStedu 	** Remove old file, if any, to snap links.
14512059035fStedu 	*/
1452f0dc9fbaSderaadt 	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
14534dc71ee2Stedu 		err(1, "Can't remove %s", fullname);
14542059035fStedu 	if ((fp = fopen(fullname, "wb")) == NULL) {
14552059035fStedu 		if (mkdirs(fullname) != 0)
14562059035fStedu 			exit(EXIT_FAILURE);
1457f0dc9fbaSderaadt 		if ((fp = fopen(fullname, "wb")) == NULL)
14584dc71ee2Stedu 			err(1, "Can't create %s", fullname);
14592059035fStedu 	}
14602059035fStedu 	for (pass = 1; pass <= 2; ++pass) {
146166e58127Stedu 		int	thistimei, thistimecnt;
146266e58127Stedu 		int	thisleapi, thisleapcnt;
146366e58127Stedu 		int	thistimelim, thisleaplim;
14642059035fStedu 		int	writetype[TZ_MAX_TIMES];
14652059035fStedu 		int	typemap[TZ_MAX_TYPES];
146666e58127Stedu 		int	thistypecnt;
14672059035fStedu 		char	thischars[TZ_MAX_CHARS];
14682059035fStedu 		char	thischarcnt;
14692059035fStedu 		int	indmap[TZ_MAX_CHARS];
14702059035fStedu 
14712059035fStedu 		if (pass == 1) {
14722059035fStedu 			thistimei = timei32;
14732059035fStedu 			thistimecnt = timecnt32;
14742059035fStedu 			thisleapi = leapi32;
14752059035fStedu 			thisleapcnt = leapcnt32;
14762059035fStedu 		} else {
14772059035fStedu 			thistimei = 0;
14782059035fStedu 			thistimecnt = timecnt;
14792059035fStedu 			thisleapi = 0;
14802059035fStedu 			thisleapcnt = leapcnt;
14812059035fStedu 		}
14822059035fStedu 		thistimelim = thistimei + thistimecnt;
14832059035fStedu 		thisleaplim = thisleapi + thisleapcnt;
14842059035fStedu 		for (i = 0; i < typecnt; ++i)
14852059035fStedu 			writetype[i] = thistimecnt == timecnt;
14862059035fStedu 		if (thistimecnt == 0) {
14872059035fStedu 			/*
14882059035fStedu 			** No transition times fall in the current
14892059035fStedu 			** (32- or 64-bit) window.
14902059035fStedu 			*/
14912059035fStedu 			if (typecnt != 0)
14922059035fStedu 				writetype[typecnt - 1] = TRUE;
14932059035fStedu 		} else {
14942059035fStedu 			for (i = thistimei - 1; i < thistimelim; ++i)
14952059035fStedu 				if (i >= 0)
14962059035fStedu 					writetype[types[i]] = TRUE;
14972059035fStedu 			/*
14982059035fStedu 			** For America/Godthab and Antarctica/Palmer
14992059035fStedu 			*/
15002059035fStedu 			if (thistimei == 0)
15012059035fStedu 				writetype[0] = TRUE;
15022059035fStedu 		}
15032059035fStedu #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
15042059035fStedu 		/*
15052059035fStedu 		** For some pre-2011 systems: if the last-to-be-written
15062059035fStedu 		** standard (or daylight) type has an offset different from the
15072059035fStedu 		** most recently used offset,
15082059035fStedu 		** append an (unused) copy of the most recently used type
15092059035fStedu 		** (to help get global "altzone" and "timezone" variables
15102059035fStedu 		** set correctly).
15112059035fStedu 		*/
15122059035fStedu 		{
151366e58127Stedu 			int	mrudst, mrustd, hidst, histd, type;
15142059035fStedu 
15152059035fStedu 			hidst = histd = mrudst = mrustd = -1;
15162059035fStedu 			for (i = thistimei; i < thistimelim; ++i)
15172059035fStedu 				if (isdsts[types[i]])
15182059035fStedu 					mrudst = types[i];
1519f0dc9fbaSderaadt 				else
1520f0dc9fbaSderaadt 					mrustd = types[i];
15212059035fStedu 			for (i = 0; i < typecnt; ++i)
15222059035fStedu 				if (writetype[i]) {
15232059035fStedu 					if (isdsts[i])
15242059035fStedu 						hidst = i;
1525f0dc9fbaSderaadt 					else
1526f0dc9fbaSderaadt 						histd = i;
15272059035fStedu 				}
15282059035fStedu 			if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
15292059035fStedu 			    gmtoffs[hidst] != gmtoffs[mrudst]) {
15302059035fStedu 				isdsts[mrudst] = -1;
15312059035fStedu 				type = addtype(gmtoffs[mrudst],
15322059035fStedu 				    &chars[abbrinds[mrudst]],
1533f0dc9fbaSderaadt 				    TRUE, ttisstds[mrudst],
15342059035fStedu 				    ttisgmts[mrudst]);
15352059035fStedu 				isdsts[mrudst] = TRUE;
15362059035fStedu 				writetype[type] = TRUE;
15372059035fStedu 			}
15382059035fStedu 			if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
15392059035fStedu 			    gmtoffs[histd] != gmtoffs[mrustd]) {
15402059035fStedu 				isdsts[mrustd] = -1;
15412059035fStedu 				type = addtype(gmtoffs[mrustd],
15422059035fStedu 				    &chars[abbrinds[mrustd]],
1543f0dc9fbaSderaadt 				    FALSE, ttisstds[mrustd],
15442059035fStedu 				    ttisgmts[mrustd]);
15452059035fStedu 				isdsts[mrustd] = FALSE;
15462059035fStedu 				writetype[type] = TRUE;
15472059035fStedu 			}
15482059035fStedu 		}
15492059035fStedu #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
15502059035fStedu 		thistypecnt = 0;
15512059035fStedu 		for (i = 0; i < typecnt; ++i)
15522059035fStedu 			typemap[i] = writetype[i] ?  thistypecnt++ : -1;
15532059035fStedu 		for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
15542059035fStedu 			indmap[i] = -1;
15552059035fStedu 		thischarcnt = 0;
15562059035fStedu 		for (i = 0; i < typecnt; ++i) {
155766e58127Stedu 			char	*thisabbr;
15582059035fStedu 
15592059035fStedu 			if (!writetype[i])
15602059035fStedu 				continue;
15612059035fStedu 			if (indmap[abbrinds[i]] >= 0)
15622059035fStedu 				continue;
15632059035fStedu 			thisabbr = &chars[abbrinds[i]];
15642059035fStedu 			for (j = 0; j < thischarcnt; ++j)
15652059035fStedu 				if (strcmp(&thischars[j], thisabbr) == 0)
15662059035fStedu 					break;
15672059035fStedu 			if (j == thischarcnt) {
156875b1bb69Stedu 				strlcpy(&thischars[(int) thischarcnt],
15692059035fStedu 				    thisabbr, sizeof(thischars) - thischarcnt);
15702059035fStedu 				thischarcnt += strlen(thisabbr) + 1;
15712059035fStedu 			}
15722059035fStedu 			indmap[abbrinds[i]] = j;
15732059035fStedu 		}
157475b1bb69Stedu #define DO(field)	fwrite(tzh.field, sizeof tzh.field, 1, fp)
15752059035fStedu 		tzh = tzh0;
157675b1bb69Stedu 		strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
15772059035fStedu 		tzh.tzh_version[0] = ZIC_VERSION;
15782059035fStedu 		convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
15792059035fStedu 		convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
15802059035fStedu 		convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
15812059035fStedu 		convert(eitol(thistimecnt), tzh.tzh_timecnt);
15822059035fStedu 		convert(eitol(thistypecnt), tzh.tzh_typecnt);
15832059035fStedu 		convert(eitol(thischarcnt), tzh.tzh_charcnt);
15842059035fStedu 		DO(tzh_magic);
15852059035fStedu 		DO(tzh_version);
15862059035fStedu 		DO(tzh_reserved);
15872059035fStedu 		DO(tzh_ttisgmtcnt);
15882059035fStedu 		DO(tzh_ttisstdcnt);
15892059035fStedu 		DO(tzh_leapcnt);
15902059035fStedu 		DO(tzh_timecnt);
15912059035fStedu 		DO(tzh_typecnt);
15922059035fStedu 		DO(tzh_charcnt);
15932059035fStedu #undef DO
15942059035fStedu 		for (i = thistimei; i < thistimelim; ++i)
15952059035fStedu 			if (pass == 1)
15962059035fStedu 				puttzcode((long) ats[i], fp);
1597f0dc9fbaSderaadt 			else
1598f0dc9fbaSderaadt 				puttzcode64(ats[i], fp);
15992059035fStedu 		for (i = thistimei; i < thistimelim; ++i) {
16002059035fStedu 			unsigned char	uc;
16012059035fStedu 
16022059035fStedu 			uc = typemap[types[i]];
160375b1bb69Stedu 			fwrite(&uc, sizeof uc, 1, fp);
16042059035fStedu 		}
16052059035fStedu 		for (i = 0; i < typecnt; ++i)
16062059035fStedu 			if (writetype[i]) {
16072059035fStedu 				puttzcode(gmtoffs[i], fp);
160875b1bb69Stedu 				putc(isdsts[i], fp);
160975b1bb69Stedu 				putc((unsigned char)indmap[abbrinds[i]], fp);
16102059035fStedu 			}
16112059035fStedu 		if (thischarcnt != 0)
161275b1bb69Stedu 			fwrite(thischars, sizeof thischars[0], thischarcnt, fp);
16132059035fStedu 		for (i = thisleapi; i < thisleaplim; ++i) {
161466e58127Stedu 			zic_t	todo;
16152059035fStedu 
16162059035fStedu 			if (roll[i]) {
16172059035fStedu 				if (timecnt == 0 || trans[i] < ats[0]) {
16182059035fStedu 					j = 0;
16192059035fStedu 					while (isdsts[j])
16202059035fStedu 						if (++j >= typecnt) {
16212059035fStedu 							j = 0;
16222059035fStedu 							break;
16232059035fStedu 						}
16242059035fStedu 				} else {
16252059035fStedu 					j = 1;
16262059035fStedu 					while (j < timecnt &&
16272059035fStedu 					    trans[i] >= ats[j])
16282059035fStedu 						++j;
16292059035fStedu 					j = types[j - 1];
16302059035fStedu 				}
16312059035fStedu 				todo = tadd(trans[i], -gmtoffs[j]);
1632f0dc9fbaSderaadt 			} else
1633f0dc9fbaSderaadt 				todo = trans[i];
16342059035fStedu 			if (pass == 1)
16352059035fStedu 				puttzcode((long) todo, fp);
1636f0dc9fbaSderaadt 			else
1637f0dc9fbaSderaadt 				puttzcode64(todo, fp);
16382059035fStedu 			puttzcode(corr[i], fp);
16392059035fStedu 		}
16402059035fStedu 		for (i = 0; i < typecnt; ++i)
16412059035fStedu 			if (writetype[i])
164275b1bb69Stedu 				putc(ttisstds[i], fp);
16432059035fStedu 		for (i = 0; i < typecnt; ++i)
16442059035fStedu 			if (writetype[i])
164575b1bb69Stedu 				putc(ttisgmts[i], fp);
16462059035fStedu 	}
164775b1bb69Stedu 	fprintf(fp, "\n%s\n", string);
1648f0dc9fbaSderaadt 	if (ferror(fp) || fclose(fp))
16494dc71ee2Stedu 		errx(1, "Error writing %s", fullname);
16502059035fStedu }
16512059035fStedu 
1652*6a4024dcSmillert static char const *
1653*6a4024dcSmillert abbroffset(char *buf, zic_t offset)
1654*6a4024dcSmillert {
1655*6a4024dcSmillert 	char sign = '+';
1656*6a4024dcSmillert 	int seconds, minutes;
1657*6a4024dcSmillert 
1658*6a4024dcSmillert 	if (offset < 0) {
1659*6a4024dcSmillert 		offset = -offset;
1660*6a4024dcSmillert 		sign = '-';
1661*6a4024dcSmillert 	}
1662*6a4024dcSmillert 
1663*6a4024dcSmillert 	seconds = offset % SECSPERMIN;
1664*6a4024dcSmillert 	offset /= SECSPERMIN;
1665*6a4024dcSmillert 	minutes = offset % MINSPERHOUR;
1666*6a4024dcSmillert 	offset /= MINSPERHOUR;
1667*6a4024dcSmillert 	if (100 <= offset) {
1668*6a4024dcSmillert 		error("%%z UTC offset magnitude exceeds 99:59:59");
1669*6a4024dcSmillert 		return "%z";
1670*6a4024dcSmillert 	} else {
1671*6a4024dcSmillert 		char *p = buf;
1672*6a4024dcSmillert 		*p++ = sign;
1673*6a4024dcSmillert 		*p++ = '0' + offset / 10;
1674*6a4024dcSmillert 		*p++ = '0' + offset % 10;
1675*6a4024dcSmillert 		if (minutes | seconds) {
1676*6a4024dcSmillert 			*p++ = '0' + minutes / 10;
1677*6a4024dcSmillert 			*p++ = '0' + minutes % 10;
1678*6a4024dcSmillert 			if (seconds) {
1679*6a4024dcSmillert 				*p++ = '0' + seconds / 10;
1680*6a4024dcSmillert 				*p++ = '0' + seconds % 10;
1681*6a4024dcSmillert 			}
1682*6a4024dcSmillert 		}
1683*6a4024dcSmillert 		*p = '\0';
1684*6a4024dcSmillert 		return buf;
1685*6a4024dcSmillert 	}
1686*6a4024dcSmillert }
1687*6a4024dcSmillert 
16882059035fStedu static void
1689*6a4024dcSmillert doabbr(char *abbr, size_t size, struct zone const *zp, const char *letters,
1690f0dc9fbaSderaadt     int isdst, int doquotes)
16912059035fStedu {
1692f0dc9fbaSderaadt 	char	*cp, *slashp;
1693*6a4024dcSmillert 	size_t	len;
1694*6a4024dcSmillert 	char const *format = zp->z_format;
16952059035fStedu 
16962059035fStedu 	slashp = strchr(format, '/');
16972059035fStedu 	if (slashp == NULL) {
1698*6a4024dcSmillert 		char letterbuf[PERCENT_Z_LEN_BOUND + 1];
1699*6a4024dcSmillert 		if (zp->z_format_specifier == 'z')
1700*6a4024dcSmillert 			letters = abbroffset(letterbuf, -zp->z_gmtoff);
1701*6a4024dcSmillert 		else if (letters == NULL)
1702*6a4024dcSmillert 			letters = "%s";
1703f0dc9fbaSderaadt 		snprintf(abbr, size, format, letters);
17042059035fStedu 	} else if (isdst) {
170575b1bb69Stedu 		strlcpy(abbr, slashp + 1, size);
17062059035fStedu 	} else {
17072059035fStedu 		if (slashp - format + 1 < size)
17082059035fStedu 			size = slashp - format + 1;
170975b1bb69Stedu 		strlcpy(abbr, format, size);
17102059035fStedu 	}
17112059035fStedu 	if (!doquotes)
17122059035fStedu 		return;
17132059035fStedu 	for (cp = abbr; *cp != '\0'; ++cp)
17142059035fStedu 		if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
17152059035fStedu 			strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
17162059035fStedu 				break;
17172059035fStedu 	len = strlen(abbr);
17182059035fStedu 	if (len > 0 && *cp == '\0')
17192059035fStedu 		return;
17202059035fStedu 	abbr[len + 2] = '\0';
17212059035fStedu 	abbr[len + 1] = '>';
17222059035fStedu 	for ( ; len > 0; --len)
17232059035fStedu 		abbr[len] = abbr[len - 1];
17242059035fStedu 	abbr[0] = '<';
17252059035fStedu }
17262059035fStedu 
17272059035fStedu static void
1728738fad2fStedu updateminmax(int x)
17292059035fStedu {
17302059035fStedu 	if (min_year > x)
17312059035fStedu 		min_year = x;
17322059035fStedu 	if (max_year < x)
17332059035fStedu 		max_year = x;
17342059035fStedu }
17352059035fStedu 
17362059035fStedu static int
1737738fad2fStedu stringoffset(char *result, size_t size, long offset)
17382059035fStedu {
1739f0dc9fbaSderaadt 	int	hours, minutes, seconds;
174066e58127Stedu 	char	*ep;
17412059035fStedu 
17422059035fStedu 	result[0] = '\0';
17432059035fStedu 	if (offset < 0) {
174475b1bb69Stedu 		strlcpy(result, "-", size);
17452059035fStedu 		offset = -offset;
17462059035fStedu 	}
17472059035fStedu 	seconds = offset % SECSPERMIN;
17482059035fStedu 	offset /= SECSPERMIN;
17492059035fStedu 	minutes = offset % MINSPERHOUR;
17502059035fStedu 	offset /= MINSPERHOUR;
17512059035fStedu 	hours = offset;
17522059035fStedu 	if (hours >= HOURSPERDAY) {
17532059035fStedu 		result[0] = '\0';
17542059035fStedu 		return -1;
17552059035fStedu 	}
17562059035fStedu 	ep = end(result, size);
175775b1bb69Stedu 	snprintf(ep, size - (ep - result), "%d", hours);
17582059035fStedu 	if (minutes != 0 || seconds != 0) {
17592059035fStedu 		ep = end(result, size);
176075b1bb69Stedu 		snprintf(ep, size - (ep - result), ":%02d", minutes);
17612059035fStedu 		if (seconds != 0) {
17622059035fStedu 			ep = end(result, size);
176375b1bb69Stedu 			snprintf(ep, size - (ep - result), ":%02d", seconds);
17642059035fStedu 		}
17652059035fStedu 	}
17662059035fStedu 	return 0;
17672059035fStedu }
17682059035fStedu 
17692059035fStedu static int
1770738fad2fStedu stringrule(char *result, size_t size, const struct rule *rp, long dstoff, long gmtoff)
17712059035fStedu {
177266e58127Stedu 	long	tod;
177366e58127Stedu 	char	*ep;
17742059035fStedu 
17752059035fStedu 	ep = end(result, size);
17762059035fStedu 	size -= ep - result;
17772059035fStedu 	result = ep;
17782059035fStedu 	if (rp->r_dycode == DC_DOM) {
177966e58127Stedu 		int	month, total;
17802059035fStedu 
17812059035fStedu 		if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
17822059035fStedu 			return -1;
17832059035fStedu 		total = 0;
17842059035fStedu 		for (month = 0; month < rp->r_month; ++month)
17852059035fStedu 			total += len_months[0][month];
178675b1bb69Stedu 		snprintf(result, size, "J%d", total + rp->r_dayofmonth);
17872059035fStedu 	} else {
178866e58127Stedu 		int	week;
17892059035fStedu 
17902059035fStedu 		if (rp->r_dycode == DC_DOWGEQ) {
17912059035fStedu 			if ((rp->r_dayofmonth % DAYSPERWEEK) != 1)
17922059035fStedu 				return -1;
17932059035fStedu 			week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
17942059035fStedu 		} else if (rp->r_dycode == DC_DOWLEQ) {
17952059035fStedu 			if (rp->r_dayofmonth == len_months[1][rp->r_month])
17962059035fStedu 				week = 5;
17972059035fStedu 			else {
17982059035fStedu 				if ((rp->r_dayofmonth % DAYSPERWEEK) != 0)
17992059035fStedu 					return -1;
18002059035fStedu 				week = rp->r_dayofmonth / DAYSPERWEEK;
18012059035fStedu 			}
1802f0dc9fbaSderaadt 		} else
1803f0dc9fbaSderaadt 			return -1;	/* "cannot happen" */
180475b1bb69Stedu 		snprintf(result, size, "M%d.%d.%d",
18052059035fStedu 			rp->r_month + 1, week, rp->r_wday);
18062059035fStedu 	}
18072059035fStedu 	tod = rp->r_tod;
18082059035fStedu 	if (rp->r_todisgmt)
18092059035fStedu 		tod += gmtoff;
18102059035fStedu 	if (rp->r_todisstd && rp->r_stdoff == 0)
18112059035fStedu 		tod += dstoff;
18122059035fStedu 	if (tod < 0) {
18132059035fStedu 		result[0] = '\0';
18142059035fStedu 		return -1;
18152059035fStedu 	}
18162059035fStedu 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
181775b1bb69Stedu 		strlcat(result, "/", size);
18182059035fStedu 		ep = end(result, size);
18192059035fStedu 		if (stringoffset(ep, size - (ep - result), tod) != 0)
18202059035fStedu 			return -1;
18212059035fStedu 	}
18222059035fStedu 	return 0;
18232059035fStedu }
18242059035fStedu 
18252059035fStedu static void
1826738fad2fStedu stringzone(char *result, size_t size, const struct zone *zpfirst, int zonecount)
18272059035fStedu {
182866e58127Stedu 	const struct zone	*zp;
1829f0dc9fbaSderaadt 	struct rule		*rp, *stdrp, *dstrp;
183066e58127Stedu 	int			i;
183166e58127Stedu 	const char		*abbrvar;
183266e58127Stedu 	char			*ep;
18332059035fStedu 
18342059035fStedu 	result[0] = '\0';
18352059035fStedu 	zp = zpfirst + zonecount - 1;
18362059035fStedu 	stdrp = dstrp = NULL;
18372059035fStedu 	for (i = 0; i < zp->z_nrules; ++i) {
18382059035fStedu 		rp = &zp->z_rules[i];
18392059035fStedu 		if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
18402059035fStedu 			continue;
18412059035fStedu 		if (rp->r_stdoff == 0) {
18422059035fStedu 			if (stdrp == NULL)
18432059035fStedu 				stdrp = rp;
1844f0dc9fbaSderaadt 			else
1845f0dc9fbaSderaadt 				return;
18462059035fStedu 		} else {
18472059035fStedu 			if (dstrp == NULL)
18482059035fStedu 				dstrp = rp;
1849f0dc9fbaSderaadt 			else
1850f0dc9fbaSderaadt 				return;
18512059035fStedu 		}
18522059035fStedu 	}
18532059035fStedu 	if (stdrp == NULL && dstrp == NULL) {
18542059035fStedu 		/*
18552059035fStedu 		** There are no rules running through "max".
18562059035fStedu 		** Let's find the latest rule.
18572059035fStedu 		*/
18582059035fStedu 		for (i = 0; i < zp->z_nrules; ++i) {
18592059035fStedu 			rp = &zp->z_rules[i];
18602059035fStedu 			if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
18612059035fStedu 				(rp->r_hiyear == stdrp->r_hiyear &&
18622059035fStedu 				rp->r_month > stdrp->r_month))
18632059035fStedu 					stdrp = rp;
18642059035fStedu 		}
18652059035fStedu 		if (stdrp != NULL && stdrp->r_stdoff != 0)
18662059035fStedu 			return;	/* We end up in DST (a POSIX no-no). */
18672059035fStedu 		/*
18682059035fStedu 		** Horrid special case: if year is 2037,
18692059035fStedu 		** presume this is a zone handled on a year-by-year basis;
18702059035fStedu 		** do not try to apply a rule to the zone.
18712059035fStedu 		*/
18722059035fStedu 		if (stdrp != NULL && stdrp->r_hiyear == 2037)
18732059035fStedu 			return;
18742059035fStedu 	}
18752059035fStedu 	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
18762059035fStedu 		return;
18772059035fStedu 	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
1878*6a4024dcSmillert 	doabbr(result, size, zp, abbrvar, FALSE, TRUE);
18792059035fStedu 	ep = end(result, size);
18802059035fStedu 	if (stringoffset(ep, size - (ep - result), -zp->z_gmtoff) != 0) {
18812059035fStedu 		result[0] = '\0';
18822059035fStedu 		return;
18832059035fStedu 	}
18842059035fStedu 	if (dstrp == NULL)
18852059035fStedu 		return;
18862059035fStedu 	ep = end(result, size);
1887*6a4024dcSmillert 	doabbr(ep, size - (ep - result), zp, dstrp->r_abbrvar, TRUE, TRUE);
18882059035fStedu 	if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) {
18892059035fStedu 		ep = end(result, size);
18902059035fStedu 		if (stringoffset(ep, size - (ep - result),
18912059035fStedu 			-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
18922059035fStedu 				result[0] = '\0';
18932059035fStedu 				return;
18942059035fStedu 		}
18952059035fStedu 	}
189675b1bb69Stedu 	strlcat(result, ",", size);
18972059035fStedu 	if (stringrule(result, size, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
18982059035fStedu 		result[0] = '\0';
18992059035fStedu 		return;
19002059035fStedu 	}
190175b1bb69Stedu 	strlcat(result, ",", size);
19022059035fStedu 	if (stringrule(result, size, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
19032059035fStedu 		result[0] = '\0';
19042059035fStedu 		return;
19052059035fStedu 	}
19062059035fStedu }
19072059035fStedu 
19082059035fStedu static void
1909738fad2fStedu outzone(const struct zone *zpfirst, int zonecount)
19102059035fStedu {
191166e58127Stedu 	const struct zone	*zp;
191266e58127Stedu 	struct rule		*rp;
1913f0dc9fbaSderaadt 	int			i, j, usestart, useuntil, type;
1914f0dc9fbaSderaadt 	zic_t			starttime = 0, untiltime = 0;
1915f0dc9fbaSderaadt 	long			gmtoff, stdoff, startoff;
1916f0dc9fbaSderaadt 	int			year, startttisstd = FALSE, startttisgmt = FALSE;
1917f0dc9fbaSderaadt 	char			*startbuf, *ab, *envvar;
1918f0dc9fbaSderaadt 	int			max_abbr_len, max_envvar_len;
191966e58127Stedu 	int			prodstic; /* all rules are min to max */
19202059035fStedu 
19212059035fStedu 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
19222059035fStedu 	max_envvar_len = 2 * max_abbr_len + 5 * 9;
19232059035fStedu 	startbuf = emalloc(max_abbr_len + 1);
19242059035fStedu 	ab = emalloc(max_abbr_len + 1);
19252059035fStedu 	envvar = emalloc(max_envvar_len + 1);
19262059035fStedu 	/*
19272059035fStedu 	** Now. . .finally. . .generate some useful data!
19282059035fStedu 	*/
19292059035fStedu 	timecnt = 0;
19302059035fStedu 	typecnt = 0;
19312059035fStedu 	charcnt = 0;
19322059035fStedu 	prodstic = zonecount == 1;
19332059035fStedu 	/*
19342059035fStedu 	** Thanks to Earl Chew
19352059035fStedu 	** for noting the need to unconditionally initialize startttisstd.
19362059035fStedu 	*/
19372059035fStedu 	min_year = max_year = EPOCH_YEAR;
19382059035fStedu 	if (leapseen) {
19392059035fStedu 		updateminmax(leapminyear);
19402059035fStedu 		updateminmax(leapmaxyear + (leapmaxyear < INT_MAX));
19412059035fStedu 	}
19422059035fStedu 	for (i = 0; i < zonecount; ++i) {
19432059035fStedu 		zp = &zpfirst[i];
19442059035fStedu 		if (i < zonecount - 1)
19452059035fStedu 			updateminmax(zp->z_untilrule.r_loyear);
19462059035fStedu 		for (j = 0; j < zp->z_nrules; ++j) {
19472059035fStedu 			rp = &zp->z_rules[j];
19482059035fStedu 			if (rp->r_lowasnum)
19492059035fStedu 				updateminmax(rp->r_loyear);
19502059035fStedu 			if (rp->r_hiwasnum)
19512059035fStedu 				updateminmax(rp->r_hiyear);
19522059035fStedu 			if (rp->r_lowasnum || rp->r_hiwasnum)
19532059035fStedu 				prodstic = FALSE;
19542059035fStedu 		}
19552059035fStedu 	}
19562059035fStedu 	/*
19572059035fStedu 	** Generate lots of data if a rule can't cover all future times.
19582059035fStedu 	*/
19592059035fStedu 	stringzone(envvar, max_envvar_len + 1, zpfirst, zonecount);
19602059035fStedu 	if (noise && envvar[0] == '\0') {
196166e58127Stedu 		char *	wp;
19622059035fStedu 
1963f0dc9fbaSderaadt 		wp = ecpyalloc("no POSIX environment variable for zone");
19642059035fStedu 		wp = ecatalloc(wp, " ");
19652059035fStedu 		wp = ecatalloc(wp, zpfirst->z_name);
19662059035fStedu 		warning(wp);
19672059035fStedu 		free(wp);
19682059035fStedu 	}
19692059035fStedu 	if (envvar[0] == '\0') {
19702059035fStedu 		if (min_year >= INT_MIN + YEARSPERREPEAT)
19712059035fStedu 			min_year -= YEARSPERREPEAT;
1972f0dc9fbaSderaadt 		else
1973f0dc9fbaSderaadt 			min_year = INT_MIN;
19742059035fStedu 		if (max_year <= INT_MAX - YEARSPERREPEAT)
19752059035fStedu 			max_year += YEARSPERREPEAT;
1976f0dc9fbaSderaadt 		else
1977f0dc9fbaSderaadt 			max_year = INT_MAX;
19782059035fStedu 		/*
19792059035fStedu 		** Regardless of any of the above,
19802059035fStedu 		** for a "proDSTic" zone which specifies that its rules
19812059035fStedu 		** always have and always will be in effect,
19822059035fStedu 		** we only need one cycle to define the zone.
19832059035fStedu 		*/
19842059035fStedu 		if (prodstic) {
19852059035fStedu 			min_year = 1900;
19862059035fStedu 			max_year = min_year + YEARSPERREPEAT;
19872059035fStedu 		}
19882059035fStedu 	}
19892059035fStedu 	/*
19902059035fStedu 	** For the benefit of older systems,
19912059035fStedu 	** generate data from 1900 through 2037.
19922059035fStedu 	*/
19932059035fStedu 	if (min_year > 1900)
19942059035fStedu 		min_year = 1900;
19952059035fStedu 	if (max_year < 2037)
19962059035fStedu 		max_year = 2037;
19972059035fStedu 	for (i = 0; i < zonecount; ++i) {
19982059035fStedu 		/*
19992059035fStedu 		** A guess that may well be corrected later.
20002059035fStedu 		*/
20012059035fStedu 		stdoff = 0;
20022059035fStedu 		zp = &zpfirst[i];
20032059035fStedu 		usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
20042059035fStedu 		useuntil = i < (zonecount - 1);
20052059035fStedu 		if (useuntil && zp->z_untiltime <= min_time)
20062059035fStedu 			continue;
20072059035fStedu 		gmtoff = zp->z_gmtoff;
20082059035fStedu 		eat(zp->z_filename, zp->z_linenum);
20092059035fStedu 		*startbuf = '\0';
20102059035fStedu 		startoff = zp->z_gmtoff;
20112059035fStedu 		if (zp->z_nrules == 0) {
20122059035fStedu 			stdoff = zp->z_stdoff;
2013*6a4024dcSmillert 			doabbr(startbuf, max_abbr_len + 1, zp, NULL,
2014*6a4024dcSmillert 			    stdoff != 0, FALSE);
20152059035fStedu 			type = addtype(oadd(zp->z_gmtoff, stdoff),
20162059035fStedu 				startbuf, stdoff != 0, startttisstd,
20172059035fStedu 				startttisgmt);
20182059035fStedu 			if (usestart) {
20192059035fStedu 				addtt(starttime, type);
20202059035fStedu 				usestart = FALSE;
20212059035fStedu 			} else if (stdoff != 0)
20222059035fStedu 				addtt(min_time, type);
20232059035fStedu 		} else for (year = min_year; year <= max_year; ++year) {
20242059035fStedu 			if (useuntil && year > zp->z_untilrule.r_hiyear)
20252059035fStedu 				break;
20262059035fStedu 			/*
20272059035fStedu 			** Mark which rules to do in the current year.
20282059035fStedu 			** For those to do, calculate rpytime(rp, year);
20292059035fStedu 			*/
20302059035fStedu 			for (j = 0; j < zp->z_nrules; ++j) {
20312059035fStedu 				rp = &zp->z_rules[j];
20322059035fStedu 				eats(zp->z_filename, zp->z_linenum,
20332059035fStedu 					rp->r_filename, rp->r_linenum);
20342059035fStedu 				rp->r_todo = year >= rp->r_loyear &&
2035a188d4d1Smillert 				    year <= rp->r_hiyear;
20362059035fStedu 				if (rp->r_todo)
20372059035fStedu 					rp->r_temp = rpytime(rp, year);
20382059035fStedu 			}
20392059035fStedu 			for ( ; ; ) {
204066e58127Stedu 				int	k;
2041f0dc9fbaSderaadt 				zic_t	jtime, ktime = 0;
204266e58127Stedu 				long	offset;
20432059035fStedu 
20442059035fStedu 				if (useuntil) {
20452059035fStedu 					/*
20462059035fStedu 					** Turn untiltime into UTC
20472059035fStedu 					** assuming the current gmtoff and
20482059035fStedu 					** stdoff values.
20492059035fStedu 					*/
20502059035fStedu 					untiltime = zp->z_untiltime;
20512059035fStedu 					if (!zp->z_untilrule.r_todisgmt)
20522059035fStedu 						untiltime = tadd(untiltime,
20532059035fStedu 							-gmtoff);
20542059035fStedu 					if (!zp->z_untilrule.r_todisstd)
20552059035fStedu 						untiltime = tadd(untiltime,
20562059035fStedu 							-stdoff);
20572059035fStedu 				}
20582059035fStedu 				/*
20592059035fStedu 				** Find the rule (of those to do, if any)
20602059035fStedu 				** that takes effect earliest in the year.
20612059035fStedu 				*/
20622059035fStedu 				k = -1;
20632059035fStedu 				for (j = 0; j < zp->z_nrules; ++j) {
20642059035fStedu 					rp = &zp->z_rules[j];
20652059035fStedu 					if (!rp->r_todo)
20662059035fStedu 						continue;
20672059035fStedu 					eats(zp->z_filename, zp->z_linenum,
20682059035fStedu 						rp->r_filename, rp->r_linenum);
20692059035fStedu 					offset = rp->r_todisgmt ? 0 : gmtoff;
20702059035fStedu 					if (!rp->r_todisstd)
20712059035fStedu 						offset = oadd(offset, stdoff);
20722059035fStedu 					jtime = rp->r_temp;
20732059035fStedu 					if (jtime == min_time ||
20742059035fStedu 						jtime == max_time)
20752059035fStedu 							continue;
20762059035fStedu 					jtime = tadd(jtime, -offset);
20772059035fStedu 					if (k < 0 || jtime < ktime) {
20782059035fStedu 						k = j;
20792059035fStedu 						ktime = jtime;
20802059035fStedu 					}
20812059035fStedu 				}
20822059035fStedu 				if (k < 0)
20832059035fStedu 					break;	/* go on to next year */
20842059035fStedu 				rp = &zp->z_rules[k];
20852059035fStedu 				rp->r_todo = FALSE;
20862059035fStedu 				if (useuntil && ktime >= untiltime)
20872059035fStedu 					break;
20882059035fStedu 				stdoff = rp->r_stdoff;
20892059035fStedu 				if (usestart && ktime == starttime)
20902059035fStedu 					usestart = FALSE;
20912059035fStedu 				if (usestart) {
20922059035fStedu 					if (ktime < starttime) {
20932059035fStedu 						startoff = oadd(zp->z_gmtoff,
20942059035fStedu 						    stdoff);
20952059035fStedu 						doabbr(startbuf,
20962059035fStedu 						    max_abbr_len + 1,
2097*6a4024dcSmillert 						    zp,
20982059035fStedu 						    rp->r_abbrvar,
20992059035fStedu 						    rp->r_stdoff != 0,
21002059035fStedu 						    FALSE);
21012059035fStedu 						continue;
21022059035fStedu 					}
21032059035fStedu 					if (*startbuf == '\0' &&
21042059035fStedu 					    startoff == oadd(zp->z_gmtoff,
21052059035fStedu 					    stdoff)) {
21062059035fStedu 						doabbr(startbuf,
21072059035fStedu 						    max_abbr_len + 1,
2108*6a4024dcSmillert 						    zp,
21092059035fStedu 						    rp->r_abbrvar,
2110f0dc9fbaSderaadt 						    rp->r_stdoff != 0,
21112059035fStedu 						    FALSE);
21122059035fStedu 					}
21132059035fStedu 				}
21142059035fStedu 				eats(zp->z_filename, zp->z_linenum,
21152059035fStedu 				    rp->r_filename, rp->r_linenum);
2116*6a4024dcSmillert 				doabbr(ab, max_abbr_len + 1, zp,
2117f0dc9fbaSderaadt 				    rp->r_abbrvar, rp->r_stdoff != 0, FALSE);
21182059035fStedu 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
21192059035fStedu 				type = addtype(offset, ab, rp->r_stdoff != 0,
21202059035fStedu 				    rp->r_todisstd, rp->r_todisgmt);
21212059035fStedu 				addtt(ktime, type);
21222059035fStedu 			}
21232059035fStedu 		}
21242059035fStedu 		if (usestart) {
21252059035fStedu 			if (*startbuf == '\0' &&
21262059035fStedu 			    zp->z_format != NULL &&
21272059035fStedu 			    strchr(zp->z_format, '%') == NULL &&
21282059035fStedu 			    strchr(zp->z_format, '/') == NULL)
2129f0dc9fbaSderaadt 				strlcpy(startbuf, zp->z_format, max_abbr_len + 1);
21302059035fStedu 			eat(zp->z_filename, zp->z_linenum);
21312059035fStedu 			if (*startbuf == '\0')
2132f0dc9fbaSderaadt 				error("can't determine time zone abbreviation to use just after until time");
2133f0dc9fbaSderaadt 			else
2134f0dc9fbaSderaadt 				addtt(starttime,
21352059035fStedu 				    addtype(startoff, startbuf,
21362059035fStedu 				    startoff != zp->z_gmtoff,
2137f0dc9fbaSderaadt 				    startttisstd, startttisgmt));
21382059035fStedu 		}
21392059035fStedu 		/*
21402059035fStedu 		** Now we may get to set starttime for the next zone line.
21412059035fStedu 		*/
21422059035fStedu 		if (useuntil) {
21432059035fStedu 			startttisstd = zp->z_untilrule.r_todisstd;
21442059035fStedu 			startttisgmt = zp->z_untilrule.r_todisgmt;
21452059035fStedu 			starttime = zp->z_untiltime;
21462059035fStedu 			if (!startttisstd)
21472059035fStedu 				starttime = tadd(starttime, -stdoff);
21482059035fStedu 			if (!startttisgmt)
21492059035fStedu 				starttime = tadd(starttime, -gmtoff);
21502059035fStedu 		}
21512059035fStedu 	}
21522059035fStedu 	writezone(zpfirst->z_name, envvar);
21532059035fStedu 	free(startbuf);
21542059035fStedu 	free(ab);
21552059035fStedu 	free(envvar);
21562059035fStedu }
21572059035fStedu 
21582059035fStedu static void
2159f0dc9fbaSderaadt addtt(const zic_t starttime, int type)
21602059035fStedu {
21612059035fStedu 	size_t len;
21622059035fStedu 
21632059035fStedu 	if (starttime <= min_time ||
21642059035fStedu 	    (timecnt == 1 && attypes[0].at < min_time)) {
21652059035fStedu 		gmtoffs[0] = gmtoffs[type];
21662059035fStedu 		isdsts[0] = isdsts[type];
21672059035fStedu 		ttisstds[0] = ttisstds[type];
21682059035fStedu 		ttisgmts[0] = ttisgmts[type];
21692059035fStedu 		if (abbrinds[type] != 0) {
21702059035fStedu 			len = strlen(&chars[abbrinds[type]]) + 1;
217175b1bb69Stedu 			memmove(chars, &chars[abbrinds[type]], len);
21722059035fStedu 		}
21732059035fStedu 		abbrinds[0] = 0;
21742059035fStedu 		charcnt = strlen(chars) + 1;
21752059035fStedu 		typecnt = 1;
21762059035fStedu 		timecnt = 0;
21772059035fStedu 		type = 0;
21782059035fStedu 	}
21792059035fStedu 	if (timecnt >= TZ_MAX_TIMES) {
2180f0dc9fbaSderaadt 		error("too many transitions?!");
21812059035fStedu 		exit(EXIT_FAILURE);
21822059035fStedu 	}
21832059035fStedu 	attypes[timecnt].at = starttime;
21842059035fStedu 	attypes[timecnt].type = type;
21852059035fStedu 	++timecnt;
21862059035fStedu }
21872059035fStedu 
21882059035fStedu static int
2189738fad2fStedu addtype(long gmtoff, const char *abbr, int isdst, int ttisstd, int ttisgmt)
21902059035fStedu {
219166e58127Stedu 	int	i, j;
21922059035fStedu 
21932059035fStedu 	if (isdst != TRUE && isdst != FALSE) {
2194f0dc9fbaSderaadt 		error("internal error - addtype called with bad isdst");
21952059035fStedu 		exit(EXIT_FAILURE);
21962059035fStedu 	}
21972059035fStedu 	if (ttisstd != TRUE && ttisstd != FALSE) {
2198f0dc9fbaSderaadt 		error("internal error - addtype called with bad ttisstd");
21992059035fStedu 		exit(EXIT_FAILURE);
22002059035fStedu 	}
22012059035fStedu 	if (ttisgmt != TRUE && ttisgmt != FALSE) {
2202f0dc9fbaSderaadt 		error("internal error - addtype called with bad ttisgmt");
22032059035fStedu 		exit(EXIT_FAILURE);
22042059035fStedu 	}
22052059035fStedu 	/*
22062059035fStedu 	** See if there's already an entry for this zone type.
22072059035fStedu 	** If so, just return its index.
22082059035fStedu 	*/
22092059035fStedu 	for (i = 0; i < typecnt; ++i) {
22102059035fStedu 		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
22112059035fStedu 		    strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
22122059035fStedu 		    ttisstd == ttisstds[i] &&
22132059035fStedu 		    ttisgmt == ttisgmts[i])
22142059035fStedu 			return i;
22152059035fStedu 	}
22162059035fStedu 	/*
22172059035fStedu 	** There isn't one; add a new one, unless there are already too
22182059035fStedu 	** many.
22192059035fStedu 	*/
22202059035fStedu 	if (typecnt >= TZ_MAX_TYPES) {
2221f0dc9fbaSderaadt 		error("too many local time types");
22222059035fStedu 		exit(EXIT_FAILURE);
22232059035fStedu 	}
22242059035fStedu 	if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
2225f0dc9fbaSderaadt 		error("UTC offset out of range");
22262059035fStedu 		exit(EXIT_FAILURE);
22272059035fStedu 	}
22282059035fStedu 	gmtoffs[i] = gmtoff;
22292059035fStedu 	isdsts[i] = isdst;
22302059035fStedu 	ttisstds[i] = ttisstd;
22312059035fStedu 	ttisgmts[i] = ttisgmt;
22322059035fStedu 
22332059035fStedu 	for (j = 0; j < charcnt; ++j)
22342059035fStedu 		if (strcmp(&chars[j], abbr) == 0)
22352059035fStedu 			break;
22362059035fStedu 	if (j == charcnt)
22372059035fStedu 		newabbr(abbr);
22382059035fStedu 	abbrinds[i] = j;
22392059035fStedu 	++typecnt;
22402059035fStedu 	return i;
22412059035fStedu }
22422059035fStedu 
22432059035fStedu static void
2244738fad2fStedu leapadd(zic_t t, int positive, int rolling, int count)
22452059035fStedu {
224666e58127Stedu 	int	i, j;
22472059035fStedu 
22482059035fStedu 	if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
2249f0dc9fbaSderaadt 		error("too many leap seconds");
22502059035fStedu 		exit(EXIT_FAILURE);
22512059035fStedu 	}
22522059035fStedu 	for (i = 0; i < leapcnt; ++i)
22532059035fStedu 		if (t <= trans[i]) {
22542059035fStedu 			if (t == trans[i]) {
2255f0dc9fbaSderaadt 				error("repeated leap second moment");
22562059035fStedu 				exit(EXIT_FAILURE);
22572059035fStedu 			}
22582059035fStedu 			break;
22592059035fStedu 		}
22602059035fStedu 	do {
22612059035fStedu 		for (j = leapcnt; j > i; --j) {
22622059035fStedu 			trans[j] = trans[j - 1];
22632059035fStedu 			corr[j] = corr[j - 1];
22642059035fStedu 			roll[j] = roll[j - 1];
22652059035fStedu 		}
22662059035fStedu 		trans[i] = t;
22672059035fStedu 		corr[i] = positive ? 1L : eitol(-count);
22682059035fStedu 		roll[i] = rolling;
22692059035fStedu 		++leapcnt;
22702059035fStedu 	} while (positive && --count != 0);
22712059035fStedu }
22722059035fStedu 
22732059035fStedu static void
22742059035fStedu adjleap(void)
22752059035fStedu {
227666e58127Stedu 	int	i;
227766e58127Stedu 	long	last = 0;
22782059035fStedu 
22792059035fStedu 	/*
22802059035fStedu 	** propagate leap seconds forward
22812059035fStedu 	*/
22822059035fStedu 	for (i = 0; i < leapcnt; ++i) {
22832059035fStedu 		trans[i] = tadd(trans[i], last);
22842059035fStedu 		last = corr[i] += last;
22852059035fStedu 	}
22862059035fStedu }
22872059035fStedu 
2288fb356290Stedu /* this function is not strncasecmp */
228938cd2fb1Stedu static int
2290fb356290Stedu itsabbr(const char *sabbr, const char *sword)
229138cd2fb1Stedu {
2292fb356290Stedu 	const unsigned char *abbr = sabbr;
2293fb356290Stedu 	const unsigned char *word = sword;
229438cd2fb1Stedu 
2295fb356290Stedu 	if (tolower(*abbr) != tolower(*word))
229638cd2fb1Stedu 		return FALSE;
229738cd2fb1Stedu 	while (*++abbr != '\0')
229838cd2fb1Stedu 		do {
2299fb356290Stedu 			++word;
230038cd2fb1Stedu 			if (*word == '\0')
230138cd2fb1Stedu 				return FALSE;
2302fb356290Stedu 		} while (tolower(*word) != tolower(*abbr));
230338cd2fb1Stedu 	return TRUE;
230438cd2fb1Stedu }
230538cd2fb1Stedu 
23062059035fStedu static const struct lookup *
2307738fad2fStedu byword(const char *word, const struct lookup *table)
23082059035fStedu {
230966e58127Stedu 	const struct lookup	*foundlp;
231066e58127Stedu 	const struct lookup	*lp;
23112059035fStedu 
231238cd2fb1Stedu 	if (word == NULL || table == NULL)
23132059035fStedu 		return NULL;
23142059035fStedu 	/*
23152059035fStedu 	** Look for exact match.
23162059035fStedu 	*/
23172059035fStedu 	for (lp = table; lp->l_word != NULL; ++lp)
2318fb356290Stedu 		if (strcasecmp(word, lp->l_word) == 0)
23192059035fStedu 			return lp;
23202059035fStedu 	/*
23212059035fStedu 	** Look for inexact match.
23222059035fStedu 	*/
23232059035fStedu 	foundlp = NULL;
23242059035fStedu 	for (lp = table; lp->l_word != NULL; ++lp)
232538cd2fb1Stedu 		if (itsabbr(word, lp->l_word)) {
23262059035fStedu 			if (foundlp == NULL)
23272059035fStedu 				foundlp = lp;
2328fb356290Stedu 			else
2329fb356290Stedu 				return NULL;	/* multiple inexact matches */
23302059035fStedu 		}
23312059035fStedu 	return foundlp;
23322059035fStedu }
23332059035fStedu 
23342059035fStedu static char **
2335738fad2fStedu getfields(char *cp)
23362059035fStedu {
233766e58127Stedu 	char		*dp;
233866e58127Stedu 	char		**array;
233966e58127Stedu 	int		nsubs;
23402059035fStedu 
23412059035fStedu 	if (cp == NULL)
23422059035fStedu 		return NULL;
2343cc917873Sderaadt 	array = ereallocarray(NULL, strlen(cp) + 1, sizeof *array);
23442059035fStedu 	nsubs = 0;
23452059035fStedu 	for ( ; ; ) {
23462059035fStedu 		while (isascii((unsigned char)*cp) &&
23472059035fStedu 		    isspace((unsigned char)*cp))
23482059035fStedu 			++cp;
23492059035fStedu 		if (*cp == '\0' || *cp == '#')
23502059035fStedu 			break;
23512059035fStedu 		array[nsubs++] = dp = cp;
23522059035fStedu 		do {
2353f0dc9fbaSderaadt 			if ((*dp = *cp++) != '"') {
23542059035fStedu 				++dp;
2355f0dc9fbaSderaadt 			} else {
2356f0dc9fbaSderaadt 				while ((*dp = *cp++) != '"') {
23572059035fStedu 					if (*dp != '\0')
23582059035fStedu 						++dp;
23592059035fStedu 					else {
2360f0dc9fbaSderaadt 						error("Odd number of quotation marks");
2361b3cc38c2Stedu 						exit(EXIT_FAILURE);
23622059035fStedu 					}
2363f0dc9fbaSderaadt 				}
2364f0dc9fbaSderaadt 			}
23652059035fStedu 		} while (*cp != '\0' && *cp != '#' &&
23662059035fStedu 		    (!isascii((unsigned char)*cp) || !isspace((unsigned char)*cp)));
23672059035fStedu 		if (isascii((unsigned char)*cp) && isspace((unsigned char)*cp))
23682059035fStedu 			++cp;
23692059035fStedu 		*dp = '\0';
23702059035fStedu 	}
23712059035fStedu 	array[nsubs] = NULL;
23722059035fStedu 	return array;
23732059035fStedu }
23742059035fStedu 
23752059035fStedu static long
2376738fad2fStedu oadd(long t1, long t2)
23772059035fStedu {
2378f0dc9fbaSderaadt 	long	t = t1 + t2;
23792059035fStedu 
23802059035fStedu 	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2381f0dc9fbaSderaadt 		error("time overflow");
23822059035fStedu 		exit(EXIT_FAILURE);
23832059035fStedu 	}
23842059035fStedu 	return t;
23852059035fStedu }
23862059035fStedu 
23872059035fStedu static zic_t
2388738fad2fStedu tadd(zic_t t1, long t2)
23892059035fStedu {
239066e58127Stedu 	zic_t	t;
23912059035fStedu 
23922059035fStedu 	if (t1 == max_time && t2 > 0)
23932059035fStedu 		return max_time;
23942059035fStedu 	if (t1 == min_time && t2 < 0)
23952059035fStedu 		return min_time;
23962059035fStedu 	t = t1 + t2;
23972059035fStedu 	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2398f0dc9fbaSderaadt 		error("time overflow");
23992059035fStedu 		exit(EXIT_FAILURE);
24002059035fStedu 	}
24012059035fStedu 	return t;
24022059035fStedu }
24032059035fStedu 
24042059035fStedu /*
24052059035fStedu ** Given a rule, and a year, compute the date - in seconds since January 1,
24062059035fStedu ** 1970, 00:00 LOCAL time - in that year that the rule refers to.
24072059035fStedu */
24082059035fStedu static zic_t
2409738fad2fStedu rpytime(const struct rule *rp, int wantedy)
24102059035fStedu {
241166e58127Stedu 	int	y, m, i;
241266e58127Stedu 	long	dayoff;			/* with a nod to Margaret O. */
241366e58127Stedu 	zic_t	t;
24142059035fStedu 
24152059035fStedu 	if (wantedy == INT_MIN)
24162059035fStedu 		return min_time;
24172059035fStedu 	if (wantedy == INT_MAX)
24182059035fStedu 		return max_time;
24192059035fStedu 	dayoff = 0;
24202059035fStedu 	m = TM_JANUARY;
24212059035fStedu 	y = EPOCH_YEAR;
24222059035fStedu 	while (wantedy != y) {
24232059035fStedu 		if (wantedy > y) {
24242059035fStedu 			i = len_years[isleap(y)];
24252059035fStedu 			++y;
24262059035fStedu 		} else {
24272059035fStedu 			--y;
24282059035fStedu 			i = -len_years[isleap(y)];
24292059035fStedu 		}
24302059035fStedu 		dayoff = oadd(dayoff, eitol(i));
24312059035fStedu 	}
24322059035fStedu 	while (m != rp->r_month) {
24332059035fStedu 		i = len_months[isleap(y)][m];
24342059035fStedu 		dayoff = oadd(dayoff, eitol(i));
24352059035fStedu 		++m;
24362059035fStedu 	}
24372059035fStedu 	i = rp->r_dayofmonth;
24382059035fStedu 	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
24392059035fStedu 		if (rp->r_dycode == DC_DOWLEQ)
24402059035fStedu 			--i;
24412059035fStedu 		else {
2442f0dc9fbaSderaadt 			error("use of 2/29 in non leap-year");
24432059035fStedu 			exit(EXIT_FAILURE);
24442059035fStedu 		}
24452059035fStedu 	}
24462059035fStedu 	--i;
24472059035fStedu 	dayoff = oadd(dayoff, eitol(i));
24482059035fStedu 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
244966e58127Stedu 		long	wday;
24502059035fStedu 
24512059035fStedu #define LDAYSPERWEEK	((long) DAYSPERWEEK)
24522059035fStedu 		wday = eitol(EPOCH_WDAY);
24532059035fStedu 		/*
24542059035fStedu 		** Don't trust mod of negative numbers.
24552059035fStedu 		*/
24562059035fStedu 		if (dayoff >= 0)
24572059035fStedu 			wday = (wday + dayoff) % LDAYSPERWEEK;
24582059035fStedu 		else {
24592059035fStedu 			wday -= ((-dayoff) % LDAYSPERWEEK);
24602059035fStedu 			if (wday < 0)
24612059035fStedu 				wday += LDAYSPERWEEK;
24622059035fStedu 		}
24632059035fStedu 		while (wday != eitol(rp->r_wday))
24642059035fStedu 			if (rp->r_dycode == DC_DOWGEQ) {
246575b1bb69Stedu 				dayoff = oadd(dayoff, 1);
24662059035fStedu 				if (++wday >= LDAYSPERWEEK)
24672059035fStedu 					wday = 0;
24682059035fStedu 				++i;
24692059035fStedu 			} else {
247075b1bb69Stedu 				dayoff = oadd(dayoff, -1);
24712059035fStedu 				if (--wday < 0)
24722059035fStedu 					wday = LDAYSPERWEEK - 1;
24732059035fStedu 				--i;
24742059035fStedu 			}
24752059035fStedu 	}
24762059035fStedu 	if (dayoff < min_time / SECSPERDAY)
24772059035fStedu 		return min_time;
24782059035fStedu 	if (dayoff > max_time / SECSPERDAY)
24792059035fStedu 		return max_time;
24802059035fStedu 	t = (zic_t) dayoff * SECSPERDAY;
24812059035fStedu 	return tadd(t, rp->r_tod);
24822059035fStedu }
24832059035fStedu 
24842059035fStedu static void
2485738fad2fStedu newabbr(const char *string)
24862059035fStedu {
248766e58127Stedu 	int	i;
24882059035fStedu 
24892059035fStedu 	if (strcmp(string, GRANDPARENTED) != 0) {
249066e58127Stedu 		const char *	cp;
249166e58127Stedu 		char *		wp;
24922059035fStedu 
24932059035fStedu 		cp = string;
24942059035fStedu 		wp = NULL;
24952059035fStedu 		while (isascii((unsigned char)*cp) &&
2496fcefcf04Smillert 		    (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
24972059035fStedu 			++cp;
24982059035fStedu 		if (noise && cp - string > 3)
2499fcefcf04Smillert 			wp = "time zone abbreviation has more than 3 characters";
25002059035fStedu 		if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
2501fcefcf04Smillert 			wp = "time zone abbreviation has too many characters";
25022059035fStedu 		if (*cp != '\0')
2503f0dc9fbaSderaadt 			wp = "time zone abbreviation differs from POSIX standard";
25042059035fStedu 		if (wp != NULL) {
25052059035fStedu 			wp = ecpyalloc(wp);
25062059035fStedu 			wp = ecatalloc(wp, " (");
25072059035fStedu 			wp = ecatalloc(wp, string);
25082059035fStedu 			wp = ecatalloc(wp, ")");
25092059035fStedu 			warning(wp);
25102059035fStedu 			free(wp);
25112059035fStedu 		}
25122059035fStedu 	}
25132059035fStedu 	i = strlen(string) + 1;
25142059035fStedu 	if (charcnt + i > TZ_MAX_CHARS) {
2515f0dc9fbaSderaadt 		error("too many, or too long, time zone abbreviations");
25162059035fStedu 		exit(EXIT_FAILURE);
25172059035fStedu 	}
251875b1bb69Stedu 	strlcpy(&chars[charcnt], string, sizeof(chars) - charcnt);
25192059035fStedu 	charcnt += eitol(i);
25202059035fStedu }
25212059035fStedu 
25222059035fStedu static int
2523738fad2fStedu mkdirs(char *argname)
25242059035fStedu {
252566e58127Stedu 	char *	name;
252666e58127Stedu 	char *	cp;
25272059035fStedu 
25282059035fStedu 	if (argname == NULL || *argname == '\0')
25292059035fStedu 		return 0;
25302059035fStedu 	cp = name = ecpyalloc(argname);
25312059035fStedu 	while ((cp = strchr(cp + 1, '/')) != 0) {
25322059035fStedu 		*cp = '\0';
25332059035fStedu 		if (!itsdir(name)) {
25342059035fStedu 			/*
25352059035fStedu 			** It doesn't seem to exist, so we try to create it.
25362059035fStedu 			** Creation may fail because of the directory being
25372059035fStedu 			** created by some other multiprocessor, so we get
25382059035fStedu 			** to do extra checking.
25392059035fStedu 			*/
25402059035fStedu 			if (mkdir(name, MKDIR_UMASK) != 0) {
25412059035fStedu 				const char *e = strerror(errno);
25422059035fStedu 
25432059035fStedu 				if (errno != EEXIST || !itsdir(name)) {
254475b1bb69Stedu 					fprintf(stderr,
2545f0dc9fbaSderaadt 					    "%s: Can't create directory %s: %s\n",
2546f0dc9fbaSderaadt 					    __progname, name, e);
25472059035fStedu 					free(name);
25482059035fStedu 					return -1;
25492059035fStedu 				}
25502059035fStedu 			}
25512059035fStedu 		}
25522059035fStedu 		*cp = '/';
25532059035fStedu 	}
25542059035fStedu 	free(name);
25552059035fStedu 	return 0;
25562059035fStedu }
25572059035fStedu 
25582059035fStedu static long
2559738fad2fStedu eitol(int i)
25602059035fStedu {
2561f0dc9fbaSderaadt 	long	l = i;
25622059035fStedu 
2563f0dc9fbaSderaadt 	if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
25644dc71ee2Stedu 		errx(1, "%d did not sign extend correctly", i);
25652059035fStedu 	return l;
25662059035fStedu }
2567