1192625Sedwin/*
2192625Sedwin** This file is in the public domain, so clarified as of
3192625Sedwin** 2006-07-17 by Arthur David Olson.
4192625Sedwin*/
52702Swollman
6214411Sedwinstatic const char	elsieid[] = "@(#)zic.c	8.22";
7192625Sedwin
830829Scharnier#ifndef lint
930829Scharnierstatic const char rcsid[] =
1050479Speter  "$FreeBSD: stable/11/contrib/tzcode/zic/zic.c 307358 2016-10-15 12:37:57Z bapt $";
1130829Scharnier#endif /* not lint */
1230829Scharnier
132702Swollman#include "private.h"
142702Swollman#include "tzfile.h"
1530829Scharnier#include <err.h>
1630829Scharnier#include <locale.h>
179937Swollman#include <sys/stat.h>			/* for umask manifest constants */
189937Swollman#include <sys/types.h>
199937Swollman#include <unistd.h>
202702Swollman
21192625Sedwin#define	ZIC_VERSION	'2'
22192625Sedwin
23192625Sedwintypedef int_fast64_t	zic_t;
24192625Sedwin
25192625Sedwin#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
26192625Sedwin#define ZIC_MAX_ABBR_LEN_WO_WARN	6
27192625Sedwin#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
28192625Sedwin
29130819Sstefanf#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
30130819Sstefanf
3117214Swollman/*
3217214Swollman** On some ancient hosts, predicates like `isspace(C)' are defined
33192625Sedwin** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
3417214Swollman** which says they are defined only if C == ((unsigned char) C) || C == EOF.
3569315Scharnier** Neither the C Standard nor POSIX require that `isascii' exist.
3617214Swollman** For portability, we check both ancient and modern requirements.
3717214Swollman** If isascii is not defined, the isascii check succeeds trivially.
3817214Swollman*/
3917214Swollman#include "ctype.h"
4017214Swollman#ifndef isascii
4117214Swollman#define isascii(x) 1
4217214Swollman#endif
4317214Swollman
44192625Sedwin#define OFFSET_STRLEN_MAXIMUM	(7 + INT_STRLEN_MAXIMUM(long))
45192625Sedwin#define RULE_STRLEN_MAXIMUM	8	/* "Mdd.dd.d" */
46192625Sedwin
47192625Sedwin#define end(cp)	(strchr((cp), '\0'))
48192625Sedwin
492702Swollmanstruct rule {
502702Swollman	const char *	r_filename;
512702Swollman	int		r_linenum;
522702Swollman	const char *	r_name;
532702Swollman
542702Swollman	int		r_loyear;	/* for example, 1986 */
552702Swollman	int		r_hiyear;	/* for example, 1986 */
562702Swollman	const char *	r_yrtype;
57192625Sedwin	int		r_lowasnum;
58192625Sedwin	int		r_hiwasnum;
592702Swollman
602702Swollman	int		r_month;	/* 0..11 */
612702Swollman
622702Swollman	int		r_dycode;	/* see below */
632702Swollman	int		r_dayofmonth;
642702Swollman	int		r_wday;
652702Swollman
662702Swollman	long		r_tod;		/* time from midnight */
672702Swollman	int		r_todisstd;	/* above is standard time if TRUE */
682702Swollman					/* or wall clock time if FALSE */
699937Swollman	int		r_todisgmt;	/* above is GMT if TRUE */
709937Swollman					/* or local time if FALSE */
712702Swollman	long		r_stdoff;	/* offset from standard time */
722702Swollman	const char *	r_abbrvar;	/* variable part of abbreviation */
732702Swollman
742702Swollman	int		r_todo;		/* a rule to do (used in outzone) */
75192625Sedwin	zic_t		r_temp;		/* used in outzone */
762702Swollman};
772702Swollman
782702Swollman/*
792702Swollman**	r_dycode		r_dayofmonth	r_wday
802702Swollman*/
812702Swollman
822702Swollman#define DC_DOM		0	/* 1..31 */	/* unused */
832702Swollman#define DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */
842702Swollman#define DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */
852702Swollman
862702Swollmanstruct zone {
872702Swollman	const char *	z_filename;
882702Swollman	int		z_linenum;
892702Swollman
902702Swollman	const char *	z_name;
912702Swollman	long		z_gmtoff;
922702Swollman	const char *	z_rule;
932702Swollman	const char *	z_format;
942702Swollman
952702Swollman	long		z_stdoff;
962702Swollman
972702Swollman	struct rule *	z_rules;
982702Swollman	int		z_nrules;
992702Swollman
1002702Swollman	struct rule	z_untilrule;
101192625Sedwin	zic_t		z_untiltime;
1022702Swollman};
1032702Swollman
104192625Sedwinstatic void	addtt(zic_t starttime, int type);
105192625Sedwinstatic int	addtype(long gmtoff, const char * abbr, int isdst,
106192625Sedwin				int ttisstd, int ttisgmt);
107192625Sedwinstatic void	leapadd(zic_t t, int positive, int rolling, int count);
108192625Sedwinstatic void	adjleap(void);
109192625Sedwinstatic void	associate(void);
110192625Sedwinstatic int	ciequal(const char * ap, const char * bp);
111192625Sedwinstatic void	convert(long val, char * buf);
112192625Sedwinstatic void	convert64(zic_t val, char * buf);
113192625Sedwinstatic void	dolink(const char * fromfield, const char * tofield);
114192625Sedwinstatic void	doabbr(char * abbr, const char * format,
115192625Sedwin			const char * letters, int isdst, int doquotes);
116192625Sedwinstatic void	eat(const char * name, int num);
117192625Sedwinstatic void	eats(const char * name, int num,
118192625Sedwin			const char * rname, int rnum);
119192625Sedwinstatic long	eitol(int i);
120192625Sedwinstatic void	error(const char * message);
121192625Sedwinstatic char **	getfields(char * buf);
122192625Sedwinstatic long	gethms(const char * string, const char * errstrng,
123192625Sedwin			int signable);
124192625Sedwinstatic void	infile(const char * filename);
125192625Sedwinstatic void	inleap(char ** fields, int nfields);
126192625Sedwinstatic void	inlink(char ** fields, int nfields);
127192625Sedwinstatic void	inrule(char ** fields, int nfields);
128192625Sedwinstatic int	inzcont(char ** fields, int nfields);
129192625Sedwinstatic int	inzone(char ** fields, int nfields);
130192625Sedwinstatic int	inzsub(char ** fields, int nfields, int iscont);
131192625Sedwinstatic int	is32(zic_t x);
132192625Sedwinstatic int	itsabbr(const char * abbr, const char * word);
133192625Sedwinstatic int	itsdir(const char * name);
134192625Sedwinstatic int	lowerit(int c);
135192625Sedwinstatic char *	memcheck(char * tocheck);
136192625Sedwinstatic int	mkdirs(char * filename);
137192625Sedwinstatic void	newabbr(const char * abbr);
138192625Sedwinstatic long	oadd(long t1, long t2);
139192625Sedwinstatic void	outzone(const struct zone * zp, int ntzones);
140192625Sedwinstatic void	puttzcode(long code, FILE * fp);
141192625Sedwinstatic void	puttzcode64(zic_t code, FILE * fp);
142192625Sedwinstatic int	rcomp(const void * leftp, const void * rightp);
143192625Sedwinstatic zic_t	rpytime(const struct rule * rp, int wantedy);
144192625Sedwinstatic void	rulesub(struct rule * rp,
1459937Swollman			const char * loyearp, const char * hiyearp,
1469937Swollman			const char * typep, const char * monthp,
147192625Sedwin			const char * dayp, const char * timep);
148192625Sedwinstatic int 	stringoffset(char * result, long offset);
149192625Sedwinstatic int	stringrule(char * result, const struct rule * rp,
150192625Sedwin			long dstoff, long gmtoff);
151192625Sedwinstatic void 	stringzone(char * result,
152192625Sedwin			const struct zone * zp, int ntzones);
153192625Sedwinstatic void	setboundaries(void);
154192625Sedwinstatic void	setgroup(gid_t *flag, const char *name);
155192625Sedwinstatic void	setuser(uid_t *flag, const char *name);
156192625Sedwinstatic zic_t	tadd(zic_t t1, long t2);
157192625Sedwinstatic void	usage(FILE *stream, int status);
158192625Sedwinstatic void	writezone(const char * name, const char * string);
159192625Sedwinstatic int	yearistype(int year, const char * type);
1602702Swollman
1612702Swollmanstatic int		charcnt;
1622702Swollmanstatic int		errors;
1632702Swollmanstatic const char *	filename;
1642702Swollmanstatic int		leapcnt;
165192625Sedwinstatic int		leapseen;
166192625Sedwinstatic int		leapminyear;
167192625Sedwinstatic int		leapmaxyear;
1682702Swollmanstatic int		linenum;
169192625Sedwinstatic int		max_abbrvar_len;
170192625Sedwinstatic int		max_format_len;
171192625Sedwinstatic zic_t		max_time;
1722702Swollmanstatic int		max_year;
173192625Sedwinstatic zic_t		min_time;
1742702Swollmanstatic int		min_year;
175192625Sedwinstatic zic_t		min_time;
1762702Swollmanstatic int		noise;
1772702Swollmanstatic const char *	rfilename;
1782702Swollmanstatic int		rlinenum;
1792702Swollmanstatic int		timecnt;
1802702Swollmanstatic int		typecnt;
1812702Swollman
1822702Swollman/*
1832702Swollman** Line codes.
1842702Swollman*/
1852702Swollman
1862702Swollman#define LC_RULE		0
1872702Swollman#define LC_ZONE		1
1882702Swollman#define LC_LINK		2
1892702Swollman#define LC_LEAP		3
1902702Swollman
1912702Swollman/*
1922702Swollman** Which fields are which on a Zone line.
1932702Swollman*/
1942702Swollman
1952702Swollman#define ZF_NAME		1
1962702Swollman#define ZF_GMTOFF	2
1972702Swollman#define ZF_RULE		3
1982702Swollman#define ZF_FORMAT	4
1992702Swollman#define ZF_TILYEAR	5
2002702Swollman#define ZF_TILMONTH	6
2012702Swollman#define ZF_TILDAY	7
2022702Swollman#define ZF_TILTIME	8
2032702Swollman#define ZONE_MINFIELDS	5
2042702Swollman#define ZONE_MAXFIELDS	9
2052702Swollman
2062702Swollman/*
2072702Swollman** Which fields are which on a Zone continuation line.
2082702Swollman*/
2092702Swollman
2102702Swollman#define ZFC_GMTOFF	0
2112702Swollman#define ZFC_RULE	1
2122702Swollman#define ZFC_FORMAT	2
2132702Swollman#define ZFC_TILYEAR	3
2142702Swollman#define ZFC_TILMONTH	4
2152702Swollman#define ZFC_TILDAY	5
2162702Swollman#define ZFC_TILTIME	6
2172702Swollman#define ZONEC_MINFIELDS	3
2182702Swollman#define ZONEC_MAXFIELDS	7
2192702Swollman
2202702Swollman/*
2212702Swollman** Which files are which on a Rule line.
2222702Swollman*/
2232702Swollman
2242702Swollman#define RF_NAME		1
2252702Swollman#define RF_LOYEAR	2
2262702Swollman#define RF_HIYEAR	3
2272702Swollman#define RF_COMMAND	4
2282702Swollman#define RF_MONTH	5
2292702Swollman#define RF_DAY		6
2302702Swollman#define RF_TOD		7
2312702Swollman#define RF_STDOFF	8
2322702Swollman#define RF_ABBRVAR	9
2332702Swollman#define RULE_FIELDS	10
2342702Swollman
2352702Swollman/*
2362702Swollman** Which fields are which on a Link line.
2372702Swollman*/
2382702Swollman
2392702Swollman#define LF_FROM		1
2402702Swollman#define LF_TO		2
2412702Swollman#define LINK_FIELDS	3
2422702Swollman
2432702Swollman/*
2442702Swollman** Which fields are which on a Leap line.
2452702Swollman*/
2462702Swollman
2472702Swollman#define LP_YEAR		1
2482702Swollman#define LP_MONTH	2
2492702Swollman#define LP_DAY		3
2502702Swollman#define LP_TIME		4
2512702Swollman#define LP_CORR		5
2522702Swollman#define LP_ROLL		6
2532702Swollman#define LEAP_FIELDS	7
2542702Swollman
2552702Swollman/*
2562702Swollman** Year synonyms.
2572702Swollman*/
2582702Swollman
2592702Swollman#define YR_MINIMUM	0
2602702Swollman#define YR_MAXIMUM	1
2612702Swollman#define YR_ONLY		2
2622702Swollman
2632702Swollmanstatic struct rule *	rules;
2642702Swollmanstatic int		nrules;	/* number of rules */
2652702Swollman
2662702Swollmanstatic struct zone *	zones;
2672702Swollmanstatic int		nzones;	/* number of zones */
2682702Swollman
2692702Swollmanstruct link {
2702702Swollman	const char *	l_filename;
2712702Swollman	int		l_linenum;
2722702Swollman	const char *	l_from;
2732702Swollman	const char *	l_to;
2742702Swollman};
2752702Swollman
2762702Swollmanstatic struct link *	links;
2772702Swollmanstatic int		nlinks;
2782702Swollman
2792702Swollmanstruct lookup {
2802702Swollman	const char *	l_word;
2812702Swollman	const int	l_value;
2822702Swollman};
2832702Swollman
284192625Sedwinstatic struct lookup const *	byword(const char * string,
285192625Sedwin					const struct lookup * lp);
2862702Swollman
2872702Swollmanstatic struct lookup const	line_codes[] = {
2882702Swollman	{ "Rule",	LC_RULE },
2892702Swollman	{ "Zone",	LC_ZONE },
2902702Swollman	{ "Link",	LC_LINK },
2912702Swollman	{ "Leap",	LC_LEAP },
2922702Swollman	{ NULL,		0}
2932702Swollman};
2942702Swollman
2952702Swollmanstatic struct lookup const	mon_names[] = {
2962702Swollman	{ "January",	TM_JANUARY },
2972702Swollman	{ "February",	TM_FEBRUARY },
2982702Swollman	{ "March",	TM_MARCH },
2992702Swollman	{ "April",	TM_APRIL },
3002702Swollman	{ "May",	TM_MAY },
3012702Swollman	{ "June",	TM_JUNE },
3022702Swollman	{ "July",	TM_JULY },
3032702Swollman	{ "August",	TM_AUGUST },
3042702Swollman	{ "September",	TM_SEPTEMBER },
3052702Swollman	{ "October",	TM_OCTOBER },
3062702Swollman	{ "November",	TM_NOVEMBER },
3072702Swollman	{ "December",	TM_DECEMBER },
3082702Swollman	{ NULL,		0 }
3092702Swollman};
3102702Swollman
3112702Swollmanstatic struct lookup const	wday_names[] = {
3122702Swollman	{ "Sunday",	TM_SUNDAY },
3132702Swollman	{ "Monday",	TM_MONDAY },
3142702Swollman	{ "Tuesday",	TM_TUESDAY },
3152702Swollman	{ "Wednesday",	TM_WEDNESDAY },
3162702Swollman	{ "Thursday",	TM_THURSDAY },
3172702Swollman	{ "Friday",	TM_FRIDAY },
3182702Swollman	{ "Saturday",	TM_SATURDAY },
3192702Swollman	{ NULL,		0 }
3202702Swollman};
3212702Swollman
3222702Swollmanstatic struct lookup const	lasts[] = {
3232702Swollman	{ "last-Sunday",	TM_SUNDAY },
3242702Swollman	{ "last-Monday",	TM_MONDAY },
3252702Swollman	{ "last-Tuesday",	TM_TUESDAY },
3262702Swollman	{ "last-Wednesday",	TM_WEDNESDAY },
3272702Swollman	{ "last-Thursday",	TM_THURSDAY },
3282702Swollman	{ "last-Friday",	TM_FRIDAY },
3292702Swollman	{ "last-Saturday",	TM_SATURDAY },
3302702Swollman	{ NULL,			0 }
3312702Swollman};
3322702Swollman
3332702Swollmanstatic struct lookup const	begin_years[] = {
3342702Swollman	{ "minimum",	YR_MINIMUM },
3352702Swollman	{ "maximum",	YR_MAXIMUM },
3362702Swollman	{ NULL,		0 }
3372702Swollman};
3382702Swollman
3392702Swollmanstatic struct lookup const	end_years[] = {
3402702Swollman	{ "minimum",	YR_MINIMUM },
3412702Swollman	{ "maximum",	YR_MAXIMUM },
3422702Swollman	{ "only",	YR_ONLY },
3432702Swollman	{ NULL,		0 }
3442702Swollman};
3452702Swollman
3462702Swollmanstatic struct lookup const	leap_types[] = {
3472702Swollman	{ "Rolling",	TRUE },
3482702Swollman	{ "Stationary",	FALSE },
3492702Swollman	{ NULL,		0 }
3502702Swollman};
3512702Swollman
3522702Swollmanstatic const int	len_months[2][MONSPERYEAR] = {
3532702Swollman	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
3542702Swollman	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
3552702Swollman};
3562702Swollman
3572702Swollmanstatic const int	len_years[2] = {
3582702Swollman	DAYSPERNYEAR, DAYSPERLYEAR
3592702Swollman};
3602702Swollman
36117214Swollmanstatic struct attype {
362192625Sedwin	zic_t		at;
36317214Swollman	unsigned char	type;
36417214Swollman}			attypes[TZ_MAX_TIMES];
3652702Swollmanstatic long		gmtoffs[TZ_MAX_TYPES];
3662702Swollmanstatic char		isdsts[TZ_MAX_TYPES];
3672702Swollmanstatic unsigned char	abbrinds[TZ_MAX_TYPES];
3682702Swollmanstatic char		ttisstds[TZ_MAX_TYPES];
3699937Swollmanstatic char		ttisgmts[TZ_MAX_TYPES];
3702702Swollmanstatic char		chars[TZ_MAX_CHARS];
371192625Sedwinstatic zic_t		trans[TZ_MAX_LEAPS];
3722702Swollmanstatic long		corr[TZ_MAX_LEAPS];
3732702Swollmanstatic char		roll[TZ_MAX_LEAPS];
3742702Swollman
3752702Swollman/*
3762702Swollman** Memory allocation.
3772702Swollman*/
3782702Swollman
3792702Swollmanstatic char *
3802702Swollmanmemcheck(ptr)
3812702Swollmanchar * const	ptr;
3822702Swollman{
38330829Scharnier	if (ptr == NULL)
38430829Scharnier		errx(EXIT_FAILURE, _("memory exhausted"));
3852702Swollman	return ptr;
3862702Swollman}
3872702Swollman
3882702Swollman#define emalloc(size)		memcheck(imalloc(size))
3899937Swollman#define erealloc(ptr, size)	memcheck(irealloc((ptr), (size)))
3902702Swollman#define ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
3919937Swollman#define ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
3922702Swollman
3932702Swollman/*
3942702Swollman** Error handling.
3952702Swollman*/
3962702Swollman
3972702Swollmanstatic void
3982702Swollmaneats(name, num, rname, rnum)
3992702Swollmanconst char * const	name;
4002702Swollmanconst int		num;
4012702Swollmanconst char * const	rname;
4022702Swollmanconst int		rnum;
4032702Swollman{
4042702Swollman	filename = name;
4052702Swollman	linenum = num;
4062702Swollman	rfilename = rname;
4072702Swollman	rlinenum = rnum;
4082702Swollman}
4092702Swollman
4102702Swollmanstatic void
4112702Swollmaneat(name, num)
4122702Swollmanconst char * const	name;
4132702Swollmanconst int		num;
4142702Swollman{
4152702Swollman	eats(name, num, (char *) NULL, -1);
4162702Swollman}
4172702Swollman
4182702Swollmanstatic void
4192702Swollmanerror(string)
4202702Swollmanconst char * const	string;
4212702Swollman{
4222702Swollman	/*
4232702Swollman	** Match the format of "cc" to allow sh users to
4249937Swollman	**	zic ... 2>&1 | error -t "*" -v
4252702Swollman	** on BSD systems.
4262702Swollman	*/
42717214Swollman	(void) fprintf(stderr, _("\"%s\", line %d: %s"),
4282702Swollman		filename, linenum, string);
4292702Swollman	if (rfilename != NULL)
43017214Swollman		(void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
4312702Swollman			rfilename, rlinenum);
4322702Swollman	(void) fprintf(stderr, "\n");
4332702Swollman	++errors;
4342702Swollman}
4352702Swollman
4362702Swollmanstatic void
43717214Swollmanwarning(string)
43817214Swollmanconst char * const	string;
43917214Swollman{
44017214Swollman	char *	cp;
44117214Swollman
44242997Swollman	cp = ecpyalloc(_("warning: "));
44317214Swollman	cp = ecatalloc(cp, string);
44442997Swollman	error(cp);
44517214Swollman	ifree(cp);
44617214Swollman	--errors;
44717214Swollman}
44817214Swollman
44917214Swollmanstatic void
450192625Sedwinusage(FILE *stream, int status)
451192625Sedwin  {
452192625Sedwin	(void) fprintf(stream, _("usage is zic \
453192625Sedwin[ --version ] [--help] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
454192625Sedwin\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\
455192625Sedwin\n\
456192625SedwinReport bugs to tz@elsie.nci.nih.gov.\n"));
457192625Sedwin	exit(status);
4582702Swollman}
4592702Swollman
4602702Swollmanstatic const char *	psxrules;
4612702Swollmanstatic const char *	lcltime;
4622702Swollmanstatic const char *	directory;
4632702Swollmanstatic const char *	leapsec;
4642702Swollmanstatic const char *	yitcommand;
46542997Swollmanstatic int		Dflag;
46643006Swollmanstatic uid_t		uflag = (uid_t)-1;
46742997Swollmanstatic gid_t		gflag = (gid_t)-1;
46842997Swollmanstatic mode_t		mflag = (S_IRUSR | S_IRGRP | S_IROTH
46942997Swollman				 | S_IWUSR);
4702702Swollman
4712702Swollmanint
4722702Swollmanmain(argc, argv)
4732702Swollmanint	argc;
4742702Swollmanchar *	argv[];
4752702Swollman{
4769937Swollman	register int	i;
4779937Swollman	register int	j;
4782702Swollman	register int	c;
4792702Swollman
4802702Swollman#ifdef unix
4819937Swollman	(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
4822702Swollman#endif /* defined unix */
483192625Sedwin#if HAVE_GETTEXT
484192625Sedwin	(void) setlocale(LC_ALL, "");
48517214Swollman#ifdef TZ_DOMAINDIR
48617214Swollman	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
48717214Swollman#endif /* defined TEXTDOMAINDIR */
48817214Swollman	(void) textdomain(TZ_DOMAIN);
489192625Sedwin#endif /* HAVE_GETTEXT */
490192625Sedwin	if (TYPE_BIT(zic_t) < 64) {
491192625Sedwin		(void) fprintf(stderr, "zic: %s\n",
492192625Sedwin			_("wild compilation-time specification of zic_t"));
493192625Sedwin		exit(EXIT_FAILURE);
494192625Sedwin	}
495130819Sstefanf	for (i = 1; i < argc; ++i)
496130819Sstefanf		if (strcmp(argv[i], "--version") == 0) {
497130819Sstefanf			errx(EXIT_SUCCESS, "%s", elsieid);
498192625Sedwin 		} else if (strcmp(argv[i], "--help") == 0) {
499192625Sedwin 			usage(stdout, EXIT_SUCCESS);
500130819Sstefanf		}
50142997Swollman	while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
5022702Swollman		switch (c) {
5032702Swollman			default:
504192625Sedwin				usage(stderr, EXIT_FAILURE);
50542997Swollman			case 'D':
50642997Swollman				Dflag = 1;
50742997Swollman				break;
5082702Swollman			case 'd':
5092702Swollman				if (directory == NULL)
5102702Swollman					directory = optarg;
51130829Scharnier				else
51230829Scharnier					errx(EXIT_FAILURE,
51330829Scharnier_("more than one -d option specified"));
5142702Swollman				break;
51542997Swollman			case 'g':
51642997Swollman				setgroup(&gflag, optarg);
51742997Swollman				break;
5182702Swollman			case 'l':
5192702Swollman				if (lcltime == NULL)
5202702Swollman					lcltime = optarg;
52130829Scharnier				else
52230829Scharnier					errx(EXIT_FAILURE,
52330829Scharnier_("more than one -l option specified"));
5242702Swollman				break;
52542997Swollman			case 'm':
52642997Swollman			{
52742997Swollman				void *set = setmode(optarg);
528111414Smikeh				if (set == NULL)
529111414Smikeh					errx(EXIT_FAILURE,
530111414Smikeh_("invalid file mode"));
531136708Sru				mflag = getmode(set, mflag);
532111414Smikeh				free(set);
53342997Swollman				break;
53442997Swollman			}
5352702Swollman			case 'p':
5362702Swollman				if (psxrules == NULL)
5372702Swollman					psxrules = optarg;
53830829Scharnier				else
53930829Scharnier					errx(EXIT_FAILURE,
54030829Scharnier_("more than one -p option specified"));
5412702Swollman				break;
54242997Swollman			case 'u':
54342997Swollman				setuser(&uflag, optarg);
54442997Swollman				break;
5452702Swollman			case 'y':
5462702Swollman				if (yitcommand == NULL)
5472702Swollman					yitcommand = optarg;
54830829Scharnier				else
54930829Scharnier					errx(EXIT_FAILURE,
55030829Scharnier_("more than one -y option specified"));
5512702Swollman				break;
5522702Swollman			case 'L':
5532702Swollman				if (leapsec == NULL)
5542702Swollman					leapsec = optarg;
55530829Scharnier				else
55630829Scharnier					errx(EXIT_FAILURE,
55730829Scharnier_("more than one -L option specified"));
5582702Swollman				break;
5592702Swollman			case 'v':
5602702Swollman				noise = TRUE;
5612702Swollman				break;
5622702Swollman			case 's':
563192625Sedwin				(void) printf("zic: -s ignored\n");
5642702Swollman				break;
5652702Swollman		}
5662702Swollman	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
567192625Sedwin		usage(stderr, EXIT_FAILURE);	/* usage message by request */
5682702Swollman	if (directory == NULL)
5692702Swollman		directory = TZDIR;
5702702Swollman	if (yitcommand == NULL)
5712702Swollman		yitcommand = "yearistype";
5722702Swollman
5732702Swollman	setboundaries();
5742702Swollman
5752702Swollman	if (optind < argc && leapsec != NULL) {
5762702Swollman		infile(leapsec);
5772702Swollman		adjleap();
5782702Swollman	}
5792702Swollman
5802702Swollman	for (i = optind; i < argc; ++i)
5812702Swollman		infile(argv[i]);
5822702Swollman	if (errors)
583192625Sedwin		exit(EXIT_FAILURE);
5842702Swollman	associate();
5852702Swollman	for (i = 0; i < nzones; i = j) {
5862702Swollman		/*
5872702Swollman		** Find the next non-continuation zone entry.
5882702Swollman		*/
5892702Swollman		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
5902702Swollman			continue;
5912702Swollman		outzone(&zones[i], j - i);
5922702Swollman	}
5932702Swollman	/*
5942702Swollman	** Make links.
5952702Swollman	*/
596130819Sstefanf	for (i = 0; i < nlinks; ++i) {
597130819Sstefanf		eat(links[i].l_filename, links[i].l_linenum);
5982702Swollman		dolink(links[i].l_from, links[i].l_to);
599192625Sedwin		if (noise)
600192625Sedwin			for (j = 0; j < nlinks; ++j)
601192625Sedwin				if (strcmp(links[i].l_to,
602192625Sedwin					links[j].l_from) == 0)
603192625Sedwin						warning(_("link to link"));
604130819Sstefanf	}
605130819Sstefanf	if (lcltime != NULL) {
606130819Sstefanf		eat("command line", 1);
6072702Swollman		dolink(lcltime, TZDEFAULT);
608130819Sstefanf	}
609130819Sstefanf	if (psxrules != NULL) {
610130819Sstefanf		eat("command line", 1);
6112702Swollman		dolink(psxrules, TZDEFRULES);
612130819Sstefanf	}
6132702Swollman	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
6142702Swollman}
6152702Swollman
6162702Swollmanstatic void
617192625Sedwindolink(fromfield, tofield)
618192625Sedwinconst char * const	fromfield;
619192625Sedwinconst char * const	tofield;
6202702Swollman{
6212702Swollman	register char *	fromname;
6222702Swollman	register char *	toname;
6232702Swollman
624192625Sedwin	if (fromfield[0] == '/')
625192625Sedwin		fromname = ecpyalloc(fromfield);
6262702Swollman	else {
6272702Swollman		fromname = ecpyalloc(directory);
6282702Swollman		fromname = ecatalloc(fromname, "/");
629192625Sedwin		fromname = ecatalloc(fromname, fromfield);
6302702Swollman	}
631192625Sedwin	if (tofield[0] == '/')
632192625Sedwin		toname = ecpyalloc(tofield);
6332702Swollman	else {
6342702Swollman		toname = ecpyalloc(directory);
6352702Swollman		toname = ecatalloc(toname, "/");
636192625Sedwin		toname = ecatalloc(toname, tofield);
6372702Swollman	}
6382702Swollman	/*
6392702Swollman	** We get to be careful here since
6402702Swollman	** there's a fair chance of root running us.
6412702Swollman	*/
6422702Swollman	if (!itsdir(toname))
6432702Swollman		(void) remove(toname);
6442702Swollman	if (link(fromname, toname) != 0) {
64542997Swollman		int	result;
64642997Swollman
6472702Swollman		if (mkdirs(toname) != 0)
648192625Sedwin			exit(EXIT_FAILURE);
649130819Sstefanf
65042997Swollman		result = link(fromname, toname);
651192625Sedwin#if HAVE_SYMLINK
652130819Sstefanf		if (result != 0 &&
653192625Sedwin			access(fromname, F_OK) == 0 &&
654192625Sedwin			!itsdir(fromname)) {
655192625Sedwin				const char *s = tofield;
656192625Sedwin				register char * symlinkcontents = NULL;
657192625Sedwin				while ((s = strchr(s+1, '/')) != NULL)
658192625Sedwin					symlinkcontents =
659192625Sedwin						ecatalloc(symlinkcontents,
660192625Sedwin						"../");
661192625Sedwin				symlinkcontents =
662192625Sedwin					ecatalloc(symlinkcontents,
663192625Sedwin					fromname);
664192625Sedwin				result =
665192625Sedwin					symlink(symlinkcontents,
666192625Sedwin					toname);
667192625Sedwin				if (result == 0)
66842997Swollmanwarning(_("hard link failed, symbolic link used"));
669192625Sedwin				ifree(symlinkcontents);
67042997Swollman		}
671192625Sedwin#endif /* HAVE_SYMLINK */
67242997Swollman		if (result != 0) {
67343774Swollman			err(EXIT_FAILURE, _("can't link from %s to %s"),
67442997Swollman			    fromname, toname);
67542997Swollman		}
6762702Swollman	}
6779937Swollman	ifree(fromname);
6789937Swollman	ifree(toname);
6792702Swollman}
6802702Swollman
681192625Sedwin#define TIME_T_BITS_IN_FILE	64
68217214Swollman
6832702Swollmanstatic void
684192625Sedwinsetboundaries (void)
6852702Swollman{
686192625Sedwin	register int	i;
687192625Sedwin
688192625Sedwin	min_time = -1;
689192625Sedwin	for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
690192625Sedwin		min_time *= 2;
691192625Sedwin	max_time = -(min_time + 1);
6922702Swollman}
6932702Swollman
6942702Swollmanstatic int
6952702Swollmanitsdir(name)
6962702Swollmanconst char * const	name;
6972702Swollman{
6982702Swollman	register char *	myname;
6992702Swollman	register int	accres;
7002702Swollman
7012702Swollman	myname = ecpyalloc(name);
7022702Swollman	myname = ecatalloc(myname, "/.");
7039937Swollman	accres = access(myname, F_OK);
7042702Swollman	ifree(myname);
7052702Swollman	return accres == 0;
7062702Swollman}
7072702Swollman
7082702Swollman/*
7092702Swollman** Associate sets of rules with zones.
7102702Swollman*/
7112702Swollman
7122702Swollman/*
7132702Swollman** Sort by rule name.
7142702Swollman*/
7152702Swollman
7162702Swollmanstatic int
7172702Swollmanrcomp(cp1, cp2)
7189937Swollmanconst void *	cp1;
7199937Swollmanconst void *	cp2;
7202702Swollman{
7219937Swollman	return strcmp(((const struct rule *) cp1)->r_name,
7229937Swollman		((const struct rule *) cp2)->r_name);
7232702Swollman}
7242702Swollman
7252702Swollmanstatic void
726192625Sedwinassociate(void)
7272702Swollman{
7282702Swollman	register struct zone *	zp;
7292702Swollman	register struct rule *	rp;
7302702Swollman	register int		base, out;
73117214Swollman	register int		i, j;
7322702Swollman
73317214Swollman	if (nrules != 0) {
7349937Swollman		(void) qsort((void *) rules, (size_t) nrules,
7359937Swollman			(size_t) sizeof *rules, rcomp);
73617214Swollman		for (i = 0; i < nrules - 1; ++i) {
73717214Swollman			if (strcmp(rules[i].r_name,
73817214Swollman				rules[i + 1].r_name) != 0)
73917214Swollman					continue;
74017214Swollman			if (strcmp(rules[i].r_filename,
74117214Swollman				rules[i + 1].r_filename) == 0)
74217214Swollman					continue;
74317214Swollman			eat(rules[i].r_filename, rules[i].r_linenum);
74417214Swollman			warning(_("same rule name in multiple files"));
74517214Swollman			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
74617214Swollman			warning(_("same rule name in multiple files"));
74717214Swollman			for (j = i + 2; j < nrules; ++j) {
74817214Swollman				if (strcmp(rules[i].r_name,
74917214Swollman					rules[j].r_name) != 0)
75017214Swollman						break;
75117214Swollman				if (strcmp(rules[i].r_filename,
75217214Swollman					rules[j].r_filename) == 0)
75317214Swollman						continue;
75417214Swollman				if (strcmp(rules[i + 1].r_filename,
75517214Swollman					rules[j].r_filename) == 0)
75617214Swollman						continue;
75717214Swollman				break;
75817214Swollman			}
75917214Swollman			i = j - 1;
76017214Swollman		}
76117214Swollman	}
7622702Swollman	for (i = 0; i < nzones; ++i) {
7632702Swollman		zp = &zones[i];
7642702Swollman		zp->z_rules = NULL;
7652702Swollman		zp->z_nrules = 0;
7662702Swollman	}
7672702Swollman	for (base = 0; base < nrules; base = out) {
7682702Swollman		rp = &rules[base];
7692702Swollman		for (out = base + 1; out < nrules; ++out)
7702702Swollman			if (strcmp(rp->r_name, rules[out].r_name) != 0)
7712702Swollman				break;
7722702Swollman		for (i = 0; i < nzones; ++i) {
7732702Swollman			zp = &zones[i];
7742702Swollman			if (strcmp(zp->z_rule, rp->r_name) != 0)
7752702Swollman				continue;
7762702Swollman			zp->z_rules = rp;
7772702Swollman			zp->z_nrules = out - base;
7782702Swollman		}
7792702Swollman	}
7802702Swollman	for (i = 0; i < nzones; ++i) {
7812702Swollman		zp = &zones[i];
7822702Swollman		if (zp->z_nrules == 0) {
7832702Swollman			/*
7842702Swollman			** Maybe we have a local standard time offset.
7852702Swollman			*/
7862702Swollman			eat(zp->z_filename, zp->z_linenum);
78717214Swollman			zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
788192625Sedwin				TRUE);
7892702Swollman			/*
7902702Swollman			** Note, though, that if there's no rule,
7912702Swollman			** a '%s' in the format is a bad thing.
7922702Swollman			*/
7932702Swollman			if (strchr(zp->z_format, '%') != 0)
79417214Swollman				error(_("%s in ruleless zone"));
7952702Swollman		}
7962702Swollman	}
7972702Swollman	if (errors)
798192625Sedwin		exit(EXIT_FAILURE);
7992702Swollman}
8002702Swollman
8012702Swollmanstatic void
8022702Swollmaninfile(name)
8032702Swollmanconst char *	name;
8042702Swollman{
8052702Swollman	register FILE *			fp;
8062702Swollman	register char **		fields;
8072702Swollman	register char *			cp;
8082702Swollman	register const struct lookup *	lp;
8092702Swollman	register int			nfields;
8102702Swollman	register int			wantcont;
8112702Swollman	register int			num;
8122702Swollman	char				buf[BUFSIZ];
8132702Swollman
8142702Swollman	if (strcmp(name, "-") == 0) {
81517214Swollman		name = _("standard input");
8162702Swollman		fp = stdin;
81730829Scharnier	} else if ((fp = fopen(name, "r")) == NULL)
81830829Scharnier		err(EXIT_FAILURE, _("can't open %s"), name);
8192702Swollman	wantcont = FALSE;
8202702Swollman	for (num = 1; ; ++num) {
8212702Swollman		eat(name, num);
8222702Swollman		if (fgets(buf, (int) sizeof buf, fp) != buf)
8232702Swollman			break;
8242702Swollman		cp = strchr(buf, '\n');
8252702Swollman		if (cp == NULL) {
82617214Swollman			error(_("line too long"));
827192625Sedwin			exit(EXIT_FAILURE);
8282702Swollman		}
8292702Swollman		*cp = '\0';
8302702Swollman		fields = getfields(buf);
8312702Swollman		nfields = 0;
8322702Swollman		while (fields[nfields] != NULL) {
8339937Swollman			static char	nada;
8342702Swollman
83517214Swollman			if (strcmp(fields[nfields], "-") == 0)
8369937Swollman				fields[nfields] = &nada;
8372702Swollman			++nfields;
8382702Swollman		}
8392702Swollman		if (nfields == 0) {
8402702Swollman			/* nothing to do */
8412702Swollman		} else if (wantcont) {
8422702Swollman			wantcont = inzcont(fields, nfields);
8432702Swollman		} else {
8442702Swollman			lp = byword(fields[0], line_codes);
8452702Swollman			if (lp == NULL)
84617214Swollman				error(_("input line of unknown type"));
8472702Swollman			else switch ((int) (lp->l_value)) {
8482702Swollman				case LC_RULE:
8492702Swollman					inrule(fields, nfields);
8502702Swollman					wantcont = FALSE;
8512702Swollman					break;
8522702Swollman				case LC_ZONE:
8532702Swollman					wantcont = inzone(fields, nfields);
8542702Swollman					break;
8552702Swollman				case LC_LINK:
8562702Swollman					inlink(fields, nfields);
8572702Swollman					wantcont = FALSE;
8582702Swollman					break;
8592702Swollman				case LC_LEAP:
8602702Swollman					if (name != leapsec)
86130829Scharnier						warnx(
86230829Scharnier_("leap line in non leap seconds file %s"), name);
8632702Swollman					else	inleap(fields, nfields);
8642702Swollman					wantcont = FALSE;
8652702Swollman					break;
8662702Swollman				default:	/* "cannot happen" */
86730829Scharnier					errx(EXIT_FAILURE,
86830829Scharnier_("panic: invalid l_value %d"), lp->l_value);
8692702Swollman			}
8702702Swollman		}
8712702Swollman		ifree((char *) fields);
8722702Swollman	}
87330829Scharnier	if (ferror(fp))
87430829Scharnier		errx(EXIT_FAILURE, _("error reading %s"), filename);
87530829Scharnier	if (fp != stdin && fclose(fp))
87630829Scharnier		err(EXIT_FAILURE, _("error closing %s"), filename);
8772702Swollman	if (wantcont)
87817214Swollman		error(_("expected continuation line not found"));
8792702Swollman}
8802702Swollman
8812702Swollman/*
8822702Swollman** Convert a string of one of the forms
8839937Swollman**	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
8842702Swollman** into a number of seconds.
8852702Swollman** A null string maps to zero.
8862702Swollman** Call error with errstring and return zero on errors.
8872702Swollman*/
8882702Swollman
8892702Swollmanstatic long
8902702Swollmangethms(string, errstring, signable)
8912702Swollmanconst char *		string;
8922702Swollmanconst char * const	errstring;
8932702Swollmanconst int		signable;
8942702Swollman{
895192625Sedwin	long	hh;
896192625Sedwin	int	mm, ss, sign;
8972702Swollman
8982702Swollman	if (string == NULL || *string == '\0')
8992702Swollman		return 0;
9002702Swollman	if (!signable)
9012702Swollman		sign = 1;
9022702Swollman	else if (*string == '-') {
9032702Swollman		sign = -1;
9042702Swollman		++string;
9052702Swollman	} else	sign = 1;
906192625Sedwin	if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
9072702Swollman		mm = ss = 0;
908192625Sedwin	else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
9092702Swollman		ss = 0;
910192625Sedwin	else if (sscanf(string, scheck(string, "%ld:%d:%d"),
9112702Swollman		&hh, &mm, &ss) != 3) {
9122702Swollman			error(errstring);
9132702Swollman			return 0;
9142702Swollman	}
915192625Sedwin	if (hh < 0 ||
9162702Swollman		mm < 0 || mm >= MINSPERHOUR ||
917192625Sedwin		ss < 0 || ss > SECSPERMIN) {
9182702Swollman			error(errstring);
9192702Swollman			return 0;
9202702Swollman	}
921192625Sedwin	if (LONG_MAX / SECSPERHOUR < hh) {
922192625Sedwin		error(_("time overflow"));
923192625Sedwin		return 0;
924192625Sedwin	}
925192625Sedwin	if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
926130819Sstefanf		warning(_("24:00 not handled by pre-1998 versions of zic"));
927192625Sedwin	if (noise && (hh > HOURSPERDAY ||
928192625Sedwin		(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
929192625Sedwinwarning(_("values over 24 hours not handled by pre-2007 versions of zic"));
930192625Sedwin	return oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
931192625Sedwin		    eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)));
9322702Swollman}
9332702Swollman
9342702Swollmanstatic void
9352702Swollmaninrule(fields, nfields)
9362702Swollmanregister char ** const	fields;
9372702Swollmanconst int		nfields;
9382702Swollman{
9392702Swollman	static struct rule	r;
9402702Swollman
9412702Swollman	if (nfields != RULE_FIELDS) {
94217214Swollman		error(_("wrong number of fields on Rule line"));
9432702Swollman		return;
9442702Swollman	}
9452702Swollman	if (*fields[RF_NAME] == '\0') {
94617214Swollman		error(_("nameless rule"));
9472702Swollman		return;
9482702Swollman	}
9492702Swollman	r.r_filename = filename;
9502702Swollman	r.r_linenum = linenum;
95117214Swollman	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
9522702Swollman	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
9532702Swollman		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
9542702Swollman	r.r_name = ecpyalloc(fields[RF_NAME]);
9552702Swollman	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
956192625Sedwin	if (max_abbrvar_len < strlen(r.r_abbrvar))
957192625Sedwin		max_abbrvar_len = strlen(r.r_abbrvar);
9589937Swollman	rules = (struct rule *) (void *) erealloc((char *) rules,
9592702Swollman		(int) ((nrules + 1) * sizeof *rules));
9602702Swollman	rules[nrules++] = r;
9612702Swollman}
9622702Swollman
9632702Swollmanstatic int
9642702Swollmaninzone(fields, nfields)
9652702Swollmanregister char ** const	fields;
9662702Swollmanconst int		nfields;
9672702Swollman{
9682702Swollman	register int	i;
9692702Swollman	static char *	buf;
9702702Swollman
9712702Swollman	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
97217214Swollman		error(_("wrong number of fields on Zone line"));
9732702Swollman		return FALSE;
9742702Swollman	}
9752702Swollman	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
9769937Swollman		buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
9772702Swollman		(void) sprintf(buf,
97817214Swollman_("\"Zone %s\" line and -l option are mutually exclusive"),
9792702Swollman			TZDEFAULT);
9802702Swollman		error(buf);
9812702Swollman		return FALSE;
9822702Swollman	}
9832702Swollman	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
9849937Swollman		buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
9852702Swollman		(void) sprintf(buf,
98617214Swollman_("\"Zone %s\" line and -p option are mutually exclusive"),
9872702Swollman			TZDEFRULES);
9882702Swollman		error(buf);
9892702Swollman		return FALSE;
9902702Swollman	}
9912702Swollman	for (i = 0; i < nzones; ++i)
9922702Swollman		if (zones[i].z_name != NULL &&
9932702Swollman			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
9949937Swollman				buf = erealloc(buf, (int) (132 +
9952702Swollman					strlen(fields[ZF_NAME]) +
9969937Swollman					strlen(zones[i].z_filename)));
9972702Swollman				(void) sprintf(buf,
99817214Swollman_("duplicate zone name %s (file \"%s\", line %d)"),
9992702Swollman					fields[ZF_NAME],
10002702Swollman					zones[i].z_filename,
10012702Swollman					zones[i].z_linenum);
10022702Swollman				error(buf);
10032702Swollman				return FALSE;
10042702Swollman		}
10052702Swollman	return inzsub(fields, nfields, FALSE);
10062702Swollman}
10072702Swollman
10082702Swollmanstatic int
10092702Swollmaninzcont(fields, nfields)
10102702Swollmanregister char ** const	fields;
10112702Swollmanconst int		nfields;
10122702Swollman{
10132702Swollman	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
101417214Swollman		error(_("wrong number of fields on Zone continuation line"));
10152702Swollman		return FALSE;
10162702Swollman	}
10172702Swollman	return inzsub(fields, nfields, TRUE);
10182702Swollman}
10192702Swollman
10202702Swollmanstatic int
10212702Swollmaninzsub(fields, nfields, iscont)
10222702Swollmanregister char ** const	fields;
10232702Swollmanconst int		nfields;
10242702Swollmanconst int		iscont;
10252702Swollman{
10262702Swollman	register char *		cp;
10272702Swollman	static struct zone	z;
10282702Swollman	register int		i_gmtoff, i_rule, i_format;
10292702Swollman	register int		i_untilyear, i_untilmonth;
10302702Swollman	register int		i_untilday, i_untiltime;
10312702Swollman	register int		hasuntil;
10322702Swollman
10332702Swollman	if (iscont) {
10342702Swollman		i_gmtoff = ZFC_GMTOFF;
10352702Swollman		i_rule = ZFC_RULE;
10362702Swollman		i_format = ZFC_FORMAT;
10372702Swollman		i_untilyear = ZFC_TILYEAR;
10382702Swollman		i_untilmonth = ZFC_TILMONTH;
10392702Swollman		i_untilday = ZFC_TILDAY;
10402702Swollman		i_untiltime = ZFC_TILTIME;
10412702Swollman		z.z_name = NULL;
10422702Swollman	} else {
10432702Swollman		i_gmtoff = ZF_GMTOFF;
10442702Swollman		i_rule = ZF_RULE;
10452702Swollman		i_format = ZF_FORMAT;
10462702Swollman		i_untilyear = ZF_TILYEAR;
10472702Swollman		i_untilmonth = ZF_TILMONTH;
10482702Swollman		i_untilday = ZF_TILDAY;
10492702Swollman		i_untiltime = ZF_TILTIME;
10502702Swollman		z.z_name = ecpyalloc(fields[ZF_NAME]);
10512702Swollman	}
10522702Swollman	z.z_filename = filename;
10532702Swollman	z.z_linenum = linenum;
105442997Swollman	z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
10552702Swollman	if ((cp = strchr(fields[i_format], '%')) != 0) {
10562702Swollman		if (*++cp != 's' || strchr(cp, '%') != 0) {
105717214Swollman			error(_("invalid abbreviation format"));
10582702Swollman			return FALSE;
10592702Swollman		}
10602702Swollman	}
10612702Swollman	z.z_rule = ecpyalloc(fields[i_rule]);
10622702Swollman	z.z_format = ecpyalloc(fields[i_format]);
1063192625Sedwin	if (max_format_len < strlen(z.z_format))
1064192625Sedwin		max_format_len = strlen(z.z_format);
10652702Swollman	hasuntil = nfields > i_untilyear;
10662702Swollman	if (hasuntil) {
10672702Swollman		z.z_untilrule.r_filename = filename;
10682702Swollman		z.z_untilrule.r_linenum = linenum;
10692702Swollman		rulesub(&z.z_untilrule,
10702702Swollman			fields[i_untilyear],
10712702Swollman			"only",
10722702Swollman			"",
10732702Swollman			(nfields > i_untilmonth) ?
10742702Swollman			fields[i_untilmonth] : "Jan",
10752702Swollman			(nfields > i_untilday) ? fields[i_untilday] : "1",
10762702Swollman			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
10772702Swollman		z.z_untiltime = rpytime(&z.z_untilrule,
10782702Swollman			z.z_untilrule.r_loyear);
10792702Swollman		if (iscont && nzones > 0 &&
10802702Swollman			z.z_untiltime > min_time &&
10812702Swollman			z.z_untiltime < max_time &&
10822702Swollman			zones[nzones - 1].z_untiltime > min_time &&
10832702Swollman			zones[nzones - 1].z_untiltime < max_time &&
10842702Swollman			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
1085192625Sedwin				error(_(
1086192625Sedwin"Zone continuation line end time is not after end time of previous line"
1087192625Sedwin					));
10882702Swollman				return FALSE;
10892702Swollman		}
10902702Swollman	}
10919937Swollman	zones = (struct zone *) (void *) erealloc((char *) zones,
10922702Swollman		(int) ((nzones + 1) * sizeof *zones));
10932702Swollman	zones[nzones++] = z;
10942702Swollman	/*
10952702Swollman	** If there was an UNTIL field on this line,
10962702Swollman	** there's more information about the zone on the next line.
10972702Swollman	*/
10982702Swollman	return hasuntil;
10992702Swollman}
11002702Swollman
11012702Swollmanstatic void
11022702Swollmaninleap(fields, nfields)
11032702Swollmanregister char ** const	fields;
11042702Swollmanconst int		nfields;
11052702Swollman{
11062702Swollman	register const char *		cp;
11072702Swollman	register const struct lookup *	lp;
11082702Swollman	register int			i, j;
11092702Swollman	int				year, month, day;
11102702Swollman	long				dayoff, tod;
1111192625Sedwin	zic_t				t;
11122702Swollman
11132702Swollman	if (nfields != LEAP_FIELDS) {
111417214Swollman		error(_("wrong number of fields on Leap line"));
11152702Swollman		return;
11162702Swollman	}
11172702Swollman	dayoff = 0;
11182702Swollman	cp = fields[LP_YEAR];
11192702Swollman	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
1120192625Sedwin		/*
1121192625Sedwin		** Leapin' Lizards!
1122192625Sedwin		*/
1123192625Sedwin		error(_("invalid leaping year"));
1124192625Sedwin		return;
11252702Swollman	}
1126192625Sedwin	if (!leapseen || leapmaxyear < year)
1127192625Sedwin		leapmaxyear = year;
1128192625Sedwin	if (!leapseen || leapminyear > year)
1129192625Sedwin		leapminyear = year;
1130192625Sedwin	leapseen = TRUE;
11312702Swollman	j = EPOCH_YEAR;
11322702Swollman	while (j != year) {
11332702Swollman		if (year > j) {
11342702Swollman			i = len_years[isleap(j)];
11352702Swollman			++j;
11362702Swollman		} else {
11372702Swollman			--j;
11382702Swollman			i = -len_years[isleap(j)];
11392702Swollman		}
11402702Swollman		dayoff = oadd(dayoff, eitol(i));
11412702Swollman	}
11422702Swollman	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
114317214Swollman		error(_("invalid month name"));
11442702Swollman		return;
11452702Swollman	}
11462702Swollman	month = lp->l_value;
11472702Swollman	j = TM_JANUARY;
11482702Swollman	while (j != month) {
11492702Swollman		i = len_months[isleap(year)][j];
11502702Swollman		dayoff = oadd(dayoff, eitol(i));
11512702Swollman		++j;
11522702Swollman	}
11532702Swollman	cp = fields[LP_DAY];
11542702Swollman	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
11552702Swollman		day <= 0 || day > len_months[isleap(year)][month]) {
115617214Swollman			error(_("invalid day of month"));
11572702Swollman			return;
11582702Swollman	}
11592702Swollman	dayoff = oadd(dayoff, eitol(day - 1));
1160192625Sedwin	if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
116117214Swollman		error(_("time before zero"));
11622702Swollman		return;
11632702Swollman	}
1164130819Sstefanf	if (dayoff < min_time / SECSPERDAY) {
1165130819Sstefanf		error(_("time too small"));
11662702Swollman		return;
11672702Swollman	}
1168130819Sstefanf	if (dayoff > max_time / SECSPERDAY) {
1169130819Sstefanf		error(_("time too large"));
1170130819Sstefanf		return;
1171130819Sstefanf	}
1172192625Sedwin	t = (zic_t) dayoff * SECSPERDAY;
117317214Swollman	tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
11742702Swollman	cp = fields[LP_CORR];
11752702Swollman	{
11762702Swollman		register int	positive;
11772702Swollman		int		count;
11782702Swollman
11792702Swollman		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
11802702Swollman			positive = FALSE;
11812702Swollman			count = 1;
11822702Swollman		} else if (strcmp(cp, "--") == 0) {
11832702Swollman			positive = FALSE;
11842702Swollman			count = 2;
11852702Swollman		} else if (strcmp(cp, "+") == 0) {
11862702Swollman			positive = TRUE;
11872702Swollman			count = 1;
11882702Swollman		} else if (strcmp(cp, "++") == 0) {
11892702Swollman			positive = TRUE;
11902702Swollman			count = 2;
11912702Swollman		} else {
119217214Swollman			error(_("illegal CORRECTION field on Leap line"));
11932702Swollman			return;
11942702Swollman		}
11952702Swollman		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
1196192625Sedwin			error(_(
1197192625Sedwin				"illegal Rolling/Stationary field on Leap line"
1198192625Sedwin				));
11992702Swollman			return;
12002702Swollman		}
12012702Swollman		leapadd(tadd(t, tod), positive, lp->l_value, count);
12022702Swollman	}
12032702Swollman}
12042702Swollman
12052702Swollmanstatic void
12062702Swollmaninlink(fields, nfields)
12072702Swollmanregister char ** const	fields;
12082702Swollmanconst int		nfields;
12092702Swollman{
12102702Swollman	struct link	l;
12112702Swollman
12122702Swollman	if (nfields != LINK_FIELDS) {
121317214Swollman		error(_("wrong number of fields on Link line"));
12142702Swollman		return;
12152702Swollman	}
12162702Swollman	if (*fields[LF_FROM] == '\0') {
121717214Swollman		error(_("blank FROM field on Link line"));
12182702Swollman		return;
12192702Swollman	}
12202702Swollman	if (*fields[LF_TO] == '\0') {
122117214Swollman		error(_("blank TO field on Link line"));
12222702Swollman		return;
12232702Swollman	}
12242702Swollman	l.l_filename = filename;
12252702Swollman	l.l_linenum = linenum;
12262702Swollman	l.l_from = ecpyalloc(fields[LF_FROM]);
12272702Swollman	l.l_to = ecpyalloc(fields[LF_TO]);
12289937Swollman	links = (struct link *) (void *) erealloc((char *) links,
12292702Swollman		(int) ((nlinks + 1) * sizeof *links));
12302702Swollman	links[nlinks++] = l;
12312702Swollman}
12322702Swollman
12332702Swollmanstatic void
12342702Swollmanrulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
12352702Swollmanregister struct rule * const	rp;
12369937Swollmanconst char * const		loyearp;
12379937Swollmanconst char * const		hiyearp;
12389937Swollmanconst char * const		typep;
12399937Swollmanconst char * const		monthp;
12409937Swollmanconst char * const		dayp;
12419937Swollmanconst char * const		timep;
12422702Swollman{
12439937Swollman	register const struct lookup *	lp;
12449937Swollman	register const char *		cp;
12459937Swollman	register char *			dp;
12469937Swollman	register char *			ep;
12472702Swollman
12482702Swollman	if ((lp = byword(monthp, mon_names)) == NULL) {
124917214Swollman		error(_("invalid month name"));
12502702Swollman		return;
12512702Swollman	}
12522702Swollman	rp->r_month = lp->l_value;
12532702Swollman	rp->r_todisstd = FALSE;
12549937Swollman	rp->r_todisgmt = FALSE;
12559937Swollman	dp = ecpyalloc(timep);
12569937Swollman	if (*dp != '\0') {
12579937Swollman		ep = dp + strlen(dp) - 1;
12589937Swollman		switch (lowerit(*ep)) {
12599937Swollman			case 's':	/* Standard */
12602702Swollman				rp->r_todisstd = TRUE;
12619937Swollman				rp->r_todisgmt = FALSE;
12629937Swollman				*ep = '\0';
12632702Swollman				break;
12649937Swollman			case 'w':	/* Wall */
12652702Swollman				rp->r_todisstd = FALSE;
12669937Swollman				rp->r_todisgmt = FALSE;
12679937Swollman				*ep = '\0';
126842997Swollman				break;
12699937Swollman			case 'g':	/* Greenwich */
12709937Swollman			case 'u':	/* Universal */
12719937Swollman			case 'z':	/* Zulu */
12729937Swollman				rp->r_todisstd = TRUE;
12739937Swollman				rp->r_todisgmt = TRUE;
12749937Swollman				*ep = '\0';
12752702Swollman				break;
12762702Swollman		}
12772702Swollman	}
127817214Swollman	rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
12799937Swollman	ifree(dp);
12802702Swollman	/*
12812702Swollman	** Year work.
12822702Swollman	*/
12832702Swollman	cp = loyearp;
12849937Swollman	lp = byword(cp, begin_years);
1285192625Sedwin	rp->r_lowasnum = lp == NULL;
1286192625Sedwin	if (!rp->r_lowasnum) switch ((int) lp->l_value) {
12872702Swollman		case YR_MINIMUM:
128817214Swollman			rp->r_loyear = INT_MIN;
12892702Swollman			break;
12902702Swollman		case YR_MAXIMUM:
129117214Swollman			rp->r_loyear = INT_MAX;
12922702Swollman			break;
12932702Swollman		default:	/* "cannot happen" */
129430829Scharnier			errx(EXIT_FAILURE,
129530829Scharnier				_("panic: invalid l_value %d"), lp->l_value);
12962702Swollman	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
129717214Swollman		error(_("invalid starting year"));
12982702Swollman		return;
12992702Swollman	}
13002702Swollman	cp = hiyearp;
1301192625Sedwin	lp = byword(cp, end_years);
1302192625Sedwin	rp->r_hiwasnum = lp == NULL;
1303192625Sedwin	if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
13042702Swollman		case YR_MINIMUM:
130517214Swollman			rp->r_hiyear = INT_MIN;
13062702Swollman			break;
13072702Swollman		case YR_MAXIMUM:
130817214Swollman			rp->r_hiyear = INT_MAX;
13092702Swollman			break;
13102702Swollman		case YR_ONLY:
13112702Swollman			rp->r_hiyear = rp->r_loyear;
13122702Swollman			break;
13132702Swollman		default:	/* "cannot happen" */
131430829Scharnier			errx(EXIT_FAILURE,
131530829Scharnier				_("panic: invalid l_value %d"), lp->l_value);
13162702Swollman	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
131717214Swollman		error(_("invalid ending year"));
13182702Swollman		return;
13192702Swollman	}
13202702Swollman	if (rp->r_loyear > rp->r_hiyear) {
132117214Swollman		error(_("starting year greater than ending year"));
13222702Swollman		return;
13232702Swollman	}
13242702Swollman	if (*typep == '\0')
13252702Swollman		rp->r_yrtype = NULL;
13262702Swollman	else {
13272702Swollman		if (rp->r_loyear == rp->r_hiyear) {
132817214Swollman			error(_("typed single year"));
13292702Swollman			return;
13302702Swollman		}
13312702Swollman		rp->r_yrtype = ecpyalloc(typep);
13322702Swollman	}
13332702Swollman	/*
13342702Swollman	** Day work.
13352702Swollman	** Accept things such as:
13362702Swollman	**	1
13372702Swollman	**	last-Sunday
13382702Swollman	**	Sun<=20
13392702Swollman	**	Sun>=7
13402702Swollman	*/
13419937Swollman	dp = ecpyalloc(dayp);
13429937Swollman	if ((lp = byword(dp, lasts)) != NULL) {
13432702Swollman		rp->r_dycode = DC_DOWLEQ;
13442702Swollman		rp->r_wday = lp->l_value;
13452702Swollman		rp->r_dayofmonth = len_months[1][rp->r_month];
13462702Swollman	} else {
13479937Swollman		if ((ep = strchr(dp, '<')) != 0)
13482702Swollman			rp->r_dycode = DC_DOWLEQ;
13499937Swollman		else if ((ep = strchr(dp, '>')) != 0)
13502702Swollman			rp->r_dycode = DC_DOWGEQ;
13512702Swollman		else {
13529937Swollman			ep = dp;
13532702Swollman			rp->r_dycode = DC_DOM;
13542702Swollman		}
13552702Swollman		if (rp->r_dycode != DC_DOM) {
13569937Swollman			*ep++ = 0;
13579937Swollman			if (*ep++ != '=') {
135817214Swollman				error(_("invalid day of month"));
13599937Swollman				ifree(dp);
13602702Swollman				return;
13612702Swollman			}
13629937Swollman			if ((lp = byword(dp, wday_names)) == NULL) {
136317214Swollman				error(_("invalid weekday name"));
13649937Swollman				ifree(dp);
13652702Swollman				return;
13662702Swollman			}
13672702Swollman			rp->r_wday = lp->l_value;
13682702Swollman		}
13699937Swollman		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
13702702Swollman			rp->r_dayofmonth <= 0 ||
13712702Swollman			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
137217214Swollman				error(_("invalid day of month"));
13739937Swollman				ifree(dp);
13742702Swollman				return;
13752702Swollman		}
13762702Swollman	}
13779937Swollman	ifree(dp);
13782702Swollman}
13792702Swollman
13802702Swollmanstatic void
13812702Swollmanconvert(val, buf)
13822702Swollmanconst long	val;
13832702Swollmanchar * const	buf;
13842702Swollman{
13852702Swollman	register int	i;
1386192625Sedwin	register int	shift;
13872702Swollman
13882702Swollman	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
13892702Swollman		buf[i] = val >> shift;
13902702Swollman}
13912702Swollman
13922702Swollmanstatic void
1393192625Sedwinconvert64(val, buf)
1394192625Sedwinconst zic_t	val;
1395192625Sedwinchar * const	buf;
1396192625Sedwin{
1397192625Sedwin	register int	i;
1398192625Sedwin	register int	shift;
1399192625Sedwin
1400192625Sedwin	for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
1401192625Sedwin		buf[i] = val >> shift;
1402192625Sedwin}
1403192625Sedwin
1404192625Sedwinstatic void
14052702Swollmanputtzcode(val, fp)
14062702Swollmanconst long	val;
14072702SwollmanFILE * const	fp;
14082702Swollman{
14092702Swollman	char	buf[4];
14102702Swollman
14112702Swollman	convert(val, buf);
14129937Swollman	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
14132702Swollman}
14142702Swollman
1415192625Sedwinstatic void
1416192625Sedwinputtzcode64(val, fp)
1417192625Sedwinconst zic_t	val;
1418192625SedwinFILE * const	fp;
1419192625Sedwin{
1420192625Sedwin	char	buf[8];
1421192625Sedwin
1422192625Sedwin	convert64(val, buf);
1423192625Sedwin	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
1424192625Sedwin}
1425192625Sedwin
142617214Swollmanstatic int
142717214Swollmanatcomp(avp, bvp)
1428192625Sedwinconst void *	avp;
1429192625Sedwinconst void *	bvp;
143017214Swollman{
1431192625Sedwin	const zic_t	a = ((const struct attype *) avp)->at;
1432192625Sedwin	const zic_t	b = ((const struct attype *) bvp)->at;
1433192625Sedwin
1434192625Sedwin	return (a < b) ? -1 : (a > b);
143517214Swollman}
143617214Swollman
1437192625Sedwinstatic int
1438192625Sedwinis32(x)
1439192625Sedwinconst zic_t	x;
1440192625Sedwin{
1441192625Sedwin	return INT32_MIN <= x && x <= INT32_MAX;
1442192625Sedwin}
1443192625Sedwin
14442702Swollmanstatic void
1445192625Sedwinwritezone(name, string)
14462702Swollmanconst char * const	name;
1447192625Sedwinconst char * const	string;
14482702Swollman{
1449192625Sedwin	register FILE *			fp;
1450192625Sedwin	register int			i, j;
1451192625Sedwin	register int			leapcnt32, leapi32;
1452192625Sedwin	register int			timecnt32, timei32;
1453192625Sedwin	register int			pass;
1454192625Sedwin	static char *			fullname;
1455192625Sedwin	static const struct tzhead	tzh0;
1456192625Sedwin	static struct tzhead		tzh;
1457192625Sedwin	zic_t				ats[TZ_MAX_TIMES];
1458192625Sedwin	unsigned char			types[TZ_MAX_TIMES];
14592702Swollman
146017214Swollman	/*
146117214Swollman	** Sort.
146217214Swollman	*/
146317214Swollman	if (timecnt > 1)
146417214Swollman		(void) qsort((void *) attypes, (size_t) timecnt,
146517214Swollman			(size_t) sizeof *attypes, atcomp);
146617214Swollman	/*
146717214Swollman	** Optimize.
146817214Swollman	*/
146917214Swollman	{
147017214Swollman		int	fromi;
147117214Swollman		int	toi;
147217214Swollman
147317214Swollman		toi = 0;
147417214Swollman		fromi = 0;
147542997Swollman		while (fromi < timecnt && attypes[fromi].at < min_time)
147642997Swollman			++fromi;
147717214Swollman		if (isdsts[0] == 0)
147842997Swollman			while (fromi < timecnt && attypes[fromi].type == 0)
147917214Swollman				++fromi;	/* handled by default rule */
148017214Swollman		for ( ; fromi < timecnt; ++fromi) {
1481192625Sedwin			if (toi != 0 && ((attypes[fromi].at +
1482192625Sedwin				gmtoffs[attypes[toi - 1].type]) <=
1483192625Sedwin				(attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
1484192625Sedwin				: attypes[toi - 2].type]))) {
1485192625Sedwin					attypes[toi - 1].type =
1486192625Sedwin						attypes[fromi].type;
1487192625Sedwin					continue;
148817214Swollman			}
148917214Swollman			if (toi == 0 ||
149017214Swollman				attypes[toi - 1].type != attypes[fromi].type)
149117214Swollman					attypes[toi++] = attypes[fromi];
149217214Swollman		}
149317214Swollman		timecnt = toi;
149417214Swollman	}
149517214Swollman	/*
149617214Swollman	** Transfer.
149717214Swollman	*/
149817214Swollman	for (i = 0; i < timecnt; ++i) {
149917214Swollman		ats[i] = attypes[i].at;
150017214Swollman		types[i] = attypes[i].type;
150117214Swollman	}
1502192625Sedwin	/*
1503192625Sedwin	** Correct for leap seconds.
1504192625Sedwin	*/
1505192625Sedwin	for (i = 0; i < timecnt; ++i) {
1506192625Sedwin		j = leapcnt;
1507192625Sedwin		while (--j >= 0)
1508192625Sedwin			if (ats[i] > trans[j] - corr[j]) {
1509192625Sedwin				ats[i] = tadd(ats[i], corr[j]);
1510192625Sedwin				break;
1511192625Sedwin			}
1512192625Sedwin	}
1513192625Sedwin	/*
1514192625Sedwin	** Figure out 32-bit-limited starts and counts.
1515192625Sedwin	*/
1516192625Sedwin	timecnt32 = timecnt;
1517192625Sedwin	timei32 = 0;
1518192625Sedwin	leapcnt32 = leapcnt;
1519192625Sedwin	leapi32 = 0;
1520192625Sedwin	while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
1521192625Sedwin		--timecnt32;
1522192625Sedwin	while (timecnt32 > 0 && !is32(ats[timei32])) {
1523192625Sedwin		--timecnt32;
1524192625Sedwin		++timei32;
1525192625Sedwin	}
1526192625Sedwin	while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
1527192625Sedwin		--leapcnt32;
1528192625Sedwin	while (leapcnt32 > 0 && !is32(trans[leapi32])) {
1529192625Sedwin		--leapcnt32;
1530192625Sedwin		++leapi32;
1531192625Sedwin	}
15322702Swollman	fullname = erealloc(fullname,
15339937Swollman		(int) (strlen(directory) + 1 + strlen(name) + 1));
15342702Swollman	(void) sprintf(fullname, "%s/%s", directory, name);
153523859Sbde
153623859Sbde	/*
153723859Sbde	 * Remove old file, if any, to snap links.
153823859Sbde	 */
153930829Scharnier	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
154030829Scharnier		err(EXIT_FAILURE, _("can't remove %s"), fullname);
154123859Sbde
15422702Swollman	if ((fp = fopen(fullname, "wb")) == NULL) {
15432702Swollman		if (mkdirs(fullname) != 0)
1544192625Sedwin			exit(EXIT_FAILURE);
154530829Scharnier		if ((fp = fopen(fullname, "wb")) == NULL)
154630829Scharnier			err(EXIT_FAILURE, _("can't create %s"), fullname);
15472702Swollman	}
1548192625Sedwin	for (pass = 1; pass <= 2; ++pass) {
1549192625Sedwin		register int	thistimei, thistimecnt;
1550192625Sedwin		register int	thisleapi, thisleapcnt;
1551192625Sedwin		register int	thistimelim, thisleaplim;
1552192625Sedwin		int		writetype[TZ_MAX_TIMES];
1553192625Sedwin		int		typemap[TZ_MAX_TYPES];
1554192625Sedwin		register int	thistypecnt;
1555192625Sedwin		char		thischars[TZ_MAX_CHARS];
1556192625Sedwin		char		thischarcnt;
1557192625Sedwin		int 		indmap[TZ_MAX_CHARS];
1558192625Sedwin
1559192625Sedwin		if (pass == 1) {
1560192625Sedwin			thistimei = timei32;
1561192625Sedwin			thistimecnt = timecnt32;
1562192625Sedwin			thisleapi = leapi32;
1563192625Sedwin			thisleapcnt = leapcnt32;
1564192625Sedwin		} else {
1565192625Sedwin			thistimei = 0;
1566192625Sedwin			thistimecnt = timecnt;
1567192625Sedwin			thisleapi = 0;
1568192625Sedwin			thisleapcnt = leapcnt;
1569192625Sedwin		}
1570192625Sedwin		thistimelim = thistimei + thistimecnt;
1571192625Sedwin		thisleaplim = thisleapi + thisleapcnt;
1572192625Sedwin		for (i = 0; i < typecnt; ++i)
1573192625Sedwin			writetype[i] = thistimecnt == timecnt;
1574192625Sedwin		if (thistimecnt == 0) {
1575192625Sedwin			/*
1576192625Sedwin			** No transition times fall in the current
1577192625Sedwin			** (32- or 64-bit) window.
1578192625Sedwin			*/
1579192625Sedwin			if (typecnt != 0)
1580192625Sedwin				writetype[typecnt - 1] = TRUE;
1581192625Sedwin		} else {
1582192625Sedwin			for (i = thistimei - 1; i < thistimelim; ++i)
1583192625Sedwin				if (i >= 0)
1584192625Sedwin					writetype[types[i]] = TRUE;
1585192625Sedwin			/*
1586192625Sedwin			** For America/Godthab and Antarctica/Palmer
1587192625Sedwin			*/
1588192625Sedwin			if (thistimei == 0)
1589192625Sedwin				writetype[0] = TRUE;
1590192625Sedwin		}
1591214411Sedwin#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
1592214411Sedwin		/*
1593214411Sedwin		** For some pre-2011 systems: if the last-to-be-written
1594214411Sedwin		** standard (or daylight) type has an offset different from the
1595214411Sedwin		** most recently used offset,
1596214411Sedwin		** append an (unused) copy of the most recently used type
1597214411Sedwin		** (to help get global "altzone" and "timezone" variables
1598214411Sedwin		** set correctly).
1599214411Sedwin		*/
1600214411Sedwin		{
1601214411Sedwin			register int	mrudst, mrustd, hidst, histd, type;
1602214411Sedwin
1603214411Sedwin			hidst = histd = mrudst = mrustd = -1;
1604214411Sedwin			for (i = thistimei; i < thistimelim; ++i)
1605214411Sedwin				if (isdsts[types[i]])
1606214411Sedwin					mrudst = types[i];
1607214411Sedwin				else	mrustd = types[i];
1608214411Sedwin			for (i = 0; i < typecnt; ++i)
1609214411Sedwin				if (writetype[i]) {
1610214411Sedwin					if (isdsts[i])
1611214411Sedwin						hidst = i;
1612214411Sedwin					else	histd = i;
1613214411Sedwin				}
1614214411Sedwin			if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
1615214411Sedwin				gmtoffs[hidst] != gmtoffs[mrudst]) {
1616214411Sedwin					isdsts[mrudst] = -1;
1617214411Sedwin					type = addtype(gmtoffs[mrudst],
1618214411Sedwin						&chars[abbrinds[mrudst]],
1619214411Sedwin						TRUE,
1620214411Sedwin						ttisstds[mrudst],
1621214411Sedwin						ttisgmts[mrudst]);
1622214411Sedwin					isdsts[mrudst] = TRUE;
1623214411Sedwin					writetype[type] = TRUE;
1624214411Sedwin			}
1625214411Sedwin			if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
1626214411Sedwin				gmtoffs[histd] != gmtoffs[mrustd]) {
1627214411Sedwin					isdsts[mrustd] = -1;
1628214411Sedwin					type = addtype(gmtoffs[mrustd],
1629214411Sedwin						&chars[abbrinds[mrustd]],
1630214411Sedwin						FALSE,
1631214411Sedwin						ttisstds[mrustd],
1632214411Sedwin						ttisgmts[mrustd]);
1633214411Sedwin					isdsts[mrustd] = FALSE;
1634214411Sedwin					writetype[type] = TRUE;
1635214411Sedwin			}
1636214411Sedwin		}
1637214411Sedwin#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
1638192625Sedwin		thistypecnt = 0;
1639192625Sedwin		for (i = 0; i < typecnt; ++i)
1640192625Sedwin			typemap[i] = writetype[i] ?  thistypecnt++ : -1;
1641192625Sedwin		for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
1642192625Sedwin			indmap[i] = -1;
1643192625Sedwin		thischarcnt = 0;
1644192625Sedwin		for (i = 0; i < typecnt; ++i) {
1645192625Sedwin			register char *	thisabbr;
1646192625Sedwin
1647192625Sedwin			if (!writetype[i])
1648192625Sedwin				continue;
1649192625Sedwin			if (indmap[abbrinds[i]] >= 0)
1650192625Sedwin				continue;
1651192625Sedwin			thisabbr = &chars[abbrinds[i]];
1652192625Sedwin			for (j = 0; j < thischarcnt; ++j)
1653192625Sedwin				if (strcmp(&thischars[j], thisabbr) == 0)
1654192625Sedwin					break;
1655192625Sedwin			if (j == thischarcnt) {
1656192625Sedwin				(void) strcpy(&thischars[(int) thischarcnt],
1657192625Sedwin					thisabbr);
1658192625Sedwin				thischarcnt += strlen(thisabbr) + 1;
1659192625Sedwin			}
1660192625Sedwin			indmap[abbrinds[i]] = j;
1661192625Sedwin		}
1662192625Sedwin#define DO(field)	(void) fwrite((void *) tzh.field, \
1663192625Sedwin				(size_t) sizeof tzh.field, (size_t) 1, fp)
1664192625Sedwin		tzh = tzh0;
1665192625Sedwin		(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
1666192625Sedwin		tzh.tzh_version[0] = ZIC_VERSION;
1667192625Sedwin		convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
1668192625Sedwin		convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
1669192625Sedwin		convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
1670192625Sedwin		convert(eitol(thistimecnt), tzh.tzh_timecnt);
1671192625Sedwin		convert(eitol(thistypecnt), tzh.tzh_typecnt);
1672192625Sedwin		convert(eitol(thischarcnt), tzh.tzh_charcnt);
1673192625Sedwin		DO(tzh_magic);
1674192625Sedwin		DO(tzh_version);
1675192625Sedwin		DO(tzh_reserved);
1676192625Sedwin		DO(tzh_ttisgmtcnt);
1677192625Sedwin		DO(tzh_ttisstdcnt);
1678192625Sedwin		DO(tzh_leapcnt);
1679192625Sedwin		DO(tzh_timecnt);
1680192625Sedwin		DO(tzh_typecnt);
1681192625Sedwin		DO(tzh_charcnt);
16829937Swollman#undef DO
1683192625Sedwin		for (i = thistimei; i < thistimelim; ++i)
1684192625Sedwin			if (pass == 1)
1685192625Sedwin				puttzcode((long) ats[i], fp);
1686192625Sedwin			else	puttzcode64(ats[i], fp);
1687192625Sedwin		for (i = thistimei; i < thistimelim; ++i) {
1688192625Sedwin			unsigned char	uc;
1689192625Sedwin
1690192625Sedwin			uc = typemap[types[i]];
1691192625Sedwin			(void) fwrite((void *) &uc,
1692192625Sedwin				(size_t) sizeof uc,
1693192625Sedwin				(size_t) 1,
1694192625Sedwin				fp);
1695192625Sedwin		}
1696192625Sedwin		for (i = 0; i < typecnt; ++i)
1697192625Sedwin			if (writetype[i]) {
1698192625Sedwin				puttzcode(gmtoffs[i], fp);
1699192625Sedwin				(void) putc(isdsts[i], fp);
1700192625Sedwin				(void) putc((unsigned char) indmap[abbrinds[i]], fp);
17012702Swollman			}
1702192625Sedwin		if (thischarcnt != 0)
1703192625Sedwin			(void) fwrite((void *) thischars,
1704192625Sedwin				(size_t) sizeof thischars[0],
1705192625Sedwin				(size_t) thischarcnt, fp);
1706192625Sedwin		for (i = thisleapi; i < thisleaplim; ++i) {
1707192625Sedwin			register zic_t	todo;
1708192625Sedwin
1709192625Sedwin			if (roll[i]) {
1710192625Sedwin				if (timecnt == 0 || trans[i] < ats[0]) {
1711192625Sedwin					j = 0;
1712192625Sedwin					while (isdsts[j])
1713192625Sedwin						if (++j >= typecnt) {
1714192625Sedwin							j = 0;
1715192625Sedwin							break;
1716192625Sedwin						}
1717192625Sedwin				} else {
1718192625Sedwin					j = 1;
1719192625Sedwin					while (j < timecnt &&
1720192625Sedwin						trans[i] >= ats[j])
1721192625Sedwin							++j;
1722192625Sedwin					j = types[j - 1];
1723192625Sedwin				}
1724192625Sedwin				todo = tadd(trans[i], -gmtoffs[j]);
1725192625Sedwin			} else	todo = trans[i];
1726192625Sedwin			if (pass == 1)
1727192625Sedwin				puttzcode((long) todo, fp);
1728192625Sedwin			else	puttzcode64(todo, fp);
1729192625Sedwin			puttzcode(corr[i], fp);
1730192625Sedwin		}
1731192625Sedwin		for (i = 0; i < typecnt; ++i)
1732192625Sedwin			if (writetype[i])
1733192625Sedwin				(void) putc(ttisstds[i], fp);
1734192625Sedwin		for (i = 0; i < typecnt; ++i)
1735192625Sedwin			if (writetype[i])
1736192625Sedwin				(void) putc(ttisgmts[i], fp);
17372702Swollman	}
1738192625Sedwin	(void) fprintf(fp, "\n%s\n", string);
173930829Scharnier	if (ferror(fp) || fclose(fp))
174030829Scharnier		errx(EXIT_FAILURE, _("error writing %s"), fullname);
174142997Swollman	if (chmod(fullname, mflag) < 0)
174242997Swollman		err(EXIT_FAILURE, _("cannot change mode of %s to %03o"),
174342997Swollman		    fullname, (unsigned)mflag);
174442997Swollman	if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
174542997Swollman	    && chown(fullname, uflag, gflag) < 0)
174642997Swollman		err(EXIT_FAILURE, _("cannot change ownership of %s"),
174742997Swollman		    fullname);
17482702Swollman}
17492702Swollman
17502702Swollmanstatic void
1751192625Sedwindoabbr(abbr, format, letters, isdst, doquotes)
17529937Swollmanchar * const		abbr;
17539937Swollmanconst char * const	format;
17549937Swollmanconst char * const	letters;
17559937Swollmanconst int		isdst;
1756192625Sedwinconst int		doquotes;
17579937Swollman{
1758192625Sedwin	register char *	cp;
1759192625Sedwin	register char *	slashp;
1760192625Sedwin	register int	len;
1761192625Sedwin
1762192625Sedwin	slashp = strchr(format, '/');
1763192625Sedwin	if (slashp == NULL) {
17649937Swollman		if (letters == NULL)
17659937Swollman			(void) strcpy(abbr, format);
17669937Swollman		else	(void) sprintf(abbr, format, letters);
1767192625Sedwin	} else if (isdst) {
1768192625Sedwin		(void) strcpy(abbr, slashp + 1);
1769192625Sedwin	} else {
1770192625Sedwin		if (slashp > format)
1771192625Sedwin			(void) strncpy(abbr, format,
1772192625Sedwin				(unsigned) (slashp - format));
1773192625Sedwin		abbr[slashp - format] = '\0';
17749937Swollman	}
1775192625Sedwin	if (!doquotes)
1776192625Sedwin		return;
1777192625Sedwin	for (cp = abbr; *cp != '\0'; ++cp)
1778192625Sedwin		if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
1779192625Sedwin			strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
1780192625Sedwin				break;
1781192625Sedwin	len = strlen(abbr);
1782192625Sedwin	if (len > 0 && *cp == '\0')
1783192625Sedwin		return;
1784192625Sedwin	abbr[len + 2] = '\0';
1785192625Sedwin	abbr[len + 1] = '>';
1786192625Sedwin	for ( ; len > 0; --len)
1787192625Sedwin		abbr[len] = abbr[len - 1];
1788192625Sedwin	abbr[0] = '<';
17899937Swollman}
17909937Swollman
17919937Swollmanstatic void
1792192625Sedwinupdateminmax(x)
1793192625Sedwinconst int	x;
1794192625Sedwin{
1795192625Sedwin	if (min_year > x)
1796192625Sedwin		min_year = x;
1797192625Sedwin	if (max_year < x)
1798192625Sedwin		max_year = x;
1799192625Sedwin}
1800192625Sedwin
1801192625Sedwinstatic int
1802192625Sedwinstringoffset(result, offset)
1803192625Sedwinchar *	result;
1804192625Sedwinlong	offset;
1805192625Sedwin{
1806192625Sedwin	register int	hours;
1807192625Sedwin	register int	minutes;
1808192625Sedwin	register int	seconds;
1809192625Sedwin
1810192625Sedwin	result[0] = '\0';
1811192625Sedwin	if (offset < 0) {
1812192625Sedwin		(void) strcpy(result, "-");
1813192625Sedwin		offset = -offset;
1814192625Sedwin	}
1815192625Sedwin	seconds = offset % SECSPERMIN;
1816192625Sedwin	offset /= SECSPERMIN;
1817192625Sedwin	minutes = offset % MINSPERHOUR;
1818192625Sedwin	offset /= MINSPERHOUR;
1819192625Sedwin	hours = offset;
1820192625Sedwin	if (hours >= HOURSPERDAY) {
1821192625Sedwin		result[0] = '\0';
1822192625Sedwin		return -1;
1823192625Sedwin	}
1824192625Sedwin	(void) sprintf(end(result), "%d", hours);
1825192625Sedwin	if (minutes != 0 || seconds != 0) {
1826192625Sedwin		(void) sprintf(end(result), ":%02d", minutes);
1827192625Sedwin		if (seconds != 0)
1828192625Sedwin			(void) sprintf(end(result), ":%02d", seconds);
1829192625Sedwin	}
1830192625Sedwin	return 0;
1831192625Sedwin}
1832192625Sedwin
1833192625Sedwinstatic int
1834192625Sedwinstringrule(result, rp, dstoff, gmtoff)
1835192625Sedwinchar *				result;
1836192625Sedwinconst struct rule * const	rp;
1837192625Sedwinconst long			dstoff;
1838192625Sedwinconst long			gmtoff;
1839192625Sedwin{
1840192625Sedwin	register long	tod;
1841192625Sedwin
1842192625Sedwin	result = end(result);
1843192625Sedwin	if (rp->r_dycode == DC_DOM) {
1844192625Sedwin		register int	month, total;
1845192625Sedwin
1846192625Sedwin		if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
1847192625Sedwin			return -1;
1848192625Sedwin		total = 0;
1849192625Sedwin		for (month = 0; month < rp->r_month; ++month)
1850192625Sedwin			total += len_months[0][month];
1851192625Sedwin		(void) sprintf(result, "J%d", total + rp->r_dayofmonth);
1852192625Sedwin	} else {
1853192625Sedwin		register int	week;
1854192625Sedwin
1855192625Sedwin		if (rp->r_dycode == DC_DOWGEQ) {
1856192625Sedwin			week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
1857192625Sedwin			if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth)
1858192625Sedwin				return -1;
1859192625Sedwin		} else if (rp->r_dycode == DC_DOWLEQ) {
1860192625Sedwin			if (rp->r_dayofmonth == len_months[1][rp->r_month])
1861192625Sedwin				week = 5;
1862192625Sedwin			else {
1863192625Sedwin				week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
1864192625Sedwin				if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth)
1865192625Sedwin					return -1;
1866192625Sedwin			}
1867192625Sedwin		} else	return -1;	/* "cannot happen" */
1868192625Sedwin		(void) sprintf(result, "M%d.%d.%d",
1869192625Sedwin			rp->r_month + 1, week, rp->r_wday);
1870192625Sedwin	}
1871192625Sedwin	tod = rp->r_tod;
1872192625Sedwin	if (rp->r_todisgmt)
1873192625Sedwin		tod += gmtoff;
1874192625Sedwin	if (rp->r_todisstd && rp->r_stdoff == 0)
1875192625Sedwin		tod += dstoff;
1876192625Sedwin	if (tod < 0) {
1877192625Sedwin		result[0] = '\0';
1878192625Sedwin		return -1;
1879192625Sedwin	}
1880192625Sedwin	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
1881192625Sedwin		(void) strcat(result, "/");
1882192625Sedwin		if (stringoffset(end(result), tod) != 0)
1883192625Sedwin			return -1;
1884192625Sedwin	}
1885192625Sedwin	return 0;
1886192625Sedwin}
1887192625Sedwin
1888192625Sedwinstatic void
1889192625Sedwinstringzone(result, zpfirst, zonecount)
1890192625Sedwinchar *				result;
1891192625Sedwinconst struct zone * const	zpfirst;
1892192625Sedwinconst int			zonecount;
1893192625Sedwin{
1894192625Sedwin	register const struct zone *	zp;
1895192625Sedwin	register struct rule *		rp;
1896192625Sedwin	register struct rule *		stdrp;
1897192625Sedwin	register struct rule *		dstrp;
1898192625Sedwin	register int			i;
1899192625Sedwin	register const char *		abbrvar;
1900192625Sedwin
1901192625Sedwin	result[0] = '\0';
1902192625Sedwin	zp = zpfirst + zonecount - 1;
1903192625Sedwin	stdrp = dstrp = NULL;
1904192625Sedwin	for (i = 0; i < zp->z_nrules; ++i) {
1905192625Sedwin		rp = &zp->z_rules[i];
1906192625Sedwin		if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
1907192625Sedwin			continue;
1908192625Sedwin		if (rp->r_yrtype != NULL)
1909192625Sedwin			continue;
1910192625Sedwin		if (rp->r_stdoff == 0) {
1911192625Sedwin			if (stdrp == NULL)
1912192625Sedwin				stdrp = rp;
1913192625Sedwin			else	return;
1914192625Sedwin		} else {
1915192625Sedwin			if (dstrp == NULL)
1916192625Sedwin				dstrp = rp;
1917192625Sedwin			else	return;
1918192625Sedwin		}
1919192625Sedwin	}
1920192625Sedwin	if (stdrp == NULL && dstrp == NULL) {
1921192625Sedwin		/*
1922192625Sedwin		** There are no rules running through "max".
1923192625Sedwin		** Let's find the latest rule.
1924192625Sedwin		*/
1925192625Sedwin		for (i = 0; i < zp->z_nrules; ++i) {
1926192625Sedwin			rp = &zp->z_rules[i];
1927192625Sedwin			if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
1928192625Sedwin				(rp->r_hiyear == stdrp->r_hiyear &&
1929192625Sedwin				rp->r_month > stdrp->r_month))
1930192625Sedwin					stdrp = rp;
1931192625Sedwin		}
1932192625Sedwin		if (stdrp != NULL && stdrp->r_stdoff != 0)
1933192625Sedwin			return;	/* We end up in DST (a POSIX no-no). */
1934192625Sedwin		/*
1935192625Sedwin		** Horrid special case: if year is 2037,
1936192625Sedwin		** presume this is a zone handled on a year-by-year basis;
1937192625Sedwin		** do not try to apply a rule to the zone.
1938192625Sedwin		*/
1939192625Sedwin		if (stdrp != NULL && stdrp->r_hiyear == 2037)
1940192625Sedwin			return;
1941192625Sedwin	}
1942196587Sedwin	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
1943192625Sedwin		return;
1944192625Sedwin	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
1945192625Sedwin	doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
1946192625Sedwin	if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
1947192625Sedwin		result[0] = '\0';
1948192625Sedwin		return;
1949192625Sedwin	}
1950192625Sedwin	if (dstrp == NULL)
1951192625Sedwin		return;
1952192625Sedwin	doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
1953192625Sedwin	if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
1954192625Sedwin		if (stringoffset(end(result),
1955192625Sedwin			-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
1956192625Sedwin				result[0] = '\0';
1957192625Sedwin				return;
1958192625Sedwin		}
1959192625Sedwin	(void) strcat(result, ",");
1960192625Sedwin	if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
1961192625Sedwin		result[0] = '\0';
1962192625Sedwin		return;
1963192625Sedwin	}
1964192625Sedwin	(void) strcat(result, ",");
1965192625Sedwin	if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
1966192625Sedwin		result[0] = '\0';
1967192625Sedwin		return;
1968192625Sedwin	}
1969192625Sedwin}
1970192625Sedwin
1971192625Sedwinstatic void
19722702Swollmanoutzone(zpfirst, zonecount)
19732702Swollmanconst struct zone * const	zpfirst;
19742702Swollmanconst int			zonecount;
19752702Swollman{
19762702Swollman	register const struct zone *	zp;
19772702Swollman	register struct rule *		rp;
19782702Swollman	register int			i, j;
19792702Swollman	register int			usestart, useuntil;
1980192625Sedwin	register zic_t			starttime, untiltime;
19812702Swollman	register long			gmtoff;
19822702Swollman	register long			stdoff;
19832702Swollman	register int			year;
19842702Swollman	register long			startoff;
19852702Swollman	register int			startttisstd;
19869937Swollman	register int			startttisgmt;
19872702Swollman	register int			type;
1988192625Sedwin	register char *			startbuf;
1989192625Sedwin	register char *			ab;
1990192625Sedwin	register char *			envvar;
1991192625Sedwin	register int			max_abbr_len;
1992192625Sedwin	register int			max_envvar_len;
19932702Swollman
1994192625Sedwin	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
1995192625Sedwin	max_envvar_len = 2 * max_abbr_len + 5 * 9;
1996192625Sedwin	startbuf = emalloc(max_abbr_len + 1);
1997192625Sedwin	ab = emalloc(max_abbr_len + 1);
1998192625Sedwin	envvar = emalloc(max_envvar_len + 1);
19999937Swollman	INITIALIZE(untiltime);
20009937Swollman	INITIALIZE(starttime);
20012702Swollman	/*
20022702Swollman	** Now. . .finally. . .generate some useful data!
20032702Swollman	*/
20042702Swollman	timecnt = 0;
20052702Swollman	typecnt = 0;
20062702Swollman	charcnt = 0;
20072702Swollman	/*
2008192625Sedwin	** Thanks to Earl Chew
20092702Swollman	** for noting the need to unconditionally initialize startttisstd.
20102702Swollman	*/
20112702Swollman	startttisstd = FALSE;
20129937Swollman	startttisgmt = FALSE;
2013192625Sedwin	min_year = max_year = EPOCH_YEAR;
2014192625Sedwin	if (leapseen) {
2015192625Sedwin		updateminmax(leapminyear);
2016192625Sedwin		updateminmax(leapmaxyear + (leapmaxyear < INT_MAX));
2017192625Sedwin	}
20182702Swollman	for (i = 0; i < zonecount; ++i) {
2019192625Sedwin		zp = &zpfirst[i];
2020192625Sedwin		if (i < zonecount - 1)
2021192625Sedwin			updateminmax(zp->z_untilrule.r_loyear);
2022192625Sedwin		for (j = 0; j < zp->z_nrules; ++j) {
2023192625Sedwin			rp = &zp->z_rules[j];
2024192625Sedwin			if (rp->r_lowasnum)
2025192625Sedwin				updateminmax(rp->r_loyear);
2026192625Sedwin			if (rp->r_hiwasnum)
2027192625Sedwin				updateminmax(rp->r_hiyear);
2028192625Sedwin		}
2029192625Sedwin	}
2030192625Sedwin	/*
2031192625Sedwin	** Generate lots of data if a rule can't cover all future times.
2032192625Sedwin	*/
2033192625Sedwin	stringzone(envvar, zpfirst, zonecount);
2034192625Sedwin	if (noise && envvar[0] == '\0') {
2035192625Sedwin		register char *	wp;
2036192625Sedwin
2037192625Sedwinwp = ecpyalloc(_("no POSIX environment variable for zone"));
2038192625Sedwin		wp = ecatalloc(wp, " ");
2039192625Sedwin		wp = ecatalloc(wp, zpfirst->z_name);
2040192625Sedwin		warning(wp);
2041192625Sedwin		ifree(wp);
2042192625Sedwin	}
2043192625Sedwin	if (envvar[0] == '\0') {
2044192625Sedwin		if (min_year >= INT_MIN + YEARSPERREPEAT)
2045192625Sedwin			min_year -= YEARSPERREPEAT;
2046192625Sedwin		else	min_year = INT_MIN;
2047192625Sedwin		if (max_year <= INT_MAX - YEARSPERREPEAT)
2048192625Sedwin			max_year += YEARSPERREPEAT;
2049192625Sedwin		else	max_year = INT_MAX;
2050192625Sedwin	}
2051192625Sedwin	/*
2052192625Sedwin	** For the benefit of older systems,
2053192625Sedwin	** generate data from 1900 through 2037.
2054192625Sedwin	*/
2055192625Sedwin	if (min_year > 1900)
2056192625Sedwin		min_year = 1900;
2057192625Sedwin	if (max_year < 2037)
2058192625Sedwin		max_year = 2037;
2059192625Sedwin	for (i = 0; i < zonecount; ++i) {
2060130819Sstefanf		/*
2061130819Sstefanf		** A guess that may well be corrected later.
2062130819Sstefanf		*/
2063130819Sstefanf		stdoff = 0;
20642702Swollman		zp = &zpfirst[i];
20652702Swollman		usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
20662702Swollman		useuntil = i < (zonecount - 1);
20672702Swollman		if (useuntil && zp->z_untiltime <= min_time)
20682702Swollman			continue;
20692702Swollman		gmtoff = zp->z_gmtoff;
20702702Swollman		eat(zp->z_filename, zp->z_linenum);
207117214Swollman		*startbuf = '\0';
207217214Swollman		startoff = zp->z_gmtoff;
20732702Swollman		if (zp->z_nrules == 0) {
20742702Swollman			stdoff = zp->z_stdoff;
20759937Swollman			doabbr(startbuf, zp->z_format,
2076192625Sedwin				(char *) NULL, stdoff != 0, FALSE);
20772702Swollman			type = addtype(oadd(zp->z_gmtoff, stdoff),
20789937Swollman				startbuf, stdoff != 0, startttisstd,
20799937Swollman				startttisgmt);
208017214Swollman			if (usestart) {
20812702Swollman				addtt(starttime, type);
208217214Swollman				usestart = FALSE;
2083130819Sstefanf			} else if (stdoff != 0)
20842702Swollman				addtt(min_time, type);
20852702Swollman		} else for (year = min_year; year <= max_year; ++year) {
20862702Swollman			if (useuntil && year > zp->z_untilrule.r_hiyear)
20872702Swollman				break;
20882702Swollman			/*
20892702Swollman			** Mark which rules to do in the current year.
20902702Swollman			** For those to do, calculate rpytime(rp, year);
20912702Swollman			*/
20922702Swollman			for (j = 0; j < zp->z_nrules; ++j) {
20932702Swollman				rp = &zp->z_rules[j];
20942702Swollman				eats(zp->z_filename, zp->z_linenum,
20952702Swollman					rp->r_filename, rp->r_linenum);
20962702Swollman				rp->r_todo = year >= rp->r_loyear &&
20972702Swollman						year <= rp->r_hiyear &&
20982702Swollman						yearistype(year, rp->r_yrtype);
20992702Swollman				if (rp->r_todo)
21002702Swollman					rp->r_temp = rpytime(rp, year);
21012702Swollman			}
21022702Swollman			for ( ; ; ) {
21032702Swollman				register int	k;
2104192625Sedwin				register zic_t	jtime, ktime;
21052702Swollman				register long	offset;
21062702Swollman
21079937Swollman				INITIALIZE(ktime);
21082702Swollman				if (useuntil) {
21092702Swollman					/*
211042997Swollman					** Turn untiltime into UTC
21112702Swollman					** assuming the current gmtoff and
21122702Swollman					** stdoff values.
21132702Swollman					*/
21149937Swollman					untiltime = zp->z_untiltime;
21159937Swollman					if (!zp->z_untilrule.r_todisgmt)
21169937Swollman						untiltime = tadd(untiltime,
21179937Swollman							-gmtoff);
21182702Swollman					if (!zp->z_untilrule.r_todisstd)
21192702Swollman						untiltime = tadd(untiltime,
21202702Swollman							-stdoff);
21212702Swollman				}
21222702Swollman				/*
21232702Swollman				** Find the rule (of those to do, if any)
21242702Swollman				** that takes effect earliest in the year.
21252702Swollman				*/
21262702Swollman				k = -1;
21272702Swollman				for (j = 0; j < zp->z_nrules; ++j) {
21282702Swollman					rp = &zp->z_rules[j];
21292702Swollman					if (!rp->r_todo)
21302702Swollman						continue;
21312702Swollman					eats(zp->z_filename, zp->z_linenum,
21322702Swollman						rp->r_filename, rp->r_linenum);
21339937Swollman					offset = rp->r_todisgmt ? 0 : gmtoff;
21342702Swollman					if (!rp->r_todisstd)
21352702Swollman						offset = oadd(offset, stdoff);
21362702Swollman					jtime = rp->r_temp;
21372702Swollman					if (jtime == min_time ||
21382702Swollman						jtime == max_time)
21392702Swollman							continue;
21402702Swollman					jtime = tadd(jtime, -offset);
21412702Swollman					if (k < 0 || jtime < ktime) {
21422702Swollman						k = j;
21432702Swollman						ktime = jtime;
21442702Swollman					}
21452702Swollman				}
21462702Swollman				if (k < 0)
21472702Swollman					break;	/* go on to next year */
21482702Swollman				rp = &zp->z_rules[k];
21492702Swollman				rp->r_todo = FALSE;
21502702Swollman				if (useuntil && ktime >= untiltime)
21512702Swollman					break;
215217214Swollman				stdoff = rp->r_stdoff;
215317214Swollman				if (usestart && ktime == starttime)
215417214Swollman					usestart = FALSE;
21552702Swollman				if (usestart) {
215617214Swollman					if (ktime < starttime) {
215717214Swollman						startoff = oadd(zp->z_gmtoff,
215817214Swollman							stdoff);
215917214Swollman						doabbr(startbuf, zp->z_format,
216017214Swollman							rp->r_abbrvar,
2161192625Sedwin							rp->r_stdoff != 0,
2162192625Sedwin							FALSE);
216317214Swollman						continue;
21642702Swollman					}
216517214Swollman					if (*startbuf == '\0' &&
2166192625Sedwin						startoff == oadd(zp->z_gmtoff,
2167192625Sedwin						stdoff)) {
2168192625Sedwin							doabbr(startbuf,
2169192625Sedwin								zp->z_format,
2170192625Sedwin								rp->r_abbrvar,
2171192625Sedwin								rp->r_stdoff !=
2172192625Sedwin								0,
2173192625Sedwin								FALSE);
217417214Swollman					}
21752702Swollman				}
21762702Swollman				eats(zp->z_filename, zp->z_linenum,
21772702Swollman					rp->r_filename, rp->r_linenum);
2178192625Sedwin				doabbr(ab, zp->z_format, rp->r_abbrvar,
2179192625Sedwin					rp->r_stdoff != 0, FALSE);
21802702Swollman				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
2181192625Sedwin				type = addtype(offset, ab, rp->r_stdoff != 0,
21829937Swollman					rp->r_todisstd, rp->r_todisgmt);
21832702Swollman				addtt(ktime, type);
21842702Swollman			}
21852702Swollman		}
218617214Swollman		if (usestart) {
218717214Swollman			if (*startbuf == '\0' &&
218817214Swollman				zp->z_format != NULL &&
218917214Swollman				strchr(zp->z_format, '%') == NULL &&
219017214Swollman				strchr(zp->z_format, '/') == NULL)
219117214Swollman					(void) strcpy(startbuf, zp->z_format);
219217214Swollman			eat(zp->z_filename, zp->z_linenum);
219317214Swollman			if (*startbuf == '\0')
219442997Swollmanerror(_("can't determine time zone abbreviation to use just after until time"));
219517214Swollman			else	addtt(starttime,
219617214Swollman					addtype(startoff, startbuf,
219717214Swollman						startoff != zp->z_gmtoff,
219817214Swollman						startttisstd,
219917214Swollman						startttisgmt));
220017214Swollman		}
22012702Swollman		/*
22022702Swollman		** Now we may get to set starttime for the next zone line.
22032702Swollman		*/
22042702Swollman		if (useuntil) {
22052702Swollman			startttisstd = zp->z_untilrule.r_todisstd;
22069937Swollman			startttisgmt = zp->z_untilrule.r_todisgmt;
220717214Swollman			starttime = zp->z_untiltime;
22082702Swollman			if (!startttisstd)
22092702Swollman				starttime = tadd(starttime, -stdoff);
221017214Swollman			if (!startttisgmt)
221117214Swollman				starttime = tadd(starttime, -gmtoff);
22122702Swollman		}
22132702Swollman	}
2214192625Sedwin	writezone(zpfirst->z_name, envvar);
2215192625Sedwin	ifree(startbuf);
2216192625Sedwin	ifree(ab);
2217192625Sedwin	ifree(envvar);
22182702Swollman}
22192702Swollman
22202702Swollmanstatic void
22212702Swollmanaddtt(starttime, type)
2222192625Sedwinconst zic_t	starttime;
222342997Swollmanint		type;
22242702Swollman{
222542997Swollman	if (starttime <= min_time ||
222642997Swollman		(timecnt == 1 && attypes[0].at < min_time)) {
222742997Swollman		gmtoffs[0] = gmtoffs[type];
222842997Swollman		isdsts[0] = isdsts[type];
222942997Swollman		ttisstds[0] = ttisstds[type];
223042997Swollman		ttisgmts[0] = ttisgmts[type];
223142997Swollman		if (abbrinds[type] != 0)
223242997Swollman			(void) strcpy(chars, &chars[abbrinds[type]]);
223342997Swollman		abbrinds[0] = 0;
223442997Swollman		charcnt = strlen(chars) + 1;
223542997Swollman		typecnt = 1;
223642997Swollman		timecnt = 0;
223742997Swollman		type = 0;
223842997Swollman	}
22392702Swollman	if (timecnt >= TZ_MAX_TIMES) {
224017214Swollman		error(_("too many transitions?!"));
2241192625Sedwin		exit(EXIT_FAILURE);
22422702Swollman	}
224317214Swollman	attypes[timecnt].at = starttime;
224417214Swollman	attypes[timecnt].type = type;
22452702Swollman	++timecnt;
22462702Swollman}
22472702Swollman
22482702Swollmanstatic int
22499937Swollmanaddtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
22502702Swollmanconst long		gmtoff;
22512702Swollmanconst char * const	abbr;
22522702Swollmanconst int		isdst;
22532702Swollmanconst int		ttisstd;
22549937Swollmanconst int		ttisgmt;
22552702Swollman{
22562702Swollman	register int	i, j;
22572702Swollman
225817214Swollman	if (isdst != TRUE && isdst != FALSE) {
225917214Swollman		error(_("internal error - addtype called with bad isdst"));
2260192625Sedwin		exit(EXIT_FAILURE);
226117214Swollman	}
226217214Swollman	if (ttisstd != TRUE && ttisstd != FALSE) {
226317214Swollman		error(_("internal error - addtype called with bad ttisstd"));
2264192625Sedwin		exit(EXIT_FAILURE);
226517214Swollman	}
226617214Swollman	if (ttisgmt != TRUE && ttisgmt != FALSE) {
226717214Swollman		error(_("internal error - addtype called with bad ttisgmt"));
2268192625Sedwin		exit(EXIT_FAILURE);
226917214Swollman	}
22702702Swollman	/*
22712702Swollman	** See if there's already an entry for this zone type.
22722702Swollman	** If so, just return its index.
22732702Swollman	*/
22742702Swollman	for (i = 0; i < typecnt; ++i) {
22752702Swollman		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
22762702Swollman			strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
22779937Swollman			ttisstd == ttisstds[i] &&
22789937Swollman			ttisgmt == ttisgmts[i])
22792702Swollman				return i;
22802702Swollman	}
22812702Swollman	/*
22822702Swollman	** There isn't one; add a new one, unless there are already too
22832702Swollman	** many.
22842702Swollman	*/
22852702Swollman	if (typecnt >= TZ_MAX_TYPES) {
228617214Swollman		error(_("too many local time types"));
2287192625Sedwin		exit(EXIT_FAILURE);
22882702Swollman	}
2289192625Sedwin	if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
2290192625Sedwin		error(_("UTC offset out of range"));
2291192625Sedwin		exit(EXIT_FAILURE);
2292192625Sedwin	}
22932702Swollman	gmtoffs[i] = gmtoff;
22942702Swollman	isdsts[i] = isdst;
22952702Swollman	ttisstds[i] = ttisstd;
22969937Swollman	ttisgmts[i] = ttisgmt;
22972702Swollman
22982702Swollman	for (j = 0; j < charcnt; ++j)
22992702Swollman		if (strcmp(&chars[j], abbr) == 0)
23002702Swollman			break;
23012702Swollman	if (j == charcnt)
23022702Swollman		newabbr(abbr);
23032702Swollman	abbrinds[i] = j;
23042702Swollman	++typecnt;
23052702Swollman	return i;
23062702Swollman}
23072702Swollman
23082702Swollmanstatic void
23092702Swollmanleapadd(t, positive, rolling, count)
2310192625Sedwinconst zic_t	t;
23112702Swollmanconst int	positive;
23122702Swollmanconst int	rolling;
23132702Swollmanint		count;
23142702Swollman{
23152702Swollman	register int	i, j;
23162702Swollman
23172702Swollman	if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
231817214Swollman		error(_("too many leap seconds"));
2319192625Sedwin		exit(EXIT_FAILURE);
23202702Swollman	}
23212702Swollman	for (i = 0; i < leapcnt; ++i)
23222702Swollman		if (t <= trans[i]) {
23232702Swollman			if (t == trans[i]) {
232417214Swollman				error(_("repeated leap second moment"));
2325192625Sedwin				exit(EXIT_FAILURE);
23262702Swollman			}
23272702Swollman			break;
23282702Swollman		}
23292702Swollman	do {
23302702Swollman		for (j = leapcnt; j > i; --j) {
23312702Swollman			trans[j] = trans[j - 1];
23322702Swollman			corr[j] = corr[j - 1];
23332702Swollman			roll[j] = roll[j - 1];
23342702Swollman		}
23352702Swollman		trans[i] = t;
23362702Swollman		corr[i] = positive ? 1L : eitol(-count);
23372702Swollman		roll[i] = rolling;
23382702Swollman		++leapcnt;
23392702Swollman	} while (positive && --count != 0);
23402702Swollman}
23412702Swollman
23422702Swollmanstatic void
2343192625Sedwinadjleap(void)
23442702Swollman{
23452702Swollman	register int	i;
23462702Swollman	register long	last = 0;
23472702Swollman
23482702Swollman	/*
23492702Swollman	** propagate leap seconds forward
23502702Swollman	*/
23512702Swollman	for (i = 0; i < leapcnt; ++i) {
23522702Swollman		trans[i] = tadd(trans[i], last);
23532702Swollman		last = corr[i] += last;
23542702Swollman	}
23552702Swollman}
23562702Swollman
23572702Swollmanstatic int
23582702Swollmanyearistype(year, type)
23592702Swollmanconst int		year;
23602702Swollmanconst char * const	type;
23612702Swollman{
23622702Swollman	static char *	buf;
23632702Swollman	int		result;
23642702Swollman
23652702Swollman	if (type == NULL || *type == '\0')
23662702Swollman		return TRUE;
23679937Swollman	buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
23682702Swollman	(void) sprintf(buf, "%s %d %s", yitcommand, year, type);
23692702Swollman	result = system(buf);
2370130819Sstefanf	if (WIFEXITED(result)) switch (WEXITSTATUS(result)) {
2371130819Sstefanf		case 0:
2372130819Sstefanf			return TRUE;
2373130819Sstefanf		case 1:
2374130819Sstefanf			return FALSE;
2375130819Sstefanf	}
237630829Scharnier	error(_("wild result from command execution"));
237730829Scharnier	warnx(_("command was '%s', result was %d"), buf, result);
23782702Swollman	for ( ; ; )
2379192625Sedwin		exit(EXIT_FAILURE);
23802702Swollman}
23812702Swollman
23822702Swollmanstatic int
23832702Swollmanlowerit(a)
238417214Swollmanint	a;
23852702Swollman{
238617214Swollman	a = (unsigned char) a;
23872702Swollman	return (isascii(a) && isupper(a)) ? tolower(a) : a;
23882702Swollman}
23892702Swollman
23902702Swollmanstatic int
23912702Swollmanciequal(ap, bp)		/* case-insensitive equality */
23922702Swollmanregister const char *	ap;
23932702Swollmanregister const char *	bp;
23942702Swollman{
23952702Swollman	while (lowerit(*ap) == lowerit(*bp++))
23962702Swollman		if (*ap++ == '\0')
23972702Swollman			return TRUE;
23982702Swollman	return FALSE;
23992702Swollman}
24002702Swollman
24012702Swollmanstatic int
24022702Swollmanitsabbr(abbr, word)
24032702Swollmanregister const char *	abbr;
24042702Swollmanregister const char *	word;
24052702Swollman{
24062702Swollman	if (lowerit(*abbr) != lowerit(*word))
24072702Swollman		return FALSE;
24082702Swollman	++word;
24092702Swollman	while (*++abbr != '\0')
241017214Swollman		do {
241117214Swollman			if (*word == '\0')
241217214Swollman				return FALSE;
241317214Swollman		} while (lowerit(*word++) != lowerit(*abbr));
24142702Swollman	return TRUE;
24152702Swollman}
24162702Swollman
24172702Swollmanstatic const struct lookup *
24182702Swollmanbyword(word, table)
24192702Swollmanregister const char * const		word;
24202702Swollmanregister const struct lookup * const	table;
24212702Swollman{
24222702Swollman	register const struct lookup *	foundlp;
24232702Swollman	register const struct lookup *	lp;
24242702Swollman
24252702Swollman	if (word == NULL || table == NULL)
24262702Swollman		return NULL;
24272702Swollman	/*
24282702Swollman	** Look for exact match.
24292702Swollman	*/
24302702Swollman	for (lp = table; lp->l_word != NULL; ++lp)
24312702Swollman		if (ciequal(word, lp->l_word))
24322702Swollman			return lp;
24332702Swollman	/*
24342702Swollman	** Look for inexact match.
24352702Swollman	*/
24362702Swollman	foundlp = NULL;
24372702Swollman	for (lp = table; lp->l_word != NULL; ++lp)
243842997Swollman		if (itsabbr(word, lp->l_word)) {
24392702Swollman			if (foundlp == NULL)
24402702Swollman				foundlp = lp;
24412702Swollman			else	return NULL;	/* multiple inexact matches */
244242997Swollman		}
24432702Swollman	return foundlp;
24442702Swollman}
24452702Swollman
24462702Swollmanstatic char **
24472702Swollmangetfields(cp)
24482702Swollmanregister char *	cp;
24492702Swollman{
24502702Swollman	register char *		dp;
24512702Swollman	register char **	array;
24522702Swollman	register int		nsubs;
24532702Swollman
24542702Swollman	if (cp == NULL)
24552702Swollman		return NULL;
24569937Swollman	array = (char **) (void *)
24579937Swollman		emalloc((int) ((strlen(cp) + 1) * sizeof *array));
24582702Swollman	nsubs = 0;
24592702Swollman	for ( ; ; ) {
2460192625Sedwin		while (isascii((unsigned char) *cp) &&
2461192625Sedwin			isspace((unsigned char) *cp))
2462192625Sedwin				++cp;
24632702Swollman		if (*cp == '\0' || *cp == '#')
24642702Swollman			break;
24652702Swollman		array[nsubs++] = dp = cp;
24662702Swollman		do {
24672702Swollman			if ((*dp = *cp++) != '"')
24682702Swollman				++dp;
24692702Swollman			else while ((*dp = *cp++) != '"')
24702702Swollman				if (*dp != '\0')
24712702Swollman					++dp;
2472174210Skevlo				else {
2473174210Skevlo					error(_("odd number of quotation marks"));
2474174210Skevlo					exit(EXIT_FAILURE);
2475174210Skevlo				}
24762702Swollman		} while (*cp != '\0' && *cp != '#' &&
247717214Swollman			(!isascii(*cp) || !isspace((unsigned char) *cp)));
247817214Swollman		if (isascii(*cp) && isspace((unsigned char) *cp))
24792702Swollman			++cp;
24802702Swollman		*dp = '\0';
24812702Swollman	}
24822702Swollman	array[nsubs] = NULL;
24832702Swollman	return array;
24842702Swollman}
24852702Swollman
24862702Swollmanstatic long
24872702Swollmanoadd(t1, t2)
24882702Swollmanconst long	t1;
24892702Swollmanconst long	t2;
24902702Swollman{
24912702Swollman	register long	t;
24922702Swollman
24932702Swollman	t = t1 + t2;
24942702Swollman	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
249517214Swollman		error(_("time overflow"));
2496192625Sedwin		exit(EXIT_FAILURE);
24972702Swollman	}
24982702Swollman	return t;
24992702Swollman}
25002702Swollman
2501192625Sedwinstatic zic_t
25022702Swollmantadd(t1, t2)
2503192625Sedwinconst zic_t	t1;
25042702Swollmanconst long	t2;
25052702Swollman{
2506192625Sedwin	register zic_t	t;
25072702Swollman
25082702Swollman	if (t1 == max_time && t2 > 0)
25092702Swollman		return max_time;
25102702Swollman	if (t1 == min_time && t2 < 0)
25112702Swollman		return min_time;
25122702Swollman	t = t1 + t2;
25132702Swollman	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
251417214Swollman		error(_("time overflow"));
2515192625Sedwin		exit(EXIT_FAILURE);
25162702Swollman	}
25172702Swollman	return t;
25182702Swollman}
25192702Swollman
25202702Swollman/*
25212702Swollman** Given a rule, and a year, compute the date - in seconds since January 1,
25222702Swollman** 1970, 00:00 LOCAL time - in that year that the rule refers to.
25232702Swollman*/
25242702Swollman
2525192625Sedwinstatic zic_t
25262702Swollmanrpytime(rp, wantedy)
25272702Swollmanregister const struct rule * const	rp;
25282702Swollmanregister const int			wantedy;
25292702Swollman{
25302702Swollman	register int	y, m, i;
25312702Swollman	register long	dayoff;			/* with a nod to Margaret O. */
2532192625Sedwin	register zic_t	t;
25332702Swollman
253417214Swollman	if (wantedy == INT_MIN)
25352702Swollman		return min_time;
253617214Swollman	if (wantedy == INT_MAX)
25372702Swollman		return max_time;
25382702Swollman	dayoff = 0;
25392702Swollman	m = TM_JANUARY;
25402702Swollman	y = EPOCH_YEAR;
25412702Swollman	while (wantedy != y) {
25422702Swollman		if (wantedy > y) {
25432702Swollman			i = len_years[isleap(y)];
25442702Swollman			++y;
25452702Swollman		} else {
25462702Swollman			--y;
25472702Swollman			i = -len_years[isleap(y)];
25482702Swollman		}
25492702Swollman		dayoff = oadd(dayoff, eitol(i));
25502702Swollman	}
25512702Swollman	while (m != rp->r_month) {
25522702Swollman		i = len_months[isleap(y)][m];
25532702Swollman		dayoff = oadd(dayoff, eitol(i));
25542702Swollman		++m;
25552702Swollman	}
25562702Swollman	i = rp->r_dayofmonth;
25572702Swollman	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
25582702Swollman		if (rp->r_dycode == DC_DOWLEQ)
25592702Swollman			--i;
25602702Swollman		else {
256117214Swollman			error(_("use of 2/29 in non leap-year"));
2562192625Sedwin			exit(EXIT_FAILURE);
25632702Swollman		}
25642702Swollman	}
25652702Swollman	--i;
25662702Swollman	dayoff = oadd(dayoff, eitol(i));
25672702Swollman	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
25682702Swollman		register long	wday;
25692702Swollman
25702702Swollman#define LDAYSPERWEEK	((long) DAYSPERWEEK)
25712702Swollman		wday = eitol(EPOCH_WDAY);
25722702Swollman		/*
25732702Swollman		** Don't trust mod of negative numbers.
25742702Swollman		*/
25752702Swollman		if (dayoff >= 0)
25762702Swollman			wday = (wday + dayoff) % LDAYSPERWEEK;
25772702Swollman		else {
25782702Swollman			wday -= ((-dayoff) % LDAYSPERWEEK);
25792702Swollman			if (wday < 0)
25802702Swollman				wday += LDAYSPERWEEK;
25812702Swollman		}
25822702Swollman		while (wday != eitol(rp->r_wday))
25832702Swollman			if (rp->r_dycode == DC_DOWGEQ) {
25842702Swollman				dayoff = oadd(dayoff, (long) 1);
25852702Swollman				if (++wday >= LDAYSPERWEEK)
25862702Swollman					wday = 0;
25872702Swollman				++i;
25882702Swollman			} else {
25892702Swollman				dayoff = oadd(dayoff, (long) -1);
25902702Swollman				if (--wday < 0)
25912702Swollman					wday = LDAYSPERWEEK - 1;
25922702Swollman				--i;
25932702Swollman			}
25942702Swollman		if (i < 0 || i >= len_months[isleap(y)][m]) {
2595130819Sstefanf			if (noise)
2596192625Sedwin				warning(_("rule goes past start/end of month--\
2597192625Sedwinwill not work with pre-2004 versions of zic"));
25982702Swollman		}
25992702Swollman	}
2600130819Sstefanf	if (dayoff < min_time / SECSPERDAY)
2601130819Sstefanf		return min_time;
2602130819Sstefanf	if (dayoff > max_time / SECSPERDAY)
2603130819Sstefanf		return max_time;
2604192625Sedwin	t = (zic_t) dayoff * SECSPERDAY;
26052702Swollman	return tadd(t, rp->r_tod);
26062702Swollman}
26072702Swollman
26082702Swollmanstatic void
26092702Swollmannewabbr(string)
26102702Swollmanconst char * const	string;
26112702Swollman{
26122702Swollman	register int	i;
26132702Swollman
2614192625Sedwin	if (strcmp(string, GRANDPARENTED) != 0) {
2615192625Sedwin		register const char *	cp;
2616192625Sedwin		register char *		wp;
2617192625Sedwin
2618192625Sedwin		cp = string;
2619192625Sedwin		wp = NULL;
2620192625Sedwin		while (isascii((unsigned char) *cp) &&
2621307358Sbapt			(isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
2622192625Sedwin				++cp;
2623192625Sedwin		if (noise && cp - string > 3)
2624307358Sbaptwp = _("time zone abbreviation has more than 3 characters");
2625192625Sedwin		if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
2626307358Sbaptwp = _("time zone abbreviation has too many characters");
2627192625Sedwin		if (*cp != '\0')
2628192625Sedwinwp = _("time zone abbreviation differs from POSIX standard");
2629192625Sedwin		if (wp != NULL) {
2630192625Sedwin			wp = ecpyalloc(wp);
2631192625Sedwin			wp = ecatalloc(wp, " (");
2632192625Sedwin			wp = ecatalloc(wp, string);
2633192625Sedwin			wp = ecatalloc(wp, ")");
2634192625Sedwin			warning(wp);
2635192625Sedwin			ifree(wp);
2636192625Sedwin		}
2637192625Sedwin	}
26382702Swollman	i = strlen(string) + 1;
26392702Swollman	if (charcnt + i > TZ_MAX_CHARS) {
264017214Swollman		error(_("too many, or too long, time zone abbreviations"));
2641192625Sedwin		exit(EXIT_FAILURE);
26422702Swollman	}
26432702Swollman	(void) strcpy(&chars[charcnt], string);
26442702Swollman	charcnt += eitol(i);
26452702Swollman}
26462702Swollman
26472702Swollmanstatic int
26482702Swollmanmkdirs(argname)
2649192625Sedwinchar *		argname;
26502702Swollman{
26512702Swollman	register char *	name;
26522702Swollman	register char *	cp;
26532702Swollman
265442997Swollman	if (argname == NULL || *argname == '\0' || Dflag)
26552702Swollman		return 0;
26562702Swollman	cp = name = ecpyalloc(argname);
26572702Swollman	while ((cp = strchr(cp + 1, '/')) != 0) {
26582702Swollman		*cp = '\0';
26592702Swollman#ifndef unix
26602702Swollman		/*
26619937Swollman		** DOS drive specifier?
26622702Swollman		*/
266317214Swollman		if (isalpha((unsigned char) name[0]) &&
266417214Swollman			name[1] == ':' && name[2] == '\0') {
26652702Swollman				*cp = '/';
26662702Swollman				continue;
26672702Swollman		}
26682702Swollman#endif /* !defined unix */
26692702Swollman		if (!itsdir(name)) {
26702702Swollman			/*
26712702Swollman			** It doesn't seem to exist, so we try to create it.
267242997Swollman			** Creation may fail because of the directory being
267342997Swollman			** created by some other multiprocessor, so we get
267442997Swollman			** to do extra checking.
26752702Swollman			*/
2676130819Sstefanf			if (mkdir(name, MKDIR_UMASK) != 0
267742997Swollman				&& (errno != EEXIST || !itsdir(name))) {
267830829Scharnier				warn(_("can't create directory %s"), name);
26792702Swollman				ifree(name);
26802702Swollman				return -1;
26812702Swollman			}
26822702Swollman		}
26832702Swollman		*cp = '/';
26842702Swollman	}
26852702Swollman	ifree(name);
26862702Swollman	return 0;
26872702Swollman}
26882702Swollman
26892702Swollmanstatic long
26902702Swollmaneitol(i)
26912702Swollmanconst int	i;
26922702Swollman{
26932702Swollman	long	l;
26942702Swollman
26952702Swollman	l = i;
269630829Scharnier	if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
269730829Scharnier		errx(EXIT_FAILURE, _("%d did not sign extend correctly"), i);
26982702Swollman	return l;
26992702Swollman}
27002702Swollman
270142997Swollman#include <grp.h>
270242997Swollman#include <pwd.h>
270342997Swollman
270442997Swollmanstatic void
270542997Swollmansetgroup(flag, name)
270642997Swollman	gid_t *flag;
270742997Swollman	const char *name;
270842997Swollman{
270942997Swollman	struct group *gr;
271042997Swollman
271142997Swollman	if (*flag != (gid_t)-1)
271242997Swollman		errx(EXIT_FAILURE, _("multiple -g flags specified"));
271342997Swollman
271442997Swollman	gr = getgrnam(name);
271542997Swollman	if (gr == 0) {
271642997Swollman		char *ep;
271742997Swollman		unsigned long ul;
271842997Swollman
271942997Swollman		ul = strtoul(name, &ep, 10);
272042997Swollman		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
272142997Swollman			*flag = ul;
272242997Swollman			return;
272342997Swollman		}
272442997Swollman		errx(EXIT_FAILURE, _("group `%s' not found"), name);
272542997Swollman	}
272642997Swollman	*flag = gr->gr_gid;
272742997Swollman}
272842997Swollman
272942997Swollmanstatic void
273042997Swollmansetuser(flag, name)
273142997Swollman	uid_t *flag;
273242997Swollman	const char *name;
273342997Swollman{
273442997Swollman	struct passwd *pw;
273542997Swollman
273642997Swollman	if (*flag != (gid_t)-1)
273742997Swollman		errx(EXIT_FAILURE, _("multiple -u flags specified"));
273842997Swollman
273942997Swollman	pw = getpwnam(name);
274042997Swollman	if (pw == 0) {
274142997Swollman		char *ep;
274242997Swollman		unsigned long ul;
274342997Swollman
274442997Swollman		ul = strtoul(name, &ep, 10);
274542997Swollman		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
274642997Swollman			*flag = ul;
274742997Swollman			return;
274842997Swollman		}
274942997Swollman		errx(EXIT_FAILURE, _("user `%s' not found"), name);
275042997Swollman	}
275142997Swollman	*flag = pw->pw_uid;
275242997Swollman}
275342997Swollman
27542702Swollman/*
2755130819Sstefanf** UNIX was a registered trademark of The Open Group in 2003.
27562702Swollman*/
2757