zic.c revision 42997
1#ifndef lint
2#ifndef NOID
3static char	elsieid[] = "@(#)zic.c	7.96";
4#endif /* !defined NOID */
5#endif /* !defined lint */
6
7#ifndef lint
8static const char rcsid[] =
9	"$Id: zic.c,v 1.8 1999/01/21 17:12:49 wollman Exp $";
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		oflag = (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				getmode(set, mflag);
504				break;
505			}
506			case 'p':
507				if (psxrules == NULL)
508					psxrules = optarg;
509				else
510					errx(EXIT_FAILURE,
511_("more than one -p option specified"));
512				break;
513			case 'u':
514				setuser(&uflag, optarg);
515				break;
516			case 'y':
517				if (yitcommand == NULL)
518					yitcommand = optarg;
519				else
520					errx(EXIT_FAILURE,
521_("more than one -y option specified"));
522				break;
523			case 'L':
524				if (leapsec == NULL)
525					leapsec = optarg;
526				else
527					errx(EXIT_FAILURE,
528_("more than one -L option specified"));
529				break;
530			case 'v':
531				noise = TRUE;
532				break;
533			case 's':
534				sflag = TRUE;
535				break;
536		}
537	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
538		usage();	/* usage message by request */
539	if (directory == NULL)
540		directory = TZDIR;
541	if (yitcommand == NULL)
542		yitcommand = "yearistype";
543
544	setboundaries();
545
546	if (optind < argc && leapsec != NULL) {
547		infile(leapsec);
548		adjleap();
549	}
550
551	for (i = optind; i < argc; ++i)
552		infile(argv[i]);
553	if (errors)
554		(void) exit(EXIT_FAILURE);
555	associate();
556	for (i = 0; i < nzones; i = j) {
557		/*
558		** Find the next non-continuation zone entry.
559		*/
560		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
561			continue;
562		outzone(&zones[i], j - i);
563	}
564	/*
565	** Make links.
566	*/
567	for (i = 0; i < nlinks; ++i)
568		dolink(links[i].l_from, links[i].l_to);
569	if (lcltime != NULL)
570		dolink(lcltime, TZDEFAULT);
571	if (psxrules != NULL)
572		dolink(psxrules, TZDEFRULES);
573	return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
574}
575
576static void
577dolink(fromfile, tofile)
578const char * const	fromfile;
579const char * const	tofile;
580{
581	register char *	fromname;
582	register char *	toname;
583
584	if (fromfile[0] == '/')
585		fromname = ecpyalloc(fromfile);
586	else {
587		fromname = ecpyalloc(directory);
588		fromname = ecatalloc(fromname, "/");
589		fromname = ecatalloc(fromname, fromfile);
590	}
591	if (tofile[0] == '/')
592		toname = ecpyalloc(tofile);
593	else {
594		toname = ecpyalloc(directory);
595		toname = ecatalloc(toname, "/");
596		toname = ecatalloc(toname, tofile);
597	}
598	/*
599	** We get to be careful here since
600	** there's a fair chance of root running us.
601	*/
602	if (!itsdir(toname))
603		(void) remove(toname);
604	if (link(fromname, toname) != 0) {
605		int	result;
606
607		if (mkdirs(toname) != 0)
608			(void) exit(EXIT_FAILURE);
609		result = link(fromname, toname);
610#if (HAVE_SYMLINK - 0)
611		if (result != 0) {
612			result = symlink(fromname, toname);
613			if (result == 0)
614warning(_("hard link failed, symbolic link used"));
615		}
616#endif
617		if (result != 0) {
618			err(EXIT_FAILURE,
619			    _("%s: Can't link from %s to %s"),
620			    fromname, toname);
621		}
622	}
623	ifree(fromname);
624	ifree(toname);
625}
626
627#ifndef INT_MAX
628#define INT_MAX	((int) (((unsigned)~0)>>1))
629#endif /* !defined INT_MAX */
630
631#ifndef INT_MIN
632#define INT_MIN	((int) ~(((unsigned)~0)>>1))
633#endif /* !defined INT_MIN */
634
635/*
636** The tz file format currently allows at most 32-bit quantities.
637** This restriction should be removed before signed 32-bit values
638** wrap around in 2038, but unfortunately this will require a
639** change to the tz file format.
640*/
641
642#define MAX_BITS_IN_FILE	32
643#define TIME_T_BITS_IN_FILE	((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
644
645static void
646setboundaries P((void))
647{
648	if (TYPE_SIGNED(time_t)) {
649		min_time = ~ (time_t) 0;
650		min_time <<= TIME_T_BITS_IN_FILE - 1;
651		max_time = ~ (time_t) 0 - min_time;
652		if (sflag)
653			min_time = 0;
654	} else {
655		min_time = 0;
656		max_time = 2 - sflag;
657		max_time <<= TIME_T_BITS_IN_FILE - 1;
658		--max_time;
659	}
660	min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
661	max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
662	min_year_representable = min_year;
663	max_year_representable = max_year;
664}
665
666static int
667itsdir(name)
668const char * const	name;
669{
670	register char *	myname;
671	register int	accres;
672
673	myname = ecpyalloc(name);
674	myname = ecatalloc(myname, "/.");
675	accres = access(myname, F_OK);
676	ifree(myname);
677	return accres == 0;
678}
679
680/*
681** Associate sets of rules with zones.
682*/
683
684/*
685** Sort by rule name.
686*/
687
688static int
689rcomp(cp1, cp2)
690const void *	cp1;
691const void *	cp2;
692{
693	return strcmp(((const struct rule *) cp1)->r_name,
694		((const struct rule *) cp2)->r_name);
695}
696
697static void
698associate P((void))
699{
700	register struct zone *	zp;
701	register struct rule *	rp;
702	register int		base, out;
703	register int		i, j;
704
705	if (nrules != 0) {
706		(void) qsort((void *) rules, (size_t) nrules,
707			(size_t) sizeof *rules, rcomp);
708		for (i = 0; i < nrules - 1; ++i) {
709			if (strcmp(rules[i].r_name,
710				rules[i + 1].r_name) != 0)
711					continue;
712			if (strcmp(rules[i].r_filename,
713				rules[i + 1].r_filename) == 0)
714					continue;
715			eat(rules[i].r_filename, rules[i].r_linenum);
716			warning(_("same rule name in multiple files"));
717			eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
718			warning(_("same rule name in multiple files"));
719			for (j = i + 2; j < nrules; ++j) {
720				if (strcmp(rules[i].r_name,
721					rules[j].r_name) != 0)
722						break;
723				if (strcmp(rules[i].r_filename,
724					rules[j].r_filename) == 0)
725						continue;
726				if (strcmp(rules[i + 1].r_filename,
727					rules[j].r_filename) == 0)
728						continue;
729				break;
730			}
731			i = j - 1;
732		}
733	}
734	for (i = 0; i < nzones; ++i) {
735		zp = &zones[i];
736		zp->z_rules = NULL;
737		zp->z_nrules = 0;
738	}
739	for (base = 0; base < nrules; base = out) {
740		rp = &rules[base];
741		for (out = base + 1; out < nrules; ++out)
742			if (strcmp(rp->r_name, rules[out].r_name) != 0)
743				break;
744		for (i = 0; i < nzones; ++i) {
745			zp = &zones[i];
746			if (strcmp(zp->z_rule, rp->r_name) != 0)
747				continue;
748			zp->z_rules = rp;
749			zp->z_nrules = out - base;
750		}
751	}
752	for (i = 0; i < nzones; ++i) {
753		zp = &zones[i];
754		if (zp->z_nrules == 0) {
755			/*
756			** Maybe we have a local standard time offset.
757			*/
758			eat(zp->z_filename, zp->z_linenum);
759			zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
760					      TRUE);
761			/*
762			** Note, though, that if there's no rule,
763			** a '%s' in the format is a bad thing.
764			*/
765			if (strchr(zp->z_format, '%') != 0)
766				error(_("%s in ruleless zone"));
767		}
768	}
769	if (errors)
770		(void) exit(EXIT_FAILURE);
771}
772
773static void
774infile(name)
775const char *	name;
776{
777	register FILE *			fp;
778	register char **		fields;
779	register char *			cp;
780	register const struct lookup *	lp;
781	register int			nfields;
782	register int			wantcont;
783	register int			num;
784	char				buf[BUFSIZ];
785
786	if (strcmp(name, "-") == 0) {
787		name = _("standard input");
788		fp = stdin;
789	} else if ((fp = fopen(name, "r")) == NULL)
790		err(EXIT_FAILURE, _("can't open %s"), name);
791	wantcont = FALSE;
792	for (num = 1; ; ++num) {
793		eat(name, num);
794		if (fgets(buf, (int) sizeof buf, fp) != buf)
795			break;
796		cp = strchr(buf, '\n');
797		if (cp == NULL) {
798			error(_("line too long"));
799			(void) exit(EXIT_FAILURE);
800		}
801		*cp = '\0';
802		fields = getfields(buf);
803		nfields = 0;
804		while (fields[nfields] != NULL) {
805			static char	nada;
806
807			if (strcmp(fields[nfields], "-") == 0)
808				fields[nfields] = &nada;
809			++nfields;
810		}
811		if (nfields == 0) {
812			/* nothing to do */
813		} else if (wantcont) {
814			wantcont = inzcont(fields, nfields);
815		} else {
816			lp = byword(fields[0], line_codes);
817			if (lp == NULL)
818				error(_("input line of unknown type"));
819			else switch ((int) (lp->l_value)) {
820				case LC_RULE:
821					inrule(fields, nfields);
822					wantcont = FALSE;
823					break;
824				case LC_ZONE:
825					wantcont = inzone(fields, nfields);
826					break;
827				case LC_LINK:
828					inlink(fields, nfields);
829					wantcont = FALSE;
830					break;
831				case LC_LEAP:
832					if (name != leapsec)
833						warnx(
834_("leap line in non leap seconds file %s"), name);
835					else	inleap(fields, nfields);
836					wantcont = FALSE;
837					break;
838				default:	/* "cannot happen" */
839					errx(EXIT_FAILURE,
840_("panic: invalid l_value %d"), lp->l_value);
841			}
842		}
843		ifree((char *) fields);
844	}
845	if (ferror(fp))
846		errx(EXIT_FAILURE, _("error reading %s"), filename);
847	if (fp != stdin && fclose(fp))
848		err(EXIT_FAILURE, _("error closing %s"), filename);
849	if (wantcont)
850		error(_("expected continuation line not found"));
851}
852
853/*
854** Convert a string of one of the forms
855**	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
856** into a number of seconds.
857** A null string maps to zero.
858** Call error with errstring and return zero on errors.
859*/
860
861static long
862gethms(string, errstring, signable)
863const char *		string;
864const char * const	errstring;
865const int		signable;
866{
867	int	hh, mm, ss, sign;
868
869	if (string == NULL || *string == '\0')
870		return 0;
871	if (!signable)
872		sign = 1;
873	else if (*string == '-') {
874		sign = -1;
875		++string;
876	} else	sign = 1;
877	if (sscanf(string, scheck(string, "%d"), &hh) == 1)
878		mm = ss = 0;
879	else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
880		ss = 0;
881	else if (sscanf(string, scheck(string, "%d:%d:%d"),
882		&hh, &mm, &ss) != 3) {
883			error(errstring);
884			return 0;
885	}
886	if ((hh < 0 || hh >= HOURSPERDAY ||
887		mm < 0 || mm >= MINSPERHOUR ||
888		ss < 0 || ss > SECSPERMIN) &&
889		!(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
890			error(errstring);
891			return 0;
892	}
893	return eitol(sign) *
894		(eitol(hh * MINSPERHOUR + mm) *
895		eitol(SECSPERMIN) + eitol(ss));
896}
897
898static void
899inrule(fields, nfields)
900register char ** const	fields;
901const int		nfields;
902{
903	static struct rule	r;
904
905	if (nfields != RULE_FIELDS) {
906		error(_("wrong number of fields on Rule line"));
907		return;
908	}
909	if (*fields[RF_NAME] == '\0') {
910		error(_("nameless rule"));
911		return;
912	}
913	r.r_filename = filename;
914	r.r_linenum = linenum;
915	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
916	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
917		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
918	r.r_name = ecpyalloc(fields[RF_NAME]);
919	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
920	rules = (struct rule *) (void *) erealloc((char *) rules,
921		(int) ((nrules + 1) * sizeof *rules));
922	rules[nrules++] = r;
923}
924
925static int
926inzone(fields, nfields)
927register char ** const	fields;
928const int		nfields;
929{
930	register int	i;
931	static char *	buf;
932
933	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
934		error(_("wrong number of fields on Zone line"));
935		return FALSE;
936	}
937	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
938		buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
939		(void) sprintf(buf,
940_("\"Zone %s\" line and -l option are mutually exclusive"),
941			TZDEFAULT);
942		error(buf);
943		return FALSE;
944	}
945	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
946		buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
947		(void) sprintf(buf,
948_("\"Zone %s\" line and -p option are mutually exclusive"),
949			TZDEFRULES);
950		error(buf);
951		return FALSE;
952	}
953	for (i = 0; i < nzones; ++i)
954		if (zones[i].z_name != NULL &&
955			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
956				buf = erealloc(buf, (int) (132 +
957					strlen(fields[ZF_NAME]) +
958					strlen(zones[i].z_filename)));
959				(void) sprintf(buf,
960_("duplicate zone name %s (file \"%s\", line %d)"),
961					fields[ZF_NAME],
962					zones[i].z_filename,
963					zones[i].z_linenum);
964				error(buf);
965				return FALSE;
966		}
967	return inzsub(fields, nfields, FALSE);
968}
969
970static int
971inzcont(fields, nfields)
972register char ** const	fields;
973const int		nfields;
974{
975	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
976		error(_("wrong number of fields on Zone continuation line"));
977		return FALSE;
978	}
979	return inzsub(fields, nfields, TRUE);
980}
981
982static int
983inzsub(fields, nfields, iscont)
984register char ** const	fields;
985const int		nfields;
986const int		iscont;
987{
988	register char *		cp;
989	static struct zone	z;
990	register int		i_gmtoff, i_rule, i_format;
991	register int		i_untilyear, i_untilmonth;
992	register int		i_untilday, i_untiltime;
993	register int		hasuntil;
994
995	if (iscont) {
996		i_gmtoff = ZFC_GMTOFF;
997		i_rule = ZFC_RULE;
998		i_format = ZFC_FORMAT;
999		i_untilyear = ZFC_TILYEAR;
1000		i_untilmonth = ZFC_TILMONTH;
1001		i_untilday = ZFC_TILDAY;
1002		i_untiltime = ZFC_TILTIME;
1003		z.z_name = NULL;
1004	} else {
1005		i_gmtoff = ZF_GMTOFF;
1006		i_rule = ZF_RULE;
1007		i_format = ZF_FORMAT;
1008		i_untilyear = ZF_TILYEAR;
1009		i_untilmonth = ZF_TILMONTH;
1010		i_untilday = ZF_TILDAY;
1011		i_untiltime = ZF_TILTIME;
1012		z.z_name = ecpyalloc(fields[ZF_NAME]);
1013	}
1014	z.z_filename = filename;
1015	z.z_linenum = linenum;
1016	z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
1017	if ((cp = strchr(fields[i_format], '%')) != 0) {
1018		if (*++cp != 's' || strchr(cp, '%') != 0) {
1019			error(_("invalid abbreviation format"));
1020			return FALSE;
1021		}
1022	}
1023	z.z_rule = ecpyalloc(fields[i_rule]);
1024	z.z_format = ecpyalloc(fields[i_format]);
1025	hasuntil = nfields > i_untilyear;
1026	if (hasuntil) {
1027		z.z_untilrule.r_filename = filename;
1028		z.z_untilrule.r_linenum = linenum;
1029		rulesub(&z.z_untilrule,
1030			fields[i_untilyear],
1031			"only",
1032			"",
1033			(nfields > i_untilmonth) ?
1034			fields[i_untilmonth] : "Jan",
1035			(nfields > i_untilday) ? fields[i_untilday] : "1",
1036			(nfields > i_untiltime) ? fields[i_untiltime] : "0");
1037		z.z_untiltime = rpytime(&z.z_untilrule,
1038			z.z_untilrule.r_loyear);
1039		if (iscont && nzones > 0 &&
1040			z.z_untiltime > min_time &&
1041			z.z_untiltime < max_time &&
1042			zones[nzones - 1].z_untiltime > min_time &&
1043			zones[nzones - 1].z_untiltime < max_time &&
1044			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
1045				error(_("Zone continuation line end time is not after end time of previous line"));
1046				return FALSE;
1047		}
1048	}
1049	zones = (struct zone *) (void *) erealloc((char *) zones,
1050		(int) ((nzones + 1) * sizeof *zones));
1051	zones[nzones++] = z;
1052	/*
1053	** If there was an UNTIL field on this line,
1054	** there's more information about the zone on the next line.
1055	*/
1056	return hasuntil;
1057}
1058
1059static void
1060inleap(fields, nfields)
1061register char ** const	fields;
1062const int		nfields;
1063{
1064	register const char *		cp;
1065	register const struct lookup *	lp;
1066	register int			i, j;
1067	int				year, month, day;
1068	long				dayoff, tod;
1069	time_t				t;
1070
1071	if (nfields != LEAP_FIELDS) {
1072		error(_("wrong number of fields on Leap line"));
1073		return;
1074	}
1075	dayoff = 0;
1076	cp = fields[LP_YEAR];
1077	if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
1078			/*
1079			 * Leapin' Lizards!
1080			 */
1081			error(_("invalid leaping year"));
1082			return;
1083	}
1084	j = EPOCH_YEAR;
1085	while (j != year) {
1086		if (year > j) {
1087			i = len_years[isleap(j)];
1088			++j;
1089		} else {
1090			--j;
1091			i = -len_years[isleap(j)];
1092		}
1093		dayoff = oadd(dayoff, eitol(i));
1094	}
1095	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
1096		error(_("invalid month name"));
1097		return;
1098	}
1099	month = lp->l_value;
1100	j = TM_JANUARY;
1101	while (j != month) {
1102		i = len_months[isleap(year)][j];
1103		dayoff = oadd(dayoff, eitol(i));
1104		++j;
1105	}
1106	cp = fields[LP_DAY];
1107	if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
1108		day <= 0 || day > len_months[isleap(year)][month]) {
1109			error(_("invalid day of month"));
1110			return;
1111	}
1112	dayoff = oadd(dayoff, eitol(day - 1));
1113	if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
1114		error(_("time before zero"));
1115		return;
1116	}
1117	t = (time_t) dayoff * SECSPERDAY;
1118	/*
1119	** Cheap overflow check.
1120	*/
1121	if (t / SECSPERDAY != dayoff) {
1122		error(_("time overflow"));
1123		return;
1124	}
1125	tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
1126	cp = fields[LP_CORR];
1127	{
1128		register int	positive;
1129		int		count;
1130
1131		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
1132			positive = FALSE;
1133			count = 1;
1134		} else if (strcmp(cp, "--") == 0) {
1135			positive = FALSE;
1136			count = 2;
1137		} else if (strcmp(cp, "+") == 0) {
1138			positive = TRUE;
1139			count = 1;
1140		} else if (strcmp(cp, "++") == 0) {
1141			positive = TRUE;
1142			count = 2;
1143		} else {
1144			error(_("illegal CORRECTION field on Leap line"));
1145			return;
1146		}
1147		if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
1148			error(_("illegal Rolling/Stationary field on Leap line"));
1149			return;
1150		}
1151		leapadd(tadd(t, tod), positive, lp->l_value, count);
1152	}
1153}
1154
1155static void
1156inlink(fields, nfields)
1157register char ** const	fields;
1158const int		nfields;
1159{
1160	struct link	l;
1161
1162	if (nfields != LINK_FIELDS) {
1163		error(_("wrong number of fields on Link line"));
1164		return;
1165	}
1166	if (*fields[LF_FROM] == '\0') {
1167		error(_("blank FROM field on Link line"));
1168		return;
1169	}
1170	if (*fields[LF_TO] == '\0') {
1171		error(_("blank TO field on Link line"));
1172		return;
1173	}
1174	l.l_filename = filename;
1175	l.l_linenum = linenum;
1176	l.l_from = ecpyalloc(fields[LF_FROM]);
1177	l.l_to = ecpyalloc(fields[LF_TO]);
1178	links = (struct link *) (void *) erealloc((char *) links,
1179		(int) ((nlinks + 1) * sizeof *links));
1180	links[nlinks++] = l;
1181}
1182
1183static void
1184rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
1185register struct rule * const	rp;
1186const char * const		loyearp;
1187const char * const		hiyearp;
1188const char * const		typep;
1189const char * const		monthp;
1190const char * const		dayp;
1191const char * const		timep;
1192{
1193	register const struct lookup *	lp;
1194	register const char *		cp;
1195	register char *			dp;
1196	register char *			ep;
1197
1198	if ((lp = byword(monthp, mon_names)) == NULL) {
1199		error(_("invalid month name"));
1200		return;
1201	}
1202	rp->r_month = lp->l_value;
1203	rp->r_todisstd = FALSE;
1204	rp->r_todisgmt = FALSE;
1205	dp = ecpyalloc(timep);
1206	if (*dp != '\0') {
1207		ep = dp + strlen(dp) - 1;
1208		switch (lowerit(*ep)) {
1209			case 's':	/* Standard */
1210				rp->r_todisstd = TRUE;
1211				rp->r_todisgmt = FALSE;
1212				*ep = '\0';
1213				break;
1214			case 'w':	/* Wall */
1215				rp->r_todisstd = FALSE;
1216				rp->r_todisgmt = FALSE;
1217				*ep = '\0';
1218				break;
1219			case 'g':	/* Greenwich */
1220			case 'u':	/* Universal */
1221			case 'z':	/* Zulu */
1222				rp->r_todisstd = TRUE;
1223				rp->r_todisgmt = TRUE;
1224				*ep = '\0';
1225				break;
1226		}
1227	}
1228	rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
1229	ifree(dp);
1230	/*
1231	** Year work.
1232	*/
1233	cp = loyearp;
1234	lp = byword(cp, begin_years);
1235	if (lp != NULL) switch ((int) lp->l_value) {
1236		case YR_MINIMUM:
1237			rp->r_loyear = INT_MIN;
1238			break;
1239		case YR_MAXIMUM:
1240			rp->r_loyear = INT_MAX;
1241			break;
1242		default:	/* "cannot happen" */
1243			errx(EXIT_FAILURE,
1244				_("panic: invalid l_value %d"), lp->l_value);
1245	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
1246		error(_("invalid starting year"));
1247		return;
1248	} else if (noise) {
1249		if (rp->r_loyear < min_year_representable)
1250			warning(_("starting year too low to be represented"));
1251		else if (rp->r_loyear > max_year_representable)
1252			warning(_("starting year too high to be represented"));
1253	}
1254	cp = hiyearp;
1255	if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
1256		case YR_MINIMUM:
1257			rp->r_hiyear = INT_MIN;
1258			break;
1259		case YR_MAXIMUM:
1260			rp->r_hiyear = INT_MAX;
1261			break;
1262		case YR_ONLY:
1263			rp->r_hiyear = rp->r_loyear;
1264			break;
1265		default:	/* "cannot happen" */
1266			errx(EXIT_FAILURE,
1267				_("panic: invalid l_value %d"), lp->l_value);
1268	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
1269		error(_("invalid ending year"));
1270		return;
1271	} else if (noise) {
1272		if (rp->r_loyear < min_year_representable)
1273			warning(_("starting year too low to be represented"));
1274		else if (rp->r_loyear > max_year_representable)
1275			warning(_("starting year too high to be represented"));
1276	}
1277	if (rp->r_loyear > rp->r_hiyear) {
1278		error(_("starting year greater than ending year"));
1279		return;
1280	}
1281	if (*typep == '\0')
1282		rp->r_yrtype = NULL;
1283	else {
1284		if (rp->r_loyear == rp->r_hiyear) {
1285			error(_("typed single year"));
1286			return;
1287		}
1288		rp->r_yrtype = ecpyalloc(typep);
1289	}
1290	if (rp->r_loyear < min_year && rp->r_loyear > 0)
1291		min_year = rp->r_loyear;
1292	/*
1293	** Day work.
1294	** Accept things such as:
1295	**	1
1296	**	last-Sunday
1297	**	Sun<=20
1298	**	Sun>=7
1299	*/
1300	dp = ecpyalloc(dayp);
1301	if ((lp = byword(dp, lasts)) != NULL) {
1302		rp->r_dycode = DC_DOWLEQ;
1303		rp->r_wday = lp->l_value;
1304		rp->r_dayofmonth = len_months[1][rp->r_month];
1305	} else {
1306		if ((ep = strchr(dp, '<')) != 0)
1307			rp->r_dycode = DC_DOWLEQ;
1308		else if ((ep = strchr(dp, '>')) != 0)
1309			rp->r_dycode = DC_DOWGEQ;
1310		else {
1311			ep = dp;
1312			rp->r_dycode = DC_DOM;
1313		}
1314		if (rp->r_dycode != DC_DOM) {
1315			*ep++ = 0;
1316			if (*ep++ != '=') {
1317				error(_("invalid day of month"));
1318				ifree(dp);
1319				return;
1320			}
1321			if ((lp = byword(dp, wday_names)) == NULL) {
1322				error(_("invalid weekday name"));
1323				ifree(dp);
1324				return;
1325			}
1326			rp->r_wday = lp->l_value;
1327		}
1328		if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
1329			rp->r_dayofmonth <= 0 ||
1330			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
1331				error(_("invalid day of month"));
1332				ifree(dp);
1333				return;
1334		}
1335	}
1336	ifree(dp);
1337}
1338
1339static void
1340convert(val, buf)
1341const long	val;
1342char * const	buf;
1343{
1344	register int	i;
1345	register long	shift;
1346
1347	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
1348		buf[i] = val >> shift;
1349}
1350
1351static void
1352puttzcode(val, fp)
1353const long	val;
1354FILE * const	fp;
1355{
1356	char	buf[4];
1357
1358	convert(val, buf);
1359	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
1360}
1361
1362static int
1363atcomp(avp, bvp)
1364void *	avp;
1365void *	bvp;
1366{
1367	if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
1368		return -1;
1369	else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
1370		return 1;
1371	else	return 0;
1372}
1373
1374static void
1375writezone(name)
1376const char * const	name;
1377{
1378	register FILE *		fp;
1379	register int		i, j;
1380	static char *		fullname;
1381	static struct tzhead	tzh;
1382	time_t			ats[TZ_MAX_TIMES];
1383	unsigned char		types[TZ_MAX_TIMES];
1384
1385	/*
1386	** Sort.
1387	*/
1388	if (timecnt > 1)
1389		(void) qsort((void *) attypes, (size_t) timecnt,
1390			(size_t) sizeof *attypes, atcomp);
1391	/*
1392	** Optimize.
1393	*/
1394	{
1395		int	fromi;
1396		int	toi;
1397
1398		toi = 0;
1399		fromi = 0;
1400		while (fromi < timecnt && attypes[fromi].at < min_time)
1401			++fromi;
1402		if (isdsts[0] == 0)
1403			while (fromi < timecnt && attypes[fromi].type == 0)
1404				++fromi;	/* handled by default rule */
1405		for ( ; fromi < timecnt; ++fromi) {
1406			if (toi != 0
1407			    && ((attypes[fromi].at
1408				 + gmtoffs[attypes[toi - 1].type])
1409				<= (attypes[toi - 1].at
1410				    + gmtoffs[toi == 1 ? 0
1411					      : attypes[toi - 2].type]))) {
1412				attypes[toi - 1].type = attypes[fromi].type;
1413				continue;
1414			}
1415			if (toi == 0 ||
1416				attypes[toi - 1].type != attypes[fromi].type)
1417					attypes[toi++] = attypes[fromi];
1418		}
1419		timecnt = toi;
1420	}
1421	/*
1422	** Transfer.
1423	*/
1424	for (i = 0; i < timecnt; ++i) {
1425		ats[i] = attypes[i].at;
1426		types[i] = attypes[i].type;
1427	}
1428	fullname = erealloc(fullname,
1429		(int) (strlen(directory) + 1 + strlen(name) + 1));
1430	(void) sprintf(fullname, "%s/%s", directory, name);
1431
1432	/*
1433	 * Remove old file, if any, to snap links.
1434	 */
1435	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
1436		err(EXIT_FAILURE, _("can't remove %s"), fullname);
1437
1438	if ((fp = fopen(fullname, "wb")) == NULL) {
1439		if (mkdirs(fullname) != 0)
1440			(void) exit(EXIT_FAILURE);
1441		if ((fp = fopen(fullname, "wb")) == NULL)
1442			err(EXIT_FAILURE, _("can't create %s"), fullname);
1443	}
1444	convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
1445	convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
1446	convert(eitol(leapcnt), tzh.tzh_leapcnt);
1447	convert(eitol(timecnt), tzh.tzh_timecnt);
1448	convert(eitol(typecnt), tzh.tzh_typecnt);
1449	convert(eitol(charcnt), tzh.tzh_charcnt);
1450	(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
1451#define DO(field)	(void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
1452	DO(tzh_magic);
1453	DO(tzh_reserved);
1454	DO(tzh_ttisgmtcnt);
1455	DO(tzh_ttisstdcnt);
1456	DO(tzh_leapcnt);
1457	DO(tzh_timecnt);
1458	DO(tzh_typecnt);
1459	DO(tzh_charcnt);
1460#undef DO
1461	for (i = 0; i < timecnt; ++i) {
1462		j = leapcnt;
1463		while (--j >= 0)
1464			if (ats[i] >= trans[j]) {
1465				ats[i] = tadd(ats[i], corr[j]);
1466				break;
1467			}
1468		puttzcode((long) ats[i], fp);
1469	}
1470	if (timecnt > 0)
1471		(void) fwrite((void *) types, (size_t) sizeof types[0],
1472			(size_t) timecnt, fp);
1473	for (i = 0; i < typecnt; ++i) {
1474		puttzcode((long) gmtoffs[i], fp);
1475		(void) putc(isdsts[i], fp);
1476		(void) putc(abbrinds[i], fp);
1477	}
1478	if (charcnt != 0)
1479		(void) fwrite((void *) chars, (size_t) sizeof chars[0],
1480			(size_t) charcnt, fp);
1481	for (i = 0; i < leapcnt; ++i) {
1482		if (roll[i]) {
1483			if (timecnt == 0 || trans[i] < ats[0]) {
1484				j = 0;
1485				while (isdsts[j])
1486					if (++j >= typecnt) {
1487						j = 0;
1488						break;
1489					}
1490			} else {
1491				j = 1;
1492				while (j < timecnt && trans[i] >= ats[j])
1493					++j;
1494				j = types[j - 1];
1495			}
1496			puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
1497		} else	puttzcode((long) trans[i], fp);
1498		puttzcode((long) corr[i], fp);
1499	}
1500	for (i = 0; i < typecnt; ++i)
1501		(void) putc(ttisstds[i], fp);
1502	for (i = 0; i < typecnt; ++i)
1503		(void) putc(ttisgmts[i], fp);
1504	if (ferror(fp) || fclose(fp))
1505		errx(EXIT_FAILURE, _("error writing %s"), fullname);
1506	if (chmod(fullname, mflag) < 0)
1507		err(EXIT_FAILURE, _("cannot change mode of %s to %03o"),
1508		    fullname, (unsigned)mflag);
1509	if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
1510	    && chown(fullname, uflag, gflag) < 0)
1511		err(EXIT_FAILURE, _("cannot change ownership of %s"),
1512		    fullname);
1513}
1514
1515static void
1516doabbr(abbr, format, letters, isdst)
1517char * const		abbr;
1518const char * const	format;
1519const char * const	letters;
1520const int		isdst;
1521{
1522	if (strchr(format, '/') == NULL) {
1523		if (letters == NULL)
1524			(void) strcpy(abbr, format);
1525		else	(void) sprintf(abbr, format, letters);
1526	} else if (isdst)
1527		(void) strcpy(abbr, strchr(format, '/') + 1);
1528	else {
1529		(void) strcpy(abbr, format);
1530		*strchr(abbr, '/') = '\0';
1531	}
1532}
1533
1534static void
1535outzone(zpfirst, zonecount)
1536const struct zone * const	zpfirst;
1537const int			zonecount;
1538{
1539	register const struct zone *	zp;
1540	register struct rule *		rp;
1541	register int			i, j;
1542	register int			usestart, useuntil;
1543	register time_t			starttime, untiltime;
1544	register long			gmtoff;
1545	register long			stdoff;
1546	register int			year;
1547	register long			startoff;
1548	register int			startttisstd;
1549	register int			startttisgmt;
1550	register int			type;
1551	char				startbuf[BUFSIZ];
1552
1553	INITIALIZE(untiltime);
1554	INITIALIZE(starttime);
1555	/*
1556	** Now. . .finally. . .generate some useful data!
1557	*/
1558	timecnt = 0;
1559	typecnt = 0;
1560	charcnt = 0;
1561	/*
1562	** A guess that may well be corrected later.
1563	*/
1564	stdoff = 0;
1565	/*
1566	** Thanks to Earl Chew (earl@dnd.icp.nec.com.au)
1567	** for noting the need to unconditionally initialize startttisstd.
1568	*/
1569	startttisstd = FALSE;
1570	startttisgmt = FALSE;
1571	for (i = 0; i < zonecount; ++i) {
1572		zp = &zpfirst[i];
1573		usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
1574		useuntil = i < (zonecount - 1);
1575		if (useuntil && zp->z_untiltime <= min_time)
1576			continue;
1577		gmtoff = zp->z_gmtoff;
1578		eat(zp->z_filename, zp->z_linenum);
1579		*startbuf = '\0';
1580		startoff = zp->z_gmtoff;
1581		if (zp->z_nrules == 0) {
1582			stdoff = zp->z_stdoff;
1583			doabbr(startbuf, zp->z_format,
1584				(char *) NULL, stdoff != 0);
1585			type = addtype(oadd(zp->z_gmtoff, stdoff),
1586				startbuf, stdoff != 0, startttisstd,
1587				startttisgmt);
1588			if (usestart) {
1589				addtt(starttime, type);
1590				usestart = FALSE;
1591			}
1592			else if (stdoff != 0)
1593				addtt(min_time, type);
1594		} else for (year = min_year; year <= max_year; ++year) {
1595			if (useuntil && year > zp->z_untilrule.r_hiyear)
1596				break;
1597			/*
1598			** Mark which rules to do in the current year.
1599			** For those to do, calculate rpytime(rp, year);
1600			*/
1601			for (j = 0; j < zp->z_nrules; ++j) {
1602				rp = &zp->z_rules[j];
1603				eats(zp->z_filename, zp->z_linenum,
1604					rp->r_filename, rp->r_linenum);
1605				rp->r_todo = year >= rp->r_loyear &&
1606						year <= rp->r_hiyear &&
1607						yearistype(year, rp->r_yrtype);
1608				if (rp->r_todo)
1609					rp->r_temp = rpytime(rp, year);
1610			}
1611			for ( ; ; ) {
1612				register int	k;
1613				register time_t	jtime, ktime;
1614				register long	offset;
1615				char		buf[BUFSIZ];
1616
1617				INITIALIZE(ktime);
1618				if (useuntil) {
1619					/*
1620					** Turn untiltime into UTC
1621					** assuming the current gmtoff and
1622					** stdoff values.
1623					*/
1624					untiltime = zp->z_untiltime;
1625					if (!zp->z_untilrule.r_todisgmt)
1626						untiltime = tadd(untiltime,
1627							-gmtoff);
1628					if (!zp->z_untilrule.r_todisstd)
1629						untiltime = tadd(untiltime,
1630							-stdoff);
1631				}
1632				/*
1633				** Find the rule (of those to do, if any)
1634				** that takes effect earliest in the year.
1635				*/
1636				k = -1;
1637				for (j = 0; j < zp->z_nrules; ++j) {
1638					rp = &zp->z_rules[j];
1639					if (!rp->r_todo)
1640						continue;
1641					eats(zp->z_filename, zp->z_linenum,
1642						rp->r_filename, rp->r_linenum);
1643					offset = rp->r_todisgmt ? 0 : gmtoff;
1644					if (!rp->r_todisstd)
1645						offset = oadd(offset, stdoff);
1646					jtime = rp->r_temp;
1647					if (jtime == min_time ||
1648						jtime == max_time)
1649							continue;
1650					jtime = tadd(jtime, -offset);
1651					if (k < 0 || jtime < ktime) {
1652						k = j;
1653						ktime = jtime;
1654					}
1655				}
1656				if (k < 0)
1657					break;	/* go on to next year */
1658				rp = &zp->z_rules[k];
1659				rp->r_todo = FALSE;
1660				if (useuntil && ktime >= untiltime)
1661					break;
1662				stdoff = rp->r_stdoff;
1663				if (usestart && ktime == starttime)
1664					usestart = FALSE;
1665				if (usestart) {
1666					if (ktime < starttime) {
1667						startoff = oadd(zp->z_gmtoff,
1668							stdoff);
1669						doabbr(startbuf, zp->z_format,
1670							rp->r_abbrvar,
1671							rp->r_stdoff != 0);
1672						continue;
1673					}
1674					if (*startbuf == '\0' &&
1675					    startoff == oadd(zp->z_gmtoff,
1676					    stdoff)) {
1677						doabbr(startbuf, zp->z_format,
1678							rp->r_abbrvar,
1679							rp->r_stdoff != 0);
1680					}
1681				}
1682				eats(zp->z_filename, zp->z_linenum,
1683					rp->r_filename, rp->r_linenum);
1684				doabbr(buf, zp->z_format, rp->r_abbrvar,
1685					rp->r_stdoff != 0);
1686				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
1687				type = addtype(offset, buf, rp->r_stdoff != 0,
1688					rp->r_todisstd, rp->r_todisgmt);
1689				addtt(ktime, type);
1690			}
1691		}
1692		if (usestart) {
1693			if (*startbuf == '\0' &&
1694				zp->z_format != NULL &&
1695				strchr(zp->z_format, '%') == NULL &&
1696				strchr(zp->z_format, '/') == NULL)
1697					(void) strcpy(startbuf, zp->z_format);
1698			eat(zp->z_filename, zp->z_linenum);
1699			if (*startbuf == '\0')
1700error(_("can't determine time zone abbreviation to use just after until time"));
1701			else	addtt(starttime,
1702					addtype(startoff, startbuf,
1703						startoff != zp->z_gmtoff,
1704						startttisstd,
1705						startttisgmt));
1706		}
1707		/*
1708		** Now we may get to set starttime for the next zone line.
1709		*/
1710		if (useuntil) {
1711			startttisstd = zp->z_untilrule.r_todisstd;
1712			startttisgmt = zp->z_untilrule.r_todisgmt;
1713			starttime = zp->z_untiltime;
1714			if (!startttisstd)
1715				starttime = tadd(starttime, -stdoff);
1716			if (!startttisgmt)
1717				starttime = tadd(starttime, -gmtoff);
1718		}
1719	}
1720	writezone(zpfirst->z_name);
1721}
1722
1723static void
1724addtt(starttime, type)
1725const time_t	starttime;
1726int		type;
1727{
1728	if (starttime <= min_time ||
1729		(timecnt == 1 && attypes[0].at < min_time)) {
1730		gmtoffs[0] = gmtoffs[type];
1731		isdsts[0] = isdsts[type];
1732		ttisstds[0] = ttisstds[type];
1733		ttisgmts[0] = ttisgmts[type];
1734		if (abbrinds[type] != 0)
1735			(void) strcpy(chars, &chars[abbrinds[type]]);
1736		abbrinds[0] = 0;
1737		charcnt = strlen(chars) + 1;
1738		typecnt = 1;
1739		timecnt = 0;
1740		type = 0;
1741	}
1742	if (timecnt >= TZ_MAX_TIMES) {
1743		error(_("too many transitions?!"));
1744		(void) exit(EXIT_FAILURE);
1745	}
1746	attypes[timecnt].at = starttime;
1747	attypes[timecnt].type = type;
1748	++timecnt;
1749}
1750
1751static int
1752addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
1753const long		gmtoff;
1754const char * const	abbr;
1755const int		isdst;
1756const int		ttisstd;
1757const int		ttisgmt;
1758{
1759	register int	i, j;
1760
1761	if (isdst != TRUE && isdst != FALSE) {
1762		error(_("internal error - addtype called with bad isdst"));
1763		(void) exit(EXIT_FAILURE);
1764	}
1765	if (ttisstd != TRUE && ttisstd != FALSE) {
1766		error(_("internal error - addtype called with bad ttisstd"));
1767		(void) exit(EXIT_FAILURE);
1768	}
1769	if (ttisgmt != TRUE && ttisgmt != FALSE) {
1770		error(_("internal error - addtype called with bad ttisgmt"));
1771		(void) exit(EXIT_FAILURE);
1772	}
1773	/*
1774	** See if there's already an entry for this zone type.
1775	** If so, just return its index.
1776	*/
1777	for (i = 0; i < typecnt; ++i) {
1778		if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
1779			strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
1780			ttisstd == ttisstds[i] &&
1781			ttisgmt == ttisgmts[i])
1782				return i;
1783	}
1784	/*
1785	** There isn't one; add a new one, unless there are already too
1786	** many.
1787	*/
1788	if (typecnt >= TZ_MAX_TYPES) {
1789		error(_("too many local time types"));
1790		(void) exit(EXIT_FAILURE);
1791	}
1792	gmtoffs[i] = gmtoff;
1793	isdsts[i] = isdst;
1794	ttisstds[i] = ttisstd;
1795	ttisgmts[i] = ttisgmt;
1796
1797	for (j = 0; j < charcnt; ++j)
1798		if (strcmp(&chars[j], abbr) == 0)
1799			break;
1800	if (j == charcnt)
1801		newabbr(abbr);
1802	abbrinds[i] = j;
1803	++typecnt;
1804	return i;
1805}
1806
1807static void
1808leapadd(t, positive, rolling, count)
1809const time_t	t;
1810const int	positive;
1811const int	rolling;
1812int		count;
1813{
1814	register int	i, j;
1815
1816	if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
1817		error(_("too many leap seconds"));
1818		(void) exit(EXIT_FAILURE);
1819	}
1820	for (i = 0; i < leapcnt; ++i)
1821		if (t <= trans[i]) {
1822			if (t == trans[i]) {
1823				error(_("repeated leap second moment"));
1824				(void) exit(EXIT_FAILURE);
1825			}
1826			break;
1827		}
1828	do {
1829		for (j = leapcnt; j > i; --j) {
1830			trans[j] = trans[j - 1];
1831			corr[j] = corr[j - 1];
1832			roll[j] = roll[j - 1];
1833		}
1834		trans[i] = t;
1835		corr[i] = positive ? 1L : eitol(-count);
1836		roll[i] = rolling;
1837		++leapcnt;
1838	} while (positive && --count != 0);
1839}
1840
1841static void
1842adjleap P((void))
1843{
1844	register int	i;
1845	register long	last = 0;
1846
1847	/*
1848	** propagate leap seconds forward
1849	*/
1850	for (i = 0; i < leapcnt; ++i) {
1851		trans[i] = tadd(trans[i], last);
1852		last = corr[i] += last;
1853	}
1854}
1855
1856static int
1857yearistype(year, type)
1858const int		year;
1859const char * const	type;
1860{
1861	static char *	buf;
1862	int		result;
1863
1864	if (type == NULL || *type == '\0')
1865		return TRUE;
1866	buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
1867	(void) sprintf(buf, "%s %d %s", yitcommand, year, type);
1868	result = system(buf);
1869	if (result == 0)
1870		return TRUE;
1871	if (result == (1 << 8))
1872		return FALSE;
1873	error(_("wild result from command execution"));
1874	warnx(_("command was '%s', result was %d"), buf, result);
1875	for ( ; ; )
1876		(void) exit(EXIT_FAILURE);
1877}
1878
1879static int
1880lowerit(a)
1881int	a;
1882{
1883	a = (unsigned char) a;
1884	return (isascii(a) && isupper(a)) ? tolower(a) : a;
1885}
1886
1887static int
1888ciequal(ap, bp)		/* case-insensitive equality */
1889register const char *	ap;
1890register const char *	bp;
1891{
1892	while (lowerit(*ap) == lowerit(*bp++))
1893		if (*ap++ == '\0')
1894			return TRUE;
1895	return FALSE;
1896}
1897
1898static int
1899itsabbr(abbr, word)
1900register const char *	abbr;
1901register const char *	word;
1902{
1903	if (lowerit(*abbr) != lowerit(*word))
1904		return FALSE;
1905	++word;
1906	while (*++abbr != '\0')
1907		do {
1908			if (*word == '\0')
1909				return FALSE;
1910		} while (lowerit(*word++) != lowerit(*abbr));
1911	return TRUE;
1912}
1913
1914static const struct lookup *
1915byword(word, table)
1916register const char * const		word;
1917register const struct lookup * const	table;
1918{
1919	register const struct lookup *	foundlp;
1920	register const struct lookup *	lp;
1921
1922	if (word == NULL || table == NULL)
1923		return NULL;
1924	/*
1925	** Look for exact match.
1926	*/
1927	for (lp = table; lp->l_word != NULL; ++lp)
1928		if (ciequal(word, lp->l_word))
1929			return lp;
1930	/*
1931	** Look for inexact match.
1932	*/
1933	foundlp = NULL;
1934	for (lp = table; lp->l_word != NULL; ++lp)
1935		if (itsabbr(word, lp->l_word)) {
1936			if (foundlp == NULL)
1937				foundlp = lp;
1938			else	return NULL;	/* multiple inexact matches */
1939		}
1940	return foundlp;
1941}
1942
1943static char **
1944getfields(cp)
1945register char *	cp;
1946{
1947	register char *		dp;
1948	register char **	array;
1949	register int		nsubs;
1950
1951	if (cp == NULL)
1952		return NULL;
1953	array = (char **) (void *)
1954		emalloc((int) ((strlen(cp) + 1) * sizeof *array));
1955	nsubs = 0;
1956	for ( ; ; ) {
1957		while (isascii(*cp) && isspace((unsigned char) *cp))
1958			++cp;
1959		if (*cp == '\0' || *cp == '#')
1960			break;
1961		array[nsubs++] = dp = cp;
1962		do {
1963			if ((*dp = *cp++) != '"')
1964				++dp;
1965			else while ((*dp = *cp++) != '"')
1966				if (*dp != '\0')
1967					++dp;
1968				else	error(_("odd number of quotation marks"));
1969		} while (*cp != '\0' && *cp != '#' &&
1970			(!isascii(*cp) || !isspace((unsigned char) *cp)));
1971		if (isascii(*cp) && isspace((unsigned char) *cp))
1972			++cp;
1973		*dp = '\0';
1974	}
1975	array[nsubs] = NULL;
1976	return array;
1977}
1978
1979static long
1980oadd(t1, t2)
1981const long	t1;
1982const long	t2;
1983{
1984	register long	t;
1985
1986	t = t1 + t2;
1987	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
1988		error(_("time overflow"));
1989		(void) exit(EXIT_FAILURE);
1990	}
1991	return t;
1992}
1993
1994static time_t
1995tadd(t1, t2)
1996const time_t	t1;
1997const long	t2;
1998{
1999	register time_t	t;
2000
2001	if (t1 == max_time && t2 > 0)
2002		return max_time;
2003	if (t1 == min_time && t2 < 0)
2004		return min_time;
2005	t = t1 + t2;
2006	if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
2007		error(_("time overflow"));
2008		(void) exit(EXIT_FAILURE);
2009	}
2010	return t;
2011}
2012
2013/*
2014** Given a rule, and a year, compute the date - in seconds since January 1,
2015** 1970, 00:00 LOCAL time - in that year that the rule refers to.
2016*/
2017
2018static time_t
2019rpytime(rp, wantedy)
2020register const struct rule * const	rp;
2021register const int			wantedy;
2022{
2023	register int	y, m, i;
2024	register long	dayoff;			/* with a nod to Margaret O. */
2025	register time_t	t;
2026
2027	if (wantedy == INT_MIN)
2028		return min_time;
2029	if (wantedy == INT_MAX)
2030		return max_time;
2031	dayoff = 0;
2032	m = TM_JANUARY;
2033	y = EPOCH_YEAR;
2034	while (wantedy != y) {
2035		if (wantedy > y) {
2036			i = len_years[isleap(y)];
2037			++y;
2038		} else {
2039			--y;
2040			i = -len_years[isleap(y)];
2041		}
2042		dayoff = oadd(dayoff, eitol(i));
2043	}
2044	while (m != rp->r_month) {
2045		i = len_months[isleap(y)][m];
2046		dayoff = oadd(dayoff, eitol(i));
2047		++m;
2048	}
2049	i = rp->r_dayofmonth;
2050	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
2051		if (rp->r_dycode == DC_DOWLEQ)
2052			--i;
2053		else {
2054			error(_("use of 2/29 in non leap-year"));
2055			(void) exit(EXIT_FAILURE);
2056		}
2057	}
2058	--i;
2059	dayoff = oadd(dayoff, eitol(i));
2060	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
2061		register long	wday;
2062
2063#define LDAYSPERWEEK	((long) DAYSPERWEEK)
2064		wday = eitol(EPOCH_WDAY);
2065		/*
2066		** Don't trust mod of negative numbers.
2067		*/
2068		if (dayoff >= 0)
2069			wday = (wday + dayoff) % LDAYSPERWEEK;
2070		else {
2071			wday -= ((-dayoff) % LDAYSPERWEEK);
2072			if (wday < 0)
2073				wday += LDAYSPERWEEK;
2074		}
2075		while (wday != eitol(rp->r_wday))
2076			if (rp->r_dycode == DC_DOWGEQ) {
2077				dayoff = oadd(dayoff, (long) 1);
2078				if (++wday >= LDAYSPERWEEK)
2079					wday = 0;
2080				++i;
2081			} else {
2082				dayoff = oadd(dayoff, (long) -1);
2083				if (--wday < 0)
2084					wday = LDAYSPERWEEK - 1;
2085				--i;
2086			}
2087		if (i < 0 || i >= len_months[isleap(y)][m]) {
2088			error(_("no day in month matches rule"));
2089			(void) exit(EXIT_FAILURE);
2090		}
2091	}
2092	if (dayoff < 0 && !TYPE_SIGNED(time_t))
2093		return min_time;
2094	t = (time_t) dayoff * SECSPERDAY;
2095	/*
2096	** Cheap overflow check.
2097	*/
2098	if (t / SECSPERDAY != dayoff)
2099		return (dayoff > 0) ? max_time : min_time;
2100	return tadd(t, rp->r_tod);
2101}
2102
2103static void
2104newabbr(string)
2105const char * const	string;
2106{
2107	register int	i;
2108
2109	i = strlen(string) + 1;
2110	if (charcnt + i > TZ_MAX_CHARS) {
2111		error(_("too many, or too long, time zone abbreviations"));
2112		(void) exit(EXIT_FAILURE);
2113	}
2114	(void) strcpy(&chars[charcnt], string);
2115	charcnt += eitol(i);
2116}
2117
2118static int
2119mkdirs(argname)
2120char * const	argname;
2121{
2122	register char *	name;
2123	register char *	cp;
2124
2125	if (argname == NULL || *argname == '\0' || Dflag)
2126		return 0;
2127	cp = name = ecpyalloc(argname);
2128	while ((cp = strchr(cp + 1, '/')) != 0) {
2129		*cp = '\0';
2130#ifndef unix
2131		/*
2132		** DOS drive specifier?
2133		*/
2134		if (isalpha((unsigned char) name[0]) &&
2135			name[1] == ':' && name[2] == '\0') {
2136				*cp = '/';
2137				continue;
2138		}
2139#endif /* !defined unix */
2140		if (!itsdir(name)) {
2141			/*
2142			** It doesn't seem to exist, so we try to create it.
2143			** Creation may fail because of the directory being
2144			** created by some other multiprocessor, so we get
2145			** to do extra checking.
2146			*/
2147			if (mkdir(name, (S_IRUSR | S_IWUSR | S_IXUSR
2148					 | S_IRGRP | S_IXGRP | S_IROTH
2149					 | S_IXOTH)) != 0
2150				&& (errno != EEXIST || !itsdir(name))) {
2151				warn(_("can't create directory %s"), name);
2152				ifree(name);
2153				return -1;
2154			}
2155		}
2156		*cp = '/';
2157	}
2158	ifree(name);
2159	return 0;
2160}
2161
2162static long
2163eitol(i)
2164const int	i;
2165{
2166	long	l;
2167
2168	l = i;
2169	if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
2170		errx(EXIT_FAILURE, _("%d did not sign extend correctly"), i);
2171	return l;
2172}
2173
2174#include <grp.h>
2175#include <pwd.h>
2176
2177static void
2178setgroup(flag, name)
2179	gid_t *flag;
2180	const char *name;
2181{
2182	struct group *gr;
2183
2184	if (*flag != (gid_t)-1)
2185		errx(EXIT_FAILURE, _("multiple -g flags specified"));
2186
2187	gr = getgrnam(name);
2188	if (gr == 0) {
2189		char *ep;
2190		unsigned long ul;
2191
2192		ul = strtoul(name, &ep, 10);
2193		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
2194			*flag = ul;
2195			return;
2196		}
2197		errx(EXIT_FAILURE, _("group `%s' not found"), name);
2198	}
2199	*flag = gr->gr_gid;
2200}
2201
2202static void
2203setuser(flag, name)
2204	uid_t *flag;
2205	const char *name;
2206{
2207	struct passwd *pw;
2208
2209	if (*flag != (gid_t)-1)
2210		errx(EXIT_FAILURE, _("multiple -u flags specified"));
2211
2212	pw = getpwnam(name);
2213	if (pw == 0) {
2214		char *ep;
2215		unsigned long ul;
2216
2217		ul = strtoul(name, &ep, 10);
2218		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
2219			*flag = ul;
2220			return;
2221		}
2222		errx(EXIT_FAILURE, _("user `%s' not found"), name);
2223	}
2224	*flag = pw->pw_uid;
2225}
2226
2227/*
2228** UNIX was a registered trademark of UNIX System Laboratories in 1993.
2229*/
2230