zic.c revision 111414
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 111414 2003-02-24 05:56:36Z mikeh $";
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	ciequal P((const char * ap, const char * bp));
93static void	convert P((long val, char * buf));
94static void	dolink P((const char * fromfile, const char * tofile));
95static void	doabbr P((char * abbr, const char * format,
96			const char * letters, int isdst));
97static void	eat P((const char * name, int num));
98static void	eats P((const char * name, int num,
99			const char * rname, int rnum));
100static long	eitol P((int i));
101static void	error P((const char * message));
102static char **	getfields P((char * buf));
103static long	gethms P((const char * string, const char * errstrng,
104			int signable));
105static void	infile P((const char * filename));
106static void	inleap P((char ** fields, int nfields));
107static void	inlink P((char ** fields, int nfields));
108static void	inrule P((char ** fields, int nfields));
109static int	inzcont P((char ** fields, int nfields));
110static int	inzone P((char ** fields, int nfields));
111static int	inzsub P((char ** fields, int nfields, int iscont));
112static int	itsabbr P((const char * abbr, const char * word));
113static int	itsdir P((const char * name));
114static int	lowerit P((int c));
115static char *	memcheck P((char * tocheck));
116static int	mkdirs P((char * filename));
117static void	newabbr P((const char * abbr));
118static long	oadd P((long t1, long t2));
119static void	outzone P((const struct zone * zp, int ntzones));
120static void	puttzcode P((long code, FILE * fp));
121static int	rcomp P((const void * leftp, const void * rightp));
122static time_t	rpytime P((const struct rule * rp, int wantedy));
123static void	rulesub P((struct rule * rp,
124			const char * loyearp, const char * hiyearp,
125			const char * typep, const char * monthp,
126			const char * dayp, const char * timep));
127static void	setboundaries P((void));
128static void	setgroup P((gid_t *flag, const char *name));
129static void	setuser P((uid_t *flag, const char *name));
130static time_t	tadd P((time_t t1, long t2));
131static void	usage P((void));
132static void	writezone P((const char * name));
133static int	yearistype P((int year, const char * type));
134
135#if !(HAVE_STRERROR - 0)
136static char *	strerror P((int));
137#endif /* !(HAVE_STRERROR - 0) */
138
139static int		charcnt;
140static int		errors;
141static const char *	filename;
142static int		leapcnt;
143static int		linenum;
144static time_t		max_time;
145static int		max_year;
146static int		max_year_representable;
147static time_t		min_time;
148static int		min_year;
149static int		min_year_representable;
150static int		noise;
151static const char *	rfilename;
152static int		rlinenum;
153static int		timecnt;
154static int		typecnt;
155
156/*
157** Line codes.
158*/
159
160#define LC_RULE		0
161#define LC_ZONE		1
162#define LC_LINK		2
163#define LC_LEAP		3
164
165/*
166** Which fields are which on a Zone line.
167*/
168
169#define ZF_NAME		1
170#define ZF_GMTOFF	2
171#define ZF_RULE		3
172#define ZF_FORMAT	4
173#define ZF_TILYEAR	5
174#define ZF_TILMONTH	6
175#define ZF_TILDAY	7
176#define ZF_TILTIME	8
177#define ZONE_MINFIELDS	5
178#define ZONE_MAXFIELDS	9
179
180/*
181** Which fields are which on a Zone continuation line.
182*/
183
184#define ZFC_GMTOFF	0
185#define ZFC_RULE	1
186#define ZFC_FORMAT	2
187#define ZFC_TILYEAR	3
188#define ZFC_TILMONTH	4
189#define ZFC_TILDAY	5
190#define ZFC_TILTIME	6
191#define ZONEC_MINFIELDS	3
192#define ZONEC_MAXFIELDS	7
193
194/*
195** Which files are which on a Rule line.
196*/
197
198#define RF_NAME		1
199#define RF_LOYEAR	2
200#define RF_HIYEAR	3
201#define RF_COMMAND	4
202#define RF_MONTH	5
203#define RF_DAY		6
204#define RF_TOD		7
205#define RF_STDOFF	8
206#define RF_ABBRVAR	9
207#define RULE_FIELDS	10
208
209/*
210** Which fields are which on a Link line.
211*/
212
213#define LF_FROM		1
214#define LF_TO		2
215#define LINK_FIELDS	3
216
217/*
218** Which fields are which on a Leap line.
219*/
220
221#define LP_YEAR		1
222#define LP_MONTH	2
223#define LP_DAY		3
224#define LP_TIME		4
225#define LP_CORR		5
226#define LP_ROLL		6
227#define LEAP_FIELDS	7
228
229/*
230** Year synonyms.
231*/
232
233#define YR_MINIMUM	0
234#define YR_MAXIMUM	1
235#define YR_ONLY		2
236
237static struct rule *	rules;
238static int		nrules;	/* number of rules */
239
240static struct zone *	zones;
241static int		nzones;	/* number of zones */
242
243struct link {
244	const char *	l_filename;
245	int		l_linenum;
246	const char *	l_from;
247	const char *	l_to;
248};
249
250static struct link *	links;
251static int		nlinks;
252
253struct lookup {
254	const char *	l_word;
255	const int	l_value;
256};
257
258static struct lookup const *	byword P((const char * string,
259					const struct lookup * lp));
260
261static struct lookup const	line_codes[] = {
262	{ "Rule",	LC_RULE },
263	{ "Zone",	LC_ZONE },
264	{ "Link",	LC_LINK },
265	{ "Leap",	LC_LEAP },
266	{ NULL,		0}
267};
268
269static struct lookup const	mon_names[] = {
270	{ "January",	TM_JANUARY },
271	{ "February",	TM_FEBRUARY },
272	{ "March",	TM_MARCH },
273	{ "April",	TM_APRIL },
274	{ "May",	TM_MAY },
275	{ "June",	TM_JUNE },
276	{ "July",	TM_JULY },
277	{ "August",	TM_AUGUST },
278	{ "September",	TM_SEPTEMBER },
279	{ "October",	TM_OCTOBER },
280	{ "November",	TM_NOVEMBER },
281	{ "December",	TM_DECEMBER },
282	{ NULL,		0 }
283};
284
285static struct lookup const	wday_names[] = {
286	{ "Sunday",	TM_SUNDAY },
287	{ "Monday",	TM_MONDAY },
288	{ "Tuesday",	TM_TUESDAY },
289	{ "Wednesday",	TM_WEDNESDAY },
290	{ "Thursday",	TM_THURSDAY },
291	{ "Friday",	TM_FRIDAY },
292	{ "Saturday",	TM_SATURDAY },
293	{ NULL,		0 }
294};
295
296static struct lookup const	lasts[] = {
297	{ "last-Sunday",	TM_SUNDAY },
298	{ "last-Monday",	TM_MONDAY },
299	{ "last-Tuesday",	TM_TUESDAY },
300	{ "last-Wednesday",	TM_WEDNESDAY },
301	{ "last-Thursday",	TM_THURSDAY },
302	{ "last-Friday",	TM_FRIDAY },
303	{ "last-Saturday",	TM_SATURDAY },
304	{ NULL,			0 }
305};
306
307static struct lookup const	begin_years[] = {
308	{ "minimum",	YR_MINIMUM },
309	{ "maximum",	YR_MAXIMUM },
310	{ NULL,		0 }
311};
312
313static struct lookup const	end_years[] = {
314	{ "minimum",	YR_MINIMUM },
315	{ "maximum",	YR_MAXIMUM },
316	{ "only",	YR_ONLY },
317	{ NULL,		0 }
318};
319
320static struct lookup const	leap_types[] = {
321	{ "Rolling",	TRUE },
322	{ "Stationary",	FALSE },
323	{ NULL,		0 }
324};
325
326static const int	len_months[2][MONSPERYEAR] = {
327	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
328	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
329};
330
331static const int	len_years[2] = {
332	DAYSPERNYEAR, DAYSPERLYEAR
333};
334
335static struct attype {
336	time_t		at;
337	unsigned char	type;
338}			attypes[TZ_MAX_TIMES];
339static long		gmtoffs[TZ_MAX_TYPES];
340static char		isdsts[TZ_MAX_TYPES];
341static unsigned char	abbrinds[TZ_MAX_TYPES];
342static char		ttisstds[TZ_MAX_TYPES];
343static char		ttisgmts[TZ_MAX_TYPES];
344static char		chars[TZ_MAX_CHARS];
345static time_t		trans[TZ_MAX_LEAPS];
346static long		corr[TZ_MAX_LEAPS];
347static char		roll[TZ_MAX_LEAPS];
348
349/*
350** Memory allocation.
351*/
352
353static char *
354memcheck(ptr)
355char * const	ptr;
356{
357	if (ptr == NULL)
358		errx(EXIT_FAILURE, _("memory exhausted"));
359	return ptr;
360}
361
362#define emalloc(size)		memcheck(imalloc(size))
363#define erealloc(ptr, size)	memcheck(irealloc((ptr), (size)))
364#define ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
365#define ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
366
367/*
368** Error handling.
369*/
370
371#if !(HAVE_STRERROR - 0)
372static char *
373strerror(errnum)
374int	errnum;
375{
376	extern char *	sys_errlist[];
377	extern int	sys_nerr;
378
379	return (errnum > 0 && errnum <= sys_nerr) ?
380		sys_errlist[errnum] : _("Unknown system error");
381}
382#endif /* !(HAVE_STRERROR - 0) */
383
384static void
385eats(name, num, rname, rnum)
386const char * const	name;
387const int		num;
388const char * const	rname;
389const int		rnum;
390{
391	filename = name;
392	linenum = num;
393	rfilename = rname;
394	rlinenum = rnum;
395}
396
397static void
398eat(name, num)
399const char * const	name;
400const int		num;
401{
402	eats(name, num, (char *) NULL, -1);
403}
404
405static void
406error(string)
407const char * const	string;
408{
409	/*
410	** Match the format of "cc" to allow sh users to
411	**	zic ... 2>&1 | error -t "*" -v
412	** on BSD systems.
413	*/
414	(void) fprintf(stderr, _("\"%s\", line %d: %s"),
415		filename, linenum, string);
416	if (rfilename != NULL)
417		(void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
418			rfilename, rlinenum);
419	(void) fprintf(stderr, "\n");
420	++errors;
421}
422
423static void
424warning(string)
425const char * const	string;
426{
427	char *	cp;
428
429	cp = ecpyalloc(_("warning: "));
430	cp = ecatalloc(cp, string);
431	error(cp);
432	ifree(cp);
433	--errors;
434}
435
436static void
437usage P((void))
438{
439	(void) fprintf(stderr, "%s\n%s\n",
440_("usage: zic [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"),
441_("           [-L leapseconds] [-y yearistype] [filename ... ]"));
442	(void) exit(EXIT_FAILURE);
443}
444
445static const char *	psxrules;
446static const char *	lcltime;
447static const char *	directory;
448static const char *	leapsec;
449static const char *	yitcommand;
450static int		sflag = FALSE;
451static int		Dflag;
452static uid_t		uflag = (uid_t)-1;
453static gid_t		gflag = (gid_t)-1;
454static mode_t		mflag = (S_IRUSR | S_IRGRP | S_IROTH
455				 | S_IWUSR);
456
457int
458main(argc, argv)
459int	argc;
460char *	argv[];
461{
462	register int	i;
463	register int	j;
464	register int	c;
465
466#ifdef unix
467	(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
468#endif /* defined unix */
469#if HAVE_GETTEXT - 0
470	(void) setlocale(LC_MESSAGES, "");
471#ifdef TZ_DOMAINDIR
472	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
473#endif /* defined TEXTDOMAINDIR */
474	(void) textdomain(TZ_DOMAIN);
475#endif /* HAVE_GETTEXT - 0 */
476	while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
477		switch (c) {
478			default:
479				usage();
480			case 'D':
481				Dflag = 1;
482				break;
483			case 'd':
484				if (directory == NULL)
485					directory = optarg;
486				else
487					errx(EXIT_FAILURE,
488_("more than one -d option specified"));
489				break;
490			case 'g':
491				setgroup(&gflag, optarg);
492				break;
493			case 'l':
494				if (lcltime == NULL)
495					lcltime = optarg;
496				else
497					errx(EXIT_FAILURE,
498_("more than one -l option specified"));
499				break;
500			case 'm':
501			{
502				void *set = setmode(optarg);
503				if (set == NULL)
504					errx(EXIT_FAILURE,
505_("invalid file mode"));
506				getmode(set, mflag);
507				free(set);
508				break;
509			}
510			case 'p':
511				if (psxrules == NULL)
512					psxrules = optarg;
513				else
514					errx(EXIT_FAILURE,
515_("more than one -p option specified"));
516				break;
517			case 'u':
518				setuser(&uflag, optarg);
519				break;
520			case 'y':
521				if (yitcommand == NULL)
522					yitcommand = optarg;
523				else
524					errx(EXIT_FAILURE,
525_("more than one -y option specified"));
526				break;
527			case 'L':
528				if (leapsec == NULL)
529					leapsec = optarg;
530				else
531					errx(EXIT_FAILURE,
532_("more than one -L option specified"));
533				break;
534			case 'v':
535				noise = TRUE;
536				break;
537			case 's':
538				sflag = TRUE;
539				break;
540		}
541	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
542		usage();	/* usage message by request */
543	if (directory == NULL)
544		directory = TZDIR;
545	if (yitcommand == NULL)
546		yitcommand = "yearistype";
547
548	setboundaries();
549
550	if (optind < argc && leapsec != NULL) {
551		infile(leapsec);
552		adjleap();
553	}
554
555	for (i = optind; i < argc; ++i)
556		infile(argv[i]);
557	if (errors)
558		(void) exit(EXIT_FAILURE);
559	associate();
560	for (i = 0; i < nzones; i = j) {
561		/*
562		** Find the next non-continuation zone entry.
563		*/
564		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
565			continue;
566		outzone(&zones[i], j - i);
567	}
568	/*
569	** Make links.
570	*/
571	for (i = 0; i < nlinks; ++i)
572		dolink(links[i].l_from, links[i].l_to);
573	if (lcltime != NULL)
574		dolink(lcltime, TZDEFAULT);
575	if (psxrules != NULL)
576		dolink(psxrules, TZDEFRULES);
577	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
578}
579
580static void
581dolink(fromfile, tofile)
582const char * const	fromfile;
583const char * const	tofile;
584{
585	register char *	fromname;
586	register char *	toname;
587
588	if (fromfile[0] == '/')
589		fromname = ecpyalloc(fromfile);
590	else {
591		fromname = ecpyalloc(directory);
592		fromname = ecatalloc(fromname, "/");
593		fromname = ecatalloc(fromname, fromfile);
594	}
595	if (tofile[0] == '/')
596		toname = ecpyalloc(tofile);
597	else {
598		toname = ecpyalloc(directory);
599		toname = ecatalloc(toname, "/");
600		toname = ecatalloc(toname, tofile);
601	}
602	/*
603	** We get to be careful here since
604	** there's a fair chance of root running us.
605	*/
606	if (!itsdir(toname))
607		(void) remove(toname);
608	if (link(fromname, toname) != 0) {
609		int	result;
610
611		if (mkdirs(toname) != 0)
612			(void) exit(EXIT_FAILURE);
613		result = link(fromname, toname);
614#if (HAVE_SYMLINK - 0)
615		if (result != 0) {
616			result = symlink(fromname, toname);
617			if (result == 0)
618warning(_("hard link failed, symbolic link used"));
619		}
620#endif
621		if (result != 0) {
622			err(EXIT_FAILURE, _("can't link from %s to %s"),
623			    fromname, toname);
624		}
625	}
626	ifree(fromname);
627	ifree(toname);
628}
629
630#ifndef INT_MAX
631#define INT_MAX	((int) (((unsigned)~0)>>1))
632#endif /* !defined INT_MAX */
633
634#ifndef INT_MIN
635#define INT_MIN	((int) ~(((unsigned)~0)>>1))
636#endif /* !defined INT_MIN */
637
638/*
639** The tz file format currently allows at most 32-bit quantities.
640** This restriction should be removed before signed 32-bit values
641** wrap around in 2038, but unfortunately this will require a
642** change to the tz file format.
643*/
644
645#define MAX_BITS_IN_FILE	32
646#define TIME_T_BITS_IN_FILE	((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
647
648static void
649setboundaries P((void))
650{
651	if (TYPE_SIGNED(time_t)) {
652		min_time = ~ (time_t) 0;
653		min_time <<= TIME_T_BITS_IN_FILE - 1;
654		max_time = ~ (time_t) 0 - min_time;
655		if (sflag)
656			min_time = 0;
657	} else {
658		min_time = 0;
659		max_time = 2 - sflag;
660		max_time <<= TIME_T_BITS_IN_FILE - 1;
661		--max_time;
662	}
663	min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
664	max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
665	min_year_representable = min_year;
666	max_year_representable = max_year;
667}
668
669static int
670itsdir(name)
671const char * const	name;
672{
673	register char *	myname;
674	register int	accres;
675
676	myname = ecpyalloc(name);
677	myname = ecatalloc(myname, "/.");
678	accres = access(myname, F_OK);
679	ifree(myname);
680	return accres == 0;
681}
682
683/*
684** Associate sets of rules with zones.
685*/
686
687/*
688** Sort by rule name.
689*/
690
691static int
692rcomp(cp1, cp2)
693const void *	cp1;
694const void *	cp2;
695{
696	return strcmp(((const struct rule *) cp1)->r_name,
697		((const struct rule *) cp2)->r_name);
698}
699
700static void
701associate P((void))
702{
703	register struct zone *	zp;
704	register struct rule *	rp;
705	register int		base, out;
706	register int		i, j;
707
708	if (nrules != 0) {
709		(void) qsort((void *) rules, (size_t) nrules,
710			(size_t) sizeof *rules, rcomp);
711		for (i = 0; i < nrules - 1; ++i) {
712			if (strcmp(rules[i].r_name,
713				rules[i + 1].r_name) != 0)
714					continue;
715			if (strcmp(rules[i].r_filename,
716				rules[i + 1].r_filename) == 0)
717					continue;
718			eat(rules[i].r_filename, rules[i].r_linenum);
719			warning(_("same rule name in multiple files"));
720			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
721			warning(_("same rule name in multiple files"));
722			for (j = i + 2; j < nrules; ++j) {
723				if (strcmp(rules[i].r_name,
724					rules[j].r_name) != 0)
725						break;
726				if (strcmp(rules[i].r_filename,
727					rules[j].r_filename) == 0)
728						continue;
729				if (strcmp(rules[i + 1].r_filename,
730					rules[j].r_filename) == 0)
731						continue;
732				break;
733			}
734			i = j - 1;
735		}
736	}
737	for (i = 0; i < nzones; ++i) {
738		zp = &zones[i];
739		zp->z_rules = NULL;
740		zp->z_nrules = 0;
741	}
742	for (base = 0; base < nrules; base = out) {
743		rp = &rules[base];
744		for (out = base + 1; out < nrules; ++out)
745			if (strcmp(rp->r_name, rules[out].r_name) != 0)
746				break;
747		for (i = 0; i < nzones; ++i) {
748			zp = &zones[i];
749			if (strcmp(zp->z_rule, rp->r_name) != 0)
750				continue;
751			zp->z_rules = rp;
752			zp->z_nrules = out - base;
753		}
754	}
755	for (i = 0; i < nzones; ++i) {
756		zp = &zones[i];
757		if (zp->z_nrules == 0) {
758			/*
759			** Maybe we have a local standard time offset.
760			*/
761			eat(zp->z_filename, zp->z_linenum);
762			zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
763					      TRUE);
764			/*
765			** Note, though, that if there's no rule,
766			** a '%s' in the format is a bad thing.
767			*/
768			if (strchr(zp->z_format, '%') != 0)
769				error(_("%s in ruleless zone"));
770		}
771	}
772	if (errors)
773		(void) exit(EXIT_FAILURE);
774}
775
776static void
777infile(name)
778const char *	name;
779{
780	register FILE *			fp;
781	register char **		fields;
782	register char *			cp;
783	register const struct lookup *	lp;
784	register int			nfields;
785	register int			wantcont;
786	register int			num;
787	char				buf[BUFSIZ];
788
789	if (strcmp(name, "-") == 0) {
790		name = _("standard input");
791		fp = stdin;
792	} else if ((fp = fopen(name, "r")) == NULL)
793		err(EXIT_FAILURE, _("can't open %s"), name);
794	wantcont = FALSE;
795	for (num = 1; ; ++num) {
796		eat(name, num);
797		if (fgets(buf, (int) sizeof buf, fp) != buf)
798			break;
799		cp = strchr(buf, '\n');
800		if (cp == NULL) {
801			error(_("line too long"));
802			(void) exit(EXIT_FAILURE);
803		}
804		*cp = '\0';
805		fields = getfields(buf);
806		nfields = 0;
807		while (fields[nfields] != NULL) {
808			static char	nada;
809
810			if (strcmp(fields[nfields], "-") == 0)
811				fields[nfields] = &nada;
812			++nfields;
813		}
814		if (nfields == 0) {
815			/* nothing to do */
816		} else if (wantcont) {
817			wantcont = inzcont(fields, nfields);
818		} else {
819			lp = byword(fields[0], line_codes);
820			if (lp == NULL)
821				error(_("input line of unknown type"));
822			else switch ((int) (lp->l_value)) {
823				case LC_RULE:
824					inrule(fields, nfields);
825					wantcont = FALSE;
826					break;
827				case LC_ZONE:
828					wantcont = inzone(fields, nfields);
829					break;
830				case LC_LINK:
831					inlink(fields, nfields);
832					wantcont = FALSE;
833					break;
834				case LC_LEAP:
835					if (name != leapsec)
836						warnx(
837_("leap line in non leap seconds file %s"), name);
838					else	inleap(fields, nfields);
839					wantcont = FALSE;
840					break;
841				default:	/* "cannot happen" */
842					errx(EXIT_FAILURE,
843_("panic: invalid l_value %d"), lp->l_value);
844			}
845		}
846		ifree((char *) fields);
847	}
848	if (ferror(fp))
849		errx(EXIT_FAILURE, _("error reading %s"), filename);
850	if (fp != stdin && fclose(fp))
851		err(EXIT_FAILURE, _("error closing %s"), filename);
852	if (wantcont)
853		error(_("expected continuation line not found"));
854}
855
856/*
857** Convert a string of one of the forms
858**	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
859** into a number of seconds.
860** A null string maps to zero.
861** Call error with errstring and return zero on errors.
862*/
863
864static long
865gethms(string, errstring, signable)
866const char *		string;
867const char * const	errstring;
868const int		signable;
869{
870	int	hh, mm, ss, sign;
871
872	if (string == NULL || *string == '\0')
873		return 0;
874	if (!signable)
875		sign = 1;
876	else if (*string == '-') {
877		sign = -1;
878		++string;
879	} else	sign = 1;
880	if (sscanf(string, scheck(string, "%d"), &hh) == 1)
881		mm = ss = 0;
882	else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
883		ss = 0;
884	else if (sscanf(string, scheck(string, "%d:%d:%d"),
885		&hh, &mm, &ss) != 3) {
886			error(errstring);
887			return 0;
888	}
889	if ((hh < 0 || hh >= HOURSPERDAY ||
890		mm < 0 || mm >= MINSPERHOUR ||
891		ss < 0 || ss > SECSPERMIN) &&
892		!(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
893			error(errstring);
894			return 0;
895	}
896	return eitol(sign) *
897		(eitol(hh * MINSPERHOUR + mm) *
898		eitol(SECSPERMIN) + eitol(ss));
899}
900
901static void
902inrule(fields, nfields)
903register char ** const	fields;
904const int		nfields;
905{
906	static struct rule	r;
907
908	if (nfields != RULE_FIELDS) {
909		error(_("wrong number of fields on Rule line"));
910		return;
911	}
912	if (*fields[RF_NAME] == '\0') {
913		error(_("nameless rule"));
914		return;
915	}
916	r.r_filename = filename;
917	r.r_linenum = linenum;
918	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
919	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
920		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
921	r.r_name = ecpyalloc(fields[RF_NAME]);
922	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
923	rules = (struct rule *) (void *) erealloc((char *) rules,
924		(int) ((nrules + 1) * sizeof *rules));
925	rules[nrules++] = r;
926}
927
928static int
929inzone(fields, nfields)
930register char ** const	fields;
931const int		nfields;
932{
933	register int	i;
934	static char *	buf;
935
936	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
937		error(_("wrong number of fields on Zone line"));
938		return FALSE;
939	}
940	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
941		buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
942		(void) sprintf(buf,
943_("\"Zone %s\" line and -l option are mutually exclusive"),
944			TZDEFAULT);
945		error(buf);
946		return FALSE;
947	}
948	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
949		buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
950		(void) sprintf(buf,
951_("\"Zone %s\" line and -p option are mutually exclusive"),
952			TZDEFRULES);
953		error(buf);
954		return FALSE;
955	}
956	for (i = 0; i < nzones; ++i)
957		if (zones[i].z_name != NULL &&
958			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
959				buf = erealloc(buf, (int) (132 +
960					strlen(fields[ZF_NAME]) +
961					strlen(zones[i].z_filename)));
962				(void) sprintf(buf,
963_("duplicate zone name %s (file \"%s\", line %d)"),
964					fields[ZF_NAME],
965					zones[i].z_filename,
966					zones[i].z_linenum);
967				error(buf);
968				return FALSE;
969		}
970	return inzsub(fields, nfields, FALSE);
971}
972
973static int
974inzcont(fields, nfields)
975register char ** const	fields;
976const int		nfields;
977{
978	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
979		error(_("wrong number of fields on Zone continuation line"));
980		return FALSE;
981	}
982	return inzsub(fields, nfields, TRUE);
983}
984
985static int
986inzsub(fields, nfields, iscont)
987register char ** const	fields;
988const int		nfields;
989const int		iscont;
990{
991	register char *		cp;
992	static struct zone	z;
993	register int		i_gmtoff, i_rule, i_format;
994	register int		i_untilyear, i_untilmonth;
995	register int		i_untilday, i_untiltime;
996	register int		hasuntil;
997
998	if (iscont) {
999		i_gmtoff = ZFC_GMTOFF;
1000		i_rule = ZFC_RULE;
1001		i_format = ZFC_FORMAT;
1002		i_untilyear = ZFC_TILYEAR;
1003		i_untilmonth = ZFC_TILMONTH;
1004		i_untilday = ZFC_TILDAY;
1005		i_untiltime = ZFC_TILTIME;
1006		z.z_name = NULL;
1007	} else {
1008		i_gmtoff = ZF_GMTOFF;
1009		i_rule = ZF_RULE;
1010		i_format = ZF_FORMAT;
1011		i_untilyear = ZF_TILYEAR;
1012		i_untilmonth = ZF_TILMONTH;
1013		i_untilday = ZF_TILDAY;
1014		i_untiltime = ZF_TILTIME;
1015		z.z_name = ecpyalloc(fields[ZF_NAME]);
1016	}
1017	z.z_filename = filename;
1018	z.z_linenum = linenum;
1019	z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
1020	if ((cp = strchr(fields[i_format], '%')) != 0) {
1021		if (*++cp != 's' || strchr(cp, '%') != 0) {
1022			error(_("invalid abbreviation format"));
1023			return FALSE;
1024		}
1025	}
1026	z.z_rule = ecpyalloc(fields[i_rule]);
1027	z.z_format = ecpyalloc(fields[i_format]);
1028	hasuntil = nfields > i_untilyear;
1029	if (hasuntil) {
1030		z.z_untilrule.r_filename = filename;
1031		z.z_untilrule.r_linenum = linenum;
1032		rulesub(&z.z_untilrule,
1033			fields[i_untilyear],
1034			"only",
1035			"",
1036			(nfields > i_untilmonth) ?
1037			fields[i_untilmonth] : "Jan",
1038			(nfields > i_untilday) ? fields[i_untilday] : "1",
1039			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
1040		z.z_untiltime = rpytime(&z.z_untilrule,
1041			z.z_untilrule.r_loyear);
1042		if (iscont && nzones > 0 &&
1043			z.z_untiltime > min_time &&
1044			z.z_untiltime < max_time &&
1045			zones[nzones - 1].z_untiltime > min_time &&
1046			zones[nzones - 1].z_untiltime < max_time &&
1047			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
1048				error(_("Zone continuation line end time is not after end time of previous line"));
1049				return FALSE;
1050		}
1051	}
1052	zones = (struct zone *) (void *) erealloc((char *) zones,
1053		(int) ((nzones + 1) * sizeof *zones));
1054	zones[nzones++] = z;
1055	/*
1056	** If there was an UNTIL field on this line,
1057	** there's more information about the zone on the next line.
1058	*/
1059	return hasuntil;
1060}
1061
1062static void
1063inleap(fields, nfields)
1064register char ** const	fields;
1065const int		nfields;
1066{
1067	register const char *		cp;
1068	register const struct lookup *	lp;
1069	register int			i, j;
1070	int				year, month, day;
1071	long				dayoff, tod;
1072	time_t				t;
1073
1074	if (nfields != LEAP_FIELDS) {
1075		error(_("wrong number of fields on Leap line"));
1076		return;
1077	}
1078	dayoff = 0;
1079	cp = fields[LP_YEAR];
1080	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
1081			/*
1082			 * Leapin' Lizards!
1083			 */
1084			error(_("invalid leaping year"));
1085			return;
1086	}
1087	j = EPOCH_YEAR;
1088	while (j != year) {
1089		if (year > j) {
1090			i = len_years[isleap(j)];
1091			++j;
1092		} else {
1093			--j;
1094			i = -len_years[isleap(j)];
1095		}
1096		dayoff = oadd(dayoff, eitol(i));
1097	}
1098	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
1099		error(_("invalid month name"));
1100		return;
1101	}
1102	month = lp->l_value;
1103	j = TM_JANUARY;
1104	while (j != month) {
1105		i = len_months[isleap(year)][j];
1106		dayoff = oadd(dayoff, eitol(i));
1107		++j;
1108	}
1109	cp = fields[LP_DAY];
1110	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
1111		day <= 0 || day > len_months[isleap(year)][month]) {
1112			error(_("invalid day of month"));
1113			return;
1114	}
1115	dayoff = oadd(dayoff, eitol(day - 1));
1116	if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
1117		error(_("time before zero"));
1118		return;
1119	}
1120	t = (time_t) dayoff * SECSPERDAY;
1121	/*
1122	** Cheap overflow check.
1123	*/
1124	if (t / SECSPERDAY != dayoff) {
1125		error(_("time overflow"));
1126		return;
1127	}
1128	tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
1129	cp = fields[LP_CORR];
1130	{
1131		register int	positive;
1132		int		count;
1133
1134		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
1135			positive = FALSE;
1136			count = 1;
1137		} else if (strcmp(cp, "--") == 0) {
1138			positive = FALSE;
1139			count = 2;
1140		} else if (strcmp(cp, "+") == 0) {
1141			positive = TRUE;
1142			count = 1;
1143		} else if (strcmp(cp, "++") == 0) {
1144			positive = TRUE;
1145			count = 2;
1146		} else {
1147			error(_("illegal CORRECTION field on Leap line"));
1148			return;
1149		}
1150		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
1151			error(_("illegal Rolling/Stationary field on Leap line"));
1152			return;
1153		}
1154		leapadd(tadd(t, tod), positive, lp->l_value, count);
1155	}
1156}
1157
1158static void
1159inlink(fields, nfields)
1160register char ** const	fields;
1161const int		nfields;
1162{
1163	struct link	l;
1164
1165	if (nfields != LINK_FIELDS) {
1166		error(_("wrong number of fields on Link line"));
1167		return;
1168	}
1169	if (*fields[LF_FROM] == '\0') {
1170		error(_("blank FROM field on Link line"));
1171		return;
1172	}
1173	if (*fields[LF_TO] == '\0') {
1174		error(_("blank TO field on Link line"));
1175		return;
1176	}
1177	l.l_filename = filename;
1178	l.l_linenum = linenum;
1179	l.l_from = ecpyalloc(fields[LF_FROM]);
1180	l.l_to = ecpyalloc(fields[LF_TO]);
1181	links = (struct link *) (void *) erealloc((char *) links,
1182		(int) ((nlinks + 1) * sizeof *links));
1183	links[nlinks++] = l;
1184}
1185
1186static void
1187rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
1188register struct rule * const	rp;
1189const char * const		loyearp;
1190const char * const		hiyearp;
1191const char * const		typep;
1192const char * const		monthp;
1193const char * const		dayp;
1194const char * const		timep;
1195{
1196	register const struct lookup *	lp;
1197	register const char *		cp;
1198	register char *			dp;
1199	register char *			ep;
1200
1201	if ((lp = byword(monthp, mon_names)) == NULL) {
1202		error(_("invalid month name"));
1203		return;
1204	}
1205	rp->r_month = lp->l_value;
1206	rp->r_todisstd = FALSE;
1207	rp->r_todisgmt = FALSE;
1208	dp = ecpyalloc(timep);
1209	if (*dp != '\0') {
1210		ep = dp + strlen(dp) - 1;
1211		switch (lowerit(*ep)) {
1212			case 's':	/* Standard */
1213				rp->r_todisstd = TRUE;
1214				rp->r_todisgmt = FALSE;
1215				*ep = '\0';
1216				break;
1217			case 'w':	/* Wall */
1218				rp->r_todisstd = FALSE;
1219				rp->r_todisgmt = FALSE;
1220				*ep = '\0';
1221				break;
1222			case 'g':	/* Greenwich */
1223			case 'u':	/* Universal */
1224			case 'z':	/* Zulu */
1225				rp->r_todisstd = TRUE;
1226				rp->r_todisgmt = TRUE;
1227				*ep = '\0';
1228				break;
1229		}
1230	}
1231	rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
1232	ifree(dp);
1233	/*
1234	** Year work.
1235	*/
1236	cp = loyearp;
1237	lp = byword(cp, begin_years);
1238	if (lp != NULL) switch ((int) lp->l_value) {
1239		case YR_MINIMUM:
1240			rp->r_loyear = INT_MIN;
1241			break;
1242		case YR_MAXIMUM:
1243			rp->r_loyear = INT_MAX;
1244			break;
1245		default:	/* "cannot happen" */
1246			errx(EXIT_FAILURE,
1247				_("panic: invalid l_value %d"), lp->l_value);
1248	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
1249		error(_("invalid starting year"));
1250		return;
1251	} else if (noise) {
1252		if (rp->r_loyear < min_year_representable)
1253			warning(_("starting year too low to be represented"));
1254		else if (rp->r_loyear > max_year_representable)
1255			warning(_("starting year too high to be represented"));
1256	}
1257	cp = hiyearp;
1258	if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
1259		case YR_MINIMUM:
1260			rp->r_hiyear = INT_MIN;
1261			break;
1262		case YR_MAXIMUM:
1263			rp->r_hiyear = INT_MAX;
1264			break;
1265		case YR_ONLY:
1266			rp->r_hiyear = rp->r_loyear;
1267			break;
1268		default:	/* "cannot happen" */
1269			errx(EXIT_FAILURE,
1270				_("panic: invalid l_value %d"), lp->l_value);
1271	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
1272		error(_("invalid ending year"));
1273		return;
1274	} else if (noise) {
1275		if (rp->r_loyear < min_year_representable)
1276			warning(_("starting year too low to be represented"));
1277		else if (rp->r_loyear > max_year_representable)
1278			warning(_("starting year too high to be represented"));
1279	}
1280	if (rp->r_loyear > rp->r_hiyear) {
1281		error(_("starting year greater than ending year"));
1282		return;
1283	}
1284	if (*typep == '\0')
1285		rp->r_yrtype = NULL;
1286	else {
1287		if (rp->r_loyear == rp->r_hiyear) {
1288			error(_("typed single year"));
1289			return;
1290		}
1291		rp->r_yrtype = ecpyalloc(typep);
1292	}
1293	if (rp->r_loyear < min_year && rp->r_loyear > 0)
1294		min_year = rp->r_loyear;
1295	/*
1296	** Day work.
1297	** Accept things such as:
1298	**	1
1299	**	last-Sunday
1300	**	Sun<=20
1301	**	Sun>=7
1302	*/
1303	dp = ecpyalloc(dayp);
1304	if ((lp = byword(dp, lasts)) != NULL) {
1305		rp->r_dycode = DC_DOWLEQ;
1306		rp->r_wday = lp->l_value;
1307		rp->r_dayofmonth = len_months[1][rp->r_month];
1308	} else {
1309		if ((ep = strchr(dp, '<')) != 0)
1310			rp->r_dycode = DC_DOWLEQ;
1311		else if ((ep = strchr(dp, '>')) != 0)
1312			rp->r_dycode = DC_DOWGEQ;
1313		else {
1314			ep = dp;
1315			rp->r_dycode = DC_DOM;
1316		}
1317		if (rp->r_dycode != DC_DOM) {
1318			*ep++ = 0;
1319			if (*ep++ != '=') {
1320				error(_("invalid day of month"));
1321				ifree(dp);
1322				return;
1323			}
1324			if ((lp = byword(dp, wday_names)) == NULL) {
1325				error(_("invalid weekday name"));
1326				ifree(dp);
1327				return;
1328			}
1329			rp->r_wday = lp->l_value;
1330		}
1331		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
1332			rp->r_dayofmonth <= 0 ||
1333			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
1334				error(_("invalid day of month"));
1335				ifree(dp);
1336				return;
1337		}
1338	}
1339	ifree(dp);
1340}
1341
1342static void
1343convert(val, buf)
1344const long	val;
1345char * const	buf;
1346{
1347	register int	i;
1348	register long	shift;
1349
1350	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
1351		buf[i] = val >> shift;
1352}
1353
1354static void
1355puttzcode(val, fp)
1356const long	val;
1357FILE * const	fp;
1358{
1359	char	buf[4];
1360
1361	convert(val, buf);
1362	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
1363}
1364
1365static int
1366atcomp(avp, bvp)
1367void *	avp;
1368void *	bvp;
1369{
1370	if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
1371		return -1;
1372	else if (((struct attype *) avp)->at > ((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