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