xref: /onnv-gate/usr/src/cmd/zic/zic.c (revision 4223:75d835eed918)
10Sstevel@tonic-gate /*
24218Srobbin  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
31138Srobbin  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
60Sstevel@tonic-gate 
74218Srobbin static char	elsieid[] = "@(#)zic.c	7.128.1";
80Sstevel@tonic-gate 
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate  * #define	LEAPSECOND_SUPPORT
110Sstevel@tonic-gate  */
120Sstevel@tonic-gate 
131138Srobbin /*
141138Srobbin  * Regardless of the type of time_t, we do our work using this type.
151138Srobbin  */
161138Srobbin 
171138Srobbin typedef int	zic_t;
181138Srobbin 
190Sstevel@tonic-gate #include "private.h"
200Sstevel@tonic-gate #include <tzfile.h>			/* this is in system headers at Sun */
210Sstevel@tonic-gate 
220Sstevel@tonic-gate #include <sys/stat.h>			/* for umask manifest constants */
230Sstevel@tonic-gate #include <ctype.h>
240Sstevel@tonic-gate #include <locale.h>
250Sstevel@tonic-gate #include <stdlib.h>			/* for getopt */
260Sstevel@tonic-gate 
271138Srobbin #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
281138Srobbin #define	ZIC_MAX_ABBR_LEN_WO_WARN	6
291138Srobbin #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
301138Srobbin 
311138Srobbin #ifdef S_IRUSR
321138Srobbin #define	MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
331138Srobbin #else
341138Srobbin #define	MKDIR_UMASK 0755
351138Srobbin #endif
361138Srobbin 
370Sstevel@tonic-gate struct rule {
380Sstevel@tonic-gate 	const char	*r_filename;
390Sstevel@tonic-gate 	int		r_linenum;
400Sstevel@tonic-gate 	const char	*r_name;
410Sstevel@tonic-gate 
420Sstevel@tonic-gate 	int		r_loyear;	/* for example, 1986 */
430Sstevel@tonic-gate 	int		r_hiyear;	/* for example, 1986 */
440Sstevel@tonic-gate 	const char	*r_yrtype;
450Sstevel@tonic-gate 
460Sstevel@tonic-gate 	int		r_month;	/* 0..11 */
470Sstevel@tonic-gate 
480Sstevel@tonic-gate 	int		r_dycode;	/* see below */
490Sstevel@tonic-gate 	int		r_dayofmonth;
500Sstevel@tonic-gate 	int		r_wday;
510Sstevel@tonic-gate 
520Sstevel@tonic-gate 	long		r_tod;		/* time from midnight */
530Sstevel@tonic-gate 	int		r_todisstd;	/* above is standard time if TRUE */
540Sstevel@tonic-gate 					/* or wall clock time if FALSE */
550Sstevel@tonic-gate 	int		r_todisgmt;	/* above is GMT if TRUE */
560Sstevel@tonic-gate 					/* or local time if FALSE */
570Sstevel@tonic-gate 	long		r_stdoff;	/* offset from standard time */
580Sstevel@tonic-gate 	const char	*r_abbrvar;	/* variable part of abbreviation */
590Sstevel@tonic-gate 
600Sstevel@tonic-gate 	int		r_todo;		/* a rule to do (used in outzone) */
611138Srobbin 	zic_t		r_temp;		/* used in outzone */
620Sstevel@tonic-gate };
630Sstevel@tonic-gate 
640Sstevel@tonic-gate /*
650Sstevel@tonic-gate  *	r_dycode		r_dayofmonth	r_wday
660Sstevel@tonic-gate  */
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #define	DC_DOM		0	/* 1..31 */	/* unused */
690Sstevel@tonic-gate #define	DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */
700Sstevel@tonic-gate #define	DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */
710Sstevel@tonic-gate 
720Sstevel@tonic-gate struct zone {
730Sstevel@tonic-gate 	const char	*z_filename;
740Sstevel@tonic-gate 	int		z_linenum;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 	const char	*z_name;
770Sstevel@tonic-gate 	long		z_gmtoff;
780Sstevel@tonic-gate 	const char	*z_rule;
790Sstevel@tonic-gate 	const char	*z_format;
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	long		z_stdoff;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	struct rule	*z_rules;
840Sstevel@tonic-gate 	int		z_nrules;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate 	struct rule	z_untilrule;
871138Srobbin 	zic_t		z_untiltime;
880Sstevel@tonic-gate };
890Sstevel@tonic-gate 
901138Srobbin static void	addtt(zic_t starttime, int type);
910Sstevel@tonic-gate static int	addtype(long gmtoff, const char *abbr, int isdst,
920Sstevel@tonic-gate 				int ttisstd, int ttisgmt);
930Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
941138Srobbin static void	leapadd(zic_t t, int positive, int rolling, int count);
950Sstevel@tonic-gate static void	adjleap(void);
960Sstevel@tonic-gate #endif
970Sstevel@tonic-gate static void	associate(void);
980Sstevel@tonic-gate static int	ciequal(const char *ap, const char *bp);
990Sstevel@tonic-gate static void	convert(long val, char *buf);
1004218Srobbin static void	dolink(const char *fromfield, const char *tofield);
1010Sstevel@tonic-gate static void	doabbr(char *abbr, const char *format,
1020Sstevel@tonic-gate 			const char *letters, int isdst);
1030Sstevel@tonic-gate static void	eat(const char *name, int num);
1040Sstevel@tonic-gate static void	eats(const char *name, int num,
1050Sstevel@tonic-gate 			const char *rname, int rnum);
1060Sstevel@tonic-gate static long	eitol(int i);
1070Sstevel@tonic-gate static void	error(const char *message);
1080Sstevel@tonic-gate static char	**getfields(char *buf);
1090Sstevel@tonic-gate static long	gethms(const char *string, const char *errstrng, int signable);
1100Sstevel@tonic-gate static void	infile(const char *filename);
1110Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
1120Sstevel@tonic-gate static void	inleap(char **fields, int nfields);
1130Sstevel@tonic-gate #endif
1140Sstevel@tonic-gate static void	inlink(char **fields, int nfields);
1150Sstevel@tonic-gate static void	inrule(char **fields, int nfields);
1160Sstevel@tonic-gate static int	inzcont(char **fields, int nfields);
1170Sstevel@tonic-gate static int	inzone(char **fields, int nfields);
1180Sstevel@tonic-gate static int	inzsub(char **fields, int nfields, int iscont);
1190Sstevel@tonic-gate static int	itsabbr(const char *abbr, const char *word);
1200Sstevel@tonic-gate static int	itsdir(const char *name);
1210Sstevel@tonic-gate static int	lowerit(int c);
1220Sstevel@tonic-gate static char	*memcheck(char *tocheck);
1230Sstevel@tonic-gate static int	mkdirs(char *filename);
1240Sstevel@tonic-gate static void	newabbr(const char *abbr);
1250Sstevel@tonic-gate static long	oadd(long t1, long t2);
1260Sstevel@tonic-gate static void	outzone(const struct zone *zp, int ntzones);
1270Sstevel@tonic-gate static void	puttzcode(long code, FILE *fp);
1280Sstevel@tonic-gate static int	rcomp(const void *leftp, const void *rightp);
1291138Srobbin static zic_t	rpytime(const struct rule *rp, int wantedy);
1300Sstevel@tonic-gate static void	rulesub(struct rule *rp,
1310Sstevel@tonic-gate 			const char *loyearp, const char *hiyearp,
1320Sstevel@tonic-gate 			const char *typep, const char *monthp,
1330Sstevel@tonic-gate 			const char *dayp, const char *timep);
1340Sstevel@tonic-gate static void	setboundaries(void);
1351138Srobbin static zic_t	tadd(zic_t t1, long t2);
1360Sstevel@tonic-gate static void	usage(void);
1370Sstevel@tonic-gate static void	writezone(const char *name);
1380Sstevel@tonic-gate static int	yearistype(int year, const char *type);
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate static int		charcnt;
1410Sstevel@tonic-gate static int		errors;
1420Sstevel@tonic-gate static const char	*filename;
1430Sstevel@tonic-gate static int		leapcnt;
1440Sstevel@tonic-gate static int		linenum;
1451138Srobbin static zic_t		max_time;
1460Sstevel@tonic-gate static int		max_year;
1470Sstevel@tonic-gate static int		max_year_representable;
1481138Srobbin static zic_t		min_time;
1490Sstevel@tonic-gate static int		min_year;
1500Sstevel@tonic-gate static int		min_year_representable;
1510Sstevel@tonic-gate static int		noise;
1520Sstevel@tonic-gate static const char	*rfilename;
1530Sstevel@tonic-gate static int		rlinenum;
1540Sstevel@tonic-gate static const char	*progname;
1550Sstevel@tonic-gate static int		timecnt;
1560Sstevel@tonic-gate static int		typecnt;
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate /*
1590Sstevel@tonic-gate  * Line codes.
1600Sstevel@tonic-gate  */
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate #define	LC_RULE		0
1630Sstevel@tonic-gate #define	LC_ZONE		1
1640Sstevel@tonic-gate #define	LC_LINK		2
1650Sstevel@tonic-gate #define	LC_LEAP		3
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate /*
1680Sstevel@tonic-gate  * Which fields are which on a Zone line.
1690Sstevel@tonic-gate  */
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate #define	ZF_NAME		1
1720Sstevel@tonic-gate #define	ZF_GMTOFF	2
1730Sstevel@tonic-gate #define	ZF_RULE		3
1740Sstevel@tonic-gate #define	ZF_FORMAT	4
1750Sstevel@tonic-gate #define	ZF_TILYEAR	5
1760Sstevel@tonic-gate #define	ZF_TILMONTH	6
1770Sstevel@tonic-gate #define	ZF_TILDAY	7
1780Sstevel@tonic-gate #define	ZF_TILTIME	8
1790Sstevel@tonic-gate #define	ZONE_MINFIELDS	5
1800Sstevel@tonic-gate #define	ZONE_MAXFIELDS	9
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate  * Which fields are which on a Zone continuation line.
1840Sstevel@tonic-gate  */
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate #define	ZFC_GMTOFF	0
1870Sstevel@tonic-gate #define	ZFC_RULE	1
1880Sstevel@tonic-gate #define	ZFC_FORMAT	2
1890Sstevel@tonic-gate #define	ZFC_TILYEAR	3
1900Sstevel@tonic-gate #define	ZFC_TILMONTH	4
1910Sstevel@tonic-gate #define	ZFC_TILDAY	5
1920Sstevel@tonic-gate #define	ZFC_TILTIME	6
1930Sstevel@tonic-gate #define	ZONEC_MINFIELDS	3
1940Sstevel@tonic-gate #define	ZONEC_MAXFIELDS	7
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate /*
1970Sstevel@tonic-gate  * Which files are which on a Rule line.
1980Sstevel@tonic-gate  */
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate #define	RF_NAME		1
2010Sstevel@tonic-gate #define	RF_LOYEAR	2
2020Sstevel@tonic-gate #define	RF_HIYEAR	3
2030Sstevel@tonic-gate #define	RF_COMMAND	4
2040Sstevel@tonic-gate #define	RF_MONTH	5
2050Sstevel@tonic-gate #define	RF_DAY		6
2060Sstevel@tonic-gate #define	RF_TOD		7
2070Sstevel@tonic-gate #define	RF_STDOFF	8
2080Sstevel@tonic-gate #define	RF_ABBRVAR	9
2090Sstevel@tonic-gate #define	RULE_FIELDS	10
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate /*
2120Sstevel@tonic-gate  * Which fields are which on a Link line.
2130Sstevel@tonic-gate  */
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate #define	LF_FROM		1
2160Sstevel@tonic-gate #define	LF_TO		2
2170Sstevel@tonic-gate #define	LINK_FIELDS	3
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate  * Which fields are which on a Leap line.
2210Sstevel@tonic-gate  */
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate #define	LP_YEAR		1
2240Sstevel@tonic-gate #define	LP_MONTH	2
2250Sstevel@tonic-gate #define	LP_DAY		3
2260Sstevel@tonic-gate #define	LP_TIME		4
2270Sstevel@tonic-gate #define	LP_CORR		5
2280Sstevel@tonic-gate #define	LP_ROLL		6
2290Sstevel@tonic-gate #define	LEAP_FIELDS	7
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate /*
2320Sstevel@tonic-gate  * Year synonyms.
2330Sstevel@tonic-gate  */
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate #define	YR_MINIMUM	0
2360Sstevel@tonic-gate #define	YR_MAXIMUM	1
2370Sstevel@tonic-gate #define	YR_ONLY		2
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate static struct rule	*rules;
2400Sstevel@tonic-gate static int		nrules;	/* number of rules */
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate static struct zone	*zones;
2430Sstevel@tonic-gate static int		nzones;	/* number of zones */
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate struct link {
2460Sstevel@tonic-gate 	const char	*l_filename;
2470Sstevel@tonic-gate 	int		l_linenum;
2480Sstevel@tonic-gate 	const char	*l_from;
2490Sstevel@tonic-gate 	const char	*l_to;
2500Sstevel@tonic-gate };
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate static struct link	*links;
2530Sstevel@tonic-gate static int		nlinks;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate struct lookup {
2560Sstevel@tonic-gate 	const char	*l_word;
2570Sstevel@tonic-gate 	const int	l_value;
2580Sstevel@tonic-gate };
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate static struct lookup const *byword(const char *string,
2610Sstevel@tonic-gate     const struct lookup *lp);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate static struct lookup const	line_codes[] = {
2640Sstevel@tonic-gate 	{ "Rule",	LC_RULE },
2650Sstevel@tonic-gate 	{ "Zone",	LC_ZONE },
2660Sstevel@tonic-gate 	{ "Link",	LC_LINK },
2670Sstevel@tonic-gate 	{ "Leap",	LC_LEAP },
2680Sstevel@tonic-gate 	{ NULL,		0}
2690Sstevel@tonic-gate };
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate static struct lookup const	mon_names[] = {
2720Sstevel@tonic-gate 	{ "January",	TM_JANUARY },
2730Sstevel@tonic-gate 	{ "February",	TM_FEBRUARY },
2740Sstevel@tonic-gate 	{ "March",	TM_MARCH },
2750Sstevel@tonic-gate 	{ "April",	TM_APRIL },
2760Sstevel@tonic-gate 	{ "May",	TM_MAY },
2770Sstevel@tonic-gate 	{ "June",	TM_JUNE },
2780Sstevel@tonic-gate 	{ "July",	TM_JULY },
2790Sstevel@tonic-gate 	{ "August",	TM_AUGUST },
2800Sstevel@tonic-gate 	{ "September",	TM_SEPTEMBER },
2810Sstevel@tonic-gate 	{ "October",	TM_OCTOBER },
2820Sstevel@tonic-gate 	{ "November",	TM_NOVEMBER },
2830Sstevel@tonic-gate 	{ "December",	TM_DECEMBER },
2840Sstevel@tonic-gate 	{ NULL,		0 }
2850Sstevel@tonic-gate };
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate static struct lookup const	wday_names[] = {
2880Sstevel@tonic-gate 	{ "Sunday",	TM_SUNDAY },
2890Sstevel@tonic-gate 	{ "Monday",	TM_MONDAY },
2900Sstevel@tonic-gate 	{ "Tuesday",	TM_TUESDAY },
2910Sstevel@tonic-gate 	{ "Wednesday",	TM_WEDNESDAY },
2920Sstevel@tonic-gate 	{ "Thursday",	TM_THURSDAY },
2930Sstevel@tonic-gate 	{ "Friday",	TM_FRIDAY },
2940Sstevel@tonic-gate 	{ "Saturday",	TM_SATURDAY },
2950Sstevel@tonic-gate 	{ NULL,		0 }
2960Sstevel@tonic-gate };
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate static struct lookup const	lasts[] = {
2990Sstevel@tonic-gate 	{ "last-Sunday",	TM_SUNDAY },
3000Sstevel@tonic-gate 	{ "last-Monday",	TM_MONDAY },
3010Sstevel@tonic-gate 	{ "last-Tuesday",	TM_TUESDAY },
3020Sstevel@tonic-gate 	{ "last-Wednesday",	TM_WEDNESDAY },
3030Sstevel@tonic-gate 	{ "last-Thursday",	TM_THURSDAY },
3040Sstevel@tonic-gate 	{ "last-Friday",	TM_FRIDAY },
3050Sstevel@tonic-gate 	{ "last-Saturday",	TM_SATURDAY },
3060Sstevel@tonic-gate 	{ NULL,			0 }
3070Sstevel@tonic-gate };
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate static struct lookup const	begin_years[] = {
3100Sstevel@tonic-gate 	{ "minimum",	YR_MINIMUM },
3110Sstevel@tonic-gate 	{ "maximum",	YR_MAXIMUM },
3120Sstevel@tonic-gate 	{ NULL,		0 }
3130Sstevel@tonic-gate };
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate static struct lookup const	end_years[] = {
3160Sstevel@tonic-gate 	{ "minimum",	YR_MINIMUM },
3170Sstevel@tonic-gate 	{ "maximum",	YR_MAXIMUM },
3180Sstevel@tonic-gate 	{ "only",	YR_ONLY },
3190Sstevel@tonic-gate 	{ NULL,		0 }
3200Sstevel@tonic-gate };
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate static struct lookup const	leap_types[] = {
3230Sstevel@tonic-gate 	{ "Rolling",	TRUE },
3240Sstevel@tonic-gate 	{ "Stationary",	FALSE },
3250Sstevel@tonic-gate 	{ NULL,		0 }
3260Sstevel@tonic-gate };
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate static const int	len_months[2][MONSPERYEAR] = {
3290Sstevel@tonic-gate 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
3300Sstevel@tonic-gate 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
3310Sstevel@tonic-gate };
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate static const int	len_years[2] = {
3340Sstevel@tonic-gate 	DAYSPERNYEAR, DAYSPERLYEAR
3350Sstevel@tonic-gate };
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate static struct attype {
3381138Srobbin 	zic_t		at;
3390Sstevel@tonic-gate 	unsigned char	type;
3400Sstevel@tonic-gate }			attypes[TZ_MAX_TIMES];
3410Sstevel@tonic-gate static long		gmtoffs[TZ_MAX_TYPES];
3420Sstevel@tonic-gate static char		isdsts[TZ_MAX_TYPES];
3430Sstevel@tonic-gate static unsigned char	abbrinds[TZ_MAX_TYPES];
3440Sstevel@tonic-gate static char		ttisstds[TZ_MAX_TYPES];
3450Sstevel@tonic-gate static char		ttisgmts[TZ_MAX_TYPES];
3460Sstevel@tonic-gate static char		chars[TZ_MAX_CHARS];
3471138Srobbin static zic_t		trans[TZ_MAX_LEAPS];
3480Sstevel@tonic-gate static long		corr[TZ_MAX_LEAPS];
3490Sstevel@tonic-gate static char		roll[TZ_MAX_LEAPS];
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate /*
3520Sstevel@tonic-gate  * Memory allocation.
3530Sstevel@tonic-gate  */
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate static char *
memcheck(ptr)3560Sstevel@tonic-gate memcheck(ptr)
3570Sstevel@tonic-gate char * const	ptr;
3580Sstevel@tonic-gate {
3590Sstevel@tonic-gate 	if (ptr == NULL) {
3600Sstevel@tonic-gate 		const char *e = strerror(errno);
3610Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Memory exhausted: %s\n"),
3620Sstevel@tonic-gate 			progname, e);
3631138Srobbin 		exit(EXIT_FAILURE);
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 	return (ptr);
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate #define	emalloc(size)		memcheck(imalloc(size))
3690Sstevel@tonic-gate #define	erealloc(ptr, size)	memcheck(irealloc((ptr), (size)))
3700Sstevel@tonic-gate #define	ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
3710Sstevel@tonic-gate #define	ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate /*
3740Sstevel@tonic-gate  * Error handling.
3750Sstevel@tonic-gate  */
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate static void
eats(name,num,rname,rnum)3780Sstevel@tonic-gate eats(name, num, rname, rnum)
3790Sstevel@tonic-gate const char * const	name;
3800Sstevel@tonic-gate const int		num;
3810Sstevel@tonic-gate const char * const	rname;
3820Sstevel@tonic-gate const int		rnum;
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate 	filename = name;
3850Sstevel@tonic-gate 	linenum = num;
3860Sstevel@tonic-gate 	rfilename = rname;
3870Sstevel@tonic-gate 	rlinenum = rnum;
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate static void
eat(name,num)3910Sstevel@tonic-gate eat(name, num)
3920Sstevel@tonic-gate const char * const	name;
3930Sstevel@tonic-gate const int		num;
3940Sstevel@tonic-gate {
3950Sstevel@tonic-gate 	eats(name, num, (char *)NULL, -1);
3960Sstevel@tonic-gate }
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate static void
error(string)3990Sstevel@tonic-gate error(string)
4000Sstevel@tonic-gate const char * const	string;
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 	/*
4030Sstevel@tonic-gate 	 * Match the format of "cc" to allow sh users to
4040Sstevel@tonic-gate 	 *	zic ... 2>&1 | error -t "*" -v
4050Sstevel@tonic-gate 	 * on BSD systems.
4060Sstevel@tonic-gate 	 */
4070Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("\"%s\", line %d: %s"),
4080Sstevel@tonic-gate 		filename, linenum, string);
4090Sstevel@tonic-gate 	if (rfilename != NULL)
4100Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(" (rule from \"%s\", line %d)"),
4110Sstevel@tonic-gate 			rfilename, rlinenum);
4120Sstevel@tonic-gate 	(void) fprintf(stderr, "\n");
4130Sstevel@tonic-gate 	++errors;
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate static void
warning(string)4170Sstevel@tonic-gate warning(string)
4180Sstevel@tonic-gate const char * const	string;
4190Sstevel@tonic-gate {
4200Sstevel@tonic-gate 	char *cp;
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	cp = ecpyalloc(gettext("warning: "));
4230Sstevel@tonic-gate 	cp = ecatalloc(cp, string);
4240Sstevel@tonic-gate 	error(cp);
4250Sstevel@tonic-gate 	ifree(cp);
4260Sstevel@tonic-gate 	--errors;
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate static void
usage(void)4300Sstevel@tonic-gate usage(void)
4310Sstevel@tonic-gate {
4320Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
4331138Srobbin 	(void) fprintf(stderr, gettext("%s: usage is %s "
4341138Srobbin 	    "[ --version ] [ -s ] [ -v ] [ -l localtime ] "
4351138Srobbin 	    "\n\t[ -p posixrules ] [ -d directory ] [ -L leapseconds ] "
4361138Srobbin 	    "[ -y yearistype ] [ filename ... ]\n"), progname, progname);
4370Sstevel@tonic-gate #else /* ! LEAPSECOND_SUPPORT */
4381138Srobbin 	(void) fprintf(stderr, gettext("%s: usage is %s "
4391138Srobbin 	    "[ --version ] [ -s ] [ -v ] [ -l localtime ]"
4401138Srobbin 	    "\n\t[ -p posixrules ] [ -d directory ] [ -y yearistype ] "
4411138Srobbin 	    "[ filename ... ]\n"), progname, progname);
4420Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate static const char	*psxrules;
4460Sstevel@tonic-gate static const char	*lcltime;
4470Sstevel@tonic-gate static const char	*directory;
4480Sstevel@tonic-gate static const char	*leapsec;
4490Sstevel@tonic-gate static const char	*yitcommand;
4500Sstevel@tonic-gate static int		sflag = FALSE;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate int
main(argc,argv)4530Sstevel@tonic-gate main(argc, argv)
4540Sstevel@tonic-gate int	argc;
4550Sstevel@tonic-gate char	*argv[];
4560Sstevel@tonic-gate {
4570Sstevel@tonic-gate 	register int	i;
4580Sstevel@tonic-gate 	register int	j;
4590Sstevel@tonic-gate 	register int	c;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4640Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
4650Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
4660Sstevel@tonic-gate #endif
4670Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 	progname = argv[0];
4701138Srobbin 	for (i = 1; i < argc; ++i)
4711138Srobbin 		if (strcmp(argv[i], "--version") == 0) {
4721138Srobbin 			(void) printf("%s\n", elsieid);
4731138Srobbin 			exit(EXIT_SUCCESS);
4741138Srobbin 		}
4751138Srobbin 
4760Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
4770Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF)
4780Sstevel@tonic-gate #else
4790Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "d:l:p:vsy:")) != EOF)
4800Sstevel@tonic-gate #endif
4810Sstevel@tonic-gate 		switch (c) {
4820Sstevel@tonic-gate 			default:
4830Sstevel@tonic-gate 				usage();
4840Sstevel@tonic-gate 			case 'd':
4850Sstevel@tonic-gate 				if (directory == NULL)
4860Sstevel@tonic-gate 					directory = optarg;
4870Sstevel@tonic-gate 				else {
4880Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
4890Sstevel@tonic-gate "%s: More than one -d option specified\n"),
4900Sstevel@tonic-gate 						progname);
4911138Srobbin 					exit(EXIT_FAILURE);
4920Sstevel@tonic-gate 				}
4930Sstevel@tonic-gate 				break;
4940Sstevel@tonic-gate 			case 'l':
4950Sstevel@tonic-gate 				if (lcltime == NULL)
4960Sstevel@tonic-gate 					lcltime = optarg;
4970Sstevel@tonic-gate 				else {
4980Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
4990Sstevel@tonic-gate "%s: More than one -l option specified\n"),
5000Sstevel@tonic-gate 						progname);
5011138Srobbin 					exit(EXIT_FAILURE);
5020Sstevel@tonic-gate 				}
5030Sstevel@tonic-gate 				break;
5040Sstevel@tonic-gate 			case 'p':
5050Sstevel@tonic-gate 				if (psxrules == NULL)
5060Sstevel@tonic-gate 					psxrules = optarg;
5070Sstevel@tonic-gate 				else {
5080Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
5090Sstevel@tonic-gate "%s: More than one -p option specified\n"),
5100Sstevel@tonic-gate 						progname);
5111138Srobbin 					exit(EXIT_FAILURE);
5120Sstevel@tonic-gate 				}
5130Sstevel@tonic-gate 				break;
5140Sstevel@tonic-gate 			case 'y':
5150Sstevel@tonic-gate 				if (yitcommand == NULL)
5160Sstevel@tonic-gate 					yitcommand = optarg;
5170Sstevel@tonic-gate 				else {
5180Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
5190Sstevel@tonic-gate "%s: More than one -y option specified\n"),
5200Sstevel@tonic-gate 						progname);
5211138Srobbin 					exit(EXIT_FAILURE);
5220Sstevel@tonic-gate 				}
5230Sstevel@tonic-gate 				break;
5240Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
5250Sstevel@tonic-gate 			case 'L':
5260Sstevel@tonic-gate 				if (leapsec == NULL)
5270Sstevel@tonic-gate 					leapsec = optarg;
5280Sstevel@tonic-gate 				else {
5290Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
5300Sstevel@tonic-gate "%s: More than one -L option specified\n"),
5310Sstevel@tonic-gate 						progname);
5321138Srobbin 					exit(EXIT_FAILURE);
5330Sstevel@tonic-gate 				}
5340Sstevel@tonic-gate 				break;
5350Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
5360Sstevel@tonic-gate 			case 'v':
5370Sstevel@tonic-gate 				noise = TRUE;
5380Sstevel@tonic-gate 				break;
5390Sstevel@tonic-gate 			case 's':
5400Sstevel@tonic-gate 				sflag = TRUE;
5410Sstevel@tonic-gate 				break;
5420Sstevel@tonic-gate 		}
5430Sstevel@tonic-gate 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
5440Sstevel@tonic-gate 		usage();	/* usage message by request */
5450Sstevel@tonic-gate 	if (directory == NULL)
5460Sstevel@tonic-gate 		directory = TZDIR;
5470Sstevel@tonic-gate 	if (yitcommand == NULL)
5480Sstevel@tonic-gate 		yitcommand = "yearistype";
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	setboundaries();
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
5530Sstevel@tonic-gate 	if (optind < argc && leapsec != NULL) {
5540Sstevel@tonic-gate 		infile(leapsec);
5550Sstevel@tonic-gate 		adjleap();
5560Sstevel@tonic-gate 	}
5570Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	for (i = optind; i < argc; ++i)
5600Sstevel@tonic-gate 		infile(argv[i]);
5610Sstevel@tonic-gate 	if (errors)
5621138Srobbin 		exit(EXIT_FAILURE);
5630Sstevel@tonic-gate 	associate();
5640Sstevel@tonic-gate 	for (i = 0; i < nzones; i = j) {
5650Sstevel@tonic-gate 		/*
5660Sstevel@tonic-gate 		 * Find the next non-continuation zone entry.
5670Sstevel@tonic-gate 		 */
5680Sstevel@tonic-gate 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
5690Sstevel@tonic-gate 			continue;
5700Sstevel@tonic-gate 		outzone(&zones[i], j - i);
5710Sstevel@tonic-gate 	}
5720Sstevel@tonic-gate 	/*
5730Sstevel@tonic-gate 	 * Make links.
5740Sstevel@tonic-gate 	 */
5750Sstevel@tonic-gate 	for (i = 0; i < nlinks; ++i) {
5760Sstevel@tonic-gate 		eat(links[i].l_filename, links[i].l_linenum);
5770Sstevel@tonic-gate 		dolink(links[i].l_from, links[i].l_to);
5781138Srobbin 		if (noise)
5791138Srobbin 			for (j = 0; j < nlinks; ++j)
5801138Srobbin 				if (strcmp(links[i].l_to, links[j].l_from) == 0)
5811138Srobbin 					warning(gettext("link to link"));
5820Sstevel@tonic-gate 	}
5830Sstevel@tonic-gate 	if (lcltime != NULL) {
5840Sstevel@tonic-gate 		eat("command line", 1);
5850Sstevel@tonic-gate 		dolink(lcltime, TZDEFAULT);
5860Sstevel@tonic-gate 	}
5870Sstevel@tonic-gate 	if (psxrules != NULL) {
5880Sstevel@tonic-gate 		eat("command line", 1);
5890Sstevel@tonic-gate 		dolink(psxrules, TZDEFRULES);
5900Sstevel@tonic-gate 	}
5910Sstevel@tonic-gate 	return ((errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate static void
dolink(fromfield,tofield)5954218Srobbin dolink(fromfield, tofield)
5964218Srobbin const char * const	fromfield;
5974218Srobbin const char * const	tofield;
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate 	register char *fromname;
6000Sstevel@tonic-gate 	register char *toname;
6010Sstevel@tonic-gate 
6024218Srobbin 	if (fromfield[0] == '/')
6034218Srobbin 		fromname = ecpyalloc(fromfield);
6040Sstevel@tonic-gate 	else {
6050Sstevel@tonic-gate 		fromname = ecpyalloc(directory);
6060Sstevel@tonic-gate 		fromname = ecatalloc(fromname, "/");
6074218Srobbin 		fromname = ecatalloc(fromname, fromfield);
6080Sstevel@tonic-gate 	}
6094218Srobbin 	if (tofield[0] == '/')
6104218Srobbin 		toname = ecpyalloc(tofield);
6110Sstevel@tonic-gate 	else {
6120Sstevel@tonic-gate 		toname = ecpyalloc(directory);
6130Sstevel@tonic-gate 		toname = ecatalloc(toname, "/");
6144218Srobbin 		toname = ecatalloc(toname, tofield);
6150Sstevel@tonic-gate 	}
6160Sstevel@tonic-gate 	/*
6170Sstevel@tonic-gate 	 * We get to be careful here since
6180Sstevel@tonic-gate 	 * there's a fair chance of root running us.
6190Sstevel@tonic-gate 	 */
6200Sstevel@tonic-gate 	if (!itsdir(toname))
6210Sstevel@tonic-gate 		(void) remove(toname);
6220Sstevel@tonic-gate 	if (link(fromname, toname) != 0) {
6230Sstevel@tonic-gate 		int	result;
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 		if (mkdirs(toname) != 0)
6261138Srobbin 			exit(EXIT_FAILURE);
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 		result = link(fromname, toname);
6290Sstevel@tonic-gate 
6301138Srobbin 		if (result != 0 && access(fromname, F_OK) == 0 &&
6311138Srobbin 		    !itsdir(fromname)) {
6324218Srobbin 			const char *s = tofield;
6330Sstevel@tonic-gate 			register char *symlinkcontents = NULL;
6341138Srobbin 
6350Sstevel@tonic-gate 			while ((s = strchr(s+1, '/')) != NULL)
6360Sstevel@tonic-gate 				symlinkcontents = ecatalloc(symlinkcontents,
6371138Srobbin 					"../");
6384218Srobbin 			symlinkcontents = ecatalloc(symlinkcontents, fromname);
6390Sstevel@tonic-gate 			result = symlink(symlinkcontents, toname);
6400Sstevel@tonic-gate 			if (result == 0)
6410Sstevel@tonic-gate 				warning(gettext(
6420Sstevel@tonic-gate 				    "hard link failed, symbolic link used"));
6430Sstevel@tonic-gate 			ifree(symlinkcontents);
6440Sstevel@tonic-gate 		}
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 		if (result != 0) {
6470Sstevel@tonic-gate 			const char *e = strerror(errno);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6500Sstevel@tonic-gate 				"%s: Can't link from %s to %s: %s\n"),
6510Sstevel@tonic-gate 				progname, fromname, toname, e);
6521138Srobbin 			exit(EXIT_FAILURE);
6530Sstevel@tonic-gate 		}
6540Sstevel@tonic-gate 	}
6550Sstevel@tonic-gate 	ifree(fromname);
6560Sstevel@tonic-gate 	ifree(toname);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate #ifndef INT_MAX
6600Sstevel@tonic-gate #define	INT_MAX		((int)(((unsigned)~0)>>1))
6610Sstevel@tonic-gate #endif /* !defined INT_MAX */
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate #ifndef INT_MIN
6640Sstevel@tonic-gate #define	INT_MIN		((int)~(((unsigned)~0)>>1))
6650Sstevel@tonic-gate #endif /* !defined INT_MIN */
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate  * The tz file format currently allows at most 32-bit quantities.
6690Sstevel@tonic-gate  * This restriction should be removed before signed 32-bit values
6700Sstevel@tonic-gate  * wrap around in 2038, but unfortunately this will require a
6710Sstevel@tonic-gate  * change to the tz file format.
6720Sstevel@tonic-gate  */
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate #define	MAX_BITS_IN_FILE	32
6750Sstevel@tonic-gate /* CSTYLED */
6761138Srobbin #define	TIME_T_BITS_IN_FILE	((TYPE_BIT(zic_t) < MAX_BITS_IN_FILE) ? \
6771138Srobbin 				    TYPE_BIT(zic_t): MAX_BITS_IN_FILE)
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate static void
setboundaries(void)6800Sstevel@tonic-gate setboundaries(void)
6810Sstevel@tonic-gate {
6821138Srobbin 	register int	i;
6831138Srobbin 
6841138Srobbin 	if (TYPE_SIGNED(zic_t)) {
6851138Srobbin 		min_time = -1;
6861138Srobbin 		for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
6871138Srobbin 			min_time *= 2;
6881138Srobbin 		max_time = -(min_time + 1);
6890Sstevel@tonic-gate 		if (sflag)
6900Sstevel@tonic-gate 			min_time = 0;
6910Sstevel@tonic-gate 	} else {
6920Sstevel@tonic-gate 		min_time = 0;
6930Sstevel@tonic-gate 		max_time = 2 - sflag;
6941138Srobbin 		for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
6951138Srobbin 			max_time *= 2;
6960Sstevel@tonic-gate 		--max_time;
6970Sstevel@tonic-gate 	}
6981138Srobbin 	{
6991138Srobbin 		time_t	t;
7001138Srobbin 
7011138Srobbin 		t = (time_t)min_time;
7021138Srobbin 		min_year = TM_YEAR_BASE + gmtime(&t)->tm_year;
7031138Srobbin 		t = (time_t)max_time;
7041138Srobbin 		max_year = TM_YEAR_BASE + gmtime(&t)->tm_year;
7051138Srobbin 	}
7060Sstevel@tonic-gate 	min_year_representable = min_year;
7070Sstevel@tonic-gate 	max_year_representable = max_year;
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate static int
itsdir(name)7110Sstevel@tonic-gate itsdir(name)
7120Sstevel@tonic-gate const char * const	name;
7130Sstevel@tonic-gate {
7140Sstevel@tonic-gate 	register char	*myname;
7150Sstevel@tonic-gate 	register int	accres;
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	myname = ecpyalloc(name);
7180Sstevel@tonic-gate 	myname = ecatalloc(myname, "/.");
7190Sstevel@tonic-gate 	accres = access(myname, F_OK);
7200Sstevel@tonic-gate 	ifree(myname);
7210Sstevel@tonic-gate 	return (accres == 0);
7220Sstevel@tonic-gate }
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate /*
7250Sstevel@tonic-gate  * Associate sets of rules with zones.
7260Sstevel@tonic-gate  */
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate /*
7290Sstevel@tonic-gate  * Sort by rule name.
7300Sstevel@tonic-gate  */
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate static int
rcomp(cp1,cp2)7330Sstevel@tonic-gate rcomp(cp1, cp2)
7340Sstevel@tonic-gate const void *	cp1;
7350Sstevel@tonic-gate const void *	cp2;
7360Sstevel@tonic-gate {
7370Sstevel@tonic-gate 	return (strcmp(((const struct rule *) cp1)->r_name,
7380Sstevel@tonic-gate 		((const struct rule *) cp2)->r_name));
7390Sstevel@tonic-gate }
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate static void
associate(void)7420Sstevel@tonic-gate associate(void)
7430Sstevel@tonic-gate {
7440Sstevel@tonic-gate 	register struct zone	*zp;
7450Sstevel@tonic-gate 	register struct rule	*rp;
7460Sstevel@tonic-gate 	register int		base, out;
7470Sstevel@tonic-gate 	register int		i, j;
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	if (nrules != 0) {
7500Sstevel@tonic-gate 		(void) qsort((void *)rules, (size_t)nrules,
7510Sstevel@tonic-gate 			(size_t)sizeof (*rules), rcomp);
7520Sstevel@tonic-gate 		for (i = 0; i < nrules - 1; ++i) {
7530Sstevel@tonic-gate 			if (strcmp(rules[i].r_name,
7540Sstevel@tonic-gate 				rules[i + 1].r_name) != 0)
7550Sstevel@tonic-gate 					continue;
7560Sstevel@tonic-gate 			if (strcmp(rules[i].r_filename,
7570Sstevel@tonic-gate 				rules[i + 1].r_filename) == 0)
7580Sstevel@tonic-gate 					continue;
7590Sstevel@tonic-gate 			eat(rules[i].r_filename, rules[i].r_linenum);
7600Sstevel@tonic-gate 			warning(gettext("same rule name in multiple files"));
7610Sstevel@tonic-gate 			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
7620Sstevel@tonic-gate 			warning(gettext("same rule name in multiple files"));
7630Sstevel@tonic-gate 			for (j = i + 2; j < nrules; ++j) {
7640Sstevel@tonic-gate 				if (strcmp(rules[i].r_name,
7650Sstevel@tonic-gate 					rules[j].r_name) != 0)
7660Sstevel@tonic-gate 						break;
7670Sstevel@tonic-gate 				if (strcmp(rules[i].r_filename,
7680Sstevel@tonic-gate 					rules[j].r_filename) == 0)
7690Sstevel@tonic-gate 						continue;
7700Sstevel@tonic-gate 				if (strcmp(rules[i + 1].r_filename,
7710Sstevel@tonic-gate 					rules[j].r_filename) == 0)
7720Sstevel@tonic-gate 						continue;
7730Sstevel@tonic-gate 				break;
7740Sstevel@tonic-gate 			}
7750Sstevel@tonic-gate 			i = j - 1;
7760Sstevel@tonic-gate 		}
7770Sstevel@tonic-gate 	}
7780Sstevel@tonic-gate 	for (i = 0; i < nzones; ++i) {
7790Sstevel@tonic-gate 		zp = &zones[i];
7800Sstevel@tonic-gate 		zp->z_rules = NULL;
7810Sstevel@tonic-gate 		zp->z_nrules = 0;
7820Sstevel@tonic-gate 	}
7830Sstevel@tonic-gate 	for (base = 0; base < nrules; base = out) {
7840Sstevel@tonic-gate 		rp = &rules[base];
7850Sstevel@tonic-gate 		for (out = base + 1; out < nrules; ++out)
7860Sstevel@tonic-gate 			if (strcmp(rp->r_name, rules[out].r_name) != 0)
7870Sstevel@tonic-gate 				break;
7880Sstevel@tonic-gate 		for (i = 0; i < nzones; ++i) {
7890Sstevel@tonic-gate 			zp = &zones[i];
7900Sstevel@tonic-gate 			if (strcmp(zp->z_rule, rp->r_name) != 0)
7910Sstevel@tonic-gate 				continue;
7920Sstevel@tonic-gate 			zp->z_rules = rp;
7930Sstevel@tonic-gate 			zp->z_nrules = out - base;
7940Sstevel@tonic-gate 		}
7950Sstevel@tonic-gate 	}
7960Sstevel@tonic-gate 	for (i = 0; i < nzones; ++i) {
7970Sstevel@tonic-gate 		zp = &zones[i];
7980Sstevel@tonic-gate 		if (zp->z_nrules == 0) {
7990Sstevel@tonic-gate 			/*
8000Sstevel@tonic-gate 			 * Maybe we have a local standard time offset.
8010Sstevel@tonic-gate 			 */
8020Sstevel@tonic-gate 			eat(zp->z_filename, zp->z_linenum);
8030Sstevel@tonic-gate 			zp->z_stdoff = gethms(zp->z_rule,
8040Sstevel@tonic-gate 				gettext("unruly zone"), TRUE);
8050Sstevel@tonic-gate 			/*
8060Sstevel@tonic-gate 			 * Note, though, that if there's no rule,
8070Sstevel@tonic-gate 			 * a '%s' in the format is a bad thing.
8080Sstevel@tonic-gate 			 */
8090Sstevel@tonic-gate 			if (strchr(zp->z_format, '%') != 0)
8100Sstevel@tonic-gate 				error(gettext("%s in ruleless zone"));
8110Sstevel@tonic-gate 		}
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 	if (errors)
8141138Srobbin 		exit(EXIT_FAILURE);
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate static void
infile(name)8180Sstevel@tonic-gate infile(name)
8190Sstevel@tonic-gate const char *name;
8200Sstevel@tonic-gate {
8210Sstevel@tonic-gate 	register FILE			*fp;
8220Sstevel@tonic-gate 	register char			**fields;
8230Sstevel@tonic-gate 	register char			*cp;
8240Sstevel@tonic-gate 	register const struct lookup	*lp;
8250Sstevel@tonic-gate 	register int			nfields;
8260Sstevel@tonic-gate 	register int			wantcont;
8270Sstevel@tonic-gate 	register int			num;
8280Sstevel@tonic-gate 	char				buf[BUFSIZ];
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	if (strcmp(name, "-") == 0) {
8310Sstevel@tonic-gate 		name = gettext("standard input");
8320Sstevel@tonic-gate 		fp = stdin;
8330Sstevel@tonic-gate 	} else if ((fp = fopen(name, "r")) == NULL) {
8340Sstevel@tonic-gate 		const char *e = strerror(errno);
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Can't open %s: %s\n"),
8370Sstevel@tonic-gate 			progname, name, e);
8381138Srobbin 		exit(EXIT_FAILURE);
8390Sstevel@tonic-gate 	}
8400Sstevel@tonic-gate 	wantcont = FALSE;
8410Sstevel@tonic-gate 	for (num = 1; ; ++num) {
8420Sstevel@tonic-gate 		eat(name, num);
8430Sstevel@tonic-gate 		if (fgets(buf, (int)sizeof (buf), fp) != buf)
8440Sstevel@tonic-gate 			break;
8450Sstevel@tonic-gate 		cp = strchr(buf, '\n');
8460Sstevel@tonic-gate 		if (cp == NULL) {
8470Sstevel@tonic-gate 			error(gettext("line too long"));
8481138Srobbin 			exit(EXIT_FAILURE);
8490Sstevel@tonic-gate 		}
8500Sstevel@tonic-gate 		*cp = '\0';
8510Sstevel@tonic-gate 		fields = getfields(buf);
8520Sstevel@tonic-gate 		nfields = 0;
8530Sstevel@tonic-gate 		while (fields[nfields] != NULL) {
8540Sstevel@tonic-gate 			static char	nada;
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 			if (strcmp(fields[nfields], "-") == 0)
8570Sstevel@tonic-gate 				fields[nfields] = &nada;
8580Sstevel@tonic-gate 			++nfields;
8590Sstevel@tonic-gate 		}
8600Sstevel@tonic-gate 		if (nfields == 0) {
8610Sstevel@tonic-gate 			/* nothing to do */
8620Sstevel@tonic-gate 		} else if (wantcont) {
8630Sstevel@tonic-gate 			wantcont = inzcont(fields, nfields);
8640Sstevel@tonic-gate 		} else {
8650Sstevel@tonic-gate 			lp = byword(fields[0], line_codes);
8660Sstevel@tonic-gate 			if (lp == NULL)
8670Sstevel@tonic-gate 				error(gettext("input line of unknown type"));
8680Sstevel@tonic-gate 			else switch ((int)(lp->l_value)) {
8690Sstevel@tonic-gate 				case LC_RULE:
8700Sstevel@tonic-gate 					inrule(fields, nfields);
8710Sstevel@tonic-gate 					wantcont = FALSE;
8720Sstevel@tonic-gate 					break;
8730Sstevel@tonic-gate 				case LC_ZONE:
8740Sstevel@tonic-gate 					wantcont = inzone(fields, nfields);
8750Sstevel@tonic-gate 					break;
8760Sstevel@tonic-gate 				case LC_LINK:
8770Sstevel@tonic-gate 					inlink(fields, nfields);
8780Sstevel@tonic-gate 					wantcont = FALSE;
8790Sstevel@tonic-gate 					break;
8800Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
8810Sstevel@tonic-gate 				case LC_LEAP:
8820Sstevel@tonic-gate 					if (name != leapsec)
8830Sstevel@tonic-gate 						(void) fprintf(stderr, gettext(
8840Sstevel@tonic-gate "%s: Leap line in non leap seconds file %s\n"),
8850Sstevel@tonic-gate 							progname, name);
8860Sstevel@tonic-gate 					else	inleap(fields, nfields);
8870Sstevel@tonic-gate 					wantcont = FALSE;
8880Sstevel@tonic-gate 					break;
8890Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
8900Sstevel@tonic-gate 				default:	/* "cannot happen" */
8910Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
8920Sstevel@tonic-gate "%s: panic: Invalid l_value %d\n"),
8930Sstevel@tonic-gate 						progname, lp->l_value);
8941138Srobbin 					exit(EXIT_FAILURE);
8950Sstevel@tonic-gate 			}
8960Sstevel@tonic-gate 		}
8970Sstevel@tonic-gate 		ifree((char *)fields);
8980Sstevel@tonic-gate 	}
8990Sstevel@tonic-gate 	if (ferror(fp)) {
9000Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Error reading %s\n"),
9010Sstevel@tonic-gate 			progname, filename);
9021138Srobbin 		exit(EXIT_FAILURE);
9030Sstevel@tonic-gate 	}
9040Sstevel@tonic-gate 	if (fp != stdin && fclose(fp)) {
9050Sstevel@tonic-gate 		const char *e = strerror(errno);
9060Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Error closing %s: %s\n"),
9070Sstevel@tonic-gate 			progname, filename, e);
9081138Srobbin 		exit(EXIT_FAILURE);
9090Sstevel@tonic-gate 	}
9100Sstevel@tonic-gate 	if (wantcont)
9110Sstevel@tonic-gate 		error(gettext("expected continuation line not found"));
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate /*
9150Sstevel@tonic-gate  * Convert a string of one of the forms
9160Sstevel@tonic-gate  *	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
9170Sstevel@tonic-gate  * into a number of seconds.
9180Sstevel@tonic-gate  * A null string maps to zero.
9190Sstevel@tonic-gate  * Call error with errstring and return zero on errors.
9200Sstevel@tonic-gate  */
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate static long
gethms(string,errstring,signable)9230Sstevel@tonic-gate gethms(string, errstring, signable)
9240Sstevel@tonic-gate const char		*string;
9250Sstevel@tonic-gate const char * const	errstring;
9260Sstevel@tonic-gate const int		signable;
9270Sstevel@tonic-gate {
9284218Srobbin 	long	hh;
9294218Srobbin 	int	mm, ss, sign;
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	if (string == NULL || *string == '\0')
9320Sstevel@tonic-gate 		return (0);
9330Sstevel@tonic-gate 	if (!signable)
9340Sstevel@tonic-gate 		sign = 1;
9350Sstevel@tonic-gate 	else if (*string == '-') {
9360Sstevel@tonic-gate 		sign = -1;
9370Sstevel@tonic-gate 		++string;
9380Sstevel@tonic-gate 	} else	sign = 1;
9394218Srobbin 	if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
9400Sstevel@tonic-gate 		mm = ss = 0;
9414218Srobbin 	else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
9420Sstevel@tonic-gate 		ss = 0;
9434218Srobbin 	else if (sscanf(string, scheck(string, "%ld:%d:%d"),
9440Sstevel@tonic-gate 		&hh, &mm, &ss) != 3) {
9450Sstevel@tonic-gate 			error(errstring);
9460Sstevel@tonic-gate 			return (0);
9470Sstevel@tonic-gate 	}
9484218Srobbin 	if (hh < 0 ||
9490Sstevel@tonic-gate 		mm < 0 || mm >= MINSPERHOUR ||
9504218Srobbin 		ss < 0 || ss > SECSPERMIN) {
9510Sstevel@tonic-gate 			error(errstring);
9520Sstevel@tonic-gate 			return (0);
9530Sstevel@tonic-gate 	}
9544218Srobbin 	if (LONG_MAX / SECSPERHOUR < hh) {
9554218Srobbin 		error(gettext("time overflow"));
9564218Srobbin 		return (0);
9574218Srobbin 	}
9584218Srobbin 	if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
9591138Srobbin 		warning(
9601138Srobbin 		    gettext("24:00 not handled by pre-1998 versions of zic"));
9614218Srobbin 	if (noise && (hh > HOURSPERDAY ||
9624218Srobbin 		(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
9634218Srobbin 		warning(gettext("values over 24 hours not handled by "
9644218Srobbin 			    "pre-2007 versions of zic"));
9654218Srobbin 
9664218Srobbin 	return (oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
9674218Srobbin 		eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))));
9680Sstevel@tonic-gate }
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate static void
inrule(fields,nfields)9710Sstevel@tonic-gate inrule(fields, nfields)
9720Sstevel@tonic-gate register char ** const	fields;
9730Sstevel@tonic-gate const int		nfields;
9740Sstevel@tonic-gate {
9750Sstevel@tonic-gate 	static struct rule	r;
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	if (nfields != RULE_FIELDS) {
9780Sstevel@tonic-gate 		error(gettext("wrong number of fields on Rule line"));
9790Sstevel@tonic-gate 		return;
9800Sstevel@tonic-gate 	}
9810Sstevel@tonic-gate 	if (*fields[RF_NAME] == '\0') {
9820Sstevel@tonic-gate 		error(gettext("nameless rule"));
9830Sstevel@tonic-gate 		return;
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate 	r.r_filename = filename;
9860Sstevel@tonic-gate 	r.r_linenum = linenum;
9870Sstevel@tonic-gate 	r.r_stdoff = gethms(fields[RF_STDOFF], gettext("invalid saved time"),
9880Sstevel@tonic-gate 		TRUE);
9890Sstevel@tonic-gate 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
9900Sstevel@tonic-gate 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
9910Sstevel@tonic-gate 	r.r_name = ecpyalloc(fields[RF_NAME]);
9920Sstevel@tonic-gate 	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
9930Sstevel@tonic-gate 	rules = (struct rule *)(void *)erealloc((char *)rules,
9940Sstevel@tonic-gate 		(int)((nrules + 1) * sizeof (*rules)));
9950Sstevel@tonic-gate 	rules[nrules++] = r;
9960Sstevel@tonic-gate }
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate static int
inzone(fields,nfields)9990Sstevel@tonic-gate inzone(fields, nfields)
10000Sstevel@tonic-gate register char ** const	fields;
10010Sstevel@tonic-gate const int		nfields;
10020Sstevel@tonic-gate {
10030Sstevel@tonic-gate 	register int	i;
10040Sstevel@tonic-gate 	static char	*buf;
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
10070Sstevel@tonic-gate 		error(gettext("wrong number of fields on Zone line"));
10080Sstevel@tonic-gate 		return (FALSE);
10090Sstevel@tonic-gate 	}
10100Sstevel@tonic-gate 	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
10110Sstevel@tonic-gate 		buf = erealloc(buf, (int)(132 + strlen(TZDEFAULT)));
10120Sstevel@tonic-gate 		(void) sprintf(buf,
10130Sstevel@tonic-gate gettext("\"Zone %s\" line and -l option are mutually exclusive"),
10140Sstevel@tonic-gate 			TZDEFAULT);
10150Sstevel@tonic-gate 		error(buf);
10160Sstevel@tonic-gate 		return (FALSE);
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
10190Sstevel@tonic-gate 		buf = erealloc(buf, (int)(132 + strlen(TZDEFRULES)));
10200Sstevel@tonic-gate 		(void) sprintf(buf,
10210Sstevel@tonic-gate gettext("\"Zone %s\" line and -p option are mutually exclusive"),
10220Sstevel@tonic-gate 			TZDEFRULES);
10230Sstevel@tonic-gate 		error(buf);
10240Sstevel@tonic-gate 		return (FALSE);
10250Sstevel@tonic-gate 	}
10260Sstevel@tonic-gate 	for (i = 0; i < nzones; ++i)
10270Sstevel@tonic-gate 		if (zones[i].z_name != NULL &&
10280Sstevel@tonic-gate 			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
10290Sstevel@tonic-gate 				buf = erealloc(buf, (int)(132 +
10300Sstevel@tonic-gate 					strlen(fields[ZF_NAME]) +
10310Sstevel@tonic-gate 					strlen(zones[i].z_filename)));
10320Sstevel@tonic-gate 				(void) sprintf(buf,
10330Sstevel@tonic-gate gettext("duplicate zone name %s (file \"%s\", line %d)"),
10340Sstevel@tonic-gate 					fields[ZF_NAME],
10350Sstevel@tonic-gate 					zones[i].z_filename,
10360Sstevel@tonic-gate 					zones[i].z_linenum);
10370Sstevel@tonic-gate 				error(buf);
10380Sstevel@tonic-gate 				return (FALSE);
10390Sstevel@tonic-gate 		}
10400Sstevel@tonic-gate 	return (inzsub(fields, nfields, FALSE));
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate static int
inzcont(fields,nfields)10440Sstevel@tonic-gate inzcont(fields, nfields)
10450Sstevel@tonic-gate register char ** const	fields;
10460Sstevel@tonic-gate const int		nfields;
10470Sstevel@tonic-gate {
10480Sstevel@tonic-gate 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
10490Sstevel@tonic-gate 		error(gettext(
10500Sstevel@tonic-gate 		    "wrong number of fields on Zone continuation line"));
10510Sstevel@tonic-gate 		return (FALSE);
10520Sstevel@tonic-gate 	}
10530Sstevel@tonic-gate 	return (inzsub(fields, nfields, TRUE));
10540Sstevel@tonic-gate }
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate static int
inzsub(fields,nfields,iscont)10570Sstevel@tonic-gate inzsub(fields, nfields, iscont)
10580Sstevel@tonic-gate register char ** const	fields;
10590Sstevel@tonic-gate const int		nfields;
10600Sstevel@tonic-gate const int		iscont;
10610Sstevel@tonic-gate {
10620Sstevel@tonic-gate 	register char		*cp;
10630Sstevel@tonic-gate 	static struct zone	z;
10640Sstevel@tonic-gate 	register int		i_gmtoff, i_rule, i_format;
10650Sstevel@tonic-gate 	register int		i_untilyear, i_untilmonth;
10660Sstevel@tonic-gate 	register int		i_untilday, i_untiltime;
10670Sstevel@tonic-gate 	register int		hasuntil;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	if (iscont) {
10700Sstevel@tonic-gate 		i_gmtoff = ZFC_GMTOFF;
10710Sstevel@tonic-gate 		i_rule = ZFC_RULE;
10720Sstevel@tonic-gate 		i_format = ZFC_FORMAT;
10730Sstevel@tonic-gate 		i_untilyear = ZFC_TILYEAR;
10740Sstevel@tonic-gate 		i_untilmonth = ZFC_TILMONTH;
10750Sstevel@tonic-gate 		i_untilday = ZFC_TILDAY;
10760Sstevel@tonic-gate 		i_untiltime = ZFC_TILTIME;
10770Sstevel@tonic-gate 		z.z_name = NULL;
10780Sstevel@tonic-gate 	} else {
10790Sstevel@tonic-gate 		i_gmtoff = ZF_GMTOFF;
10800Sstevel@tonic-gate 		i_rule = ZF_RULE;
10810Sstevel@tonic-gate 		i_format = ZF_FORMAT;
10820Sstevel@tonic-gate 		i_untilyear = ZF_TILYEAR;
10830Sstevel@tonic-gate 		i_untilmonth = ZF_TILMONTH;
10840Sstevel@tonic-gate 		i_untilday = ZF_TILDAY;
10850Sstevel@tonic-gate 		i_untiltime = ZF_TILTIME;
10860Sstevel@tonic-gate 		z.z_name = ecpyalloc(fields[ZF_NAME]);
10870Sstevel@tonic-gate 	}
10880Sstevel@tonic-gate 	z.z_filename = filename;
10890Sstevel@tonic-gate 	z.z_linenum = linenum;
10900Sstevel@tonic-gate 	z.z_gmtoff = gethms(fields[i_gmtoff], gettext("invalid UTC offset"),
10910Sstevel@tonic-gate 		TRUE);
10920Sstevel@tonic-gate 	if ((cp = strchr(fields[i_format], '%')) != 0) {
10930Sstevel@tonic-gate 		if (*++cp != 's' || strchr(cp, '%') != 0) {
10940Sstevel@tonic-gate 			error(gettext("invalid abbreviation format"));
10950Sstevel@tonic-gate 			return (FALSE);
10960Sstevel@tonic-gate 		}
10970Sstevel@tonic-gate 	}
10980Sstevel@tonic-gate 	z.z_rule = ecpyalloc(fields[i_rule]);
10990Sstevel@tonic-gate 	z.z_format = ecpyalloc(fields[i_format]);
11000Sstevel@tonic-gate 	hasuntil = nfields > i_untilyear;
11010Sstevel@tonic-gate 	if (hasuntil) {
11020Sstevel@tonic-gate 		z.z_untilrule.r_filename = filename;
11030Sstevel@tonic-gate 		z.z_untilrule.r_linenum = linenum;
11040Sstevel@tonic-gate 		rulesub(&z.z_untilrule,
11050Sstevel@tonic-gate 			fields[i_untilyear],
11060Sstevel@tonic-gate 			"only",
11070Sstevel@tonic-gate 			"",
11080Sstevel@tonic-gate 			(nfields > i_untilmonth) ?
11090Sstevel@tonic-gate 			fields[i_untilmonth] : "Jan",
11100Sstevel@tonic-gate 			(nfields > i_untilday) ? fields[i_untilday] : "1",
11110Sstevel@tonic-gate 			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
11120Sstevel@tonic-gate 		z.z_untiltime = rpytime(&z.z_untilrule,
11130Sstevel@tonic-gate 			z.z_untilrule.r_loyear);
11140Sstevel@tonic-gate 		if (iscont && nzones > 0 &&
11150Sstevel@tonic-gate 			z.z_untiltime > min_time &&
11160Sstevel@tonic-gate 			z.z_untiltime < max_time &&
11170Sstevel@tonic-gate 			zones[nzones - 1].z_untiltime > min_time &&
11180Sstevel@tonic-gate 			zones[nzones - 1].z_untiltime < max_time &&
11190Sstevel@tonic-gate 			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
11200Sstevel@tonic-gate 				error(gettext(
11210Sstevel@tonic-gate "Zone continuation line end time is not after end time of previous line"));
11220Sstevel@tonic-gate 				return (FALSE);
11230Sstevel@tonic-gate 		}
11240Sstevel@tonic-gate 	}
11250Sstevel@tonic-gate 	zones = (struct zone *)(void *)erealloc((char *)zones,
11260Sstevel@tonic-gate 		(int)((nzones + 1) * sizeof (*zones)));
11270Sstevel@tonic-gate 	zones[nzones++] = z;
11280Sstevel@tonic-gate 	/*
11290Sstevel@tonic-gate 	 * If there was an UNTIL field on this line,
11300Sstevel@tonic-gate 	 * there's more information about the zone on the next line.
11310Sstevel@tonic-gate 	 */
11320Sstevel@tonic-gate 	return (hasuntil);
11330Sstevel@tonic-gate }
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
11360Sstevel@tonic-gate static void
inleap(fields,nfields)11370Sstevel@tonic-gate inleap(fields, nfields)
11380Sstevel@tonic-gate register char ** const	fields;
11390Sstevel@tonic-gate const int		nfields;
11400Sstevel@tonic-gate {
11411138Srobbin 	register const char 		*cp;
11421138Srobbin 	register const struct lookup 	*lp;
11430Sstevel@tonic-gate 	register int			i, j;
11440Sstevel@tonic-gate 	int				year, month, day;
11450Sstevel@tonic-gate 	long				dayoff, tod;
11461138Srobbin 	zic_t				t;
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	if (nfields != LEAP_FIELDS) {
11490Sstevel@tonic-gate 		error(gettext("wrong number of fields on Leap line"));
11500Sstevel@tonic-gate 		return;
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 	dayoff = 0;
11530Sstevel@tonic-gate 	cp = fields[LP_YEAR];
11540Sstevel@tonic-gate 	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
11551138Srobbin 		/*
11561138Srobbin 		 * Leapin' Lizards!
11571138Srobbin 		 */
11581138Srobbin 		error(gettext("invalid leaping year"));
11591138Srobbin 		return;
11600Sstevel@tonic-gate 	}
11610Sstevel@tonic-gate 	j = EPOCH_YEAR;
11620Sstevel@tonic-gate 	while (j != year) {
11630Sstevel@tonic-gate 		if (year > j) {
11640Sstevel@tonic-gate 			i = len_years[isleap(j)];
11650Sstevel@tonic-gate 			++j;
11660Sstevel@tonic-gate 		} else {
11670Sstevel@tonic-gate 			--j;
11680Sstevel@tonic-gate 			i = -len_years[isleap(j)];
11690Sstevel@tonic-gate 		}
11700Sstevel@tonic-gate 		dayoff = oadd(dayoff, eitol(i));
11710Sstevel@tonic-gate 	}
11720Sstevel@tonic-gate 	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
11730Sstevel@tonic-gate 		error(gettext("invalid month name"));
11740Sstevel@tonic-gate 		return;
11750Sstevel@tonic-gate 	}
11760Sstevel@tonic-gate 	month = lp->l_value;
11770Sstevel@tonic-gate 	j = TM_JANUARY;
11780Sstevel@tonic-gate 	while (j != month) {
11790Sstevel@tonic-gate 		i = len_months[isleap(year)][j];
11800Sstevel@tonic-gate 		dayoff = oadd(dayoff, eitol(i));
11810Sstevel@tonic-gate 		++j;
11820Sstevel@tonic-gate 	}
11830Sstevel@tonic-gate 	cp = fields[LP_DAY];
11840Sstevel@tonic-gate 	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
11850Sstevel@tonic-gate 		day <= 0 || day > len_months[isleap(year)][month]) {
11860Sstevel@tonic-gate 			error(gettext("invalid day of month"));
11870Sstevel@tonic-gate 			return;
11880Sstevel@tonic-gate 	}
11890Sstevel@tonic-gate 	dayoff = oadd(dayoff, eitol(day - 1));
11901138Srobbin 	if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
11910Sstevel@tonic-gate 		error(gettext("time before zero"));
11920Sstevel@tonic-gate 		return;
11930Sstevel@tonic-gate 	}
11941138Srobbin 	if (dayoff < min_time / SECSPERDAY) {
11951138Srobbin 		error(gettext("time too small"));
11960Sstevel@tonic-gate 		return;
11970Sstevel@tonic-gate 	}
11981138Srobbin 	if (dayoff > max_time / SECSPERDAY) {
11991138Srobbin 		error(gettext("time too large"));
12001138Srobbin 		return;
12011138Srobbin 	}
12021138Srobbin 	t = (zic_t)dayoff * SECSPERDAY;
12030Sstevel@tonic-gate 	tod = gethms(fields[LP_TIME], gettext("invalid time of day"), FALSE);
12040Sstevel@tonic-gate 	cp = fields[LP_CORR];
12050Sstevel@tonic-gate 	{
12060Sstevel@tonic-gate 		register int	positive;
12070Sstevel@tonic-gate 		int		count;
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
12100Sstevel@tonic-gate 			positive = FALSE;
12110Sstevel@tonic-gate 			count = 1;
12120Sstevel@tonic-gate 		} else if (strcmp(cp, "--") == 0) {
12130Sstevel@tonic-gate 			positive = FALSE;
12140Sstevel@tonic-gate 			count = 2;
12150Sstevel@tonic-gate 		} else if (strcmp(cp, "+") == 0) {
12160Sstevel@tonic-gate 			positive = TRUE;
12170Sstevel@tonic-gate 			count = 1;
12180Sstevel@tonic-gate 		} else if (strcmp(cp, "++") == 0) {
12190Sstevel@tonic-gate 			positive = TRUE;
12200Sstevel@tonic-gate 			count = 2;
12210Sstevel@tonic-gate 		} else {
12220Sstevel@tonic-gate 			error(gettext("illegal CORRECTION field on Leap line"));
12230Sstevel@tonic-gate 			return;
12240Sstevel@tonic-gate 		}
12250Sstevel@tonic-gate 		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
12260Sstevel@tonic-gate 			error(gettext(
12271138Srobbin 			    "illegal Rolling/Stationary field on Leap line"));
12280Sstevel@tonic-gate 			return;
12290Sstevel@tonic-gate 		}
12300Sstevel@tonic-gate 		leapadd(tadd(t, tod), positive, lp->l_value, count);
12310Sstevel@tonic-gate 	}
12320Sstevel@tonic-gate }
12330Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate static void
inlink(fields,nfields)12360Sstevel@tonic-gate inlink(fields, nfields)
12370Sstevel@tonic-gate register char ** const	fields;
12380Sstevel@tonic-gate const int		nfields;
12390Sstevel@tonic-gate {
12400Sstevel@tonic-gate 	struct link	l;
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	if (nfields != LINK_FIELDS) {
12430Sstevel@tonic-gate 		error(gettext("wrong number of fields on Link line"));
12440Sstevel@tonic-gate 		return;
12450Sstevel@tonic-gate 	}
12460Sstevel@tonic-gate 	if (*fields[LF_FROM] == '\0') {
12470Sstevel@tonic-gate 		error(gettext("blank FROM field on Link line"));
12480Sstevel@tonic-gate 		return;
12490Sstevel@tonic-gate 	}
12500Sstevel@tonic-gate 	if (*fields[LF_TO] == '\0') {
12510Sstevel@tonic-gate 		error(gettext("blank TO field on Link line"));
12520Sstevel@tonic-gate 		return;
12530Sstevel@tonic-gate 	}
12540Sstevel@tonic-gate 	l.l_filename = filename;
12550Sstevel@tonic-gate 	l.l_linenum = linenum;
12560Sstevel@tonic-gate 	l.l_from = ecpyalloc(fields[LF_FROM]);
12570Sstevel@tonic-gate 	l.l_to = ecpyalloc(fields[LF_TO]);
12580Sstevel@tonic-gate 	links = (struct link *)(void *)erealloc((char *)links,
12590Sstevel@tonic-gate 		(int)((nlinks + 1) * sizeof (*links)));
12600Sstevel@tonic-gate 	links[nlinks++] = l;
12610Sstevel@tonic-gate }
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate static void
rulesub(rp,loyearp,hiyearp,typep,monthp,dayp,timep)12640Sstevel@tonic-gate rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
12650Sstevel@tonic-gate register struct rule * const	rp;
12660Sstevel@tonic-gate const char * const		loyearp;
12670Sstevel@tonic-gate const char * const		hiyearp;
12680Sstevel@tonic-gate const char * const		typep;
12690Sstevel@tonic-gate const char * const		monthp;
12700Sstevel@tonic-gate const char * const		dayp;
12710Sstevel@tonic-gate const char * const		timep;
12720Sstevel@tonic-gate {
12730Sstevel@tonic-gate 	register const struct lookup	*lp;
12740Sstevel@tonic-gate 	register const char		*cp;
12750Sstevel@tonic-gate 	register char			*dp;
12760Sstevel@tonic-gate 	register char			*ep;
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 	if ((lp = byword(monthp, mon_names)) == NULL) {
12790Sstevel@tonic-gate 		error(gettext("invalid month name"));
12800Sstevel@tonic-gate 		return;
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 	rp->r_month = lp->l_value;
12830Sstevel@tonic-gate 	rp->r_todisstd = FALSE;
12840Sstevel@tonic-gate 	rp->r_todisgmt = FALSE;
12850Sstevel@tonic-gate 	dp = ecpyalloc(timep);
12860Sstevel@tonic-gate 	if (*dp != '\0') {
12870Sstevel@tonic-gate 		ep = dp + strlen(dp) - 1;
12880Sstevel@tonic-gate 		switch (lowerit(*ep)) {
12890Sstevel@tonic-gate 			case 's':	/* Standard */
12900Sstevel@tonic-gate 				rp->r_todisstd = TRUE;
12910Sstevel@tonic-gate 				rp->r_todisgmt = FALSE;
12920Sstevel@tonic-gate 				*ep = '\0';
12930Sstevel@tonic-gate 				break;
12940Sstevel@tonic-gate 			case 'w':	/* Wall */
12950Sstevel@tonic-gate 				rp->r_todisstd = FALSE;
12960Sstevel@tonic-gate 				rp->r_todisgmt = FALSE;
12970Sstevel@tonic-gate 				*ep = '\0';
12980Sstevel@tonic-gate 				break;
12990Sstevel@tonic-gate 			case 'g':	/* Greenwich */
13000Sstevel@tonic-gate 			case 'u':	/* Universal */
13010Sstevel@tonic-gate 			case 'z':	/* Zulu */
13020Sstevel@tonic-gate 				rp->r_todisstd = TRUE;
13030Sstevel@tonic-gate 				rp->r_todisgmt = TRUE;
13040Sstevel@tonic-gate 				*ep = '\0';
13050Sstevel@tonic-gate 				break;
13060Sstevel@tonic-gate 		}
13070Sstevel@tonic-gate 	}
13080Sstevel@tonic-gate 	rp->r_tod = gethms(dp, gettext("invalid time of day"), FALSE);
13090Sstevel@tonic-gate 	ifree(dp);
13100Sstevel@tonic-gate 	/*
13110Sstevel@tonic-gate 	 * Year work.
13120Sstevel@tonic-gate 	 */
13130Sstevel@tonic-gate 	cp = loyearp;
13140Sstevel@tonic-gate 	lp = byword(cp, begin_years);
13150Sstevel@tonic-gate 	if (lp != NULL) {
13160Sstevel@tonic-gate 	    switch ((int)lp->l_value) {
13170Sstevel@tonic-gate 		case YR_MINIMUM:
13180Sstevel@tonic-gate 			rp->r_loyear = INT_MIN;
13190Sstevel@tonic-gate 			break;
13200Sstevel@tonic-gate 		case YR_MAXIMUM:
13210Sstevel@tonic-gate 			rp->r_loyear = INT_MAX;
13220Sstevel@tonic-gate 			break;
13230Sstevel@tonic-gate 		default:	/* "cannot happen" */
13240Sstevel@tonic-gate 			(void) fprintf(stderr,
13250Sstevel@tonic-gate 				gettext("%s: panic: Invalid l_value %d\n"),
13260Sstevel@tonic-gate 				progname, lp->l_value);
13271138Srobbin 			exit(EXIT_FAILURE);
13280Sstevel@tonic-gate 	    }
13290Sstevel@tonic-gate 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
13300Sstevel@tonic-gate 		error(gettext("invalid starting year"));
13310Sstevel@tonic-gate 		return;
13320Sstevel@tonic-gate 	} else if (noise) {
13330Sstevel@tonic-gate 		if (rp->r_loyear < min_year_representable)
13340Sstevel@tonic-gate 			warning(gettext(
13350Sstevel@tonic-gate 			    "starting year too low to be represented"));
13360Sstevel@tonic-gate 		else if (rp->r_loyear > max_year_representable)
13370Sstevel@tonic-gate 			warning(gettext(
13380Sstevel@tonic-gate 			    "starting year too high to be represented"));
13390Sstevel@tonic-gate 	}
13400Sstevel@tonic-gate 	cp = hiyearp;
13410Sstevel@tonic-gate 	if ((lp = byword(cp, end_years)) != NULL) {
13420Sstevel@tonic-gate 	    switch ((int)lp->l_value) {
13430Sstevel@tonic-gate 		case YR_MINIMUM:
13440Sstevel@tonic-gate 			rp->r_hiyear = INT_MIN;
13450Sstevel@tonic-gate 			break;
13460Sstevel@tonic-gate 		case YR_MAXIMUM:
13470Sstevel@tonic-gate 			rp->r_hiyear = INT_MAX;
13480Sstevel@tonic-gate 			break;
13490Sstevel@tonic-gate 		case YR_ONLY:
13500Sstevel@tonic-gate 			rp->r_hiyear = rp->r_loyear;
13510Sstevel@tonic-gate 			break;
13520Sstevel@tonic-gate 		default:	/* "cannot happen" */
13530Sstevel@tonic-gate 			(void) fprintf(stderr,
13540Sstevel@tonic-gate 				gettext("%s: panic: Invalid l_value %d\n"),
13550Sstevel@tonic-gate 				progname, lp->l_value);
13561138Srobbin 			exit(EXIT_FAILURE);
13570Sstevel@tonic-gate 	    }
13580Sstevel@tonic-gate 	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
13590Sstevel@tonic-gate 		error(gettext("invalid ending year"));
13600Sstevel@tonic-gate 		return;
13610Sstevel@tonic-gate 	} else if (noise) {
13620Sstevel@tonic-gate 		if (rp->r_loyear < min_year_representable)
13630Sstevel@tonic-gate 			warning(gettext(
13641138Srobbin 			    "ending year too low to be represented"));
13650Sstevel@tonic-gate 		else if (rp->r_loyear > max_year_representable)
13660Sstevel@tonic-gate 			warning(gettext(
13671138Srobbin 			    "ending year too high to be represented"));
13680Sstevel@tonic-gate 	}
13690Sstevel@tonic-gate 	if (rp->r_loyear > rp->r_hiyear) {
13700Sstevel@tonic-gate 		error(gettext("starting year greater than ending year"));
13710Sstevel@tonic-gate 		return;
13720Sstevel@tonic-gate 	}
13730Sstevel@tonic-gate 	if (*typep == '\0')
13740Sstevel@tonic-gate 		rp->r_yrtype = NULL;
13750Sstevel@tonic-gate 	else {
13760Sstevel@tonic-gate 		if (rp->r_loyear == rp->r_hiyear) {
13770Sstevel@tonic-gate 			error(gettext("typed single year"));
13780Sstevel@tonic-gate 			return;
13790Sstevel@tonic-gate 		}
13800Sstevel@tonic-gate 		rp->r_yrtype = ecpyalloc(typep);
13810Sstevel@tonic-gate 	}
13820Sstevel@tonic-gate 	if (rp->r_loyear < min_year && rp->r_loyear > 0)
13830Sstevel@tonic-gate 		min_year = rp->r_loyear;
13840Sstevel@tonic-gate 	/*
13850Sstevel@tonic-gate 	 * Day work.
13860Sstevel@tonic-gate 	 * Accept things such as:
13870Sstevel@tonic-gate 	 *	1
13880Sstevel@tonic-gate 	 *	last-Sunday
13890Sstevel@tonic-gate 	 *	Sun<=20
13900Sstevel@tonic-gate 	 *	Sun>=7
13910Sstevel@tonic-gate 	 */
13920Sstevel@tonic-gate 	dp = ecpyalloc(dayp);
13930Sstevel@tonic-gate 	if ((lp = byword(dp, lasts)) != NULL) {
13940Sstevel@tonic-gate 		rp->r_dycode = DC_DOWLEQ;
13950Sstevel@tonic-gate 		rp->r_wday = lp->l_value;
13960Sstevel@tonic-gate 		rp->r_dayofmonth = len_months[1][rp->r_month];
13970Sstevel@tonic-gate 	} else {
13980Sstevel@tonic-gate 		if ((ep = strchr(dp, '<')) != 0)
13990Sstevel@tonic-gate 			rp->r_dycode = DC_DOWLEQ;
14000Sstevel@tonic-gate 		else if ((ep = strchr(dp, '>')) != 0)
14010Sstevel@tonic-gate 			rp->r_dycode = DC_DOWGEQ;
14020Sstevel@tonic-gate 		else {
14030Sstevel@tonic-gate 			ep = dp;
14040Sstevel@tonic-gate 			rp->r_dycode = DC_DOM;
14050Sstevel@tonic-gate 		}
14060Sstevel@tonic-gate 		if (rp->r_dycode != DC_DOM) {
14070Sstevel@tonic-gate 			*ep++ = 0;
14080Sstevel@tonic-gate 			if (*ep++ != '=') {
14090Sstevel@tonic-gate 				error(gettext("invalid day of month"));
14100Sstevel@tonic-gate 				ifree(dp);
14110Sstevel@tonic-gate 				return;
14120Sstevel@tonic-gate 			}
14130Sstevel@tonic-gate 			if ((lp = byword(dp, wday_names)) == NULL) {
14140Sstevel@tonic-gate 				error(gettext("invalid weekday name"));
14150Sstevel@tonic-gate 				ifree(dp);
14160Sstevel@tonic-gate 				return;
14170Sstevel@tonic-gate 			}
14180Sstevel@tonic-gate 			rp->r_wday = lp->l_value;
14190Sstevel@tonic-gate 		}
14200Sstevel@tonic-gate 		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
14210Sstevel@tonic-gate 			rp->r_dayofmonth <= 0 ||
14220Sstevel@tonic-gate 			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
14230Sstevel@tonic-gate 				error(gettext("invalid day of month"));
14240Sstevel@tonic-gate 				ifree(dp);
14250Sstevel@tonic-gate 				return;
14260Sstevel@tonic-gate 		}
14270Sstevel@tonic-gate 	}
14280Sstevel@tonic-gate 	ifree(dp);
14290Sstevel@tonic-gate }
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate static void
convert(val,buf)14320Sstevel@tonic-gate convert(val, buf)
14330Sstevel@tonic-gate const long	val;
14340Sstevel@tonic-gate char * const	buf;
14350Sstevel@tonic-gate {
14360Sstevel@tonic-gate 	register int	i;
14370Sstevel@tonic-gate 	register long	shift;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
14400Sstevel@tonic-gate 		buf[i] = val >> shift;
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate static void
puttzcode(val,fp)14440Sstevel@tonic-gate puttzcode(val, fp)
14450Sstevel@tonic-gate const long	val;
14460Sstevel@tonic-gate FILE * const	fp;
14470Sstevel@tonic-gate {
14480Sstevel@tonic-gate 	char	buf[4];
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 	convert(val, buf);
14510Sstevel@tonic-gate 	(void) fwrite((void *)buf, (size_t)sizeof (buf), (size_t)1, fp);
14520Sstevel@tonic-gate }
14530Sstevel@tonic-gate 
14540Sstevel@tonic-gate static int
atcomp(avp,bvp)14550Sstevel@tonic-gate atcomp(avp, bvp)
14560Sstevel@tonic-gate const void *avp;
14570Sstevel@tonic-gate const void *bvp;
14580Sstevel@tonic-gate {
14590Sstevel@tonic-gate 	if (((struct attype *)avp)->at < ((struct attype *)bvp)->at)
14600Sstevel@tonic-gate 		return (-1);
14610Sstevel@tonic-gate 	else if (((struct attype *)avp)->at > ((struct attype *)bvp)->at)
14620Sstevel@tonic-gate 		return (1);
14630Sstevel@tonic-gate 	else	return (0);
14640Sstevel@tonic-gate }
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate static void
writezone(name)14670Sstevel@tonic-gate writezone(name)
14680Sstevel@tonic-gate const char * const	name;
14690Sstevel@tonic-gate {
14700Sstevel@tonic-gate 	register FILE		*fp;
14710Sstevel@tonic-gate 	register int		i, j;
14720Sstevel@tonic-gate 	static char		*fullname;
14730Sstevel@tonic-gate 	static struct tzhead	tzh;
14741138Srobbin 	zic_t			ats[TZ_MAX_TIMES];
14750Sstevel@tonic-gate 	unsigned char		types[TZ_MAX_TIMES];
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate 	/*
14780Sstevel@tonic-gate 	 * Sort.
14790Sstevel@tonic-gate 	 */
14800Sstevel@tonic-gate 	if (timecnt > 1)
14810Sstevel@tonic-gate 		(void) qsort((void *)attypes, (size_t)timecnt,
14820Sstevel@tonic-gate 			(size_t)sizeof (*attypes), atcomp);
14830Sstevel@tonic-gate 	/*
14840Sstevel@tonic-gate 	 * Optimize.
14850Sstevel@tonic-gate 	 */
14860Sstevel@tonic-gate 	{
14870Sstevel@tonic-gate 		int	fromi;
14880Sstevel@tonic-gate 		int	toi;
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 		toi = 0;
14910Sstevel@tonic-gate 		fromi = 0;
14920Sstevel@tonic-gate 		while (fromi < timecnt && attypes[fromi].at < min_time)
14930Sstevel@tonic-gate 			++fromi;
14940Sstevel@tonic-gate 		if (isdsts[0] == 0)
14950Sstevel@tonic-gate 			while (fromi < timecnt && attypes[fromi].type == 0)
14960Sstevel@tonic-gate 				++fromi;	/* handled by default rule */
14970Sstevel@tonic-gate 		for (; fromi < timecnt; ++fromi) {
14981138Srobbin 			if (toi != 0 && ((attypes[fromi].at +
14990Sstevel@tonic-gate 				gmtoffs[attypes[toi - 1].type]) <=
15001138Srobbin 				(attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
15010Sstevel@tonic-gate 				: attypes[toi - 2].type]))) {
15021138Srobbin 					attypes[toi - 1].type =
15031138Srobbin 						attypes[fromi].type;
15041138Srobbin 					continue;
15050Sstevel@tonic-gate 			}
15060Sstevel@tonic-gate 			if (toi == 0 ||
15070Sstevel@tonic-gate 				attypes[toi - 1].type != attypes[fromi].type)
15080Sstevel@tonic-gate 					attypes[toi++] = attypes[fromi];
15090Sstevel@tonic-gate 		}
15100Sstevel@tonic-gate 		timecnt = toi;
15110Sstevel@tonic-gate 	}
15120Sstevel@tonic-gate 	/*
15130Sstevel@tonic-gate 	 * Transfer.
15140Sstevel@tonic-gate 	 */
15150Sstevel@tonic-gate 	for (i = 0; i < timecnt; ++i) {
15160Sstevel@tonic-gate 		ats[i] = attypes[i].at;
15170Sstevel@tonic-gate 		types[i] = attypes[i].type;
15180Sstevel@tonic-gate 	}
15190Sstevel@tonic-gate 	fullname = erealloc(fullname,
15200Sstevel@tonic-gate 		(int)(strlen(directory) + 1 + strlen(name) + 1));
15210Sstevel@tonic-gate 	(void) sprintf(fullname, "%s/%s", directory, name);
15220Sstevel@tonic-gate 	/*
15230Sstevel@tonic-gate 	 * Remove old file, if any, to snap links.
15240Sstevel@tonic-gate 	 */
15250Sstevel@tonic-gate 	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
15260Sstevel@tonic-gate 		const char *e = strerror(errno);
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Can't remove %s: %s\n"),
15290Sstevel@tonic-gate 			progname, fullname, e);
15301138Srobbin 		exit(EXIT_FAILURE);
15310Sstevel@tonic-gate 	}
15320Sstevel@tonic-gate 	if ((fp = fopen(fullname, "wb")) == NULL) {
15330Sstevel@tonic-gate 		if (mkdirs(fullname) != 0)
15341138Srobbin 			exit(EXIT_FAILURE);
15350Sstevel@tonic-gate 		if ((fp = fopen(fullname, "wb")) == NULL) {
15360Sstevel@tonic-gate 			const char *e = strerror(errno);
15370Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
15380Sstevel@tonic-gate 				"%s: Can't create %s: %s\n"),
15390Sstevel@tonic-gate 				progname, fullname, e);
15401138Srobbin 			exit(EXIT_FAILURE);
15410Sstevel@tonic-gate 		}
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 	convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
15440Sstevel@tonic-gate 	convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
15450Sstevel@tonic-gate 	convert(eitol(leapcnt), tzh.tzh_leapcnt);
15460Sstevel@tonic-gate 	convert(eitol(timecnt), tzh.tzh_timecnt);
15470Sstevel@tonic-gate 	convert(eitol(typecnt), tzh.tzh_typecnt);
15480Sstevel@tonic-gate 	convert(eitol(charcnt), tzh.tzh_charcnt);
15490Sstevel@tonic-gate 	(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof (tzh.tzh_magic));
15500Sstevel@tonic-gate #define	DO(field)	(void) fwrite((void *) tzh.field, \
15510Sstevel@tonic-gate 			(size_t)sizeof (tzh.field), (size_t)1, fp)
15520Sstevel@tonic-gate 	DO(tzh_magic);
15530Sstevel@tonic-gate 	DO(tzh_reserved);
15540Sstevel@tonic-gate 	DO(tzh_ttisgmtcnt);
15550Sstevel@tonic-gate 	DO(tzh_ttisstdcnt);
15560Sstevel@tonic-gate 	DO(tzh_leapcnt);
15570Sstevel@tonic-gate 	DO(tzh_timecnt);
15580Sstevel@tonic-gate 	DO(tzh_typecnt);
15590Sstevel@tonic-gate 	DO(tzh_charcnt);
15600Sstevel@tonic-gate #undef DO
15610Sstevel@tonic-gate 	for (i = 0; i < timecnt; ++i) {
15620Sstevel@tonic-gate 		j = leapcnt;
15630Sstevel@tonic-gate 		while (--j >= 0)
15640Sstevel@tonic-gate 			if (ats[i] >= trans[j]) {
15650Sstevel@tonic-gate 				ats[i] = tadd(ats[i], corr[j]);
15660Sstevel@tonic-gate 				break;
15670Sstevel@tonic-gate 			}
15680Sstevel@tonic-gate 		puttzcode((long)ats[i], fp);
15690Sstevel@tonic-gate 	}
15700Sstevel@tonic-gate 	if (timecnt > 0)
15710Sstevel@tonic-gate 		(void) fwrite((void *)types, (size_t)sizeof (types[0]),
15720Sstevel@tonic-gate 			(size_t)timecnt, fp);
15730Sstevel@tonic-gate 	for (i = 0; i < typecnt; ++i) {
15740Sstevel@tonic-gate 		puttzcode((long)gmtoffs[i], fp);
15750Sstevel@tonic-gate 		(void) putc(isdsts[i], fp);
15760Sstevel@tonic-gate 		(void) putc(abbrinds[i], fp);
15770Sstevel@tonic-gate 	}
15780Sstevel@tonic-gate 	if (charcnt != 0)
15790Sstevel@tonic-gate 		(void) fwrite((void *)chars, (size_t)sizeof (chars[0]),
15800Sstevel@tonic-gate 			(size_t)charcnt, fp);
15810Sstevel@tonic-gate 	for (i = 0; i < leapcnt; ++i) {
15820Sstevel@tonic-gate 		if (roll[i]) {
15830Sstevel@tonic-gate 			if (timecnt == 0 || trans[i] < ats[0]) {
15840Sstevel@tonic-gate 				j = 0;
15850Sstevel@tonic-gate 				while (isdsts[j])
15860Sstevel@tonic-gate 					if (++j >= typecnt) {
15870Sstevel@tonic-gate 						j = 0;
15880Sstevel@tonic-gate 						break;
15890Sstevel@tonic-gate 					}
15900Sstevel@tonic-gate 			} else {
15910Sstevel@tonic-gate 				j = 1;
15920Sstevel@tonic-gate 				while (j < timecnt && trans[i] >= ats[j])
15930Sstevel@tonic-gate 					++j;
15940Sstevel@tonic-gate 				j = types[j - 1];
15950Sstevel@tonic-gate 			}
15960Sstevel@tonic-gate 			puttzcode((long)tadd(trans[i], -gmtoffs[j]), fp);
15970Sstevel@tonic-gate 		} else	puttzcode((long)trans[i], fp);
15980Sstevel@tonic-gate 		puttzcode((long)corr[i], fp);
15990Sstevel@tonic-gate 	}
16000Sstevel@tonic-gate 	for (i = 0; i < typecnt; ++i)
16010Sstevel@tonic-gate 		(void) putc(ttisstds[i], fp);
16020Sstevel@tonic-gate 	for (i = 0; i < typecnt; ++i)
16030Sstevel@tonic-gate 		(void) putc(ttisgmts[i], fp);
16040Sstevel@tonic-gate 	if (ferror(fp) || fclose(fp)) {
16050Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Error writing %s\n"),
16060Sstevel@tonic-gate 			progname, fullname);
16071138Srobbin 		exit(EXIT_FAILURE);
16080Sstevel@tonic-gate 	}
16090Sstevel@tonic-gate }
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate static void
doabbr(abbr,format,letters,isdst)16120Sstevel@tonic-gate doabbr(abbr, format, letters, isdst)
16130Sstevel@tonic-gate char * const		abbr;
16140Sstevel@tonic-gate const char * const	format;
16150Sstevel@tonic-gate const char * const	letters;
16160Sstevel@tonic-gate const int		isdst;
16170Sstevel@tonic-gate {
16180Sstevel@tonic-gate 	if (strchr(format, '/') == NULL) {
16190Sstevel@tonic-gate 		if (letters == NULL)
16200Sstevel@tonic-gate 			(void) strcpy(abbr, format);
16210Sstevel@tonic-gate 		else
16220Sstevel@tonic-gate 			(void) sprintf(abbr, format, letters);
16230Sstevel@tonic-gate 	} else if (isdst)
16240Sstevel@tonic-gate 		(void) strcpy(abbr, strchr(format, '/') + 1);
16250Sstevel@tonic-gate 	else {
16260Sstevel@tonic-gate 		(void) strcpy(abbr, format);
16270Sstevel@tonic-gate 		*strchr(abbr, '/') = '\0';
16280Sstevel@tonic-gate 	}
16290Sstevel@tonic-gate }
16300Sstevel@tonic-gate 
16310Sstevel@tonic-gate static void
outzone(zpfirst,zonecount)16320Sstevel@tonic-gate outzone(zpfirst, zonecount)
16330Sstevel@tonic-gate const struct zone * const	zpfirst;
16340Sstevel@tonic-gate const int			zonecount;
16350Sstevel@tonic-gate {
16360Sstevel@tonic-gate 	register const struct zone	*zp;
16370Sstevel@tonic-gate 	register struct rule		*rp;
16380Sstevel@tonic-gate 	register int			i, j;
16390Sstevel@tonic-gate 	register int			usestart, useuntil;
16401138Srobbin 	register zic_t			starttime, untiltime;
16410Sstevel@tonic-gate 	register long			gmtoff;
16420Sstevel@tonic-gate 	register long			stdoff;
16430Sstevel@tonic-gate 	register int			year;
16440Sstevel@tonic-gate 	register long			startoff;
16450Sstevel@tonic-gate 	register int			startttisstd;
16460Sstevel@tonic-gate 	register int			startttisgmt;
16470Sstevel@tonic-gate 	register int			type;
16480Sstevel@tonic-gate 	char				startbuf[BUFSIZ];
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 	INITIALIZE(untiltime);
16510Sstevel@tonic-gate 	INITIALIZE(starttime);
16520Sstevel@tonic-gate 	/*
16530Sstevel@tonic-gate 	 * Now. . .finally. . .generate some useful data!
16540Sstevel@tonic-gate 	 */
16550Sstevel@tonic-gate 	timecnt = 0;
16560Sstevel@tonic-gate 	typecnt = 0;
16570Sstevel@tonic-gate 	charcnt = 0;
16580Sstevel@tonic-gate 	/*
16591442Srobbin 	 * Thanks to Earl Chew
16600Sstevel@tonic-gate 	 * for noting the need to unconditionally initialize startttisstd.
16610Sstevel@tonic-gate 	 */
16620Sstevel@tonic-gate 	startttisstd = FALSE;
16630Sstevel@tonic-gate 	startttisgmt = FALSE;
16640Sstevel@tonic-gate 	for (i = 0; i < zonecount; ++i) {
16650Sstevel@tonic-gate 		/*
16660Sstevel@tonic-gate 		 * A guess that may well be corrected later.
16670Sstevel@tonic-gate 		 */
16680Sstevel@tonic-gate 		stdoff = 0;
16690Sstevel@tonic-gate 		zp = &zpfirst[i];
16700Sstevel@tonic-gate 		usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
16710Sstevel@tonic-gate 		useuntil = i < (zonecount - 1);
16720Sstevel@tonic-gate 		if (useuntil && zp->z_untiltime <= min_time)
16730Sstevel@tonic-gate 			continue;
16740Sstevel@tonic-gate 		gmtoff = zp->z_gmtoff;
16750Sstevel@tonic-gate 		eat(zp->z_filename, zp->z_linenum);
16760Sstevel@tonic-gate 		*startbuf = '\0';
16770Sstevel@tonic-gate 		startoff = zp->z_gmtoff;
16780Sstevel@tonic-gate 		if (zp->z_nrules == 0) {
16790Sstevel@tonic-gate 			stdoff = zp->z_stdoff;
16800Sstevel@tonic-gate 			doabbr(startbuf, zp->z_format,
16810Sstevel@tonic-gate 				(char *)NULL, stdoff != 0);
16820Sstevel@tonic-gate 			type = addtype(oadd(zp->z_gmtoff, stdoff),
16830Sstevel@tonic-gate 				startbuf, stdoff != 0, startttisstd,
16840Sstevel@tonic-gate 				startttisgmt);
16850Sstevel@tonic-gate 			if (usestart) {
16860Sstevel@tonic-gate 				addtt(starttime, type);
16870Sstevel@tonic-gate 				usestart = FALSE;
16881138Srobbin 			} else if (stdoff != 0)
16891138Srobbin 				addtt(min_time, type);
16900Sstevel@tonic-gate 		} else
16910Sstevel@tonic-gate 		    for (year = min_year; year <= max_year; ++year) {
16920Sstevel@tonic-gate 			if (useuntil && year > zp->z_untilrule.r_hiyear)
16930Sstevel@tonic-gate 				break;
16940Sstevel@tonic-gate 			/*
16950Sstevel@tonic-gate 			 * Mark which rules to do in the current year.
16960Sstevel@tonic-gate 			 * For those to do, calculate rpytime(rp, year);
16970Sstevel@tonic-gate 			 */
16980Sstevel@tonic-gate 			for (j = 0; j < zp->z_nrules; ++j) {
16990Sstevel@tonic-gate 				rp = &zp->z_rules[j];
17000Sstevel@tonic-gate 				eats(zp->z_filename, zp->z_linenum,
17010Sstevel@tonic-gate 					rp->r_filename, rp->r_linenum);
17020Sstevel@tonic-gate 				rp->r_todo = year >= rp->r_loyear &&
17030Sstevel@tonic-gate 						year <= rp->r_hiyear &&
17040Sstevel@tonic-gate 						yearistype(year, rp->r_yrtype);
17050Sstevel@tonic-gate 				if (rp->r_todo)
17060Sstevel@tonic-gate 					rp->r_temp = rpytime(rp, year);
17070Sstevel@tonic-gate 			}
17080Sstevel@tonic-gate 			for (;;) {
17090Sstevel@tonic-gate 				register int	k;
17101138Srobbin 				register zic_t	jtime, ktime;
17110Sstevel@tonic-gate 				register long	offset;
17120Sstevel@tonic-gate 				char		buf[BUFSIZ];
17130Sstevel@tonic-gate 
17140Sstevel@tonic-gate 				INITIALIZE(ktime);
17150Sstevel@tonic-gate 				if (useuntil) {
17160Sstevel@tonic-gate 					/*
17171138Srobbin 					 * Turn untiltime into UTC * assuming
17181138Srobbin 					 * the current gmtoff and stdoff values.
17190Sstevel@tonic-gate 					 */
17200Sstevel@tonic-gate 					untiltime = zp->z_untiltime;
17210Sstevel@tonic-gate 					if (!zp->z_untilrule.r_todisgmt)
17220Sstevel@tonic-gate 						untiltime = tadd(untiltime,
17230Sstevel@tonic-gate 							-gmtoff);
17240Sstevel@tonic-gate 					if (!zp->z_untilrule.r_todisstd)
17250Sstevel@tonic-gate 						untiltime = tadd(untiltime,
17260Sstevel@tonic-gate 							-stdoff);
17270Sstevel@tonic-gate 				}
17280Sstevel@tonic-gate 				/*
17290Sstevel@tonic-gate 				 * Find the rule (of those to do, if any)
17300Sstevel@tonic-gate 				 * that takes effect earliest in the year.
17310Sstevel@tonic-gate 				 */
17320Sstevel@tonic-gate 				k = -1;
17330Sstevel@tonic-gate 				for (j = 0; j < zp->z_nrules; ++j) {
17340Sstevel@tonic-gate 					rp = &zp->z_rules[j];
17350Sstevel@tonic-gate 					if (!rp->r_todo)
17360Sstevel@tonic-gate 						continue;
17370Sstevel@tonic-gate 					eats(zp->z_filename, zp->z_linenum,
17380Sstevel@tonic-gate 						rp->r_filename, rp->r_linenum);
17390Sstevel@tonic-gate 					offset = rp->r_todisgmt ? 0 : gmtoff;
17400Sstevel@tonic-gate 					if (!rp->r_todisstd)
17410Sstevel@tonic-gate 						offset = oadd(offset, stdoff);
17420Sstevel@tonic-gate 					jtime = rp->r_temp;
17430Sstevel@tonic-gate 					if (jtime == min_time ||
17440Sstevel@tonic-gate 						jtime == max_time)
17450Sstevel@tonic-gate 							continue;
17460Sstevel@tonic-gate 					jtime = tadd(jtime, -offset);
17470Sstevel@tonic-gate 					if (k < 0 || jtime < ktime) {
17480Sstevel@tonic-gate 						k = j;
17490Sstevel@tonic-gate 						ktime = jtime;
17500Sstevel@tonic-gate 					}
17510Sstevel@tonic-gate 				}
17520Sstevel@tonic-gate 				if (k < 0)
17530Sstevel@tonic-gate 					break;	/* go on to next year */
17540Sstevel@tonic-gate 				rp = &zp->z_rules[k];
17550Sstevel@tonic-gate 				rp->r_todo = FALSE;
17560Sstevel@tonic-gate 				if (useuntil && ktime >= untiltime)
17570Sstevel@tonic-gate 					break;
17580Sstevel@tonic-gate 				stdoff = rp->r_stdoff;
17590Sstevel@tonic-gate 				if (usestart && ktime == starttime)
17600Sstevel@tonic-gate 					usestart = FALSE;
17610Sstevel@tonic-gate 				if (usestart) {
17620Sstevel@tonic-gate 					if (ktime < starttime) {
17630Sstevel@tonic-gate 						startoff = oadd(zp->z_gmtoff,
17640Sstevel@tonic-gate 							stdoff);
17650Sstevel@tonic-gate 						doabbr(startbuf, zp->z_format,
17660Sstevel@tonic-gate 							rp->r_abbrvar,
17670Sstevel@tonic-gate 							rp->r_stdoff != 0);
17680Sstevel@tonic-gate 						continue;
17690Sstevel@tonic-gate 					}
17700Sstevel@tonic-gate 					if (*startbuf == '\0' &&
17710Sstevel@tonic-gate 					    startoff == oadd(zp->z_gmtoff,
17720Sstevel@tonic-gate 					    stdoff)) {
17730Sstevel@tonic-gate 						doabbr(startbuf, zp->z_format,
17740Sstevel@tonic-gate 							rp->r_abbrvar,
17750Sstevel@tonic-gate 							rp->r_stdoff != 0);
17760Sstevel@tonic-gate 					}
17770Sstevel@tonic-gate 				}
17780Sstevel@tonic-gate 				eats(zp->z_filename, zp->z_linenum,
17790Sstevel@tonic-gate 					rp->r_filename, rp->r_linenum);
17800Sstevel@tonic-gate 				doabbr(buf, zp->z_format, rp->r_abbrvar,
17810Sstevel@tonic-gate 					rp->r_stdoff != 0);
17820Sstevel@tonic-gate 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
17830Sstevel@tonic-gate 				type = addtype(offset, buf, rp->r_stdoff != 0,
17840Sstevel@tonic-gate 					rp->r_todisstd, rp->r_todisgmt);
17850Sstevel@tonic-gate 				addtt(ktime, type);
17860Sstevel@tonic-gate 			}
17870Sstevel@tonic-gate 		    }
17880Sstevel@tonic-gate 		if (usestart) {
17890Sstevel@tonic-gate 			if (*startbuf == '\0' &&
17900Sstevel@tonic-gate 				zp->z_format != NULL &&
17910Sstevel@tonic-gate 				strchr(zp->z_format, '%') == NULL &&
17920Sstevel@tonic-gate 				strchr(zp->z_format, '/') == NULL)
17930Sstevel@tonic-gate 					(void) strcpy(startbuf, zp->z_format);
17940Sstevel@tonic-gate 			eat(zp->z_filename, zp->z_linenum);
17950Sstevel@tonic-gate 			if (*startbuf == '\0')
17960Sstevel@tonic-gate 				error(gettext(
17970Sstevel@tonic-gate "can't determine time zone abbrevation to use just after until time"));
17980Sstevel@tonic-gate 			else	addtt(starttime,
17990Sstevel@tonic-gate 					addtype(startoff, startbuf,
18000Sstevel@tonic-gate 						startoff != zp->z_gmtoff,
18010Sstevel@tonic-gate 						startttisstd,
18020Sstevel@tonic-gate 						startttisgmt));
18030Sstevel@tonic-gate 		}
18040Sstevel@tonic-gate 		/*
18050Sstevel@tonic-gate 		 * Now we may get to set starttime for the next zone line.
18060Sstevel@tonic-gate 		 */
18070Sstevel@tonic-gate 		if (useuntil) {
18080Sstevel@tonic-gate 			startttisstd = zp->z_untilrule.r_todisstd;
18090Sstevel@tonic-gate 			startttisgmt = zp->z_untilrule.r_todisgmt;
18100Sstevel@tonic-gate 			starttime = zp->z_untiltime;
18110Sstevel@tonic-gate 			if (!startttisstd)
18120Sstevel@tonic-gate 				starttime = tadd(starttime, -stdoff);
18130Sstevel@tonic-gate 			if (!startttisgmt)
18140Sstevel@tonic-gate 				starttime = tadd(starttime, -gmtoff);
18150Sstevel@tonic-gate 		}
18160Sstevel@tonic-gate 	}
18170Sstevel@tonic-gate 	writezone(zpfirst->z_name);
18180Sstevel@tonic-gate }
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate static void
addtt(starttime,type)18210Sstevel@tonic-gate addtt(starttime, type)
18221138Srobbin const zic_t	starttime;
18230Sstevel@tonic-gate int		type;
18240Sstevel@tonic-gate {
18250Sstevel@tonic-gate 	if (starttime <= min_time ||
18260Sstevel@tonic-gate 		(timecnt == 1 && attypes[0].at < min_time)) {
18270Sstevel@tonic-gate 		gmtoffs[0] = gmtoffs[type];
18280Sstevel@tonic-gate 		isdsts[0] = isdsts[type];
18290Sstevel@tonic-gate 		ttisstds[0] = ttisstds[type];
18300Sstevel@tonic-gate 		ttisgmts[0] = ttisgmts[type];
18310Sstevel@tonic-gate 		if (abbrinds[type] != 0)
18320Sstevel@tonic-gate 			(void) strcpy(chars, &chars[abbrinds[type]]);
18330Sstevel@tonic-gate 		abbrinds[0] = 0;
18340Sstevel@tonic-gate 		charcnt = strlen(chars) + 1;
18350Sstevel@tonic-gate 		typecnt = 1;
18360Sstevel@tonic-gate 		timecnt = 0;
18370Sstevel@tonic-gate 		type = 0;
18380Sstevel@tonic-gate 	}
18390Sstevel@tonic-gate 	if (timecnt >= TZ_MAX_TIMES) {
18400Sstevel@tonic-gate 		error(gettext("too many transitions?!"));
18411138Srobbin 		exit(EXIT_FAILURE);
18420Sstevel@tonic-gate 	}
18430Sstevel@tonic-gate 	attypes[timecnt].at = starttime;
18440Sstevel@tonic-gate 	attypes[timecnt].type = type;
18450Sstevel@tonic-gate 	++timecnt;
18460Sstevel@tonic-gate }
18470Sstevel@tonic-gate 
18480Sstevel@tonic-gate static int
addtype(gmtoff,abbr,isdst,ttisstd,ttisgmt)18490Sstevel@tonic-gate addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
18500Sstevel@tonic-gate const long		gmtoff;
18510Sstevel@tonic-gate const char * const	abbr;
18520Sstevel@tonic-gate const int		isdst;
18530Sstevel@tonic-gate const int		ttisstd;
18540Sstevel@tonic-gate const int		ttisgmt;
18550Sstevel@tonic-gate {
18560Sstevel@tonic-gate 	register int	i, j;
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 	if (isdst != TRUE && isdst != FALSE) {
18590Sstevel@tonic-gate 		error(gettext(
18600Sstevel@tonic-gate 		    "internal error - addtype called with bad isdst"));
18611138Srobbin 		exit(EXIT_FAILURE);
18620Sstevel@tonic-gate 	}
18630Sstevel@tonic-gate 	if (ttisstd != TRUE && ttisstd != FALSE) {
18640Sstevel@tonic-gate 		error(gettext(
18650Sstevel@tonic-gate 		    "internal error - addtype called with bad ttisstd"));
18661138Srobbin 		exit(EXIT_FAILURE);
18670Sstevel@tonic-gate 	}
18680Sstevel@tonic-gate 	if (ttisgmt != TRUE && ttisgmt != FALSE) {
18690Sstevel@tonic-gate 		error(gettext(
18700Sstevel@tonic-gate 		    "internal error - addtype called with bad ttisgmt"));
18711138Srobbin 		exit(EXIT_FAILURE);
18720Sstevel@tonic-gate 	}
18730Sstevel@tonic-gate 	/*
18740Sstevel@tonic-gate 	 * See if there's already an entry for this zone type.
18750Sstevel@tonic-gate 	 * If so, just return its index.
18760Sstevel@tonic-gate 	 */
18770Sstevel@tonic-gate 	for (i = 0; i < typecnt; ++i) {
18780Sstevel@tonic-gate 		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
18790Sstevel@tonic-gate 			strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
18800Sstevel@tonic-gate 			ttisstd == ttisstds[i] &&
18810Sstevel@tonic-gate 			ttisgmt == ttisgmts[i])
18820Sstevel@tonic-gate 				return (i);
18830Sstevel@tonic-gate 	}
18840Sstevel@tonic-gate 	/*
18850Sstevel@tonic-gate 	 * There isn't one; add a new one, unless there are already too
18860Sstevel@tonic-gate 	 * many.
18870Sstevel@tonic-gate 	 */
18880Sstevel@tonic-gate 	if (typecnt >= TZ_MAX_TYPES) {
18890Sstevel@tonic-gate 		error(gettext("too many local time types"));
18901138Srobbin 		exit(EXIT_FAILURE);
18910Sstevel@tonic-gate 	}
18924218Srobbin 	if (!(-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
18934218Srobbin 		error(gettext("UTC offset out of range"));
18944218Srobbin 		exit(EXIT_FAILURE);
18954218Srobbin 	}
18960Sstevel@tonic-gate 	gmtoffs[i] = gmtoff;
18970Sstevel@tonic-gate 	isdsts[i] = isdst;
18980Sstevel@tonic-gate 	ttisstds[i] = ttisstd;
18990Sstevel@tonic-gate 	ttisgmts[i] = ttisgmt;
19000Sstevel@tonic-gate 
19010Sstevel@tonic-gate 	for (j = 0; j < charcnt; ++j)
19020Sstevel@tonic-gate 		if (strcmp(&chars[j], abbr) == 0)
19030Sstevel@tonic-gate 			break;
19040Sstevel@tonic-gate 	if (j == charcnt)
19050Sstevel@tonic-gate 		newabbr(abbr);
19060Sstevel@tonic-gate 	abbrinds[i] = j;
19070Sstevel@tonic-gate 	++typecnt;
19080Sstevel@tonic-gate 	return (i);
19090Sstevel@tonic-gate }
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
19120Sstevel@tonic-gate static void
leapadd(t,positive,rolling,count)19130Sstevel@tonic-gate leapadd(t, positive, rolling, count)
19141138Srobbin const zic_t	t;
19150Sstevel@tonic-gate const int	positive;
19160Sstevel@tonic-gate const int	rolling;
19170Sstevel@tonic-gate int		count;
19180Sstevel@tonic-gate {
19190Sstevel@tonic-gate 	register int	i, j;
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate 	if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
19220Sstevel@tonic-gate 		error(gettext("too many leap seconds"));
19231138Srobbin 		exit(EXIT_FAILURE);
19240Sstevel@tonic-gate 	}
19250Sstevel@tonic-gate 	for (i = 0; i < leapcnt; ++i)
19260Sstevel@tonic-gate 		if (t <= trans[i]) {
19270Sstevel@tonic-gate 			if (t == trans[i]) {
19280Sstevel@tonic-gate 				error(gettext("repeated leap second moment"));
19291138Srobbin 				exit(EXIT_FAILURE);
19300Sstevel@tonic-gate 			}
19310Sstevel@tonic-gate 			break;
19320Sstevel@tonic-gate 		}
19330Sstevel@tonic-gate 	do {
19340Sstevel@tonic-gate 		for (j = leapcnt; j > i; --j) {
19350Sstevel@tonic-gate 			trans[j] = trans[j - 1];
19360Sstevel@tonic-gate 			corr[j] = corr[j - 1];
19370Sstevel@tonic-gate 			roll[j] = roll[j - 1];
19380Sstevel@tonic-gate 		}
19390Sstevel@tonic-gate 		trans[i] = t;
19400Sstevel@tonic-gate 		corr[i] = positive ? 1L : eitol(-count);
19410Sstevel@tonic-gate 		roll[i] = rolling;
19420Sstevel@tonic-gate 		++leapcnt;
19430Sstevel@tonic-gate 	} while (positive && --count != 0);
19440Sstevel@tonic-gate }
19450Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
19460Sstevel@tonic-gate 
19470Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
19480Sstevel@tonic-gate static void
adjleap(void)19490Sstevel@tonic-gate adjleap(void)
19500Sstevel@tonic-gate {
19510Sstevel@tonic-gate 	register int	i;
19520Sstevel@tonic-gate 	register long	last = 0;
19530Sstevel@tonic-gate 
19540Sstevel@tonic-gate 	/*
19550Sstevel@tonic-gate 	 * propagate leap seconds forward
19560Sstevel@tonic-gate 	 */
19570Sstevel@tonic-gate 	for (i = 0; i < leapcnt; ++i) {
19580Sstevel@tonic-gate 		trans[i] = tadd(trans[i], last);
19590Sstevel@tonic-gate 		last = corr[i] += last;
19600Sstevel@tonic-gate 	}
19610Sstevel@tonic-gate }
19620Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate static int
yearistype(year,type)19650Sstevel@tonic-gate yearistype(year, type)
19660Sstevel@tonic-gate const int		year;
19670Sstevel@tonic-gate const char * const	type;
19680Sstevel@tonic-gate {
19690Sstevel@tonic-gate 	static char	*buf;
19700Sstevel@tonic-gate 	int		result;
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	if (type == NULL || *type == '\0')
19730Sstevel@tonic-gate 		return (TRUE);
19740Sstevel@tonic-gate #if defined(sun)
19750Sstevel@tonic-gate 	if (strcmp(type, "uspres") == 0)
19760Sstevel@tonic-gate 		return ((year % 4) == 0);
19770Sstevel@tonic-gate 	if (strcmp(type, "nonpres") == 0)
19780Sstevel@tonic-gate 		return ((year % 4) != 0);
19790Sstevel@tonic-gate 	if (strcmp(type, "even") == 0)
19800Sstevel@tonic-gate 		return ((year % 2) == 0);
19810Sstevel@tonic-gate 	if (strcmp(type, "odd") == 0)
19820Sstevel@tonic-gate 		return ((year % 2) != 0);
19830Sstevel@tonic-gate #endif /* defined(sun) */
19840Sstevel@tonic-gate 
19850Sstevel@tonic-gate 	buf = erealloc(buf, (int)(132 + strlen(yitcommand) + strlen(type)));
19860Sstevel@tonic-gate 	(void) sprintf(buf, "%s %d %s", yitcommand, year, type);
19870Sstevel@tonic-gate 	result = system(buf);
19881138Srobbin 	if (WIFEXITED(result)) {
19891138Srobbin 		switch (WEXITSTATUS(result)) {
19901138Srobbin 		case 0:
19911138Srobbin 			return (TRUE);
19921138Srobbin 		case 1:
19931138Srobbin 			return (FALSE);
19941138Srobbin 		}
19951138Srobbin 	}
19960Sstevel@tonic-gate 	error(gettext("Wild result from command execution"));
19970Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: command was '%s', result was %d\n"),
19980Sstevel@tonic-gate 		progname, buf, result);
19990Sstevel@tonic-gate 	for (;;)
20001138Srobbin 		exit(EXIT_FAILURE);
20010Sstevel@tonic-gate }
20020Sstevel@tonic-gate 
20030Sstevel@tonic-gate static int
lowerit(a)20040Sstevel@tonic-gate lowerit(a)
20050Sstevel@tonic-gate int	a;
20060Sstevel@tonic-gate {
20070Sstevel@tonic-gate 	a = (unsigned char) a;
20080Sstevel@tonic-gate 	return ((isascii(a) && isupper(a)) ? tolower(a) : a);
20090Sstevel@tonic-gate }
20100Sstevel@tonic-gate 
20110Sstevel@tonic-gate static int
ciequal(ap,bp)20120Sstevel@tonic-gate ciequal(ap, bp)		/* case-insensitive equality */
20130Sstevel@tonic-gate register const char *ap;
20140Sstevel@tonic-gate register const char *bp;
20150Sstevel@tonic-gate {
20160Sstevel@tonic-gate 	while (lowerit(*ap) == lowerit(*bp++))
20170Sstevel@tonic-gate 		if (*ap++ == '\0')
20180Sstevel@tonic-gate 			return (TRUE);
20190Sstevel@tonic-gate 	return (FALSE);
20200Sstevel@tonic-gate }
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate static int
itsabbr(abbr,word)20230Sstevel@tonic-gate itsabbr(abbr, word)
20240Sstevel@tonic-gate register const char *abbr;
20250Sstevel@tonic-gate register const char *word;
20260Sstevel@tonic-gate {
20270Sstevel@tonic-gate 	if (lowerit(*abbr) != lowerit(*word))
20280Sstevel@tonic-gate 		return (FALSE);
20290Sstevel@tonic-gate 	++word;
20300Sstevel@tonic-gate 	while (*++abbr != '\0')
20310Sstevel@tonic-gate 		do {
20320Sstevel@tonic-gate 			if (*word == '\0')
20330Sstevel@tonic-gate 				return (FALSE);
20340Sstevel@tonic-gate 		} while (lowerit(*word++) != lowerit(*abbr));
20350Sstevel@tonic-gate 	return (TRUE);
20360Sstevel@tonic-gate }
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate static const struct lookup *
byword(word,table)20390Sstevel@tonic-gate byword(word, table)
20400Sstevel@tonic-gate register const char * const		word;
20410Sstevel@tonic-gate register const struct lookup * const	table;
20420Sstevel@tonic-gate {
20430Sstevel@tonic-gate 	register const struct lookup *foundlp;
20440Sstevel@tonic-gate 	register const struct lookup *lp;
20450Sstevel@tonic-gate 
20460Sstevel@tonic-gate 	if (word == NULL || table == NULL)
20470Sstevel@tonic-gate 		return (NULL);
20480Sstevel@tonic-gate 	/*
20490Sstevel@tonic-gate 	 * Look for exact match.
20500Sstevel@tonic-gate 	 */
20510Sstevel@tonic-gate 	for (lp = table; lp->l_word != NULL; ++lp)
20520Sstevel@tonic-gate 		if (ciequal(word, lp->l_word))
20530Sstevel@tonic-gate 			return (lp);
20540Sstevel@tonic-gate 	/*
20550Sstevel@tonic-gate 	 * Look for inexact match.
20560Sstevel@tonic-gate 	 */
20570Sstevel@tonic-gate 	foundlp = NULL;
20580Sstevel@tonic-gate 	for (lp = table; lp->l_word != NULL; ++lp)
20590Sstevel@tonic-gate 		if (itsabbr(word, lp->l_word)) {
20600Sstevel@tonic-gate 			if (foundlp == NULL)
20610Sstevel@tonic-gate 				foundlp = lp;
20620Sstevel@tonic-gate 			else	return (NULL);	/* multiple inexact matches */
20630Sstevel@tonic-gate 		}
20640Sstevel@tonic-gate 	return (foundlp);
20650Sstevel@tonic-gate }
20660Sstevel@tonic-gate 
20670Sstevel@tonic-gate static char **
getfields(cp)20680Sstevel@tonic-gate getfields(cp)
20690Sstevel@tonic-gate register char *cp;
20700Sstevel@tonic-gate {
20710Sstevel@tonic-gate 	register char 	*dp;
20720Sstevel@tonic-gate 	register char	**array;
20730Sstevel@tonic-gate 	register int	nsubs;
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 	if (cp == NULL)
20760Sstevel@tonic-gate 		return (NULL);
20770Sstevel@tonic-gate 	array = (char **)(void *)
20780Sstevel@tonic-gate 		emalloc((int)((strlen(cp) + 1) * sizeof (*array)));
20790Sstevel@tonic-gate 	nsubs = 0;
20800Sstevel@tonic-gate 	for (;;) {
20810Sstevel@tonic-gate 		while (isascii(*cp) && isspace((unsigned char) *cp))
20820Sstevel@tonic-gate 			++cp;
20830Sstevel@tonic-gate 		if (*cp == '\0' || *cp == '#')
20840Sstevel@tonic-gate 			break;
20850Sstevel@tonic-gate 		array[nsubs++] = dp = cp;
20860Sstevel@tonic-gate 		do {
20870Sstevel@tonic-gate 			if ((*dp = *cp++) != '"')
20880Sstevel@tonic-gate 				++dp;
20890Sstevel@tonic-gate 			else while ((*dp = *cp++) != '"')
20900Sstevel@tonic-gate 				if (*dp != '\0')
20910Sstevel@tonic-gate 					++dp;
20924218Srobbin 				else {
20930Sstevel@tonic-gate 					error(gettext(
20940Sstevel@tonic-gate 					    "Odd number of quotation marks"));
20954218Srobbin 					exit(1);
20964218Srobbin 				}
20970Sstevel@tonic-gate 		} while (*cp != '\0' && *cp != '#' &&
20980Sstevel@tonic-gate 			(!isascii(*cp) || !isspace((unsigned char) *cp)));
20990Sstevel@tonic-gate 		if (isascii(*cp) && isspace((unsigned char) *cp))
21000Sstevel@tonic-gate 			++cp;
21010Sstevel@tonic-gate 		*dp = '\0';
21020Sstevel@tonic-gate 	}
21030Sstevel@tonic-gate 	array[nsubs] = NULL;
21040Sstevel@tonic-gate 	return (array);
21050Sstevel@tonic-gate }
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate static long
oadd(t1,t2)21080Sstevel@tonic-gate oadd(t1, t2)
21090Sstevel@tonic-gate const long	t1;
21100Sstevel@tonic-gate const long	t2;
21110Sstevel@tonic-gate {
21120Sstevel@tonic-gate 	register long	t;
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 	t = t1 + t2;
21150Sstevel@tonic-gate 	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
21160Sstevel@tonic-gate 		error(gettext("time overflow"));
21171138Srobbin 		exit(EXIT_FAILURE);
21180Sstevel@tonic-gate 	}
21190Sstevel@tonic-gate 	return (t);
21200Sstevel@tonic-gate }
21210Sstevel@tonic-gate 
21221138Srobbin static zic_t
tadd(t1,t2)21230Sstevel@tonic-gate tadd(t1, t2)
21241138Srobbin const zic_t	t1;
21250Sstevel@tonic-gate const long	t2;
21260Sstevel@tonic-gate {
21271138Srobbin 	register zic_t	t;
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	if (t1 == max_time && t2 > 0)
21300Sstevel@tonic-gate 		return (max_time);
21310Sstevel@tonic-gate 	if (t1 == min_time && t2 < 0)
21320Sstevel@tonic-gate 		return (min_time);
21330Sstevel@tonic-gate 	t = t1 + t2;
21340Sstevel@tonic-gate 	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
21350Sstevel@tonic-gate 		error(gettext("time overflow"));
21361138Srobbin 		exit(EXIT_FAILURE);
21370Sstevel@tonic-gate 	}
21380Sstevel@tonic-gate 	return (t);
21390Sstevel@tonic-gate }
21400Sstevel@tonic-gate 
21410Sstevel@tonic-gate /*
21420Sstevel@tonic-gate  * Given a rule, and a year, compute the date - in seconds since January 1,
21430Sstevel@tonic-gate  * 1970, 00:00 LOCAL time - in that year that the rule refers to.
21440Sstevel@tonic-gate  */
21450Sstevel@tonic-gate 
21461138Srobbin static zic_t
rpytime(rp,wantedy)21470Sstevel@tonic-gate rpytime(rp, wantedy)
21480Sstevel@tonic-gate register const struct rule * const	rp;
21490Sstevel@tonic-gate register const int			wantedy;
21500Sstevel@tonic-gate {
21510Sstevel@tonic-gate 	register int	y, m, i;
21520Sstevel@tonic-gate 	register long	dayoff;			/* with a nod to Margaret O. */
21531138Srobbin 	register zic_t	t;
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 	if (wantedy == INT_MIN)
21560Sstevel@tonic-gate 		return (min_time);
21570Sstevel@tonic-gate 	if (wantedy == INT_MAX)
21580Sstevel@tonic-gate 		return (max_time);
21590Sstevel@tonic-gate 	dayoff = 0;
21600Sstevel@tonic-gate 	m = TM_JANUARY;
21610Sstevel@tonic-gate 	y = EPOCH_YEAR;
21620Sstevel@tonic-gate 	while (wantedy != y) {
21630Sstevel@tonic-gate 		if (wantedy > y) {
21640Sstevel@tonic-gate 			i = len_years[isleap(y)];
21650Sstevel@tonic-gate 			++y;
21660Sstevel@tonic-gate 		} else {
21670Sstevel@tonic-gate 			--y;
21680Sstevel@tonic-gate 			i = -len_years[isleap(y)];
21690Sstevel@tonic-gate 		}
21700Sstevel@tonic-gate 		dayoff = oadd(dayoff, eitol(i));
21710Sstevel@tonic-gate 	}
21720Sstevel@tonic-gate 	while (m != rp->r_month) {
21730Sstevel@tonic-gate 		i = len_months[isleap(y)][m];
21740Sstevel@tonic-gate 		dayoff = oadd(dayoff, eitol(i));
21750Sstevel@tonic-gate 		++m;
21760Sstevel@tonic-gate 	}
21770Sstevel@tonic-gate 	i = rp->r_dayofmonth;
21780Sstevel@tonic-gate 	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
21790Sstevel@tonic-gate 		if (rp->r_dycode == DC_DOWLEQ)
21800Sstevel@tonic-gate 			--i;
21810Sstevel@tonic-gate 		else {
21820Sstevel@tonic-gate 			error(gettext("use of 2/29 in non leap-year"));
21831138Srobbin 			exit(EXIT_FAILURE);
21840Sstevel@tonic-gate 		}
21850Sstevel@tonic-gate 	}
21860Sstevel@tonic-gate 	--i;
21870Sstevel@tonic-gate 	dayoff = oadd(dayoff, eitol(i));
21880Sstevel@tonic-gate 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
21890Sstevel@tonic-gate 		register long	wday;
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate #define	LDAYSPERWEEK	((long)DAYSPERWEEK)
21920Sstevel@tonic-gate 		wday = eitol(EPOCH_WDAY);
21930Sstevel@tonic-gate 		/*
21940Sstevel@tonic-gate 		 * Don't trust mod of negative numbers.
21950Sstevel@tonic-gate 		 */
21960Sstevel@tonic-gate 		if (dayoff >= 0)
21970Sstevel@tonic-gate 			wday = (wday + dayoff) % LDAYSPERWEEK;
21980Sstevel@tonic-gate 		else {
21990Sstevel@tonic-gate 			wday -= ((-dayoff) % LDAYSPERWEEK);
22000Sstevel@tonic-gate 			if (wday < 0)
22010Sstevel@tonic-gate 				wday += LDAYSPERWEEK;
22020Sstevel@tonic-gate 		}
22030Sstevel@tonic-gate 		while (wday != eitol(rp->r_wday))
22040Sstevel@tonic-gate 			if (rp->r_dycode == DC_DOWGEQ) {
22050Sstevel@tonic-gate 				dayoff = oadd(dayoff, (long)1);
22060Sstevel@tonic-gate 				if (++wday >= LDAYSPERWEEK)
22070Sstevel@tonic-gate 					wday = 0;
22080Sstevel@tonic-gate 				++i;
22090Sstevel@tonic-gate 			} else {
22100Sstevel@tonic-gate 				dayoff = oadd(dayoff, (long)-1);
22110Sstevel@tonic-gate 				if (--wday < 0)
22120Sstevel@tonic-gate 					wday = LDAYSPERWEEK - 1;
22130Sstevel@tonic-gate 				--i;
22140Sstevel@tonic-gate 			}
22150Sstevel@tonic-gate 		if (i < 0 || i >= len_months[isleap(y)][m]) {
22161138Srobbin 			if (noise)
22171138Srobbin 				warning(gettext("rule goes past start/end of "
22181138Srobbin 				    "month--will not work with pre-2004 "
22191138Srobbin 				    "versions of zic"));
22200Sstevel@tonic-gate 		}
22210Sstevel@tonic-gate 	}
22221138Srobbin 	if (dayoff < 0 && !TYPE_SIGNED(zic_t))
22231138Srobbin 		return (min_time);
22241138Srobbin 	if (dayoff < min_time / SECSPERDAY)
22250Sstevel@tonic-gate 		return (min_time);
22261138Srobbin 	if (dayoff > max_time / SECSPERDAY)
22271138Srobbin 		return (max_time);
22281138Srobbin 	t = (zic_t)dayoff * SECSPERDAY;
22290Sstevel@tonic-gate 	return (tadd(t, rp->r_tod));
22300Sstevel@tonic-gate }
22310Sstevel@tonic-gate 
22320Sstevel@tonic-gate static void
newabbr(string)22330Sstevel@tonic-gate newabbr(string)
22340Sstevel@tonic-gate const char * const	string;
22350Sstevel@tonic-gate {
22360Sstevel@tonic-gate 	register int	i;
22370Sstevel@tonic-gate 
22381138Srobbin 	if (strcmp(string, GRANDPARENTED) != 0) {
22391138Srobbin 		register const char *cp;
22401138Srobbin 		register char *wp;
22411138Srobbin 
22421138Srobbin 		/*
22431138Srobbin 		 * Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics
22441138Srobbin 		 * optionally followed by a + or - and a number from 1 to 14.
22451138Srobbin 		 */
22461138Srobbin 		cp = string;
22471138Srobbin 		wp = NULL;
22481138Srobbin 		while (isascii(*cp) && isalpha(*cp))
22491138Srobbin 			++cp;
22501138Srobbin 		if (cp - string == 0)
22511138Srobbin 			wp = gettext(("time zone abbreviation lacks "
22521138Srobbin 			    "alphabetic at start"));
22531138Srobbin 		if (noise && cp - string > 3)
22541138Srobbin 			wp = gettext(("time zone abbreviation has more than 3 "
22551138Srobbin 			    "alphabetics"));
22561138Srobbin 		if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
22571138Srobbin 			wp = gettext(("time zone abbreviation has too many "
22581138Srobbin 			    "alphabetics"));
22591138Srobbin 		if (wp == NULL && (*cp == '+' || *cp == '-')) {
22601138Srobbin 			++cp;
22611138Srobbin 			if (isascii(*cp) && isdigit(*cp))
22621138Srobbin 				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
22631138Srobbin 					++cp;
22641138Srobbin 		}
22651138Srobbin 		if (*cp != '\0')
22661138Srobbin 			wp = gettext("time zone abbreviation differs from "
22671138Srobbin 			    "POSIX standard");
22681138Srobbin 		if (wp != NULL) {
22691138Srobbin 			wp = ecpyalloc(wp);
22701138Srobbin 			wp = ecatalloc(wp, " (");
22711138Srobbin 			wp = ecatalloc(wp, string);
22721138Srobbin 			wp = ecatalloc(wp, ")");
22731138Srobbin 			warning(wp);
22741138Srobbin 			ifree(wp);
22751138Srobbin 		}
22761138Srobbin 	}
22770Sstevel@tonic-gate 	i = strlen(string) + 1;
22780Sstevel@tonic-gate 	if (charcnt + i > TZ_MAX_CHARS) {
22791138Srobbin 		error(gettext("too many, or too long, time zone "
22801138Srobbin 		    "abbreviations"));
22811138Srobbin 		exit(EXIT_FAILURE);
22820Sstevel@tonic-gate 	}
22830Sstevel@tonic-gate 	(void) strcpy(&chars[charcnt], string);
22840Sstevel@tonic-gate 	charcnt += eitol(i);
22850Sstevel@tonic-gate }
22860Sstevel@tonic-gate 
22870Sstevel@tonic-gate static int
mkdirs(argname)22880Sstevel@tonic-gate mkdirs(argname)
2289*4223Srobbin char *argname;
22900Sstevel@tonic-gate {
22910Sstevel@tonic-gate 	register char *name;
22920Sstevel@tonic-gate 	register char *cp;
22930Sstevel@tonic-gate 
22940Sstevel@tonic-gate 	if (argname == NULL || *argname == '\0')
22950Sstevel@tonic-gate 		return (0);
22960Sstevel@tonic-gate 	cp = name = ecpyalloc(argname);
22970Sstevel@tonic-gate 	while ((cp = strchr(cp + 1, '/')) != 0) {
22980Sstevel@tonic-gate 		*cp = '\0';
22990Sstevel@tonic-gate 		if (!itsdir(name)) {
23000Sstevel@tonic-gate 			/*
23010Sstevel@tonic-gate 			 * It doesn't seem to exist, so we try to create it.
23020Sstevel@tonic-gate 			 * Creation may fail because of the directory being
23030Sstevel@tonic-gate 			 * created by some other multiprocessor, so we get
23040Sstevel@tonic-gate 			 * to do extra checking.
23050Sstevel@tonic-gate 			 */
23061138Srobbin 			if (mkdir(name, MKDIR_UMASK) != 0) {
23070Sstevel@tonic-gate 				const char *e = strerror(errno);
23080Sstevel@tonic-gate 
23090Sstevel@tonic-gate 				if (errno != EEXIST || !itsdir(name)) {
23100Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
23110Sstevel@tonic-gate 				    "%s: Can't create directory %s: %s\n"),
23120Sstevel@tonic-gate 						progname, name, e);
23130Sstevel@tonic-gate 					ifree(name);
23140Sstevel@tonic-gate 					return (-1);
23150Sstevel@tonic-gate 				}
23160Sstevel@tonic-gate 			}
23170Sstevel@tonic-gate 		}
23180Sstevel@tonic-gate 		*cp = '/';
23190Sstevel@tonic-gate 	}
23200Sstevel@tonic-gate 	ifree(name);
23210Sstevel@tonic-gate 	return (0);
23220Sstevel@tonic-gate }
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate static long
eitol(i)23250Sstevel@tonic-gate eitol(i)
23260Sstevel@tonic-gate const int	i;
23270Sstevel@tonic-gate {
23280Sstevel@tonic-gate 	long	l;
23290Sstevel@tonic-gate 
23300Sstevel@tonic-gate 	l = i;
23310Sstevel@tonic-gate 	if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
23320Sstevel@tonic-gate 		(void) fprintf(stderr,
23330Sstevel@tonic-gate 			gettext("%s: %d did not sign extend correctly\n"),
23340Sstevel@tonic-gate 			progname, i);
23351138Srobbin 		exit(EXIT_FAILURE);
23360Sstevel@tonic-gate 	}
23370Sstevel@tonic-gate 	return (l);
23380Sstevel@tonic-gate }
23390Sstevel@tonic-gate 
23400Sstevel@tonic-gate /*
23411138Srobbin  * UNIX was a registered trademark of The Open Group in 2003.
23420Sstevel@tonic-gate  */
2343