zic.c revision 130819
1static const char	elsieid[] = "@(#)zic.c	7.116";
2
3#ifndef lint
4static const char rcsid[] =
5  "$FreeBSD: head/usr.sbin/zic/zic.c 130819 2004-06-20 21:41:11Z stefanf $";
6#endif /* not lint */
7
8#include "private.h"
9#include "tzfile.h"
10#include <err.h>
11#include <locale.h>
12#include <sys/stat.h>			/* for umask manifest constants */
13#include <sys/types.h>
14#include <unistd.h>
15
16#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
17
18/*
19** On some ancient hosts, predicates like `isspace(C)' are defined
20** only if isascii(C) || C == EOF.  Modern hosts obey the C Standard,
21** which says they are defined only if C == ((unsigned char) C) || C == EOF.
22** Neither the C Standard nor POSIX require that `isascii' exist.
23** For portability, we check both ancient and modern requirements.
24** If isascii is not defined, the isascii check succeeds trivially.
25*/
26#include "ctype.h"
27#ifndef isascii
28#define isascii(x) 1
29#endif
30
31struct rule {
32	const char *	r_filename;
33	int		r_linenum;
34	const char *	r_name;
35
36	int		r_loyear;	/* for example, 1986 */
37	int		r_hiyear;	/* for example, 1986 */
38	const char *	r_yrtype;
39
40	int		r_month;	/* 0..11 */
41
42	int		r_dycode;	/* see below */
43	int		r_dayofmonth;
44	int		r_wday;
45
46	long		r_tod;		/* time from midnight */
47	int		r_todisstd;	/* above is standard time if TRUE */
48					/* or wall clock time if FALSE */
49	int		r_todisgmt;	/* above is GMT if TRUE */
50					/* or local time if FALSE */
51	long		r_stdoff;	/* offset from standard time */
52	const char *	r_abbrvar;	/* variable part of abbreviation */
53
54	int		r_todo;		/* a rule to do (used in outzone) */
55	time_t		r_temp;		/* used in outzone */
56};
57
58/*
59**	r_dycode		r_dayofmonth	r_wday
60*/
61
62#define DC_DOM		0	/* 1..31 */	/* unused */
63#define DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */
64#define DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */
65
66struct zone {
67	const char *	z_filename;
68	int		z_linenum;
69
70	const char *	z_name;
71	long		z_gmtoff;
72	const char *	z_rule;
73	const char *	z_format;
74
75	long		z_stdoff;
76
77	struct rule *	z_rules;
78	int		z_nrules;
79
80	struct rule	z_untilrule;
81	time_t		z_untiltime;
82};
83
84static void	addtt P((time_t starttime, int type));
85static int	addtype P((long gmtoff, const char * abbr, int isdst,
86				int ttisstd, int ttisgmt));
87static void	leapadd P((time_t t, int positive, int rolling, int count));
88static void	adjleap P((void));
89static void	associate P((void));
90static int	ciequal P((const char * ap, const char * bp));
91static void	convert P((long val, char * buf));
92static void	dolink P((const char * fromfile, const char * tofile));
93static void	doabbr P((char * abbr, const char * format,
94			const char * letters, int isdst));
95static void	eat P((const char * name, int num));
96static void	eats P((const char * name, int num,
97			const char * rname, int rnum));
98static long	eitol P((int i));
99static void	error P((const char * message));
100static char **	getfields P((char * buf));
101static long	gethms P((const char * string, const char * errstrng,
102			int signable));
103static void	infile P((const char * filename));
104static void	inleap P((char ** fields, int nfields));
105static void	inlink P((char ** fields, int nfields));
106static void	inrule P((char ** fields, int nfields));
107static int	inzcont P((char ** fields, int nfields));
108static int	inzone P((char ** fields, int nfields));
109static int	inzsub P((char ** fields, int nfields, int iscont));
110static int	itsabbr P((const char * abbr, const char * word));
111static int	itsdir P((const char * name));
112static int	lowerit P((int c));
113static char *	memcheck P((char * tocheck));
114static int	mkdirs P((char * filename));
115static void	newabbr P((const char * abbr));
116static long	oadd P((long t1, long t2));
117static void	outzone P((const struct zone * zp, int ntzones));
118static void	puttzcode P((long code, FILE * fp));
119static int	rcomp P((const void * leftp, const void * rightp));
120static time_t	rpytime P((const struct rule * rp, int wantedy));
121static void	rulesub P((struct rule * rp,
122			const char * loyearp, const char * hiyearp,
123			const char * typep, const char * monthp,
124			const char * dayp, const char * timep));
125static void	setboundaries P((void));
126static void	setgroup P((gid_t *flag, const char *name));
127static void	setuser P((uid_t *flag, const char *name));
128static time_t	tadd P((time_t t1, long t2));
129static void	usage P((void));
130static void	writezone P((const char * name));
131static int	yearistype P((int year, const char * type));
132
133#if !(HAVE_STRERROR - 0)
134static char *	strerror P((int));
135#endif /* !(HAVE_STRERROR - 0) */
136
137static int		charcnt;
138static int		errors;
139static const char *	filename;
140static int		leapcnt;
141static int		linenum;
142static time_t		max_time;
143static int		max_year;
144static int		max_year_representable;
145static time_t		min_time;
146static int		min_year;
147static int		min_year_representable;
148static int		noise;
149static const char *	rfilename;
150static int		rlinenum;
151static int		timecnt;
152static int		typecnt;
153
154/*
155** Line codes.
156*/
157
158#define LC_RULE		0
159#define LC_ZONE		1
160#define LC_LINK		2
161#define LC_LEAP		3
162
163/*
164** Which fields are which on a Zone line.
165*/
166
167#define ZF_NAME		1
168#define ZF_GMTOFF	2
169#define ZF_RULE		3
170#define ZF_FORMAT	4
171#define ZF_TILYEAR	5
172#define ZF_TILMONTH	6
173#define ZF_TILDAY	7
174#define ZF_TILTIME	8
175#define ZONE_MINFIELDS	5
176#define ZONE_MAXFIELDS	9
177
178/*
179** Which fields are which on a Zone continuation line.
180*/
181
182#define ZFC_GMTOFF	0
183#define ZFC_RULE	1
184#define ZFC_FORMAT	2
185#define ZFC_TILYEAR	3
186#define ZFC_TILMONTH	4
187#define ZFC_TILDAY	5
188#define ZFC_TILTIME	6
189#define ZONEC_MINFIELDS	3
190#define ZONEC_MAXFIELDS	7
191
192/*
193** Which files are which on a Rule line.
194*/
195
196#define RF_NAME		1
197#define RF_LOYEAR	2
198#define RF_HIYEAR	3
199#define RF_COMMAND	4
200#define RF_MONTH	5
201#define RF_DAY		6
202#define RF_TOD		7
203#define RF_STDOFF	8
204#define RF_ABBRVAR	9
205#define RULE_FIELDS	10
206
207/*
208** Which fields are which on a Link line.
209*/
210
211#define LF_FROM		1
212#define LF_TO		2
213#define LINK_FIELDS	3
214
215/*
216** Which fields are which on a Leap line.
217*/
218
219#define LP_YEAR		1
220#define LP_MONTH	2
221#define LP_DAY		3
222#define LP_TIME		4
223#define LP_CORR		5
224#define LP_ROLL		6
225#define LEAP_FIELDS	7
226
227/*
228** Year synonyms.
229*/
230
231#define YR_MINIMUM	0
232#define YR_MAXIMUM	1
233#define YR_ONLY		2
234
235static struct rule *	rules;
236static int		nrules;	/* number of rules */
237
238static struct zone *	zones;
239static int		nzones;	/* number of zones */
240
241struct link {
242	const char *	l_filename;
243	int		l_linenum;
244	const char *	l_from;
245	const char *	l_to;
246};
247
248static struct link *	links;
249static int		nlinks;
250
251struct lookup {
252	const char *	l_word;
253	const int	l_value;
254};
255
256static struct lookup const *	byword P((const char * string,
257					const struct lookup * lp));
258
259static struct lookup const	line_codes[] = {
260	{ "Rule",	LC_RULE },
261	{ "Zone",	LC_ZONE },
262	{ "Link",	LC_LINK },
263	{ "Leap",	LC_LEAP },
264	{ NULL,		0}
265};
266
267static struct lookup const	mon_names[] = {
268	{ "January",	TM_JANUARY },
269	{ "February",	TM_FEBRUARY },
270	{ "March",	TM_MARCH },
271	{ "April",	TM_APRIL },
272	{ "May",	TM_MAY },
273	{ "June",	TM_JUNE },
274	{ "July",	TM_JULY },
275	{ "August",	TM_AUGUST },
276	{ "September",	TM_SEPTEMBER },
277	{ "October",	TM_OCTOBER },
278	{ "November",	TM_NOVEMBER },
279	{ "December",	TM_DECEMBER },
280	{ NULL,		0 }
281};
282
283static struct lookup const	wday_names[] = {
284	{ "Sunday",	TM_SUNDAY },
285	{ "Monday",	TM_MONDAY },
286	{ "Tuesday",	TM_TUESDAY },
287	{ "Wednesday",	TM_WEDNESDAY },
288	{ "Thursday",	TM_THURSDAY },
289	{ "Friday",	TM_FRIDAY },
290	{ "Saturday",	TM_SATURDAY },
291	{ NULL,		0 }
292};
293
294static struct lookup const	lasts[] = {
295	{ "last-Sunday",	TM_SUNDAY },
296	{ "last-Monday",	TM_MONDAY },
297	{ "last-Tuesday",	TM_TUESDAY },
298	{ "last-Wednesday",	TM_WEDNESDAY },
299	{ "last-Thursday",	TM_THURSDAY },
300	{ "last-Friday",	TM_FRIDAY },
301	{ "last-Saturday",	TM_SATURDAY },
302	{ NULL,			0 }
303};
304
305static struct lookup const	begin_years[] = {
306	{ "minimum",	YR_MINIMUM },
307	{ "maximum",	YR_MAXIMUM },
308	{ NULL,		0 }
309};
310
311static struct lookup const	end_years[] = {
312	{ "minimum",	YR_MINIMUM },
313	{ "maximum",	YR_MAXIMUM },
314	{ "only",	YR_ONLY },
315	{ NULL,		0 }
316};
317
318static struct lookup const	leap_types[] = {
319	{ "Rolling",	TRUE },
320	{ "Stationary",	FALSE },
321	{ NULL,		0 }
322};
323
324static const int	len_months[2][MONSPERYEAR] = {
325	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
326	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
327};
328
329static const int	len_years[2] = {
330	DAYSPERNYEAR, DAYSPERLYEAR
331};
332
333static struct attype {
334	time_t		at;
335	unsigned char	type;
336}			attypes[TZ_MAX_TIMES];
337static long		gmtoffs[TZ_MAX_TYPES];
338static char		isdsts[TZ_MAX_TYPES];
339static unsigned char	abbrinds[TZ_MAX_TYPES];
340static char		ttisstds[TZ_MAX_TYPES];
341static char		ttisgmts[TZ_MAX_TYPES];
342static char		chars[TZ_MAX_CHARS];
343static time_t		trans[TZ_MAX_LEAPS];
344static long		corr[TZ_MAX_LEAPS];
345static char		roll[TZ_MAX_LEAPS];
346
347/*
348** Memory allocation.
349*/
350
351static char *
352memcheck(ptr)
353char * const	ptr;
354{
355	if (ptr == NULL)
356		errx(EXIT_FAILURE, _("memory exhausted"));
357	return ptr;
358}
359
360#define emalloc(size)		memcheck(imalloc(size))
361#define erealloc(ptr, size)	memcheck(irealloc((ptr), (size)))
362#define ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
363#define ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
364
365/*
366** Error handling.
367*/
368
369#if !(HAVE_STRERROR - 0)
370static char *
371strerror(errnum)
372int	errnum;
373{
374	extern char *	sys_errlist[];
375	extern int	sys_nerr;
376
377	return (errnum > 0 && errnum <= sys_nerr) ?
378		sys_errlist[errnum] : _("Unknown system error");
379}
380#endif /* !(HAVE_STRERROR - 0) */
381
382static void
383eats(name, num, rname, rnum)
384const char * const	name;
385const int		num;
386const char * const	rname;
387const int		rnum;
388{
389	filename = name;
390	linenum = num;
391	rfilename = rname;
392	rlinenum = rnum;
393}
394
395static void
396eat(name, num)
397const char * const	name;
398const int		num;
399{
400	eats(name, num, (char *) NULL, -1);
401}
402
403static void
404error(string)
405const char * const	string;
406{
407	/*
408	** Match the format of "cc" to allow sh users to
409	**	zic ... 2>&1 | error -t "*" -v
410	** on BSD systems.
411	*/
412	(void) fprintf(stderr, _("\"%s\", line %d: %s"),
413		filename, linenum, string);
414	if (rfilename != NULL)
415		(void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
416			rfilename, rlinenum);
417	(void) fprintf(stderr, "\n");
418	++errors;
419}
420
421static void
422warning(string)
423const char * const	string;
424{
425	char *	cp;
426
427	cp = ecpyalloc(_("warning: "));
428	cp = ecatalloc(cp, string);
429	error(cp);
430	ifree(cp);
431	--errors;
432}
433
434static void
435usage P((void))
436{
437	(void) fprintf(stderr, "%s\n%s\n",
438_("usage: zic [--version] [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"),
439_("           [-L leapseconds] [-y yearistype] [filename ... ]"));
440	(void) exit(EXIT_FAILURE);
441}
442
443static const char *	psxrules;
444static const char *	lcltime;
445static const char *	directory;
446static const char *	leapsec;
447static const char *	yitcommand;
448static int		sflag = FALSE;
449static int		Dflag;
450static uid_t		uflag = (uid_t)-1;
451static gid_t		gflag = (gid_t)-1;
452static mode_t		mflag = (S_IRUSR | S_IRGRP | S_IROTH
453				 | S_IWUSR);
454
455int
456main(argc, argv)
457int	argc;
458char *	argv[];
459{
460	register int	i;
461	register int	j;
462	register int	c;
463
464#ifdef unix
465	(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
466#endif /* defined unix */
467#if HAVE_GETTEXT - 0
468	(void) setlocale(LC_MESSAGES, "");
469#ifdef TZ_DOMAINDIR
470	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
471#endif /* defined TEXTDOMAINDIR */
472	(void) textdomain(TZ_DOMAIN);
473#endif /* HAVE_GETTEXT - 0 */
474	for (i = 1; i < argc; ++i)
475		if (strcmp(argv[i], "--version") == 0) {
476			errx(EXIT_SUCCESS, "%s", elsieid);
477		}
478	while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
479		switch (c) {
480			default:
481				usage();
482			case 'D':
483				Dflag = 1;
484				break;
485			case 'd':
486				if (directory == NULL)
487					directory = optarg;
488				else
489					errx(EXIT_FAILURE,
490_("more than one -d option specified"));
491				break;
492			case 'g':
493				setgroup(&gflag, optarg);
494				break;
495			case 'l':
496				if (lcltime == NULL)
497					lcltime = optarg;
498				else
499					errx(EXIT_FAILURE,
500_("more than one -l option specified"));
501				break;
502			case 'm':
503			{
504				void *set = setmode(optarg);
505				if (set == NULL)
506					errx(EXIT_FAILURE,
507_("invalid file mode"));
508				getmode(set, mflag);
509				free(set);
510				break;
511			}
512			case 'p':
513				if (psxrules == NULL)
514					psxrules = optarg;
515				else
516					errx(EXIT_FAILURE,
517_("more than one -p option specified"));
518				break;
519			case 'u':
520				setuser(&uflag, optarg);
521				break;
522			case 'y':
523				if (yitcommand == NULL)
524					yitcommand = optarg;
525				else
526					errx(EXIT_FAILURE,
527_("more than one -y option specified"));
528				break;
529			case 'L':
530				if (leapsec == NULL)
531					leapsec = optarg;
532				else
533					errx(EXIT_FAILURE,
534_("more than one -L option specified"));
535				break;
536			case 'v':
537				noise = TRUE;
538				break;
539			case 's':
540				sflag = TRUE;
541				break;
542		}
543	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
544		usage();	/* usage message by request */
545	if (directory == NULL)
546		directory = TZDIR;
547	if (yitcommand == NULL)
548		yitcommand = "yearistype";
549
550	setboundaries();
551
552	if (optind < argc && leapsec != NULL) {
553		infile(leapsec);
554		adjleap();
555	}
556
557	for (i = optind; i < argc; ++i)
558		infile(argv[i]);
559	if (errors)
560		(void) exit(EXIT_FAILURE);
561	associate();
562	for (i = 0; i < nzones; i = j) {
563		/*
564		** Find the next non-continuation zone entry.
565		*/
566		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
567			continue;
568		outzone(&zones[i], j - i);
569	}
570	/*
571	** Make links.
572	*/
573	for (i = 0; i < nlinks; ++i) {
574		eat(links[i].l_filename, links[i].l_linenum);
575		dolink(links[i].l_from, links[i].l_to);
576	}
577	if (lcltime != NULL) {
578		eat("command line", 1);
579		dolink(lcltime, TZDEFAULT);
580	}
581	if (psxrules != NULL) {
582		eat("command line", 1);
583		dolink(psxrules, TZDEFRULES);
584	}
585	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
586}
587
588static void
589dolink(fromfile, tofile)
590const char * const	fromfile;
591const char * const	tofile;
592{
593	register char *	fromname;
594	register char *	toname;
595
596	if (fromfile[0] == '/')
597		fromname = ecpyalloc(fromfile);
598	else {
599		fromname = ecpyalloc(directory);
600		fromname = ecatalloc(fromname, "/");
601		fromname = ecatalloc(fromname, fromfile);
602	}
603	if (tofile[0] == '/')
604		toname = ecpyalloc(tofile);
605	else {
606		toname = ecpyalloc(directory);
607		toname = ecatalloc(toname, "/");
608		toname = ecatalloc(toname, tofile);
609	}
610	/*
611	** We get to be careful here since
612	** there's a fair chance of root running us.
613	*/
614	if (!itsdir(toname))
615		(void) remove(toname);
616	if (link(fromname, toname) != 0) {
617		int	result;
618
619		if (mkdirs(toname) != 0)
620			(void) exit(EXIT_FAILURE);
621
622		result = link(fromname, toname);
623#if (HAVE_SYMLINK - 0)
624		if (result != 0 &&
625		    access(fromname, F_OK) == 0 &&
626		    !itsdir(fromname)) {
627		        const char *s = tofile;
628		        register char * symlinkcontents = NULL;
629		        while ((s = strchr(s+1, '/')) != NULL)
630			        symlinkcontents = ecatalloc(symlinkcontents, "../");
631			symlinkcontents = ecatalloc(symlinkcontents, fromfile);
632
633			result = symlink(symlinkcontents, toname);
634			if (result == 0)
635warning(_("hard link failed, symbolic link used"));
636			ifree(symlinkcontents);
637		}
638#endif
639		if (result != 0) {
640			err(EXIT_FAILURE, _("can't link from %s to %s"),
641			    fromname, toname);
642		}
643	}
644	ifree(fromname);
645	ifree(toname);
646}
647
648#ifndef INT_MAX
649#define INT_MAX	((int) (((unsigned)~0)>>1))
650#endif /* !defined INT_MAX */
651
652#ifndef INT_MIN
653#define INT_MIN	((int) ~(((unsigned)~0)>>1))
654#endif /* !defined INT_MIN */
655
656/*
657** The tz file format currently allows at most 32-bit quantities.
658** This restriction should be removed before signed 32-bit values
659** wrap around in 2038, but unfortunately this will require a
660** change to the tz file format.
661*/
662
663#define MAX_BITS_IN_FILE	32
664#define TIME_T_BITS_IN_FILE	((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
665
666static void
667setboundaries P((void))
668{
669	if (TYPE_SIGNED(time_t)) {
670		min_time = ~ (time_t) 0;
671		min_time <<= TIME_T_BITS_IN_FILE - 1;
672		max_time = ~ (time_t) 0 - min_time;
673		if (sflag)
674			min_time = 0;
675	} else {
676		min_time = 0;
677		max_time = 2 - sflag;
678		max_time <<= TIME_T_BITS_IN_FILE - 1;
679		--max_time;
680	}
681	min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
682	max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
683	min_year_representable = min_year;
684	max_year_representable = max_year;
685}
686
687static int
688itsdir(name)
689const char * const	name;
690{
691	register char *	myname;
692	register int	accres;
693
694	myname = ecpyalloc(name);
695	myname = ecatalloc(myname, "/.");
696	accres = access(myname, F_OK);
697	ifree(myname);
698	return accres == 0;
699}
700
701/*
702** Associate sets of rules with zones.
703*/
704
705/*
706** Sort by rule name.
707*/
708
709static int
710rcomp(cp1, cp2)
711const void *	cp1;
712const void *	cp2;
713{
714	return strcmp(((const struct rule *) cp1)->r_name,
715		((const struct rule *) cp2)->r_name);
716}
717
718static void
719associate P((void))
720{
721	register struct zone *	zp;
722	register struct rule *	rp;
723	register int		base, out;
724	register int		i, j;
725
726	if (nrules != 0) {
727		(void) qsort((void *) rules, (size_t) nrules,
728			(size_t) sizeof *rules, rcomp);
729		for (i = 0; i < nrules - 1; ++i) {
730			if (strcmp(rules[i].r_name,
731				rules[i + 1].r_name) != 0)
732					continue;
733			if (strcmp(rules[i].r_filename,
734				rules[i + 1].r_filename) == 0)
735					continue;
736			eat(rules[i].r_filename, rules[i].r_linenum);
737			warning(_("same rule name in multiple files"));
738			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
739			warning(_("same rule name in multiple files"));
740			for (j = i + 2; j < nrules; ++j) {
741				if (strcmp(rules[i].r_name,
742					rules[j].r_name) != 0)
743						break;
744				if (strcmp(rules[i].r_filename,
745					rules[j].r_filename) == 0)
746						continue;
747				if (strcmp(rules[i + 1].r_filename,
748					rules[j].r_filename) == 0)
749						continue;
750				break;
751			}
752			i = j - 1;
753		}
754	}
755	for (i = 0; i < nzones; ++i) {
756		zp = &zones[i];
757		zp->z_rules = NULL;
758		zp->z_nrules = 0;
759	}
760	for (base = 0; base < nrules; base = out) {
761		rp = &rules[base];
762		for (out = base + 1; out < nrules; ++out)
763			if (strcmp(rp->r_name, rules[out].r_name) != 0)
764				break;
765		for (i = 0; i < nzones; ++i) {
766			zp = &zones[i];
767			if (strcmp(zp->z_rule, rp->r_name) != 0)
768				continue;
769			zp->z_rules = rp;
770			zp->z_nrules = out - base;
771		}
772	}
773	for (i = 0; i < nzones; ++i) {
774		zp = &zones[i];
775		if (zp->z_nrules == 0) {
776			/*
777			** Maybe we have a local standard time offset.
778			*/
779			eat(zp->z_filename, zp->z_linenum);
780			zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
781					      TRUE);
782			/*
783			** Note, though, that if there's no rule,
784			** a '%s' in the format is a bad thing.
785			*/
786			if (strchr(zp->z_format, '%') != 0)
787				error(_("%s in ruleless zone"));
788		}
789	}
790	if (errors)
791		(void) exit(EXIT_FAILURE);
792}
793
794static void
795infile(name)
796const char *	name;
797{
798	register FILE *			fp;
799	register char **		fields;
800	register char *			cp;
801	register const struct lookup *	lp;
802	register int			nfields;
803	register int			wantcont;
804	register int			num;
805	char				buf[BUFSIZ];
806
807	if (strcmp(name, "-") == 0) {
808		name = _("standard input");
809		fp = stdin;
810	} else if ((fp = fopen(name, "r")) == NULL)
811		err(EXIT_FAILURE, _("can't open %s"), name);
812	wantcont = FALSE;
813	for (num = 1; ; ++num) {
814		eat(name, num);
815		if (fgets(buf, (int) sizeof buf, fp) != buf)
816			break;
817		cp = strchr(buf, '\n');
818		if (cp == NULL) {
819			error(_("line too long"));
820			(void) exit(EXIT_FAILURE);
821		}
822		*cp = '\0';
823		fields = getfields(buf);
824		nfields = 0;
825		while (fields[nfields] != NULL) {
826			static char	nada;
827
828			if (strcmp(fields[nfields], "-") == 0)
829				fields[nfields] = &nada;
830			++nfields;
831		}
832		if (nfields == 0) {
833			/* nothing to do */
834		} else if (wantcont) {
835			wantcont = inzcont(fields, nfields);
836		} else {
837			lp = byword(fields[0], line_codes);
838			if (lp == NULL)
839				error(_("input line of unknown type"));
840			else switch ((int) (lp->l_value)) {
841				case LC_RULE:
842					inrule(fields, nfields);
843					wantcont = FALSE;
844					break;
845				case LC_ZONE:
846					wantcont = inzone(fields, nfields);
847					break;
848				case LC_LINK:
849					inlink(fields, nfields);
850					wantcont = FALSE;
851					break;
852				case LC_LEAP:
853					if (name != leapsec)
854						warnx(
855_("leap line in non leap seconds file %s"), name);
856					else	inleap(fields, nfields);
857					wantcont = FALSE;
858					break;
859				default:	/* "cannot happen" */
860					errx(EXIT_FAILURE,
861_("panic: invalid l_value %d"), lp->l_value);
862			}
863		}
864		ifree((char *) fields);
865	}
866	if (ferror(fp))
867		errx(EXIT_FAILURE, _("error reading %s"), filename);
868	if (fp != stdin && fclose(fp))
869		err(EXIT_FAILURE, _("error closing %s"), filename);
870	if (wantcont)
871		error(_("expected continuation line not found"));
872}
873
874/*
875** Convert a string of one of the forms
876**	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
877** into a number of seconds.
878** A null string maps to zero.
879** Call error with errstring and return zero on errors.
880*/
881
882static long
883gethms(string, errstring, signable)
884const char *		string;
885const char * const	errstring;
886const int		signable;
887{
888	int	hh, mm, ss, sign;
889
890	if (string == NULL || *string == '\0')
891		return 0;
892	if (!signable)
893		sign = 1;
894	else if (*string == '-') {
895		sign = -1;
896		++string;
897	} else	sign = 1;
898	if (sscanf(string, scheck(string, "%d"), &hh) == 1)
899		mm = ss = 0;
900	else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
901		ss = 0;
902	else if (sscanf(string, scheck(string, "%d:%d:%d"),
903		&hh, &mm, &ss) != 3) {
904			error(errstring);
905			return 0;
906	}
907	if ((hh < 0 || hh >= HOURSPERDAY ||
908		mm < 0 || mm >= MINSPERHOUR ||
909		ss < 0 || ss > SECSPERMIN) &&
910		!(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
911			error(errstring);
912			return 0;
913	}
914	if (noise && hh == HOURSPERDAY)
915		warning(_("24:00 not handled by pre-1998 versions of zic"));
916	return eitol(sign) *
917		(eitol(hh * MINSPERHOUR + mm) *
918		eitol(SECSPERMIN) + eitol(ss));
919}
920
921static void
922inrule(fields, nfields)
923register char ** const	fields;
924const int		nfields;
925{
926	static struct rule	r;
927
928	if (nfields != RULE_FIELDS) {
929		error(_("wrong number of fields on Rule line"));
930		return;
931	}
932	if (*fields[RF_NAME] == '\0') {
933		error(_("nameless rule"));
934		return;
935	}
936	r.r_filename = filename;
937	r.r_linenum = linenum;
938	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
939	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
940		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
941	r.r_name = ecpyalloc(fields[RF_NAME]);
942	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
943	rules = (struct rule *) (void *) erealloc((char *) rules,
944		(int) ((nrules + 1) * sizeof *rules));
945	rules[nrules++] = r;
946}
947
948static int
949inzone(fields, nfields)
950register char ** const	fields;
951const int		nfields;
952{
953	register int	i;
954	static char *	buf;
955
956	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
957		error(_("wrong number of fields on Zone line"));
958		return FALSE;
959	}
960	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
961		buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
962		(void) sprintf(buf,
963_("\"Zone %s\" line and -l option are mutually exclusive"),
964			TZDEFAULT);
965		error(buf);
966		return FALSE;
967	}
968	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
969		buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
970		(void) sprintf(buf,
971_("\"Zone %s\" line and -p option are mutually exclusive"),
972			TZDEFRULES);
973		error(buf);
974		return FALSE;
975	}
976	for (i = 0; i < nzones; ++i)
977		if (zones[i].z_name != NULL &&
978			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
979				buf = erealloc(buf, (int) (132 +
980					strlen(fields[ZF_NAME]) +
981					strlen(zones[i].z_filename)));
982				(void) sprintf(buf,
983_("duplicate zone name %s (file \"%s\", line %d)"),
984					fields[ZF_NAME],
985					zones[i].z_filename,
986					zones[i].z_linenum);
987				error(buf);
988				return FALSE;
989		}
990	return inzsub(fields, nfields, FALSE);
991}
992
993static int
994inzcont(fields, nfields)
995register char ** const	fields;
996const int		nfields;
997{
998	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
999		error(_("wrong number of fields on Zone continuation line"));
1000		return FALSE;
1001	}
1002	return inzsub(fields, nfields, TRUE);
1003}
1004
1005static int
1006inzsub(fields, nfields, iscont)
1007register char ** const	fields;
1008const int		nfields;
1009const int		iscont;
1010{
1011	register char *		cp;
1012	static struct zone	z;
1013	register int		i_gmtoff, i_rule, i_format;
1014	register int		i_untilyear, i_untilmonth;
1015	register int		i_untilday, i_untiltime;
1016	register int		hasuntil;
1017
1018	if (iscont) {
1019		i_gmtoff = ZFC_GMTOFF;
1020		i_rule = ZFC_RULE;
1021		i_format = ZFC_FORMAT;
1022		i_untilyear = ZFC_TILYEAR;
1023		i_untilmonth = ZFC_TILMONTH;
1024		i_untilday = ZFC_TILDAY;
1025		i_untiltime = ZFC_TILTIME;
1026		z.z_name = NULL;
1027	} else {
1028		i_gmtoff = ZF_GMTOFF;
1029		i_rule = ZF_RULE;
1030		i_format = ZF_FORMAT;
1031		i_untilyear = ZF_TILYEAR;
1032		i_untilmonth = ZF_TILMONTH;
1033		i_untilday = ZF_TILDAY;
1034		i_untiltime = ZF_TILTIME;
1035		z.z_name = ecpyalloc(fields[ZF_NAME]);
1036	}
1037	z.z_filename = filename;
1038	z.z_linenum = linenum;
1039	z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
1040	if ((cp = strchr(fields[i_format], '%')) != 0) {
1041		if (*++cp != 's' || strchr(cp, '%') != 0) {
1042			error(_("invalid abbreviation format"));
1043			return FALSE;
1044		}
1045	}
1046	z.z_rule = ecpyalloc(fields[i_rule]);
1047	z.z_format = ecpyalloc(fields[i_format]);
1048	hasuntil = nfields > i_untilyear;
1049	if (hasuntil) {
1050		z.z_untilrule.r_filename = filename;
1051		z.z_untilrule.r_linenum = linenum;
1052		rulesub(&z.z_untilrule,
1053			fields[i_untilyear],
1054			"only",
1055			"",
1056			(nfields > i_untilmonth) ?
1057			fields[i_untilmonth] : "Jan",
1058			(nfields > i_untilday) ? fields[i_untilday] : "1",
1059			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
1060		z.z_untiltime = rpytime(&z.z_untilrule,
1061			z.z_untilrule.r_loyear);
1062		if (iscont && nzones > 0 &&
1063			z.z_untiltime > min_time &&
1064			z.z_untiltime < max_time &&
1065			zones[nzones - 1].z_untiltime > min_time &&
1066			zones[nzones - 1].z_untiltime < max_time &&
1067			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
1068				error(_("Zone continuation line end time is not after end time of previous line"));
1069				return FALSE;
1070		}
1071	}
1072	zones = (struct zone *) (void *) erealloc((char *) zones,
1073		(int) ((nzones + 1) * sizeof *zones));
1074	zones[nzones++] = z;
1075	/*
1076	** If there was an UNTIL field on this line,
1077	** there's more information about the zone on the next line.
1078	*/
1079	return hasuntil;
1080}
1081
1082static void
1083inleap(fields, nfields)
1084register char ** const	fields;
1085const int		nfields;
1086{
1087	register const char *		cp;
1088	register const struct lookup *	lp;
1089	register int			i, j;
1090	int				year, month, day;
1091	long				dayoff, tod;
1092	time_t				t;
1093
1094	if (nfields != LEAP_FIELDS) {
1095		error(_("wrong number of fields on Leap line"));
1096		return;
1097	}
1098	dayoff = 0;
1099	cp = fields[LP_YEAR];
1100	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
1101			/*
1102			 * Leapin' Lizards!
1103			 */
1104			error(_("invalid leaping year"));
1105			return;
1106	}
1107	j = EPOCH_YEAR;
1108	while (j != year) {
1109		if (year > j) {
1110			i = len_years[isleap(j)];
1111			++j;
1112		} else {
1113			--j;
1114			i = -len_years[isleap(j)];
1115		}
1116		dayoff = oadd(dayoff, eitol(i));
1117	}
1118	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
1119		error(_("invalid month name"));
1120		return;
1121	}
1122	month = lp->l_value;
1123	j = TM_JANUARY;
1124	while (j != month) {
1125		i = len_months[isleap(year)][j];
1126		dayoff = oadd(dayoff, eitol(i));
1127		++j;
1128	}
1129	cp = fields[LP_DAY];
1130	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
1131		day <= 0 || day > len_months[isleap(year)][month]) {
1132			error(_("invalid day of month"));
1133			return;
1134	}
1135	dayoff = oadd(dayoff, eitol(day - 1));
1136	if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
1137		error(_("time before zero"));
1138		return;
1139	}
1140	if (dayoff < min_time / SECSPERDAY) {
1141		error(_("time too small"));
1142		return;
1143	}
1144	if (dayoff > max_time / SECSPERDAY) {
1145		error(_("time too large"));
1146		return;
1147	}
1148	t = (time_t) dayoff * SECSPERDAY;
1149	tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
1150	cp = fields[LP_CORR];
1151	{
1152		register int	positive;
1153		int		count;
1154
1155		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
1156			positive = FALSE;
1157			count = 1;
1158		} else if (strcmp(cp, "--") == 0) {
1159			positive = FALSE;
1160			count = 2;
1161		} else if (strcmp(cp, "+") == 0) {
1162			positive = TRUE;
1163			count = 1;
1164		} else if (strcmp(cp, "++") == 0) {
1165			positive = TRUE;
1166			count = 2;
1167		} else {
1168			error(_("illegal CORRECTION field on Leap line"));
1169			return;
1170		}
1171		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
1172			error(_("illegal Rolling/Stationary field on Leap line"));
1173			return;
1174		}
1175		leapadd(tadd(t, tod), positive, lp->l_value, count);
1176	}
1177}
1178
1179static void
1180inlink(fields, nfields)
1181register char ** const	fields;
1182const int		nfields;
1183{
1184	struct link	l;
1185
1186	if (nfields != LINK_FIELDS) {
1187		error(_("wrong number of fields on Link line"));
1188		return;
1189	}
1190	if (*fields[LF_FROM] == '\0') {
1191		error(_("blank FROM field on Link line"));
1192		return;
1193	}
1194	if (*fields[LF_TO] == '\0') {
1195		error(_("blank TO field on Link line"));
1196		return;
1197	}
1198	l.l_filename = filename;
1199	l.l_linenum = linenum;
1200	l.l_from = ecpyalloc(fields[LF_FROM]);
1201	l.l_to = ecpyalloc(fields[LF_TO]);
1202	links = (struct link *) (void *) erealloc((char *) links,
1203		(int) ((nlinks + 1) * sizeof *links));
1204	links[nlinks++] = l;
1205}
1206
1207static void
1208rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
1209register struct rule * const	rp;
1210const char * const		loyearp;
1211const char * const		hiyearp;
1212const char * const		typep;
1213const char * const		monthp;
1214const char * const		dayp;
1215const char * const		timep;
1216{
1217	register const struct lookup *	lp;
1218	register const char *		cp;
1219	register char *			dp;
1220	register char *			ep;
1221
1222	if ((lp = byword(monthp, mon_names)) == NULL) {
1223		error(_("invalid month name"));
1224		return;
1225	}
1226	rp->r_month = lp->l_value;
1227	rp->r_todisstd = FALSE;
1228	rp->r_todisgmt = FALSE;
1229	dp = ecpyalloc(timep);
1230	if (*dp != '\0') {
1231		ep = dp + strlen(dp) - 1;
1232		switch (lowerit(*ep)) {
1233			case 's':	/* Standard */
1234				rp->r_todisstd = TRUE;
1235				rp->r_todisgmt = FALSE;
1236				*ep = '\0';
1237				break;
1238			case 'w':	/* Wall */
1239				rp->r_todisstd = FALSE;
1240				rp->r_todisgmt = FALSE;
1241				*ep = '\0';
1242				break;
1243			case 'g':	/* Greenwich */
1244			case 'u':	/* Universal */
1245			case 'z':	/* Zulu */
1246				rp->r_todisstd = TRUE;
1247				rp->r_todisgmt = TRUE;
1248				*ep = '\0';
1249				break;
1250		}
1251	}
1252	rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
1253	ifree(dp);
1254	/*
1255	** Year work.
1256	*/
1257	cp = loyearp;
1258	lp = byword(cp, begin_years);
1259	if (lp != NULL) switch ((int) lp->l_value) {
1260		case YR_MINIMUM:
1261			rp->r_loyear = INT_MIN;
1262			break;
1263		case YR_MAXIMUM:
1264			rp->r_loyear = INT_MAX;
1265			break;
1266		default:	/* "cannot happen" */
1267			errx(EXIT_FAILURE,
1268				_("panic: invalid l_value %d"), lp->l_value);
1269	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
1270		error(_("invalid starting year"));
1271		return;
1272	} else if (noise) {
1273		if (rp->r_loyear < min_year_representable)
1274			warning(_("starting year too low to be represented"));
1275		else if (rp->r_loyear > max_year_representable)
1276			warning(_("starting year too high to be represented"));
1277	}
1278	cp = hiyearp;
1279	if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
1280		case YR_MINIMUM:
1281			rp->r_hiyear = INT_MIN;
1282			break;
1283		case YR_MAXIMUM:
1284			rp->r_hiyear = INT_MAX;
1285			break;
1286		case YR_ONLY:
1287			rp->r_hiyear = rp->r_loyear;
1288			break;
1289		default:	/* "cannot happen" */
1290			errx(EXIT_FAILURE,
1291				_("panic: invalid l_value %d"), lp->l_value);
1292	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
1293		error(_("invalid ending year"));
1294		return;
1295	} else if (noise) {
1296		if (rp->r_loyear < min_year_representable)
1297			warning(_("ending year too low to be represented"));
1298		else if (rp->r_loyear > max_year_representable)
1299			warning(_("ending year too high to be represented"));
1300	}
1301	if (rp->r_loyear > rp->r_hiyear) {
1302		error(_("starting year greater than ending year"));
1303		return;
1304	}
1305	if (*typep == '\0')
1306		rp->r_yrtype = NULL;
1307	else {
1308		if (rp->r_loyear == rp->r_hiyear) {
1309			error(_("typed single year"));
1310			return;
1311		}
1312		rp->r_yrtype = ecpyalloc(typep);
1313	}
1314	if (rp->r_loyear < min_year && rp->r_loyear > 0)
1315		min_year = rp->r_loyear;
1316	/*
1317	** Day work.
1318	** Accept things such as:
1319	**	1
1320	**	last-Sunday
1321	**	Sun<=20
1322	**	Sun>=7
1323	*/
1324	dp = ecpyalloc(dayp);
1325	if ((lp = byword(dp, lasts)) != NULL) {
1326		rp->r_dycode = DC_DOWLEQ;
1327		rp->r_wday = lp->l_value;
1328		rp->r_dayofmonth = len_months[1][rp->r_month];
1329	} else {
1330		if ((ep = strchr(dp, '<')) != 0)
1331			rp->r_dycode = DC_DOWLEQ;
1332		else if ((ep = strchr(dp, '>')) != 0)
1333			rp->r_dycode = DC_DOWGEQ;
1334		else {
1335			ep = dp;
1336			rp->r_dycode = DC_DOM;
1337		}
1338		if (rp->r_dycode != DC_DOM) {
1339			*ep++ = 0;
1340			if (*ep++ != '=') {
1341				error(_("invalid day of month"));
1342				ifree(dp);
1343				return;
1344			}
1345			if ((lp = byword(dp, wday_names)) == NULL) {
1346				error(_("invalid weekday name"));
1347				ifree(dp);
1348				return;
1349			}
1350			rp->r_wday = lp->l_value;
1351		}
1352		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
1353			rp->r_dayofmonth <= 0 ||
1354			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
1355				error(_("invalid day of month"));
1356				ifree(dp);
1357				return;
1358		}
1359	}
1360	ifree(dp);
1361}
1362
1363static void
1364convert(val, buf)
1365const long	val;
1366char * const	buf;
1367{
1368	register int	i;
1369	register long	shift;
1370
1371	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
1372		buf[i] = val >> shift;
1373}
1374
1375static void
1376puttzcode(val, fp)
1377const long	val;
1378FILE * const	fp;
1379{
1380	char	buf[4];
1381
1382	convert(val, buf);
1383	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
1384}
1385
1386static int
1387atcomp(avp, bvp)
1388void *	avp;
1389void *	bvp;
1390{
1391	if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
1392		return -1;
1393	else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
1394		return 1;
1395	else	return 0;
1396}
1397
1398static void
1399writezone(name)
1400const char * const	name;
1401{
1402	register FILE *		fp;
1403	register int		i, j;
1404	static char *		fullname;
1405	static struct tzhead	tzh;
1406	time_t			ats[TZ_MAX_TIMES];
1407	unsigned char		types[TZ_MAX_TIMES];
1408
1409	/*
1410	** Sort.
1411	*/
1412	if (timecnt > 1)
1413		(void) qsort((void *) attypes, (size_t) timecnt,
1414			(size_t) sizeof *attypes, atcomp);
1415	/*
1416	** Optimize.
1417	*/
1418	{
1419		int	fromi;
1420		int	toi;
1421
1422		toi = 0;
1423		fromi = 0;
1424		while (fromi < timecnt && attypes[fromi].at < min_time)
1425			++fromi;
1426		if (isdsts[0] == 0)
1427			while (fromi < timecnt && attypes[fromi].type == 0)
1428				++fromi;	/* handled by default rule */
1429		for ( ; fromi < timecnt; ++fromi) {
1430			if (toi != 0
1431			    && ((attypes[fromi].at
1432				 + gmtoffs[attypes[toi - 1].type])
1433				<= (attypes[toi - 1].at
1434				    + gmtoffs[toi == 1 ? 0
1435					      : attypes[toi - 2].type]))) {
1436				attypes[toi - 1].type = attypes[fromi].type;
1437				continue;
1438			}
1439			if (toi == 0 ||
1440				attypes[toi - 1].type != attypes[fromi].type)
1441					attypes[toi++] = attypes[fromi];
1442		}
1443		timecnt = toi;
1444	}
1445	/*
1446	** Transfer.
1447	*/
1448	for (i = 0; i < timecnt; ++i) {
1449		ats[i] = attypes[i].at;
1450		types[i] = attypes[i].type;
1451	}
1452	fullname = erealloc(fullname,
1453		(int) (strlen(directory) + 1 + strlen(name) + 1));
1454	(void) sprintf(fullname, "%s/%s", directory, name);
1455
1456	/*
1457	 * Remove old file, if any, to snap links.
1458	 */
1459	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
1460		err(EXIT_FAILURE, _("can't remove %s"), fullname);
1461
1462	if ((fp = fopen(fullname, "wb")) == NULL) {
1463		if (mkdirs(fullname) != 0)
1464			(void) exit(EXIT_FAILURE);
1465		if ((fp = fopen(fullname, "wb")) == NULL)
1466			err(EXIT_FAILURE, _("can't create %s"), fullname);
1467	}
1468	convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
1469	convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
1470	convert(eitol(leapcnt), tzh.tzh_leapcnt);
1471	convert(eitol(timecnt), tzh.tzh_timecnt);
1472	convert(eitol(typecnt), tzh.tzh_typecnt);
1473	convert(eitol(charcnt), tzh.tzh_charcnt);
1474	(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
1475#define DO(field)	(void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
1476	DO(tzh_magic);
1477	DO(tzh_reserved);
1478	DO(tzh_ttisgmtcnt);
1479	DO(tzh_ttisstdcnt);
1480	DO(tzh_leapcnt);
1481	DO(tzh_timecnt);
1482	DO(tzh_typecnt);
1483	DO(tzh_charcnt);
1484#undef DO
1485	for (i = 0; i < timecnt; ++i) {
1486		j = leapcnt;
1487		while (--j >= 0)
1488			if (ats[i] >= trans[j]) {
1489				ats[i] = tadd(ats[i], corr[j]);
1490				break;
1491			}
1492		puttzcode((long) ats[i], fp);
1493	}
1494	if (timecnt > 0)
1495		(void) fwrite((void *) types, (size_t) sizeof types[0],
1496			(size_t) timecnt, fp);
1497	for (i = 0; i < typecnt; ++i) {
1498		puttzcode((long) gmtoffs[i], fp);
1499		(void) putc(isdsts[i], fp);
1500		(void) putc(abbrinds[i], fp);
1501	}
1502	if (charcnt != 0)
1503		(void) fwrite((void *) chars, (size_t) sizeof chars[0],
1504			(size_t) charcnt, fp);
1505	for (i = 0; i < leapcnt; ++i) {
1506		if (roll[i]) {
1507			if (timecnt == 0 || trans[i] < ats[0]) {
1508				j = 0;
1509				while (isdsts[j])
1510					if (++j >= typecnt) {
1511						j = 0;
1512						break;
1513					}
1514			} else {
1515				j = 1;
1516				while (j < timecnt && trans[i] >= ats[j])
1517					++j;
1518				j = types[j - 1];
1519			}
1520			puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
1521		} else	puttzcode((long) trans[i], fp);
1522		puttzcode((long) corr[i], fp);
1523	}
1524	for (i = 0; i < typecnt; ++i)
1525		(void) putc(ttisstds[i], fp);
1526	for (i = 0; i < typecnt; ++i)
1527		(void) putc(ttisgmts[i], fp);
1528	if (ferror(fp) || fclose(fp))
1529		errx(EXIT_FAILURE, _("error writing %s"), fullname);
1530	if (chmod(fullname, mflag) < 0)
1531		err(EXIT_FAILURE, _("cannot change mode of %s to %03o"),
1532		    fullname, (unsigned)mflag);
1533	if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
1534	    && chown(fullname, uflag, gflag) < 0)
1535		err(EXIT_FAILURE, _("cannot change ownership of %s"),
1536		    fullname);
1537}
1538
1539static void
1540doabbr(abbr, format, letters, isdst)
1541char * const		abbr;
1542const char * const	format;
1543const char * const	letters;
1544const int		isdst;
1545{
1546	if (strchr(format, '/') == NULL) {
1547		if (letters == NULL)
1548			(void) strcpy(abbr, format);
1549		else	(void) sprintf(abbr, format, letters);
1550	} else if (isdst)
1551		(void) strcpy(abbr, strchr(format, '/') + 1);
1552	else {
1553		(void) strcpy(abbr, format);
1554		*strchr(abbr, '/') = '\0';
1555	}
1556}
1557
1558static void
1559outzone(zpfirst, zonecount)
1560const struct zone * const	zpfirst;
1561const int			zonecount;
1562{
1563	register const struct zone *	zp;
1564	register struct rule *		rp;
1565	register int			i, j;
1566	register int			usestart, useuntil;
1567	register time_t			starttime, untiltime;
1568	register long			gmtoff;
1569	register long			stdoff;
1570	register int			year;
1571	register long			startoff;
1572	register int			startttisstd;
1573	register int			startttisgmt;
1574	register int			type;
1575	char				startbuf[BUFSIZ];
1576
1577	INITIALIZE(untiltime);
1578	INITIALIZE(starttime);
1579	/*
1580	** Now. . .finally. . .generate some useful data!
1581	*/
1582	timecnt = 0;
1583	typecnt = 0;
1584	charcnt = 0;
1585	/*
1586	** Thanks to Earl Chew (earl@dnd.icp.nec.com.au)
1587	** for noting the need to unconditionally initialize startttisstd.
1588	*/
1589	startttisstd = FALSE;
1590	startttisgmt = FALSE;
1591	for (i = 0; i < zonecount; ++i) {
1592		/*
1593		** A guess that may well be corrected later.
1594		*/
1595		stdoff = 0;
1596		zp = &zpfirst[i];
1597		usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
1598		useuntil = i < (zonecount - 1);
1599		if (useuntil && zp->z_untiltime <= min_time)
1600			continue;
1601		gmtoff = zp->z_gmtoff;
1602		eat(zp->z_filename, zp->z_linenum);
1603		*startbuf = '\0';
1604		startoff = zp->z_gmtoff;
1605		if (zp->z_nrules == 0) {
1606			stdoff = zp->z_stdoff;
1607			doabbr(startbuf, zp->z_format,
1608				(char *) NULL, stdoff != 0);
1609			type = addtype(oadd(zp->z_gmtoff, stdoff),
1610				startbuf, stdoff != 0, startttisstd,
1611				startttisgmt);
1612			if (usestart) {
1613				addtt(starttime, type);
1614				usestart = FALSE;
1615			} else if (stdoff != 0)
1616				addtt(min_time, type);
1617		} else for (year = min_year; year <= max_year; ++year) {
1618			if (useuntil && year > zp->z_untilrule.r_hiyear)
1619				break;
1620			/*
1621			** Mark which rules to do in the current year.
1622			** For those to do, calculate rpytime(rp, year);
1623			*/
1624			for (j = 0; j < zp->z_nrules; ++j) {
1625				rp = &zp->z_rules[j];
1626				eats(zp->z_filename, zp->z_linenum,
1627					rp->r_filename, rp->r_linenum);
1628				rp->r_todo = year >= rp->r_loyear &&
1629						year <= rp->r_hiyear &&
1630						yearistype(year, rp->r_yrtype);
1631				if (rp->r_todo)
1632					rp->r_temp = rpytime(rp, year);
1633			}
1634			for ( ; ; ) {
1635				register int	k;
1636				register time_t	jtime, ktime;
1637				register long	offset;
1638				char		buf[BUFSIZ];
1639
1640				INITIALIZE(ktime);
1641				if (useuntil) {
1642					/*
1643					** Turn untiltime into UTC
1644					** assuming the current gmtoff and
1645					** stdoff values.
1646					*/
1647					untiltime = zp->z_untiltime;
1648					if (!zp->z_untilrule.r_todisgmt)
1649						untiltime = tadd(untiltime,
1650							-gmtoff);
1651					if (!zp->z_untilrule.r_todisstd)
1652						untiltime = tadd(untiltime,
1653							-stdoff);
1654				}
1655				/*
1656				** Find the rule (of those to do, if any)
1657				** that takes effect earliest in the year.
1658				*/
1659				k = -1;
1660				for (j = 0; j < zp->z_nrules; ++j) {
1661					rp = &zp->z_rules[j];
1662					if (!rp->r_todo)
1663						continue;
1664					eats(zp->z_filename, zp->z_linenum,
1665						rp->r_filename, rp->r_linenum);
1666					offset = rp->r_todisgmt ? 0 : gmtoff;
1667					if (!rp->r_todisstd)
1668						offset = oadd(offset, stdoff);
1669					jtime = rp->r_temp;
1670					if (jtime == min_time ||
1671						jtime == max_time)
1672							continue;
1673					jtime = tadd(jtime, -offset);
1674					if (k < 0 || jtime < ktime) {
1675						k = j;
1676						ktime = jtime;
1677					}
1678				}
1679				if (k < 0)
1680					break;	/* go on to next year */
1681				rp = &zp->z_rules[k];
1682				rp->r_todo = FALSE;
1683				if (useuntil && ktime >= untiltime)
1684					break;
1685				stdoff = rp->r_stdoff;
1686				if (usestart && ktime == starttime)
1687					usestart = FALSE;
1688				if (usestart) {
1689					if (ktime < starttime) {
1690						startoff = oadd(zp->z_gmtoff,
1691							stdoff);
1692						doabbr(startbuf, zp->z_format,
1693							rp->r_abbrvar,
1694							rp->r_stdoff != 0);
1695						continue;
1696					}
1697					if (*startbuf == '\0' &&
1698					    startoff == oadd(zp->z_gmtoff,
1699					    stdoff)) {
1700						doabbr(startbuf, zp->z_format,
1701							rp->r_abbrvar,
1702							rp->r_stdoff != 0);
1703					}
1704				}
1705				eats(zp->z_filename, zp->z_linenum,
1706					rp->r_filename, rp->r_linenum);
1707				doabbr(buf, zp->z_format, rp->r_abbrvar,
1708					rp->r_stdoff != 0);
1709				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
1710				type = addtype(offset, buf, rp->r_stdoff != 0,
1711					rp->r_todisstd, rp->r_todisgmt);
1712				addtt(ktime, type);
1713			}
1714		}
1715		if (usestart) {
1716			if (*startbuf == '\0' &&
1717				zp->z_format != NULL &&
1718				strchr(zp->z_format, '%') == NULL &&
1719				strchr(zp->z_format, '/') == NULL)
1720					(void) strcpy(startbuf, zp->z_format);
1721			eat(zp->z_filename, zp->z_linenum);
1722			if (*startbuf == '\0')
1723error(_("can't determine time zone abbreviation to use just after until time"));
1724			else	addtt(starttime,
1725					addtype(startoff, startbuf,
1726						startoff != zp->z_gmtoff,
1727						startttisstd,
1728						startttisgmt));
1729		}
1730		/*
1731		** Now we may get to set starttime for the next zone line.
1732		*/
1733		if (useuntil) {
1734			startttisstd = zp->z_untilrule.r_todisstd;
1735			startttisgmt = zp->z_untilrule.r_todisgmt;
1736			starttime = zp->z_untiltime;
1737			if (!startttisstd)
1738				starttime = tadd(starttime, -stdoff);
1739			if (!startttisgmt)
1740				starttime = tadd(starttime, -gmtoff);
1741		}
1742	}
1743	writezone(zpfirst->z_name);
1744}
1745
1746static void
1747addtt(starttime, type)
1748const time_t	starttime;
1749int		type;
1750{
1751	if (starttime <= min_time ||
1752		(timecnt == 1 && attypes[0].at < min_time)) {
1753		gmtoffs[0] = gmtoffs[type];
1754		isdsts[0] = isdsts[type];
1755		ttisstds[0] = ttisstds[type];
1756		ttisgmts[0] = ttisgmts[type];
1757		if (abbrinds[type] != 0)
1758			(void) strcpy(chars, &chars[abbrinds[type]]);
1759		abbrinds[0] = 0;
1760		charcnt = strlen(chars) + 1;
1761		typecnt = 1;
1762		timecnt = 0;
1763		type = 0;
1764	}
1765	if (timecnt >= TZ_MAX_TIMES) {
1766		error(_("too many transitions?!"));
1767		(void) exit(EXIT_FAILURE);
1768	}
1769	attypes[timecnt].at = starttime;
1770	attypes[timecnt].type = type;
1771	++timecnt;
1772}
1773
1774static int
1775addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
1776const long		gmtoff;
1777const char * const	abbr;
1778const int		isdst;
1779const int		ttisstd;
1780const int		ttisgmt;
1781{
1782	register int	i, j;
1783
1784	if (isdst != TRUE && isdst != FALSE) {
1785		error(_("internal error - addtype called with bad isdst"));
1786		(void) exit(EXIT_FAILURE);
1787	}
1788	if (ttisstd != TRUE && ttisstd != FALSE) {
1789		error(_("internal error - addtype called with bad ttisstd"));
1790		(void) exit(EXIT_FAILURE);
1791	}
1792	if (ttisgmt != TRUE && ttisgmt != FALSE) {
1793		error(_("internal error - addtype called with bad ttisgmt"));
1794		(void) exit(EXIT_FAILURE);
1795	}
1796	/*
1797	** See if there's already an entry for this zone type.
1798	** If so, just return its index.
1799	*/
1800	for (i = 0; i < typecnt; ++i) {
1801		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
1802			strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
1803			ttisstd == ttisstds[i] &&
1804			ttisgmt == ttisgmts[i])
1805				return i;
1806	}
1807	/*
1808	** There isn't one; add a new one, unless there are already too
1809	** many.
1810	*/
1811	if (typecnt >= TZ_MAX_TYPES) {
1812		error(_("too many local time types"));
1813		(void) exit(EXIT_FAILURE);
1814	}
1815	gmtoffs[i] = gmtoff;
1816	isdsts[i] = isdst;
1817	ttisstds[i] = ttisstd;
1818	ttisgmts[i] = ttisgmt;
1819
1820	for (j = 0; j < charcnt; ++j)
1821		if (strcmp(&chars[j], abbr) == 0)
1822			break;
1823	if (j == charcnt)
1824		newabbr(abbr);
1825	abbrinds[i] = j;
1826	++typecnt;
1827	return i;
1828}
1829
1830static void
1831leapadd(t, positive, rolling, count)
1832const time_t	t;
1833const int	positive;
1834const int	rolling;
1835int		count;
1836{
1837	register int	i, j;
1838
1839	if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
1840		error(_("too many leap seconds"));
1841		(void) exit(EXIT_FAILURE);
1842	}
1843	for (i = 0; i < leapcnt; ++i)
1844		if (t <= trans[i]) {
1845			if (t == trans[i]) {
1846				error(_("repeated leap second moment"));
1847				(void) exit(EXIT_FAILURE);
1848			}
1849			break;
1850		}
1851	do {
1852		for (j = leapcnt; j > i; --j) {
1853			trans[j] = trans[j - 1];
1854			corr[j] = corr[j - 1];
1855			roll[j] = roll[j - 1];
1856		}
1857		trans[i] = t;
1858		corr[i] = positive ? 1L : eitol(-count);
1859		roll[i] = rolling;
1860		++leapcnt;
1861	} while (positive && --count != 0);
1862}
1863
1864static void
1865adjleap P((void))
1866{
1867	register int	i;
1868	register long	last = 0;
1869
1870	/*
1871	** propagate leap seconds forward
1872	*/
1873	for (i = 0; i < leapcnt; ++i) {
1874		trans[i] = tadd(trans[i], last);
1875		last = corr[i] += last;
1876	}
1877}
1878
1879static int
1880yearistype(year, type)
1881const int		year;
1882const char * const	type;
1883{
1884	static char *	buf;
1885	int		result;
1886
1887	if (type == NULL || *type == '\0')
1888		return TRUE;
1889	buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
1890	(void) sprintf(buf, "%s %d %s", yitcommand, year, type);
1891	result = system(buf);
1892	if (WIFEXITED(result)) switch (WEXITSTATUS(result)) {
1893		case 0:
1894			return TRUE;
1895		case 1:
1896			return FALSE;
1897	}
1898	error(_("wild result from command execution"));
1899	warnx(_("command was '%s', result was %d"), buf, result);
1900	for ( ; ; )
1901		(void) exit(EXIT_FAILURE);
1902}
1903
1904static int
1905lowerit(a)
1906int	a;
1907{
1908	a = (unsigned char) a;
1909	return (isascii(a) && isupper(a)) ? tolower(a) : a;
1910}
1911
1912static int
1913ciequal(ap, bp)		/* case-insensitive equality */
1914register const char *	ap;
1915register const char *	bp;
1916{
1917	while (lowerit(*ap) == lowerit(*bp++))
1918		if (*ap++ == '\0')
1919			return TRUE;
1920	return FALSE;
1921}
1922
1923static int
1924itsabbr(abbr, word)
1925register const char *	abbr;
1926register const char *	word;
1927{
1928	if (lowerit(*abbr) != lowerit(*word))
1929		return FALSE;
1930	++word;
1931	while (*++abbr != '\0')
1932		do {
1933			if (*word == '\0')
1934				return FALSE;
1935		} while (lowerit(*word++) != lowerit(*abbr));
1936	return TRUE;
1937}
1938
1939static const struct lookup *
1940byword(word, table)
1941register const char * const		word;
1942register const struct lookup * const	table;
1943{
1944	register const struct lookup *	foundlp;
1945	register const struct lookup *	lp;
1946
1947	if (word == NULL || table == NULL)
1948		return NULL;
1949	/*
1950	** Look for exact match.
1951	*/
1952	for (lp = table; lp->l_word != NULL; ++lp)
1953		if (ciequal(word, lp->l_word))
1954			return lp;
1955	/*
1956	** Look for inexact match.
1957	*/
1958	foundlp = NULL;
1959	for (lp = table; lp->l_word != NULL; ++lp)
1960		if (itsabbr(word, lp->l_word)) {
1961			if (foundlp == NULL)
1962				foundlp = lp;
1963			else	return NULL;	/* multiple inexact matches */
1964		}
1965	return foundlp;
1966}
1967
1968static char **
1969getfields(cp)
1970register char *	cp;
1971{
1972	register char *		dp;
1973	register char **	array;
1974	register int		nsubs;
1975
1976	if (cp == NULL)
1977		return NULL;
1978	array = (char **) (void *)
1979		emalloc((int) ((strlen(cp) + 1) * sizeof *array));
1980	nsubs = 0;
1981	for ( ; ; ) {
1982		while (isascii(*cp) && isspace((unsigned char) *cp))
1983			++cp;
1984		if (*cp == '\0' || *cp == '#')
1985			break;
1986		array[nsubs++] = dp = cp;
1987		do {
1988			if ((*dp = *cp++) != '"')
1989				++dp;
1990			else while ((*dp = *cp++) != '"')
1991				if (*dp != '\0')
1992					++dp;
1993				else	error(_("odd number of quotation marks"));
1994		} while (*cp != '\0' && *cp != '#' &&
1995			(!isascii(*cp) || !isspace((unsigned char) *cp)));
1996		if (isascii(*cp) && isspace((unsigned char) *cp))
1997			++cp;
1998		*dp = '\0';
1999	}
2000	array[nsubs] = NULL;
2001	return array;
2002}
2003
2004static long
2005oadd(t1, t2)
2006const long	t1;
2007const long	t2;
2008{
2009	register long	t;
2010
2011	t = t1 + t2;
2012	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2013		error(_("time overflow"));
2014		(void) exit(EXIT_FAILURE);
2015	}
2016	return t;
2017}
2018
2019static time_t
2020tadd(t1, t2)
2021const time_t	t1;
2022const long	t2;
2023{
2024	register time_t	t;
2025
2026	if (t1 == max_time && t2 > 0)
2027		return max_time;
2028	if (t1 == min_time && t2 < 0)
2029		return min_time;
2030	t = t1 + t2;
2031	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2032		error(_("time overflow"));
2033		(void) exit(EXIT_FAILURE);
2034	}
2035	return t;
2036}
2037
2038/*
2039** Given a rule, and a year, compute the date - in seconds since January 1,
2040** 1970, 00:00 LOCAL time - in that year that the rule refers to.
2041*/
2042
2043static time_t
2044rpytime(rp, wantedy)
2045register const struct rule * const	rp;
2046register const int			wantedy;
2047{
2048	register int	y, m, i;
2049	register long	dayoff;			/* with a nod to Margaret O. */
2050	register time_t	t;
2051
2052	if (wantedy == INT_MIN)
2053		return min_time;
2054	if (wantedy == INT_MAX)
2055		return max_time;
2056	dayoff = 0;
2057	m = TM_JANUARY;
2058	y = EPOCH_YEAR;
2059	while (wantedy != y) {
2060		if (wantedy > y) {
2061			i = len_years[isleap(y)];
2062			++y;
2063		} else {
2064			--y;
2065			i = -len_years[isleap(y)];
2066		}
2067		dayoff = oadd(dayoff, eitol(i));
2068	}
2069	while (m != rp->r_month) {
2070		i = len_months[isleap(y)][m];
2071		dayoff = oadd(dayoff, eitol(i));
2072		++m;
2073	}
2074	i = rp->r_dayofmonth;
2075	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
2076		if (rp->r_dycode == DC_DOWLEQ)
2077			--i;
2078		else {
2079			error(_("use of 2/29 in non leap-year"));
2080			(void) exit(EXIT_FAILURE);
2081		}
2082	}
2083	--i;
2084	dayoff = oadd(dayoff, eitol(i));
2085	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
2086		register long	wday;
2087
2088#define LDAYSPERWEEK	((long) DAYSPERWEEK)
2089		wday = eitol(EPOCH_WDAY);
2090		/*
2091		** Don't trust mod of negative numbers.
2092		*/
2093		if (dayoff >= 0)
2094			wday = (wday + dayoff) % LDAYSPERWEEK;
2095		else {
2096			wday -= ((-dayoff) % LDAYSPERWEEK);
2097			if (wday < 0)
2098				wday += LDAYSPERWEEK;
2099		}
2100		while (wday != eitol(rp->r_wday))
2101			if (rp->r_dycode == DC_DOWGEQ) {
2102				dayoff = oadd(dayoff, (long) 1);
2103				if (++wday >= LDAYSPERWEEK)
2104					wday = 0;
2105				++i;
2106			} else {
2107				dayoff = oadd(dayoff, (long) -1);
2108				if (--wday < 0)
2109					wday = LDAYSPERWEEK - 1;
2110				--i;
2111			}
2112		if (i < 0 || i >= len_months[isleap(y)][m]) {
2113			if (noise)
2114				warning(_("rule goes past start/end of month--will not work with pre-2004 versions of zic"));
2115		}
2116	}
2117	if (dayoff < 0 && !TYPE_SIGNED(time_t))
2118		return min_time;
2119	if (dayoff < min_time / SECSPERDAY)
2120		return min_time;
2121	if (dayoff > max_time / SECSPERDAY)
2122		return max_time;
2123	t = (time_t) dayoff * SECSPERDAY;
2124	return tadd(t, rp->r_tod);
2125}
2126
2127static void
2128newabbr(string)
2129const char * const	string;
2130{
2131	register int	i;
2132
2133	i = strlen(string) + 1;
2134	if (charcnt + i > TZ_MAX_CHARS) {
2135		error(_("too many, or too long, time zone abbreviations"));
2136		(void) exit(EXIT_FAILURE);
2137	}
2138	(void) strcpy(&chars[charcnt], string);
2139	charcnt += eitol(i);
2140}
2141
2142static int
2143mkdirs(argname)
2144char * const	argname;
2145{
2146	register char *	name;
2147	register char *	cp;
2148
2149	if (argname == NULL || *argname == '\0' || Dflag)
2150		return 0;
2151	cp = name = ecpyalloc(argname);
2152	while ((cp = strchr(cp + 1, '/')) != 0) {
2153		*cp = '\0';
2154#ifndef unix
2155		/*
2156		** DOS drive specifier?
2157		*/
2158		if (isalpha((unsigned char) name[0]) &&
2159			name[1] == ':' && name[2] == '\0') {
2160				*cp = '/';
2161				continue;
2162		}
2163#endif /* !defined unix */
2164		if (!itsdir(name)) {
2165			/*
2166			** It doesn't seem to exist, so we try to create it.
2167			** Creation may fail because of the directory being
2168			** created by some other multiprocessor, so we get
2169			** to do extra checking.
2170			*/
2171			if (mkdir(name, MKDIR_UMASK) != 0
2172				&& (errno != EEXIST || !itsdir(name))) {
2173				warn(_("can't create directory %s"), name);
2174				ifree(name);
2175				return -1;
2176			}
2177		}
2178		*cp = '/';
2179	}
2180	ifree(name);
2181	return 0;
2182}
2183
2184static long
2185eitol(i)
2186const int	i;
2187{
2188	long	l;
2189
2190	l = i;
2191	if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
2192		errx(EXIT_FAILURE, _("%d did not sign extend correctly"), i);
2193	return l;
2194}
2195
2196#include <grp.h>
2197#include <pwd.h>
2198
2199static void
2200setgroup(flag, name)
2201	gid_t *flag;
2202	const char *name;
2203{
2204	struct group *gr;
2205
2206	if (*flag != (gid_t)-1)
2207		errx(EXIT_FAILURE, _("multiple -g flags specified"));
2208
2209	gr = getgrnam(name);
2210	if (gr == 0) {
2211		char *ep;
2212		unsigned long ul;
2213
2214		ul = strtoul(name, &ep, 10);
2215		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
2216			*flag = ul;
2217			return;
2218		}
2219		errx(EXIT_FAILURE, _("group `%s' not found"), name);
2220	}
2221	*flag = gr->gr_gid;
2222}
2223
2224static void
2225setuser(flag, name)
2226	uid_t *flag;
2227	const char *name;
2228{
2229	struct passwd *pw;
2230
2231	if (*flag != (gid_t)-1)
2232		errx(EXIT_FAILURE, _("multiple -u flags specified"));
2233
2234	pw = getpwnam(name);
2235	if (pw == 0) {
2236		char *ep;
2237		unsigned long ul;
2238
2239		ul = strtoul(name, &ep, 10);
2240		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
2241			*flag = ul;
2242			return;
2243		}
2244		errx(EXIT_FAILURE, _("user `%s' not found"), name);
2245	}
2246	*flag = pw->pw_uid;
2247}
2248
2249/*
2250** UNIX was a registered trademark of The Open Group in 2003.
2251*/
2252