xref: /freebsd/contrib/tzcode/zic.c (revision a979394afeb5c20fc58c5f5b005d51eb8f92f666)
1bc421551SDag-Erling Smørgrav /* Compile .zi time zone data into TZif binary files.  */
2bc421551SDag-Erling Smørgrav 
3bc421551SDag-Erling Smørgrav /*
4bc421551SDag-Erling Smørgrav ** This file is in the public domain, so clarified as of
5bc421551SDag-Erling Smørgrav ** 2006-07-17 by Arthur David Olson.
6bc421551SDag-Erling Smørgrav */
7bc421551SDag-Erling Smørgrav 
8bc421551SDag-Erling Smørgrav /* Use the system 'time' function, instead of any private replacement.
9bc421551SDag-Erling Smørgrav    This avoids creating an unnecessary dependency on localtime.c.  */
10bc421551SDag-Erling Smørgrav #undef EPOCH_LOCAL
11bc421551SDag-Erling Smørgrav #undef EPOCH_OFFSET
12bc421551SDag-Erling Smørgrav #undef RESERVE_STD_EXT_IDS
13bc421551SDag-Erling Smørgrav #undef time_tz
14bc421551SDag-Erling Smørgrav 
15bc421551SDag-Erling Smørgrav #include "version.h"
16bc421551SDag-Erling Smørgrav #include "private.h"
1746c59934SDag-Erling Smørgrav #include "tzdir.h"
18bc421551SDag-Erling Smørgrav #include "tzfile.h"
19bc421551SDag-Erling Smørgrav 
20bc421551SDag-Erling Smørgrav #include <fcntl.h>
21bc421551SDag-Erling Smørgrav #include <locale.h>
22bc421551SDag-Erling Smørgrav #include <signal.h>
23bc421551SDag-Erling Smørgrav #include <stdarg.h>
24bc421551SDag-Erling Smørgrav #include <stdio.h>
25bc421551SDag-Erling Smørgrav 
26bc421551SDag-Erling Smørgrav typedef int_fast64_t	zic_t;
27bc421551SDag-Erling Smørgrav static zic_t const
28bc421551SDag-Erling Smørgrav   ZIC_MIN = INT_FAST64_MIN,
29bc421551SDag-Erling Smørgrav   ZIC_MAX = INT_FAST64_MAX,
30bc421551SDag-Erling Smørgrav   ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
31bc421551SDag-Erling Smørgrav   ZIC32_MAX = 0x7fffffff;
32bc421551SDag-Erling Smørgrav #define SCNdZIC SCNdFAST64
33bc421551SDag-Erling Smørgrav 
34bc421551SDag-Erling Smørgrav #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
35bc421551SDag-Erling Smørgrav # define ZIC_MAX_ABBR_LEN_WO_WARN 6
36bc421551SDag-Erling Smørgrav #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
37bc421551SDag-Erling Smørgrav 
3846c59934SDag-Erling Smørgrav /* Minimum and maximum years, assuming signed 32-bit time_t.  */
3946c59934SDag-Erling Smørgrav enum { YEAR_32BIT_MIN = 1901, YEAR_32BIT_MAX = 2038 };
4046c59934SDag-Erling Smørgrav 
41bc421551SDag-Erling Smørgrav /* An upper bound on how much a format might grow due to concatenation.  */
42bc421551SDag-Erling Smørgrav enum { FORMAT_LEN_GROWTH_BOUND = 5 };
43bc421551SDag-Erling Smørgrav 
44bc421551SDag-Erling Smørgrav #ifdef HAVE_DIRECT_H
45bc421551SDag-Erling Smørgrav # include <direct.h>
46bc421551SDag-Erling Smørgrav # include <io.h>
47bc421551SDag-Erling Smørgrav # undef mkdir
48bc421551SDag-Erling Smørgrav # define mkdir(name, mode) _mkdir(name)
49bc421551SDag-Erling Smørgrav #endif
50bc421551SDag-Erling Smørgrav 
51bc421551SDag-Erling Smørgrav #ifndef HAVE_GETRANDOM
52bc421551SDag-Erling Smørgrav # ifdef __has_include
53bc421551SDag-Erling Smørgrav #  if __has_include(<sys/random.h>)
54bc421551SDag-Erling Smørgrav #   include <sys/random.h>
55bc421551SDag-Erling Smørgrav #  endif
56bc421551SDag-Erling Smørgrav # elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__)
57bc421551SDag-Erling Smørgrav #  include <sys/random.h>
58bc421551SDag-Erling Smørgrav # endif
59bc421551SDag-Erling Smørgrav # define HAVE_GETRANDOM GRND_RANDOM
60bc421551SDag-Erling Smørgrav #elif HAVE_GETRANDOM
61bc421551SDag-Erling Smørgrav # include <sys/random.h>
62bc421551SDag-Erling Smørgrav #endif
63bc421551SDag-Erling Smørgrav 
64bc421551SDag-Erling Smørgrav #if HAVE_SYS_STAT_H
65bc421551SDag-Erling Smørgrav # include <sys/stat.h>
66bc421551SDag-Erling Smørgrav #endif
67bc421551SDag-Erling Smørgrav #ifdef S_IRUSR
68bc421551SDag-Erling Smørgrav # define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
69bc421551SDag-Erling Smørgrav #else
70bc421551SDag-Erling Smørgrav # define MKDIR_UMASK 0755
71bc421551SDag-Erling Smørgrav #endif
72bc421551SDag-Erling Smørgrav 
7375411d15SDag-Erling Smørgrav /* The minimum alignment of a type, for pre-C23 platforms.
7475411d15SDag-Erling Smørgrav    The __SUNPRO_C test is because Oracle Developer Studio 12.6 lacks
7575411d15SDag-Erling Smørgrav    <stdalign.h> even though __STDC_VERSION__ == 201112.  */
7675411d15SDag-Erling Smørgrav #if __STDC_VERSION__ < 201112 || defined __SUNPRO_C
77bc421551SDag-Erling Smørgrav # define alignof(type) offsetof(struct { char a; type b; }, b)
78bc421551SDag-Erling Smørgrav #elif __STDC_VERSION__ < 202311
79bc421551SDag-Erling Smørgrav # include <stdalign.h>
80bc421551SDag-Erling Smørgrav #endif
81bc421551SDag-Erling Smørgrav 
82bc421551SDag-Erling Smørgrav /* The maximum length of a text line, including the trailing newline.  */
83bc421551SDag-Erling Smørgrav #ifndef _POSIX2_LINE_MAX
84bc421551SDag-Erling Smørgrav # define _POSIX2_LINE_MAX 2048
85bc421551SDag-Erling Smørgrav #endif
86bc421551SDag-Erling Smørgrav 
87bc421551SDag-Erling Smørgrav /* The type for line numbers.  Use PRIdMAX to format them; formerly
88bc421551SDag-Erling Smørgrav    there was also "#define PRIdLINENO PRIdMAX" and formats used
89bc421551SDag-Erling Smørgrav    PRIdLINENO, but xgettext cannot grok that.  */
90bc421551SDag-Erling Smørgrav typedef intmax_t lineno;
91bc421551SDag-Erling Smørgrav 
92bc421551SDag-Erling Smørgrav struct rule {
93bc421551SDag-Erling Smørgrav 	int		r_filenum;
94bc421551SDag-Erling Smørgrav 	lineno		r_linenum;
95bc421551SDag-Erling Smørgrav 	const char *	r_name;
96bc421551SDag-Erling Smørgrav 
97bc421551SDag-Erling Smørgrav 	zic_t		r_loyear;	/* for example, 1986 */
98bc421551SDag-Erling Smørgrav 	zic_t		r_hiyear;	/* for example, 1986 */
99bc421551SDag-Erling Smørgrav 	bool		r_hiwasnum;
100bc421551SDag-Erling Smørgrav 
101bc421551SDag-Erling Smørgrav 	int		r_month;	/* 0..11 */
102bc421551SDag-Erling Smørgrav 
103bc421551SDag-Erling Smørgrav 	int		r_dycode;	/* see below */
104bc421551SDag-Erling Smørgrav 	int		r_dayofmonth;
105bc421551SDag-Erling Smørgrav 	int		r_wday;
106bc421551SDag-Erling Smørgrav 
107bc421551SDag-Erling Smørgrav 	zic_t		r_tod;		/* time from midnight */
108bc421551SDag-Erling Smørgrav 	bool		r_todisstd;	/* is r_tod standard time? */
109bc421551SDag-Erling Smørgrav 	bool		r_todisut;	/* is r_tod UT? */
110bc421551SDag-Erling Smørgrav 	bool		r_isdst;	/* is this daylight saving time? */
111bc421551SDag-Erling Smørgrav 	zic_t		r_save;		/* offset from standard time */
112bc421551SDag-Erling Smørgrav 	const char *	r_abbrvar;	/* variable part of abbreviation */
113bc421551SDag-Erling Smørgrav 
114bc421551SDag-Erling Smørgrav 	bool		r_todo;		/* a rule to do (used in outzone) */
115bc421551SDag-Erling Smørgrav 	zic_t		r_temp;		/* used in outzone */
116bc421551SDag-Erling Smørgrav };
117bc421551SDag-Erling Smørgrav 
118bc421551SDag-Erling Smørgrav /*
119bc421551SDag-Erling Smørgrav ** r_dycode	r_dayofmonth	r_wday
120bc421551SDag-Erling Smørgrav */
121bc421551SDag-Erling Smørgrav enum {
122bc421551SDag-Erling Smørgrav   DC_DOM,	/* 1..31 */	/* unused */
123bc421551SDag-Erling Smørgrav   DC_DOWGEQ,	/* 1..31 */	/* 0..6 (Sun..Sat) */
124bc421551SDag-Erling Smørgrav   DC_DOWLEQ	/* 1..31 */	/* 0..6 (Sun..Sat) */
125bc421551SDag-Erling Smørgrav };
126bc421551SDag-Erling Smørgrav 
127bc421551SDag-Erling Smørgrav struct zone {
128bc421551SDag-Erling Smørgrav 	int		z_filenum;
129bc421551SDag-Erling Smørgrav 	lineno		z_linenum;
130bc421551SDag-Erling Smørgrav 
131bc421551SDag-Erling Smørgrav 	const char *	z_name;
132bc421551SDag-Erling Smørgrav 	zic_t		z_stdoff;
133bc421551SDag-Erling Smørgrav 	char *		z_rule;
134bc421551SDag-Erling Smørgrav 	const char *	z_format;
135bc421551SDag-Erling Smørgrav 	char		z_format_specifier;
136bc421551SDag-Erling Smørgrav 
137bc421551SDag-Erling Smørgrav 	bool		z_isdst;
138bc421551SDag-Erling Smørgrav 	zic_t		z_save;
139bc421551SDag-Erling Smørgrav 
140bc421551SDag-Erling Smørgrav 	struct rule *	z_rules;
141bc421551SDag-Erling Smørgrav 	ptrdiff_t	z_nrules;
142bc421551SDag-Erling Smørgrav 
143bc421551SDag-Erling Smørgrav 	struct rule	z_untilrule;
144bc421551SDag-Erling Smørgrav 	zic_t		z_untiltime;
145bc421551SDag-Erling Smørgrav };
146bc421551SDag-Erling Smørgrav 
147bc421551SDag-Erling Smørgrav #if !HAVE_POSIX_DECLS
148bc421551SDag-Erling Smørgrav extern int	getopt(int argc, char * const argv[],
149bc421551SDag-Erling Smørgrav 			const char * options);
150bc421551SDag-Erling Smørgrav extern int	link(const char * target, const char * linkname);
151bc421551SDag-Erling Smørgrav extern char *	optarg;
152bc421551SDag-Erling Smørgrav extern int	optind;
153bc421551SDag-Erling Smørgrav #endif
154bc421551SDag-Erling Smørgrav 
155bc421551SDag-Erling Smørgrav #if ! HAVE_SYMLINK
156bc421551SDag-Erling Smørgrav static ssize_t
157bc421551SDag-Erling Smørgrav readlink(char const *restrict file, char *restrict buf, size_t size)
158bc421551SDag-Erling Smørgrav {
159bc421551SDag-Erling Smørgrav   errno = ENOTSUP;
160bc421551SDag-Erling Smørgrav   return -1;
161bc421551SDag-Erling Smørgrav }
162bc421551SDag-Erling Smørgrav static int
163bc421551SDag-Erling Smørgrav symlink(char const *target, char const *linkname)
164bc421551SDag-Erling Smørgrav {
165bc421551SDag-Erling Smørgrav   errno = ENOTSUP;
166bc421551SDag-Erling Smørgrav   return -1;
167bc421551SDag-Erling Smørgrav }
168bc421551SDag-Erling Smørgrav #endif
169bc421551SDag-Erling Smørgrav #ifndef AT_SYMLINK_FOLLOW
170bc421551SDag-Erling Smørgrav #  define linkat(targetdir, target, linknamedir, linkname, flag) \
171bc421551SDag-Erling Smørgrav      (errno = ENOTSUP, -1)
172bc421551SDag-Erling Smørgrav #endif
173bc421551SDag-Erling Smørgrav 
174bc421551SDag-Erling Smørgrav static void	addtt(zic_t starttime, int type);
175bc421551SDag-Erling Smørgrav static int	addtype(zic_t, char const *, bool, bool, bool);
176bc421551SDag-Erling Smørgrav static void	leapadd(zic_t, int, int);
177bc421551SDag-Erling Smørgrav static void	adjleap(void);
178bc421551SDag-Erling Smørgrav static void	associate(void);
179bc421551SDag-Erling Smørgrav static void	dolink(const char *, const char *, bool);
180bc421551SDag-Erling Smørgrav static int	getfields(char *, char **, int);
181bc421551SDag-Erling Smørgrav static zic_t	gethms(const char * string, const char * errstring);
182bc421551SDag-Erling Smørgrav static zic_t	getsave(char *, bool *);
183bc421551SDag-Erling Smørgrav static void	inexpires(char **, int);
184bc421551SDag-Erling Smørgrav static void	infile(int, char const *);
185bc421551SDag-Erling Smørgrav static void	inleap(char ** fields, int nfields);
186bc421551SDag-Erling Smørgrav static void	inlink(char ** fields, int nfields);
187bc421551SDag-Erling Smørgrav static void	inrule(char ** fields, int nfields);
188bc421551SDag-Erling Smørgrav static bool	inzcont(char ** fields, int nfields);
189bc421551SDag-Erling Smørgrav static bool	inzone(char ** fields, int nfields);
190bc421551SDag-Erling Smørgrav static bool	inzsub(char **, int, bool);
19146c59934SDag-Erling Smørgrav static int	itssymlink(char const *, int *);
192bc421551SDag-Erling Smørgrav static bool	is_alpha(char a);
193bc421551SDag-Erling Smørgrav static char	lowerit(char);
194bc421551SDag-Erling Smørgrav static void	mkdirs(char const *, bool);
195bc421551SDag-Erling Smørgrav static void	newabbr(const char * abbr);
196bc421551SDag-Erling Smørgrav static zic_t	oadd(zic_t t1, zic_t t2);
197bc421551SDag-Erling Smørgrav static void	outzone(const struct zone * zp, ptrdiff_t ntzones);
198bc421551SDag-Erling Smørgrav static zic_t	rpytime(const struct rule * rp, zic_t wantedy);
199bc421551SDag-Erling Smørgrav static bool	rulesub(struct rule * rp,
200bc421551SDag-Erling Smørgrav 			const char * loyearp, const char * hiyearp,
201bc421551SDag-Erling Smørgrav 			const char * typep, const char * monthp,
202bc421551SDag-Erling Smørgrav 			const char * dayp, const char * timep);
203bc421551SDag-Erling Smørgrav static void	setgroup(gid_t *flag, const char *name);
204bc421551SDag-Erling Smørgrav static void	setuser(uid_t *flag, const char *name);
205bc421551SDag-Erling Smørgrav static zic_t	tadd(zic_t t1, zic_t t2);
206bc421551SDag-Erling Smørgrav 
207bc421551SDag-Erling Smørgrav /* Bound on length of what %z can expand to.  */
208bc421551SDag-Erling Smørgrav enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
209bc421551SDag-Erling Smørgrav 
210bc421551SDag-Erling Smørgrav static int		charcnt;
211bc421551SDag-Erling Smørgrav static bool		errors;
212bc421551SDag-Erling Smørgrav static bool		warnings;
213bc421551SDag-Erling Smørgrav static int		filenum;
214bc421551SDag-Erling Smørgrav static int		leapcnt;
215bc421551SDag-Erling Smørgrav static bool		leapseen;
216bc421551SDag-Erling Smørgrav static zic_t		leapminyear;
217bc421551SDag-Erling Smørgrav static zic_t		leapmaxyear;
218bc421551SDag-Erling Smørgrav static lineno		linenum;
219bc421551SDag-Erling Smørgrav static size_t		max_abbrvar_len = PERCENT_Z_LEN_BOUND;
220bc421551SDag-Erling Smørgrav static int		max_format_len;
221bc421551SDag-Erling Smørgrav static zic_t		max_year;
222bc421551SDag-Erling Smørgrav static zic_t		min_year;
223bc421551SDag-Erling Smørgrav static bool		noise;
224bc421551SDag-Erling Smørgrav static int		rfilenum;
225bc421551SDag-Erling Smørgrav static lineno		rlinenum;
226bc421551SDag-Erling Smørgrav static const char *	progname;
227bc421551SDag-Erling Smørgrav static char const *	leapsec;
228bc421551SDag-Erling Smørgrav static char *const *	main_argv;
229bc421551SDag-Erling Smørgrav static ptrdiff_t	timecnt;
230bc421551SDag-Erling Smørgrav static ptrdiff_t	timecnt_alloc;
231bc421551SDag-Erling Smørgrav static int		typecnt;
232bc421551SDag-Erling Smørgrav static int		unspecifiedtype;
233bc421551SDag-Erling Smørgrav 
234bc421551SDag-Erling Smørgrav /*
235bc421551SDag-Erling Smørgrav ** Line codes.
236bc421551SDag-Erling Smørgrav */
237bc421551SDag-Erling Smørgrav 
238bc421551SDag-Erling Smørgrav enum {
239bc421551SDag-Erling Smørgrav   LC_RULE,
240bc421551SDag-Erling Smørgrav   LC_ZONE,
241bc421551SDag-Erling Smørgrav   LC_LINK,
242bc421551SDag-Erling Smørgrav   LC_LEAP,
243bc421551SDag-Erling Smørgrav   LC_EXPIRES
244bc421551SDag-Erling Smørgrav };
245bc421551SDag-Erling Smørgrav 
246bc421551SDag-Erling Smørgrav /*
247bc421551SDag-Erling Smørgrav ** Which fields are which on a Zone line.
248bc421551SDag-Erling Smørgrav */
249bc421551SDag-Erling Smørgrav 
250bc421551SDag-Erling Smørgrav enum {
251bc421551SDag-Erling Smørgrav   ZF_NAME = 1,
252bc421551SDag-Erling Smørgrav   ZF_STDOFF,
253bc421551SDag-Erling Smørgrav   ZF_RULE,
254bc421551SDag-Erling Smørgrav   ZF_FORMAT,
255bc421551SDag-Erling Smørgrav   ZF_TILYEAR,
256bc421551SDag-Erling Smørgrav   ZF_TILMONTH,
257bc421551SDag-Erling Smørgrav   ZF_TILDAY,
258bc421551SDag-Erling Smørgrav   ZF_TILTIME,
259bc421551SDag-Erling Smørgrav   ZONE_MAXFIELDS,
260bc421551SDag-Erling Smørgrav   ZONE_MINFIELDS = ZF_TILYEAR
261bc421551SDag-Erling Smørgrav };
262bc421551SDag-Erling Smørgrav 
263bc421551SDag-Erling Smørgrav /*
264bc421551SDag-Erling Smørgrav ** Which fields are which on a Zone continuation line.
265bc421551SDag-Erling Smørgrav */
266bc421551SDag-Erling Smørgrav 
267bc421551SDag-Erling Smørgrav enum {
268bc421551SDag-Erling Smørgrav   ZFC_STDOFF,
269bc421551SDag-Erling Smørgrav   ZFC_RULE,
270bc421551SDag-Erling Smørgrav   ZFC_FORMAT,
271bc421551SDag-Erling Smørgrav   ZFC_TILYEAR,
272bc421551SDag-Erling Smørgrav   ZFC_TILMONTH,
273bc421551SDag-Erling Smørgrav   ZFC_TILDAY,
274bc421551SDag-Erling Smørgrav   ZFC_TILTIME,
275bc421551SDag-Erling Smørgrav   ZONEC_MAXFIELDS,
276bc421551SDag-Erling Smørgrav   ZONEC_MINFIELDS = ZFC_TILYEAR
277bc421551SDag-Erling Smørgrav };
278bc421551SDag-Erling Smørgrav 
279bc421551SDag-Erling Smørgrav /*
280bc421551SDag-Erling Smørgrav ** Which files are which on a Rule line.
281bc421551SDag-Erling Smørgrav */
282bc421551SDag-Erling Smørgrav 
283bc421551SDag-Erling Smørgrav enum {
284bc421551SDag-Erling Smørgrav   RF_NAME = 1,
285bc421551SDag-Erling Smørgrav   RF_LOYEAR,
286bc421551SDag-Erling Smørgrav   RF_HIYEAR,
287bc421551SDag-Erling Smørgrav   RF_COMMAND,
288bc421551SDag-Erling Smørgrav   RF_MONTH,
289bc421551SDag-Erling Smørgrav   RF_DAY,
290bc421551SDag-Erling Smørgrav   RF_TOD,
291bc421551SDag-Erling Smørgrav   RF_SAVE,
292bc421551SDag-Erling Smørgrav   RF_ABBRVAR,
293bc421551SDag-Erling Smørgrav   RULE_FIELDS
294bc421551SDag-Erling Smørgrav };
295bc421551SDag-Erling Smørgrav 
296bc421551SDag-Erling Smørgrav /*
297bc421551SDag-Erling Smørgrav ** Which fields are which on a Link line.
298bc421551SDag-Erling Smørgrav */
299bc421551SDag-Erling Smørgrav 
300bc421551SDag-Erling Smørgrav enum {
301bc421551SDag-Erling Smørgrav   LF_TARGET = 1,
302bc421551SDag-Erling Smørgrav   LF_LINKNAME,
303bc421551SDag-Erling Smørgrav   LINK_FIELDS
304bc421551SDag-Erling Smørgrav };
305bc421551SDag-Erling Smørgrav 
306bc421551SDag-Erling Smørgrav /*
307bc421551SDag-Erling Smørgrav ** Which fields are which on a Leap line.
308bc421551SDag-Erling Smørgrav */
309bc421551SDag-Erling Smørgrav 
310bc421551SDag-Erling Smørgrav enum {
311bc421551SDag-Erling Smørgrav   LP_YEAR = 1,
312bc421551SDag-Erling Smørgrav   LP_MONTH,
313bc421551SDag-Erling Smørgrav   LP_DAY,
314bc421551SDag-Erling Smørgrav   LP_TIME,
315bc421551SDag-Erling Smørgrav   LP_CORR,
316bc421551SDag-Erling Smørgrav   LP_ROLL,
317bc421551SDag-Erling Smørgrav   LEAP_FIELDS,
318bc421551SDag-Erling Smørgrav 
319bc421551SDag-Erling Smørgrav   /* Expires lines are like Leap lines, except without CORR and ROLL fields.  */
320bc421551SDag-Erling Smørgrav   EXPIRES_FIELDS = LP_TIME + 1
321bc421551SDag-Erling Smørgrav };
322bc421551SDag-Erling Smørgrav 
323bc421551SDag-Erling Smørgrav /* The maximum number of fields on any of the above lines.
324bc421551SDag-Erling Smørgrav    (The "+"s pacify gcc -Wenum-compare.)  */
325bc421551SDag-Erling Smørgrav enum {
326bc421551SDag-Erling Smørgrav   MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
327bc421551SDag-Erling Smørgrav 		   max(+LEAP_FIELDS, +EXPIRES_FIELDS))
328bc421551SDag-Erling Smørgrav };
329bc421551SDag-Erling Smørgrav 
330bc421551SDag-Erling Smørgrav /*
331bc421551SDag-Erling Smørgrav ** Year synonyms.
332bc421551SDag-Erling Smørgrav */
333bc421551SDag-Erling Smørgrav 
334bc421551SDag-Erling Smørgrav enum {
33546c59934SDag-Erling Smørgrav   YR_MINIMUM, /* "minimum" is for backward compatibility only */
336bc421551SDag-Erling Smørgrav   YR_MAXIMUM,
337bc421551SDag-Erling Smørgrav   YR_ONLY
338bc421551SDag-Erling Smørgrav };
339bc421551SDag-Erling Smørgrav 
340bc421551SDag-Erling Smørgrav static struct rule *	rules;
341bc421551SDag-Erling Smørgrav static ptrdiff_t	nrules;	/* number of rules */
342bc421551SDag-Erling Smørgrav static ptrdiff_t	nrules_alloc;
343bc421551SDag-Erling Smørgrav 
344bc421551SDag-Erling Smørgrav static struct zone *	zones;
345bc421551SDag-Erling Smørgrav static ptrdiff_t	nzones;	/* number of zones */
346bc421551SDag-Erling Smørgrav static ptrdiff_t	nzones_alloc;
347bc421551SDag-Erling Smørgrav 
348bc421551SDag-Erling Smørgrav struct link {
349bc421551SDag-Erling Smørgrav 	int		l_filenum;
350bc421551SDag-Erling Smørgrav 	lineno		l_linenum;
351bc421551SDag-Erling Smørgrav 	const char *	l_target;
352bc421551SDag-Erling Smørgrav 	const char *	l_linkname;
353bc421551SDag-Erling Smørgrav };
354bc421551SDag-Erling Smørgrav 
355bc421551SDag-Erling Smørgrav static struct link *	links;
356bc421551SDag-Erling Smørgrav static ptrdiff_t	nlinks;
357bc421551SDag-Erling Smørgrav static ptrdiff_t	nlinks_alloc;
358bc421551SDag-Erling Smørgrav 
359bc421551SDag-Erling Smørgrav struct lookup {
360bc421551SDag-Erling Smørgrav 	const char *	l_word;
361bc421551SDag-Erling Smørgrav 	const int	l_value;
362bc421551SDag-Erling Smørgrav };
363bc421551SDag-Erling Smørgrav 
364bc421551SDag-Erling Smørgrav static struct lookup const *	byword(const char * string,
365bc421551SDag-Erling Smørgrav 					const struct lookup * lp);
366bc421551SDag-Erling Smørgrav 
367bc421551SDag-Erling Smørgrav static struct lookup const zi_line_codes[] = {
368bc421551SDag-Erling Smørgrav 	{ "Rule",	LC_RULE },
369bc421551SDag-Erling Smørgrav 	{ "Zone",	LC_ZONE },
370bc421551SDag-Erling Smørgrav 	{ "Link",	LC_LINK },
371bc421551SDag-Erling Smørgrav 	{ NULL,		0 }
372bc421551SDag-Erling Smørgrav };
373bc421551SDag-Erling Smørgrav static struct lookup const leap_line_codes[] = {
374bc421551SDag-Erling Smørgrav 	{ "Leap",	LC_LEAP },
375bc421551SDag-Erling Smørgrav 	{ "Expires",	LC_EXPIRES },
376bc421551SDag-Erling Smørgrav 	{ NULL,		0}
377bc421551SDag-Erling Smørgrav };
378bc421551SDag-Erling Smørgrav 
379bc421551SDag-Erling Smørgrav static struct lookup const	mon_names[] = {
380bc421551SDag-Erling Smørgrav 	{ "January",	TM_JANUARY },
381bc421551SDag-Erling Smørgrav 	{ "February",	TM_FEBRUARY },
382bc421551SDag-Erling Smørgrav 	{ "March",	TM_MARCH },
383bc421551SDag-Erling Smørgrav 	{ "April",	TM_APRIL },
384bc421551SDag-Erling Smørgrav 	{ "May",	TM_MAY },
385bc421551SDag-Erling Smørgrav 	{ "June",	TM_JUNE },
386bc421551SDag-Erling Smørgrav 	{ "July",	TM_JULY },
387bc421551SDag-Erling Smørgrav 	{ "August",	TM_AUGUST },
388bc421551SDag-Erling Smørgrav 	{ "September",	TM_SEPTEMBER },
389bc421551SDag-Erling Smørgrav 	{ "October",	TM_OCTOBER },
390bc421551SDag-Erling Smørgrav 	{ "November",	TM_NOVEMBER },
391bc421551SDag-Erling Smørgrav 	{ "December",	TM_DECEMBER },
392bc421551SDag-Erling Smørgrav 	{ NULL,		0 }
393bc421551SDag-Erling Smørgrav };
394bc421551SDag-Erling Smørgrav 
395bc421551SDag-Erling Smørgrav static struct lookup const	wday_names[] = {
396bc421551SDag-Erling Smørgrav 	{ "Sunday",	TM_SUNDAY },
397bc421551SDag-Erling Smørgrav 	{ "Monday",	TM_MONDAY },
398bc421551SDag-Erling Smørgrav 	{ "Tuesday",	TM_TUESDAY },
399bc421551SDag-Erling Smørgrav 	{ "Wednesday",	TM_WEDNESDAY },
400bc421551SDag-Erling Smørgrav 	{ "Thursday",	TM_THURSDAY },
401bc421551SDag-Erling Smørgrav 	{ "Friday",	TM_FRIDAY },
402bc421551SDag-Erling Smørgrav 	{ "Saturday",	TM_SATURDAY },
403bc421551SDag-Erling Smørgrav 	{ NULL,		0 }
404bc421551SDag-Erling Smørgrav };
405bc421551SDag-Erling Smørgrav 
406bc421551SDag-Erling Smørgrav static struct lookup const	lasts[] = {
407bc421551SDag-Erling Smørgrav 	{ "last-Sunday",	TM_SUNDAY },
408bc421551SDag-Erling Smørgrav 	{ "last-Monday",	TM_MONDAY },
409bc421551SDag-Erling Smørgrav 	{ "last-Tuesday",	TM_TUESDAY },
410bc421551SDag-Erling Smørgrav 	{ "last-Wednesday",	TM_WEDNESDAY },
411bc421551SDag-Erling Smørgrav 	{ "last-Thursday",	TM_THURSDAY },
412bc421551SDag-Erling Smørgrav 	{ "last-Friday",	TM_FRIDAY },
413bc421551SDag-Erling Smørgrav 	{ "last-Saturday",	TM_SATURDAY },
414bc421551SDag-Erling Smørgrav 	{ NULL,			0 }
415bc421551SDag-Erling Smørgrav };
416bc421551SDag-Erling Smørgrav 
417bc421551SDag-Erling Smørgrav static struct lookup const	begin_years[] = {
418bc421551SDag-Erling Smørgrav 	{ "minimum",	YR_MINIMUM },
419bc421551SDag-Erling Smørgrav 	{ NULL,		0 }
420bc421551SDag-Erling Smørgrav };
421bc421551SDag-Erling Smørgrav 
422bc421551SDag-Erling Smørgrav static struct lookup const	end_years[] = {
423bc421551SDag-Erling Smørgrav 	{ "maximum",	YR_MAXIMUM },
424bc421551SDag-Erling Smørgrav 	{ "only",	YR_ONLY },
425bc421551SDag-Erling Smørgrav 	{ NULL,		0 }
426bc421551SDag-Erling Smørgrav };
427bc421551SDag-Erling Smørgrav 
428bc421551SDag-Erling Smørgrav static struct lookup const	leap_types[] = {
429bc421551SDag-Erling Smørgrav 	{ "Rolling",	true },
430bc421551SDag-Erling Smørgrav 	{ "Stationary",	false },
431bc421551SDag-Erling Smørgrav 	{ NULL,		0 }
432bc421551SDag-Erling Smørgrav };
433bc421551SDag-Erling Smørgrav 
434bc421551SDag-Erling Smørgrav static const int	len_months[2][MONSPERYEAR] = {
435bc421551SDag-Erling Smørgrav 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
436bc421551SDag-Erling Smørgrav 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
437bc421551SDag-Erling Smørgrav };
438bc421551SDag-Erling Smørgrav 
439bc421551SDag-Erling Smørgrav static const int	len_years[2] = {
440bc421551SDag-Erling Smørgrav 	DAYSPERNYEAR, DAYSPERLYEAR
441bc421551SDag-Erling Smørgrav };
442bc421551SDag-Erling Smørgrav 
443bc421551SDag-Erling Smørgrav static struct attype {
444bc421551SDag-Erling Smørgrav 	zic_t		at;
445bc421551SDag-Erling Smørgrav 	bool		dontmerge;
446bc421551SDag-Erling Smørgrav 	unsigned char	type;
447bc421551SDag-Erling Smørgrav } *			attypes;
448bc421551SDag-Erling Smørgrav static zic_t		utoffs[TZ_MAX_TYPES];
449bc421551SDag-Erling Smørgrav static char		isdsts[TZ_MAX_TYPES];
450bc421551SDag-Erling Smørgrav static unsigned char	desigidx[TZ_MAX_TYPES];
451bc421551SDag-Erling Smørgrav static bool		ttisstds[TZ_MAX_TYPES];
452bc421551SDag-Erling Smørgrav static bool		ttisuts[TZ_MAX_TYPES];
453bc421551SDag-Erling Smørgrav static char		chars[TZ_MAX_CHARS];
454bc421551SDag-Erling Smørgrav static zic_t		trans[TZ_MAX_LEAPS];
455bc421551SDag-Erling Smørgrav static zic_t		corr[TZ_MAX_LEAPS];
456bc421551SDag-Erling Smørgrav static char		roll[TZ_MAX_LEAPS];
457bc421551SDag-Erling Smørgrav 
458bc421551SDag-Erling Smørgrav /*
459bc421551SDag-Erling Smørgrav ** Memory allocation.
460bc421551SDag-Erling Smørgrav */
461bc421551SDag-Erling Smørgrav 
46275411d15SDag-Erling Smørgrav ATTRIBUTE_NORETURN static void
463bc421551SDag-Erling Smørgrav memory_exhausted(const char *msg)
464bc421551SDag-Erling Smørgrav {
465bc421551SDag-Erling Smørgrav 	fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
466bc421551SDag-Erling Smørgrav 	exit(EXIT_FAILURE);
467bc421551SDag-Erling Smørgrav }
468bc421551SDag-Erling Smørgrav 
46975411d15SDag-Erling Smørgrav ATTRIBUTE_NORETURN static void
470bc421551SDag-Erling Smørgrav size_overflow(void)
471bc421551SDag-Erling Smørgrav {
472bc421551SDag-Erling Smørgrav   memory_exhausted(_("size overflow"));
473bc421551SDag-Erling Smørgrav }
474bc421551SDag-Erling Smørgrav 
475*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static ptrdiff_t
476bc421551SDag-Erling Smørgrav size_sum(size_t a, size_t b)
477bc421551SDag-Erling Smørgrav {
478bc421551SDag-Erling Smørgrav #ifdef ckd_add
47975411d15SDag-Erling Smørgrav   ptrdiff_t sum;
48075411d15SDag-Erling Smørgrav   if (!ckd_add(&sum, a, b) && sum <= INDEX_MAX)
481bc421551SDag-Erling Smørgrav     return sum;
482bc421551SDag-Erling Smørgrav #else
48375411d15SDag-Erling Smørgrav   if (a <= INDEX_MAX && b <= INDEX_MAX - a)
484bc421551SDag-Erling Smørgrav     return a + b;
485bc421551SDag-Erling Smørgrav #endif
486bc421551SDag-Erling Smørgrav   size_overflow();
487bc421551SDag-Erling Smørgrav }
488bc421551SDag-Erling Smørgrav 
489*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static ptrdiff_t
49075411d15SDag-Erling Smørgrav size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
491bc421551SDag-Erling Smørgrav {
492bc421551SDag-Erling Smørgrav #ifdef ckd_mul
49375411d15SDag-Erling Smørgrav   ptrdiff_t product;
49475411d15SDag-Erling Smørgrav   if (!ckd_mul(&product, nitems, itemsize) && product <= INDEX_MAX)
495bc421551SDag-Erling Smørgrav     return product;
496bc421551SDag-Erling Smørgrav #else
49775411d15SDag-Erling Smørgrav   ptrdiff_t nitems_max = INDEX_MAX / itemsize;
49875411d15SDag-Erling Smørgrav   if (nitems <= nitems_max)
499bc421551SDag-Erling Smørgrav     return nitems * itemsize;
500bc421551SDag-Erling Smørgrav #endif
501bc421551SDag-Erling Smørgrav   size_overflow();
502bc421551SDag-Erling Smørgrav }
503bc421551SDag-Erling Smørgrav 
504*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static ptrdiff_t
50575411d15SDag-Erling Smørgrav align_to(ptrdiff_t size, ptrdiff_t alignment)
506bc421551SDag-Erling Smørgrav {
507d5c85ac6SDag-Erling Smørgrav   size_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits);
508bc421551SDag-Erling Smørgrav   return sum & ~lo_bits;
509bc421551SDag-Erling Smørgrav }
510bc421551SDag-Erling Smørgrav 
511bc421551SDag-Erling Smørgrav #if !HAVE_STRDUP
512bc421551SDag-Erling Smørgrav static char *
513bc421551SDag-Erling Smørgrav strdup(char const *str)
514bc421551SDag-Erling Smørgrav {
515bc421551SDag-Erling Smørgrav   char *result = malloc(strlen(str) + 1);
516bc421551SDag-Erling Smørgrav   return result ? strcpy(result, str) : result;
517bc421551SDag-Erling Smørgrav }
518bc421551SDag-Erling Smørgrav #endif
519bc421551SDag-Erling Smørgrav 
520bc421551SDag-Erling Smørgrav static void *
521bc421551SDag-Erling Smørgrav memcheck(void *ptr)
522bc421551SDag-Erling Smørgrav {
523bc421551SDag-Erling Smørgrav 	if (ptr == NULL)
524bc421551SDag-Erling Smørgrav 	  memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM));
525bc421551SDag-Erling Smørgrav 	return ptr;
526bc421551SDag-Erling Smørgrav }
527bc421551SDag-Erling Smørgrav 
528*a979394aSDag-Erling Smørgrav static void *
529bc421551SDag-Erling Smørgrav emalloc(size_t size)
530bc421551SDag-Erling Smørgrav {
531bc421551SDag-Erling Smørgrav   return memcheck(malloc(size));
532bc421551SDag-Erling Smørgrav }
533bc421551SDag-Erling Smørgrav 
534bc421551SDag-Erling Smørgrav static void *
535bc421551SDag-Erling Smørgrav erealloc(void *ptr, size_t size)
536bc421551SDag-Erling Smørgrav {
537bc421551SDag-Erling Smørgrav   return memcheck(realloc(ptr, size));
538bc421551SDag-Erling Smørgrav }
539bc421551SDag-Erling Smørgrav 
540*a979394aSDag-Erling Smørgrav static char *
541bc421551SDag-Erling Smørgrav estrdup(char const *str)
542bc421551SDag-Erling Smørgrav {
543bc421551SDag-Erling Smørgrav   return memcheck(strdup(str));
544bc421551SDag-Erling Smørgrav }
545bc421551SDag-Erling Smørgrav 
546bc421551SDag-Erling Smørgrav static ptrdiff_t
547bc421551SDag-Erling Smørgrav grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize)
548bc421551SDag-Erling Smørgrav {
549bc421551SDag-Erling Smørgrav   ptrdiff_t addend = (*nitems_alloc >> 1) + 1;
550bc421551SDag-Erling Smørgrav #if defined ckd_add && defined ckd_mul
551bc421551SDag-Erling Smørgrav   ptrdiff_t product;
552bc421551SDag-Erling Smørgrav   if (!ckd_add(nitems_alloc, *nitems_alloc, addend)
55375411d15SDag-Erling Smørgrav       && !ckd_mul(&product, *nitems_alloc, itemsize) && product <= INDEX_MAX)
554bc421551SDag-Erling Smørgrav     return product;
555bc421551SDag-Erling Smørgrav #else
55675411d15SDag-Erling Smørgrav   if (*nitems_alloc <= ((INDEX_MAX - 1) / 3 * 2) / itemsize) {
557bc421551SDag-Erling Smørgrav     *nitems_alloc += addend;
558bc421551SDag-Erling Smørgrav     return *nitems_alloc * itemsize;
559bc421551SDag-Erling Smørgrav   }
560bc421551SDag-Erling Smørgrav #endif
561bc421551SDag-Erling Smørgrav   memory_exhausted(_("integer overflow"));
562bc421551SDag-Erling Smørgrav }
563bc421551SDag-Erling Smørgrav 
564bc421551SDag-Erling Smørgrav static void *
565bc421551SDag-Erling Smørgrav growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
566bc421551SDag-Erling Smørgrav 	  ptrdiff_t *nitems_alloc)
567bc421551SDag-Erling Smørgrav {
568bc421551SDag-Erling Smørgrav   return (nitems < *nitems_alloc
569bc421551SDag-Erling Smørgrav 	  ? ptr
570bc421551SDag-Erling Smørgrav 	  : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
571bc421551SDag-Erling Smørgrav }
572bc421551SDag-Erling Smørgrav 
573bc421551SDag-Erling Smørgrav /*
574bc421551SDag-Erling Smørgrav ** Error handling.
575bc421551SDag-Erling Smørgrav */
576bc421551SDag-Erling Smørgrav 
577bc421551SDag-Erling Smørgrav /* In most of the code, an input file name is represented by its index
578bc421551SDag-Erling Smørgrav    into the main argument vector, except that LEAPSEC_FILENUM stands
579bc421551SDag-Erling Smørgrav    for leapsec and COMMAND_LINE_FILENUM stands for the command line.  */
580bc421551SDag-Erling Smørgrav enum { LEAPSEC_FILENUM = -2, COMMAND_LINE_FILENUM = -1 };
581bc421551SDag-Erling Smørgrav 
582bc421551SDag-Erling Smørgrav /* Return the name of the Ith input file, for diagnostics.  */
583bc421551SDag-Erling Smørgrav static char const *
584bc421551SDag-Erling Smørgrav filename(int i)
585bc421551SDag-Erling Smørgrav {
586bc421551SDag-Erling Smørgrav   if (i == COMMAND_LINE_FILENUM)
587bc421551SDag-Erling Smørgrav     return _("command line");
588bc421551SDag-Erling Smørgrav   else {
589bc421551SDag-Erling Smørgrav     char const *fname = i == LEAPSEC_FILENUM ? leapsec : main_argv[i];
590bc421551SDag-Erling Smørgrav     return strcmp(fname, "-") == 0 ? _("standard input") : fname;
591bc421551SDag-Erling Smørgrav   }
592bc421551SDag-Erling Smørgrav }
593bc421551SDag-Erling Smørgrav 
594bc421551SDag-Erling Smørgrav static void
595bc421551SDag-Erling Smørgrav eats(int fnum, lineno num, int rfnum, lineno rnum)
596bc421551SDag-Erling Smørgrav {
597bc421551SDag-Erling Smørgrav 	filenum = fnum;
598bc421551SDag-Erling Smørgrav 	linenum = num;
599bc421551SDag-Erling Smørgrav 	rfilenum = rfnum;
600bc421551SDag-Erling Smørgrav 	rlinenum = rnum;
601bc421551SDag-Erling Smørgrav }
602bc421551SDag-Erling Smørgrav 
603bc421551SDag-Erling Smørgrav static void
604bc421551SDag-Erling Smørgrav eat(int fnum, lineno num)
605bc421551SDag-Erling Smørgrav {
606bc421551SDag-Erling Smørgrav 	eats(fnum, num, 0, -1);
607bc421551SDag-Erling Smørgrav }
608bc421551SDag-Erling Smørgrav 
60975411d15SDag-Erling Smørgrav ATTRIBUTE_FORMAT((printf, 1, 0)) static void
610bc421551SDag-Erling Smørgrav verror(const char *const string, va_list args)
611bc421551SDag-Erling Smørgrav {
612bc421551SDag-Erling Smørgrav 	/*
613bc421551SDag-Erling Smørgrav 	** Match the format of "cc" to allow sh users to
614bc421551SDag-Erling Smørgrav 	**	zic ... 2>&1 | error -t "*" -v
615bc421551SDag-Erling Smørgrav 	** on BSD systems.
616bc421551SDag-Erling Smørgrav 	*/
617bc421551SDag-Erling Smørgrav 	if (filenum)
618bc421551SDag-Erling Smørgrav 	  fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
619bc421551SDag-Erling Smørgrav 		  filename(filenum), linenum);
620bc421551SDag-Erling Smørgrav 	vfprintf(stderr, string, args);
621bc421551SDag-Erling Smørgrav 	if (rfilenum)
622bc421551SDag-Erling Smørgrav 		fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
623bc421551SDag-Erling Smørgrav 			filename(rfilenum), rlinenum);
624bc421551SDag-Erling Smørgrav 	fprintf(stderr, "\n");
625bc421551SDag-Erling Smørgrav }
626bc421551SDag-Erling Smørgrav 
62775411d15SDag-Erling Smørgrav ATTRIBUTE_FORMAT((printf, 1, 2)) static void
628bc421551SDag-Erling Smørgrav error(const char *const string, ...)
629bc421551SDag-Erling Smørgrav {
630bc421551SDag-Erling Smørgrav 	va_list args;
631bc421551SDag-Erling Smørgrav 	va_start(args, string);
632bc421551SDag-Erling Smørgrav 	verror(string, args);
633bc421551SDag-Erling Smørgrav 	va_end(args);
634bc421551SDag-Erling Smørgrav 	errors = true;
635bc421551SDag-Erling Smørgrav }
636bc421551SDag-Erling Smørgrav 
63775411d15SDag-Erling Smørgrav ATTRIBUTE_FORMAT((printf, 1, 2)) static void
638bc421551SDag-Erling Smørgrav warning(const char *const string, ...)
639bc421551SDag-Erling Smørgrav {
640bc421551SDag-Erling Smørgrav 	va_list args;
641bc421551SDag-Erling Smørgrav 	fprintf(stderr, _("warning: "));
642bc421551SDag-Erling Smørgrav 	va_start(args, string);
643bc421551SDag-Erling Smørgrav 	verror(string, args);
644bc421551SDag-Erling Smørgrav 	va_end(args);
645bc421551SDag-Erling Smørgrav 	warnings = true;
646bc421551SDag-Erling Smørgrav }
647bc421551SDag-Erling Smørgrav 
648bc421551SDag-Erling Smørgrav /* Close STREAM.  If it had an I/O error, report it against DIR/NAME,
649bc421551SDag-Erling Smørgrav    remove TEMPNAME if nonnull, and then exit.  */
650bc421551SDag-Erling Smørgrav static void
651bc421551SDag-Erling Smørgrav close_file(FILE *stream, char const *dir, char const *name,
652bc421551SDag-Erling Smørgrav 	   char const *tempname)
653bc421551SDag-Erling Smørgrav {
654bc421551SDag-Erling Smørgrav   char const *e = (ferror(stream) ? _("I/O error")
655bc421551SDag-Erling Smørgrav 		   : fclose(stream) != 0 ? strerror(errno) : NULL);
656bc421551SDag-Erling Smørgrav   if (e) {
657bc421551SDag-Erling Smørgrav     fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
658bc421551SDag-Erling Smørgrav 	    dir ? dir : "", dir ? "/" : "",
659bc421551SDag-Erling Smørgrav 	    name ? name : "", name ? ": " : "",
660bc421551SDag-Erling Smørgrav 	    e);
661bc421551SDag-Erling Smørgrav     if (tempname)
6622aad7570SDag-Erling Smørgrav       (void)remove(tempname);
663bc421551SDag-Erling Smørgrav     exit(EXIT_FAILURE);
664bc421551SDag-Erling Smørgrav   }
665bc421551SDag-Erling Smørgrav }
666bc421551SDag-Erling Smørgrav 
66775411d15SDag-Erling Smørgrav ATTRIBUTE_NORETURN static void
668bc421551SDag-Erling Smørgrav usage(FILE *stream, int status)
669bc421551SDag-Erling Smørgrav {
670bc421551SDag-Erling Smørgrav   fprintf(stream,
671bc421551SDag-Erling Smørgrav 	  _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
672bc421551SDag-Erling Smørgrav 	    "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
673bc421551SDag-Erling Smørgrav 	    " [ -L leapseconds ] \\\n"
674bc421551SDag-Erling Smørgrav 	    "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
675bc421551SDag-Erling Smørgrav 	    "\t[ -t localtime-link ] [ -D ] [ -g gid ] [ -u uid ] \\\n"
676bc421551SDag-Erling Smørgrav 	    "\t[ filename ... ]\n\n"
677bc421551SDag-Erling Smørgrav 	    "Report bugs to %s.\n"),
678bc421551SDag-Erling Smørgrav 	  progname, progname, REPORT_BUGS_TO);
679bc421551SDag-Erling Smørgrav   if (status == EXIT_SUCCESS)
680bc421551SDag-Erling Smørgrav     close_file(stream, NULL, NULL, NULL);
681bc421551SDag-Erling Smørgrav   exit(status);
682bc421551SDag-Erling Smørgrav }
683bc421551SDag-Erling Smørgrav 
684bc421551SDag-Erling Smørgrav /* Change the working directory to DIR, possibly creating DIR and its
685bc421551SDag-Erling Smørgrav    ancestors.  After this is done, all files are accessed with names
686bc421551SDag-Erling Smørgrav    relative to DIR.  */
687bc421551SDag-Erling Smørgrav static void
688bc421551SDag-Erling Smørgrav change_directory(char const *dir)
689bc421551SDag-Erling Smørgrav {
690bc421551SDag-Erling Smørgrav   if (chdir(dir) != 0) {
691bc421551SDag-Erling Smørgrav     int chdir_errno = errno;
692bc421551SDag-Erling Smørgrav     if (chdir_errno == ENOENT) {
693bc421551SDag-Erling Smørgrav       mkdirs(dir, false);
694bc421551SDag-Erling Smørgrav       chdir_errno = chdir(dir) == 0 ? 0 : errno;
695bc421551SDag-Erling Smørgrav     }
696bc421551SDag-Erling Smørgrav     if (chdir_errno != 0) {
697bc421551SDag-Erling Smørgrav       fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
698bc421551SDag-Erling Smørgrav 	      progname, dir, strerror(chdir_errno));
699bc421551SDag-Erling Smørgrav       exit(EXIT_FAILURE);
700bc421551SDag-Erling Smørgrav     }
701bc421551SDag-Erling Smørgrav   }
702bc421551SDag-Erling Smørgrav }
703bc421551SDag-Erling Smørgrav 
704bc421551SDag-Erling Smørgrav /* Compare the two links A and B, for a stable sort by link name.  */
705bc421551SDag-Erling Smørgrav static int
706bc421551SDag-Erling Smørgrav qsort_linkcmp(void const *a, void const *b)
707bc421551SDag-Erling Smørgrav {
708bc421551SDag-Erling Smørgrav   struct link const *l = a;
709bc421551SDag-Erling Smørgrav   struct link const *m = b;
710bc421551SDag-Erling Smørgrav   int cmp = strcmp(l->l_linkname, m->l_linkname);
711bc421551SDag-Erling Smørgrav   if (cmp)
712bc421551SDag-Erling Smørgrav     return cmp;
713bc421551SDag-Erling Smørgrav 
714bc421551SDag-Erling Smørgrav   /* The link names are the same.  Make the sort stable by comparing
715bc421551SDag-Erling Smørgrav      file numbers (where subtraction cannot overflow) and possibly
716bc421551SDag-Erling Smørgrav      line numbers (where it can).  */
717bc421551SDag-Erling Smørgrav   cmp = l->l_filenum - m->l_filenum;
718bc421551SDag-Erling Smørgrav   if (cmp)
719bc421551SDag-Erling Smørgrav     return cmp;
720bc421551SDag-Erling Smørgrav   return (l->l_linenum > m->l_linenum) - (l->l_linenum < m->l_linenum);
721bc421551SDag-Erling Smørgrav }
722bc421551SDag-Erling Smørgrav 
723bc421551SDag-Erling Smørgrav /* Compare the string KEY to the link B, for bsearch.  */
724bc421551SDag-Erling Smørgrav static int
725bc421551SDag-Erling Smørgrav bsearch_linkcmp(void const *key, void const *b)
726bc421551SDag-Erling Smørgrav {
727bc421551SDag-Erling Smørgrav   struct link const *m = b;
728bc421551SDag-Erling Smørgrav   return strcmp(key, m->l_linkname);
729bc421551SDag-Erling Smørgrav }
730bc421551SDag-Erling Smørgrav 
731bc421551SDag-Erling Smørgrav /* Make the links specified by the Link lines.  */
732bc421551SDag-Erling Smørgrav static void
733bc421551SDag-Erling Smørgrav make_links(void)
734bc421551SDag-Erling Smørgrav {
735bc421551SDag-Erling Smørgrav   ptrdiff_t i, j, nalinks, pass_size;
736bc421551SDag-Erling Smørgrav   if (1 < nlinks)
737bc421551SDag-Erling Smørgrav     qsort(links, nlinks, sizeof *links, qsort_linkcmp);
738bc421551SDag-Erling Smørgrav 
739bc421551SDag-Erling Smørgrav   /* Ignore each link superseded by a later link with the same name.  */
740bc421551SDag-Erling Smørgrav   j = 0;
741bc421551SDag-Erling Smørgrav   for (i = 0; i < nlinks; i++) {
742bc421551SDag-Erling Smørgrav     while (i + 1 < nlinks
743bc421551SDag-Erling Smørgrav 	   && strcmp(links[i].l_linkname, links[i + 1].l_linkname) == 0)
744bc421551SDag-Erling Smørgrav       i++;
745bc421551SDag-Erling Smørgrav     links[j++] = links[i];
746bc421551SDag-Erling Smørgrav   }
747bc421551SDag-Erling Smørgrav   nlinks = pass_size = j;
748bc421551SDag-Erling Smørgrav 
749bc421551SDag-Erling Smørgrav   /* Walk through the link array making links.  However,
750bc421551SDag-Erling Smørgrav      if a link's target has not been made yet, append a copy to the
751bc421551SDag-Erling Smørgrav      end of the array.  The end of the array will gradually fill
752bc421551SDag-Erling Smørgrav      up with a small sorted subsequence of not-yet-made links.
753bc421551SDag-Erling Smørgrav      nalinks counts all the links in the array, including copies.
754bc421551SDag-Erling Smørgrav      When we reach the copied subsequence, it may still contain
755bc421551SDag-Erling Smørgrav      a link to a not-yet-made link, so the process repeats.
756bc421551SDag-Erling Smørgrav      At any given point in time, the link array consists of the
757bc421551SDag-Erling Smørgrav      following subregions, where 0 <= i <= j <= nalinks and
758bc421551SDag-Erling Smørgrav      0 <= nlinks <= nalinks:
759bc421551SDag-Erling Smørgrav 
760bc421551SDag-Erling Smørgrav        0 .. (i - 1):
761bc421551SDag-Erling Smørgrav 	 links that either have been made, or have been copied to a
762bc421551SDag-Erling Smørgrav 	 later point point in the array (this later point can be in
763bc421551SDag-Erling Smørgrav 	 any of the three subregions)
764bc421551SDag-Erling Smørgrav        i .. (j - 1):
765bc421551SDag-Erling Smørgrav 	 not-yet-made links for this pass
766bc421551SDag-Erling Smørgrav        j .. (nalinks - 1):
767bc421551SDag-Erling Smørgrav 	 not-yet-made links that this pass has skipped because
768bc421551SDag-Erling Smørgrav 	 they were links to not-yet-made links
769bc421551SDag-Erling Smørgrav 
770bc421551SDag-Erling Smørgrav      The first subregion might not be sorted if nlinks < i;
771bc421551SDag-Erling Smørgrav      the other two subregions are sorted.  This algorithm does
772bc421551SDag-Erling Smørgrav      not alter entries 0 .. (nlinks - 1), which remain sorted.
773bc421551SDag-Erling Smørgrav 
774bc421551SDag-Erling Smørgrav      If there are L links, this algorithm is O(C*L*log(L)) where
775bc421551SDag-Erling Smørgrav      C is the length of the longest link chain.  Usually C is
776bc421551SDag-Erling Smørgrav      short (e.g., 3) though its worst-case value is L.  */
777bc421551SDag-Erling Smørgrav 
778bc421551SDag-Erling Smørgrav   j = nalinks = nlinks;
779bc421551SDag-Erling Smørgrav 
780bc421551SDag-Erling Smørgrav   for (i = 0; i < nalinks; i++) {
781bc421551SDag-Erling Smørgrav     struct link *l;
782bc421551SDag-Erling Smørgrav 
783bc421551SDag-Erling Smørgrav     eat(links[i].l_filenum, links[i].l_linenum);
784bc421551SDag-Erling Smørgrav 
785bc421551SDag-Erling Smørgrav     /* If this pass examined all its links, start the next pass.  */
786bc421551SDag-Erling Smørgrav     if (i == j) {
787bc421551SDag-Erling Smørgrav       if (nalinks - i == pass_size) {
788bc421551SDag-Erling Smørgrav 	error(_("\"Link %s %s\" is part of a link cycle"),
789bc421551SDag-Erling Smørgrav 	      links[i].l_target, links[i].l_linkname);
790bc421551SDag-Erling Smørgrav 	break;
791bc421551SDag-Erling Smørgrav       }
792bc421551SDag-Erling Smørgrav       j = nalinks;
793bc421551SDag-Erling Smørgrav       pass_size = nalinks - i;
794bc421551SDag-Erling Smørgrav     }
795bc421551SDag-Erling Smørgrav 
796bc421551SDag-Erling Smørgrav     /* Diagnose self links, which the cycle detection algorithm would not
797bc421551SDag-Erling Smørgrav        otherwise catch.  */
798bc421551SDag-Erling Smørgrav     if (strcmp(links[i].l_target, links[i].l_linkname) == 0) {
799bc421551SDag-Erling Smørgrav       error(_("link %s targets itself"), links[i].l_target);
800bc421551SDag-Erling Smørgrav       continue;
801bc421551SDag-Erling Smørgrav     }
802bc421551SDag-Erling Smørgrav 
803bc421551SDag-Erling Smørgrav     /* Make this link unless its target has not been made yet.  */
804bc421551SDag-Erling Smørgrav     l = bsearch(links[i].l_target, &links[i + 1], j - (i + 1),
805bc421551SDag-Erling Smørgrav 		sizeof *links, bsearch_linkcmp);
806bc421551SDag-Erling Smørgrav     if (!l)
807bc421551SDag-Erling Smørgrav       l = bsearch(links[i].l_target, &links[j], nalinks - j,
808bc421551SDag-Erling Smørgrav 		  sizeof *links, bsearch_linkcmp);
809bc421551SDag-Erling Smørgrav     if (!l)
810bc421551SDag-Erling Smørgrav       dolink(links[i].l_target, links[i].l_linkname, false);
811bc421551SDag-Erling Smørgrav     else {
812bc421551SDag-Erling Smørgrav       /* The link target has not been made yet; copy the link to the end.  */
813bc421551SDag-Erling Smørgrav       links = growalloc(links, sizeof *links, nalinks, &nlinks_alloc);
814bc421551SDag-Erling Smørgrav       links[nalinks++] = links[i];
815bc421551SDag-Erling Smørgrav     }
816bc421551SDag-Erling Smørgrav 
817bc421551SDag-Erling Smørgrav     if (noise && i < nlinks) {
818bc421551SDag-Erling Smørgrav       if (l)
819bc421551SDag-Erling Smørgrav 	warning(_("link %s targeting link %s mishandled by pre-2023 zic"),
820bc421551SDag-Erling Smørgrav 		links[i].l_linkname, links[i].l_target);
821bc421551SDag-Erling Smørgrav       else if (bsearch(links[i].l_target, links, nlinks, sizeof *links,
822bc421551SDag-Erling Smørgrav 		       bsearch_linkcmp))
823bc421551SDag-Erling Smørgrav 	warning(_("link %s targeting link %s"),
824bc421551SDag-Erling Smørgrav 		links[i].l_linkname, links[i].l_target);
825bc421551SDag-Erling Smørgrav     }
826bc421551SDag-Erling Smørgrav   }
827bc421551SDag-Erling Smørgrav }
828bc421551SDag-Erling Smørgrav 
829bc421551SDag-Erling Smørgrav /* Simple signal handling: just set a flag that is checked
830bc421551SDag-Erling Smørgrav    periodically outside critical sections.  To set up the handler,
831bc421551SDag-Erling Smørgrav    prefer sigaction if available to close a signal race.  */
832bc421551SDag-Erling Smørgrav 
833bc421551SDag-Erling Smørgrav static sig_atomic_t got_signal;
834bc421551SDag-Erling Smørgrav 
835bc421551SDag-Erling Smørgrav static void
836bc421551SDag-Erling Smørgrav signal_handler(int sig)
837bc421551SDag-Erling Smørgrav {
838bc421551SDag-Erling Smørgrav #ifndef SA_SIGINFO
839bc421551SDag-Erling Smørgrav   signal(sig, signal_handler);
840bc421551SDag-Erling Smørgrav #endif
841bc421551SDag-Erling Smørgrav   got_signal = sig;
842bc421551SDag-Erling Smørgrav }
843bc421551SDag-Erling Smørgrav 
844bc421551SDag-Erling Smørgrav /* Arrange for SIGINT etc. to be caught by the handler.  */
845bc421551SDag-Erling Smørgrav static void
846bc421551SDag-Erling Smørgrav catch_signals(void)
847bc421551SDag-Erling Smørgrav {
848bc421551SDag-Erling Smørgrav   static int const signals[] = {
849bc421551SDag-Erling Smørgrav #ifdef SIGHUP
850bc421551SDag-Erling Smørgrav     SIGHUP,
851bc421551SDag-Erling Smørgrav #endif
852bc421551SDag-Erling Smørgrav     SIGINT,
853bc421551SDag-Erling Smørgrav #ifdef SIGPIPE
854bc421551SDag-Erling Smørgrav     SIGPIPE,
855bc421551SDag-Erling Smørgrav #endif
856bc421551SDag-Erling Smørgrav     SIGTERM
857bc421551SDag-Erling Smørgrav   };
858bc421551SDag-Erling Smørgrav   size_t i;
859bc421551SDag-Erling Smørgrav   for (i = 0; i < sizeof signals / sizeof signals[0]; i++) {
860bc421551SDag-Erling Smørgrav #ifdef SA_SIGINFO
861bc421551SDag-Erling Smørgrav     struct sigaction act0, act;
862bc421551SDag-Erling Smørgrav     act.sa_handler = signal_handler;
863bc421551SDag-Erling Smørgrav     sigemptyset(&act.sa_mask);
864bc421551SDag-Erling Smørgrav     act.sa_flags = 0;
865bc421551SDag-Erling Smørgrav     if (sigaction(signals[i], &act, &act0) == 0
866bc421551SDag-Erling Smørgrav 	&& ! (act0.sa_flags & SA_SIGINFO) && act0.sa_handler == SIG_IGN) {
867bc421551SDag-Erling Smørgrav       sigaction(signals[i], &act0, NULL);
868bc421551SDag-Erling Smørgrav       got_signal = 0;
869bc421551SDag-Erling Smørgrav     }
870bc421551SDag-Erling Smørgrav #else
871bc421551SDag-Erling Smørgrav     if (signal(signals[i], signal_handler) == SIG_IGN) {
872bc421551SDag-Erling Smørgrav       signal(signals[i], SIG_IGN);
873bc421551SDag-Erling Smørgrav       got_signal = 0;
874bc421551SDag-Erling Smørgrav     }
875bc421551SDag-Erling Smørgrav #endif
876bc421551SDag-Erling Smørgrav   }
877bc421551SDag-Erling Smørgrav }
878bc421551SDag-Erling Smørgrav 
879bc421551SDag-Erling Smørgrav /* If a signal has arrived, terminate zic with appropriate status.  */
880bc421551SDag-Erling Smørgrav static void
881bc421551SDag-Erling Smørgrav check_for_signal(void)
882bc421551SDag-Erling Smørgrav {
883bc421551SDag-Erling Smørgrav   int sig = got_signal;
884bc421551SDag-Erling Smørgrav   if (sig) {
885bc421551SDag-Erling Smørgrav     signal(sig, SIG_DFL);
886bc421551SDag-Erling Smørgrav     raise(sig);
887bc421551SDag-Erling Smørgrav     abort(); /* A bug in 'raise'.  */
888bc421551SDag-Erling Smørgrav   }
889bc421551SDag-Erling Smørgrav }
890bc421551SDag-Erling Smørgrav 
891bc421551SDag-Erling Smørgrav enum { TIME_T_BITS_IN_FILE = 64 };
892bc421551SDag-Erling Smørgrav 
893bc421551SDag-Erling Smørgrav /* The minimum and maximum values representable in a TZif file.  */
894bc421551SDag-Erling Smørgrav static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
895bc421551SDag-Erling Smørgrav static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
896bc421551SDag-Erling Smørgrav 
897bc421551SDag-Erling Smørgrav /* The minimum, and one less than the maximum, values specified by
898bc421551SDag-Erling Smørgrav    the -r option.  These default to MIN_TIME and MAX_TIME.  */
899bc421551SDag-Erling Smørgrav static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
900bc421551SDag-Erling Smørgrav static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
901bc421551SDag-Erling Smørgrav 
90246c59934SDag-Erling Smørgrav /* The time specified by the -R option, defaulting to MIN_TIME;
90346c59934SDag-Erling Smørgrav    or lo_time, whichever is greater.  */
904bc421551SDag-Erling Smørgrav static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
905bc421551SDag-Erling Smørgrav 
906bc421551SDag-Erling Smørgrav /* The time specified by an Expires line, or negative if no such line.  */
907bc421551SDag-Erling Smørgrav static zic_t leapexpires = -1;
908bc421551SDag-Erling Smørgrav 
909bc421551SDag-Erling Smørgrav /* Set the time range of the output to TIMERANGE.
910bc421551SDag-Erling Smørgrav    Return true if successful.  */
911bc421551SDag-Erling Smørgrav static bool
912bc421551SDag-Erling Smørgrav timerange_option(char *timerange)
913bc421551SDag-Erling Smørgrav {
914bc421551SDag-Erling Smørgrav   intmax_t lo = min_time, hi = max_time;
915bc421551SDag-Erling Smørgrav   char *lo_end = timerange, *hi_end;
916bc421551SDag-Erling Smørgrav   if (*timerange == '@') {
917bc421551SDag-Erling Smørgrav     errno = 0;
918bc421551SDag-Erling Smørgrav     lo = strtoimax(timerange + 1, &lo_end, 10);
919bc421551SDag-Erling Smørgrav     if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
920bc421551SDag-Erling Smørgrav       return false;
921bc421551SDag-Erling Smørgrav   }
922bc421551SDag-Erling Smørgrav   hi_end = lo_end;
923bc421551SDag-Erling Smørgrav   if (lo_end[0] == '/' && lo_end[1] == '@') {
924bc421551SDag-Erling Smørgrav     errno = 0;
925bc421551SDag-Erling Smørgrav     hi = strtoimax(lo_end + 2, &hi_end, 10);
926bc421551SDag-Erling Smørgrav     if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
927bc421551SDag-Erling Smørgrav       return false;
928bc421551SDag-Erling Smørgrav     hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
929bc421551SDag-Erling Smørgrav   }
930bc421551SDag-Erling Smørgrav   if (*hi_end || hi < lo || max_time < lo || hi < min_time)
931bc421551SDag-Erling Smørgrav     return false;
932bc421551SDag-Erling Smørgrav   lo_time = max(lo, min_time);
933bc421551SDag-Erling Smørgrav   hi_time = min(hi, max_time);
934bc421551SDag-Erling Smørgrav   return true;
935bc421551SDag-Erling Smørgrav }
936bc421551SDag-Erling Smørgrav 
937bc421551SDag-Erling Smørgrav /* Generate redundant time stamps up to OPT.  Return true if successful.  */
938bc421551SDag-Erling Smørgrav static bool
939bc421551SDag-Erling Smørgrav redundant_time_option(char *opt)
940bc421551SDag-Erling Smørgrav {
941bc421551SDag-Erling Smørgrav   if (*opt == '@') {
942bc421551SDag-Erling Smørgrav     intmax_t redundant;
943bc421551SDag-Erling Smørgrav     char *opt_end;
944bc421551SDag-Erling Smørgrav     redundant = strtoimax(opt + 1, &opt_end, 10);
945bc421551SDag-Erling Smørgrav     if (opt_end != opt + 1 && !*opt_end) {
946bc421551SDag-Erling Smørgrav       redundant_time = max(redundant_time, redundant);
947bc421551SDag-Erling Smørgrav       return true;
948bc421551SDag-Erling Smørgrav     }
949bc421551SDag-Erling Smørgrav   }
950bc421551SDag-Erling Smørgrav   return false;
951bc421551SDag-Erling Smørgrav }
952bc421551SDag-Erling Smørgrav 
953bc421551SDag-Erling Smørgrav static const char *	psxrules;
954bc421551SDag-Erling Smørgrav static const char *	lcltime;
955bc421551SDag-Erling Smørgrav static const char *	directory;
956bc421551SDag-Erling Smørgrav static const char *	leapsec;
957bc421551SDag-Erling Smørgrav static int		Dflag;
958bc421551SDag-Erling Smørgrav static uid_t		uflag = (uid_t)-1;
959bc421551SDag-Erling Smørgrav static gid_t		gflag = (gid_t)-1;
960bc421551SDag-Erling Smørgrav static mode_t		mflag = (S_IRUSR | S_IRGRP | S_IROTH
961bc421551SDag-Erling Smørgrav 				 | S_IWUSR);
962bc421551SDag-Erling Smørgrav static const char *	tzdefault;
963bc421551SDag-Erling Smørgrav 
964bc421551SDag-Erling Smørgrav /* -1 if the TZif output file should be slim, 0 if default, 1 if the
965bc421551SDag-Erling Smørgrav    output should be fat for backward compatibility.  ZIC_BLOAT_DEFAULT
966bc421551SDag-Erling Smørgrav    determines the default.  */
967bc421551SDag-Erling Smørgrav static int bloat;
968bc421551SDag-Erling Smørgrav 
969bc421551SDag-Erling Smørgrav static bool
970bc421551SDag-Erling Smørgrav want_bloat(void)
971bc421551SDag-Erling Smørgrav {
972bc421551SDag-Erling Smørgrav   return 0 <= bloat;
973bc421551SDag-Erling Smørgrav }
974bc421551SDag-Erling Smørgrav 
975bc421551SDag-Erling Smørgrav #ifndef ZIC_BLOAT_DEFAULT
976bc421551SDag-Erling Smørgrav # define ZIC_BLOAT_DEFAULT "slim"
977bc421551SDag-Erling Smørgrav #endif
978bc421551SDag-Erling Smørgrav 
979bc421551SDag-Erling Smørgrav int
980bc421551SDag-Erling Smørgrav main(int argc, char **argv)
981bc421551SDag-Erling Smørgrav {
982bc421551SDag-Erling Smørgrav 	register int c, k;
983bc421551SDag-Erling Smørgrav 	register ptrdiff_t i, j;
984bc421551SDag-Erling Smørgrav 	bool timerange_given = false;
985bc421551SDag-Erling Smørgrav 
986bc421551SDag-Erling Smørgrav #ifdef S_IWGRP
987bc421551SDag-Erling Smørgrav 	umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
988bc421551SDag-Erling Smørgrav #endif
989bc421551SDag-Erling Smørgrav #if HAVE_GETTEXT
990bc421551SDag-Erling Smørgrav 	setlocale(LC_ALL, "");
991bc421551SDag-Erling Smørgrav # ifdef TZ_DOMAINDIR
992bc421551SDag-Erling Smørgrav 	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
993bc421551SDag-Erling Smørgrav # endif /* defined TEXTDOMAINDIR */
994bc421551SDag-Erling Smørgrav 	textdomain(TZ_DOMAIN);
995bc421551SDag-Erling Smørgrav #endif /* HAVE_GETTEXT */
996bc421551SDag-Erling Smørgrav 	main_argv = argv;
997bc421551SDag-Erling Smørgrav 	progname = /* argv[0] ? argv[0] : */ "zic";
998bc421551SDag-Erling Smørgrav 	if (TYPE_BIT(zic_t) < 64) {
999bc421551SDag-Erling Smørgrav 		fprintf(stderr, "%s: %s\n", progname,
1000bc421551SDag-Erling Smørgrav 			_("wild compilation-time specification of zic_t"));
1001bc421551SDag-Erling Smørgrav 		return EXIT_FAILURE;
1002bc421551SDag-Erling Smørgrav 	}
1003bc421551SDag-Erling Smørgrav 	for (k = 1; k < argc; k++)
1004bc421551SDag-Erling Smørgrav 		if (strcmp(argv[k], "--version") == 0) {
1005bc421551SDag-Erling Smørgrav 			printf("zic %s%s\n", PKGVERSION, TZVERSION);
1006bc421551SDag-Erling Smørgrav 			close_file(stdout, NULL, NULL, NULL);
1007bc421551SDag-Erling Smørgrav 			return EXIT_SUCCESS;
1008bc421551SDag-Erling Smørgrav 		} else if (strcmp(argv[k], "--help") == 0) {
1009bc421551SDag-Erling Smørgrav 			usage(stdout, EXIT_SUCCESS);
1010bc421551SDag-Erling Smørgrav 		}
1011bc421551SDag-Erling Smørgrav 	while ((c = getopt(argc, argv, "Db:d:g:l:L:m:p:r:R:st:u:vy:")) != EOF
1012bc421551SDag-Erling Smørgrav 	       && c != -1)
1013bc421551SDag-Erling Smørgrav 		switch (c) {
1014bc421551SDag-Erling Smørgrav 			default:
1015bc421551SDag-Erling Smørgrav 				usage(stderr, EXIT_FAILURE);
1016bc421551SDag-Erling Smørgrav 			case 'D':
1017bc421551SDag-Erling Smørgrav 				Dflag = 1;
1018bc421551SDag-Erling Smørgrav 				break;
1019bc421551SDag-Erling Smørgrav 			case 'b':
1020bc421551SDag-Erling Smørgrav 				if (strcmp(optarg, "slim") == 0) {
1021bc421551SDag-Erling Smørgrav 				  if (0 < bloat)
1022bc421551SDag-Erling Smørgrav 				    error(_("incompatible -b options"));
1023bc421551SDag-Erling Smørgrav 				  bloat = -1;
1024bc421551SDag-Erling Smørgrav 				} else if (strcmp(optarg, "fat") == 0) {
1025bc421551SDag-Erling Smørgrav 				  if (bloat < 0)
1026bc421551SDag-Erling Smørgrav 				    error(_("incompatible -b options"));
1027bc421551SDag-Erling Smørgrav 				  bloat = 1;
1028bc421551SDag-Erling Smørgrav 				} else
1029bc421551SDag-Erling Smørgrav 				  error(_("invalid option: -b '%s'"), optarg);
1030bc421551SDag-Erling Smørgrav 				break;
1031bc421551SDag-Erling Smørgrav 			case 'd':
1032bc421551SDag-Erling Smørgrav 				if (directory == NULL)
1033bc421551SDag-Erling Smørgrav 					directory = optarg;
1034bc421551SDag-Erling Smørgrav 				else {
1035bc421551SDag-Erling Smørgrav 					fprintf(stderr,
103646c59934SDag-Erling Smørgrav 						_("%s: More than one -d option"
103746c59934SDag-Erling Smørgrav 						  " specified\n"),
1038bc421551SDag-Erling Smørgrav 						progname);
1039bc421551SDag-Erling Smørgrav 					return EXIT_FAILURE;
1040bc421551SDag-Erling Smørgrav 				}
1041bc421551SDag-Erling Smørgrav 				break;
1042bc421551SDag-Erling Smørgrav 			case 'g':
1043bc421551SDag-Erling Smørgrav 				setgroup(&gflag, optarg);
1044bc421551SDag-Erling Smørgrav 				break;
1045bc421551SDag-Erling Smørgrav 			case 'l':
1046bc421551SDag-Erling Smørgrav 				if (lcltime == NULL)
1047bc421551SDag-Erling Smørgrav 					lcltime = optarg;
1048bc421551SDag-Erling Smørgrav 				else {
1049bc421551SDag-Erling Smørgrav 					fprintf(stderr,
105046c59934SDag-Erling Smørgrav 						_("%s: More than one -l option"
105146c59934SDag-Erling Smørgrav 						  " specified\n"),
1052bc421551SDag-Erling Smørgrav 						progname);
1053bc421551SDag-Erling Smørgrav 					return EXIT_FAILURE;
1054bc421551SDag-Erling Smørgrav 				}
1055bc421551SDag-Erling Smørgrav 				break;
1056bc421551SDag-Erling Smørgrav 			case 'm':
1057bc421551SDag-Erling Smørgrav 			{
1058bc421551SDag-Erling Smørgrav 				void *set = setmode(optarg);
1059bc421551SDag-Erling Smørgrav 				if (set == NULL) {
1060bc421551SDag-Erling Smørgrav 					fprintf(stderr,
1061bc421551SDag-Erling Smørgrav _("invalid file mode"));
1062bc421551SDag-Erling Smørgrav 					return EXIT_FAILURE;
1063bc421551SDag-Erling Smørgrav 				}
1064bc421551SDag-Erling Smørgrav 				mflag = getmode(set, mflag);
1065bc421551SDag-Erling Smørgrav 				free(set);
1066bc421551SDag-Erling Smørgrav 				break;
1067bc421551SDag-Erling Smørgrav 			}
1068bc421551SDag-Erling Smørgrav 			case 'p':
1069bc421551SDag-Erling Smørgrav 				if (psxrules == NULL)
1070bc421551SDag-Erling Smørgrav 					psxrules = optarg;
1071bc421551SDag-Erling Smørgrav 				else {
1072bc421551SDag-Erling Smørgrav 					fprintf(stderr,
107346c59934SDag-Erling Smørgrav 						_("%s: More than one -p option"
107446c59934SDag-Erling Smørgrav 						  " specified\n"),
1075bc421551SDag-Erling Smørgrav 						progname);
1076bc421551SDag-Erling Smørgrav 					return EXIT_FAILURE;
1077bc421551SDag-Erling Smørgrav 				}
1078bc421551SDag-Erling Smørgrav 				break;
1079bc421551SDag-Erling Smørgrav 			case 't':
1080bc421551SDag-Erling Smørgrav 				if (tzdefault != NULL) {
1081bc421551SDag-Erling Smørgrav 				  fprintf(stderr,
1082bc421551SDag-Erling Smørgrav 					  _("%s: More than one -t option"
1083bc421551SDag-Erling Smørgrav 					    " specified\n"),
1084bc421551SDag-Erling Smørgrav 					  progname);
1085bc421551SDag-Erling Smørgrav 				  return EXIT_FAILURE;
1086bc421551SDag-Erling Smørgrav 				}
1087bc421551SDag-Erling Smørgrav 				tzdefault = optarg;
1088bc421551SDag-Erling Smørgrav 				break;
1089bc421551SDag-Erling Smørgrav 			case 'u':
1090bc421551SDag-Erling Smørgrav 				setuser(&uflag, optarg);
1091bc421551SDag-Erling Smørgrav 				break;
1092bc421551SDag-Erling Smørgrav 			case 'y':
1093bc421551SDag-Erling Smørgrav 				warning(_("-y ignored"));
1094bc421551SDag-Erling Smørgrav 				break;
1095bc421551SDag-Erling Smørgrav 			case 'L':
1096bc421551SDag-Erling Smørgrav 				if (leapsec == NULL)
1097bc421551SDag-Erling Smørgrav 					leapsec = optarg;
1098bc421551SDag-Erling Smørgrav 				else {
1099bc421551SDag-Erling Smørgrav 					fprintf(stderr,
110046c59934SDag-Erling Smørgrav 						_("%s: More than one -L option"
110146c59934SDag-Erling Smørgrav 						  " specified\n"),
1102bc421551SDag-Erling Smørgrav 						progname);
1103bc421551SDag-Erling Smørgrav 					return EXIT_FAILURE;
1104bc421551SDag-Erling Smørgrav 				}
1105bc421551SDag-Erling Smørgrav 				break;
1106bc421551SDag-Erling Smørgrav 			case 'v':
1107bc421551SDag-Erling Smørgrav 				noise = true;
1108bc421551SDag-Erling Smørgrav 				break;
1109bc421551SDag-Erling Smørgrav 			case 'r':
1110bc421551SDag-Erling Smørgrav 				if (timerange_given) {
1111bc421551SDag-Erling Smørgrav 				  fprintf(stderr,
111246c59934SDag-Erling Smørgrav 					  _("%s: More than one -r option"
111346c59934SDag-Erling Smørgrav 					    " specified\n"),
1114bc421551SDag-Erling Smørgrav 					  progname);
1115bc421551SDag-Erling Smørgrav 				  return EXIT_FAILURE;
1116bc421551SDag-Erling Smørgrav 				}
1117bc421551SDag-Erling Smørgrav 				if (! timerange_option(optarg)) {
1118bc421551SDag-Erling Smørgrav 				  fprintf(stderr,
1119bc421551SDag-Erling Smørgrav 					  _("%s: invalid time range: %s\n"),
1120bc421551SDag-Erling Smørgrav 					  progname, optarg);
1121bc421551SDag-Erling Smørgrav 				  return EXIT_FAILURE;
1122bc421551SDag-Erling Smørgrav 				}
1123bc421551SDag-Erling Smørgrav 				timerange_given = true;
1124bc421551SDag-Erling Smørgrav 				break;
1125bc421551SDag-Erling Smørgrav 			case 'R':
1126bc421551SDag-Erling Smørgrav 				if (! redundant_time_option(optarg)) {
1127bc421551SDag-Erling Smørgrav 				  fprintf(stderr, _("%s: invalid time: %s\n"),
1128bc421551SDag-Erling Smørgrav 					  progname, optarg);
1129bc421551SDag-Erling Smørgrav 				  return EXIT_FAILURE;
1130bc421551SDag-Erling Smørgrav 				}
1131bc421551SDag-Erling Smørgrav 				break;
1132bc421551SDag-Erling Smørgrav 			case 's':
1133bc421551SDag-Erling Smørgrav 				warning(_("-s ignored"));
1134bc421551SDag-Erling Smørgrav 				break;
1135bc421551SDag-Erling Smørgrav 		}
1136bc421551SDag-Erling Smørgrav 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
1137bc421551SDag-Erling Smørgrav 		usage(stderr, EXIT_FAILURE);	/* usage message by request */
1138bc421551SDag-Erling Smørgrav 	if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
1139bc421551SDag-Erling Smørgrav 	  fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
1140bc421551SDag-Erling Smørgrav 	  return EXIT_FAILURE;
1141bc421551SDag-Erling Smørgrav 	}
114246c59934SDag-Erling Smørgrav 	if (redundant_time < lo_time)
114346c59934SDag-Erling Smørgrav 	  redundant_time = lo_time;
1144bc421551SDag-Erling Smørgrav 	if (bloat == 0) {
1145bc421551SDag-Erling Smørgrav 	  static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
1146bc421551SDag-Erling Smørgrav 	  if (strcmp(bloat_default, "slim") == 0)
1147bc421551SDag-Erling Smørgrav 	    bloat = -1;
1148bc421551SDag-Erling Smørgrav 	  else if (strcmp(bloat_default, "fat") == 0)
1149bc421551SDag-Erling Smørgrav 	    bloat = 1;
1150bc421551SDag-Erling Smørgrav 	  else
1151bc421551SDag-Erling Smørgrav 	    abort(); /* Configuration error.  */
1152bc421551SDag-Erling Smørgrav 	}
1153bc421551SDag-Erling Smørgrav 	if (directory == NULL)
1154bc421551SDag-Erling Smørgrav 		directory = TZDIR;
1155bc421551SDag-Erling Smørgrav 	if (tzdefault == NULL)
1156bc421551SDag-Erling Smørgrav 		tzdefault = TZDEFAULT;
1157bc421551SDag-Erling Smørgrav 
1158bc421551SDag-Erling Smørgrav 	if (optind < argc && leapsec != NULL) {
1159bc421551SDag-Erling Smørgrav 		infile(LEAPSEC_FILENUM, leapsec);
1160bc421551SDag-Erling Smørgrav 		adjleap();
1161bc421551SDag-Erling Smørgrav 	}
1162bc421551SDag-Erling Smørgrav 
1163bc421551SDag-Erling Smørgrav 	for (k = optind; k < argc; k++)
1164bc421551SDag-Erling Smørgrav 	  infile(k, argv[k]);
1165bc421551SDag-Erling Smørgrav 	if (errors)
1166bc421551SDag-Erling Smørgrav 		return EXIT_FAILURE;
1167bc421551SDag-Erling Smørgrav 	associate();
1168bc421551SDag-Erling Smørgrav 	change_directory(directory);
1169bc421551SDag-Erling Smørgrav 	catch_signals();
1170bc421551SDag-Erling Smørgrav 	for (i = 0; i < nzones; i = j) {
1171bc421551SDag-Erling Smørgrav 		/*
1172bc421551SDag-Erling Smørgrav 		** Find the next non-continuation zone entry.
1173bc421551SDag-Erling Smørgrav 		*/
1174bc421551SDag-Erling Smørgrav 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
1175bc421551SDag-Erling Smørgrav 			continue;
1176bc421551SDag-Erling Smørgrav 		outzone(&zones[i], j - i);
1177bc421551SDag-Erling Smørgrav 	}
1178bc421551SDag-Erling Smørgrav 	make_links();
1179bc421551SDag-Erling Smørgrav 	if (lcltime != NULL) {
1180bc421551SDag-Erling Smørgrav 		eat(COMMAND_LINE_FILENUM, 1);
1181bc421551SDag-Erling Smørgrav 		dolink(lcltime, tzdefault, true);
1182bc421551SDag-Erling Smørgrav 	}
1183bc421551SDag-Erling Smørgrav 	if (psxrules != NULL) {
1184bc421551SDag-Erling Smørgrav 		eat(COMMAND_LINE_FILENUM, 1);
1185bc421551SDag-Erling Smørgrav 		dolink(psxrules, TZDEFRULES, true);
1186bc421551SDag-Erling Smørgrav 	}
1187bc421551SDag-Erling Smørgrav 	if (warnings && (ferror(stderr) || fclose(stderr) != 0))
1188bc421551SDag-Erling Smørgrav 	  return EXIT_FAILURE;
1189bc421551SDag-Erling Smørgrav 	return errors ? EXIT_FAILURE : EXIT_SUCCESS;
1190bc421551SDag-Erling Smørgrav }
1191bc421551SDag-Erling Smørgrav 
1192bc421551SDag-Erling Smørgrav static bool
1193bc421551SDag-Erling Smørgrav componentcheck(char const *name, char const *component,
1194bc421551SDag-Erling Smørgrav 	       char const *component_end)
1195bc421551SDag-Erling Smørgrav {
1196bc421551SDag-Erling Smørgrav 	enum { component_len_max = 14 };
1197bc421551SDag-Erling Smørgrav 	ptrdiff_t component_len = component_end - component;
1198bc421551SDag-Erling Smørgrav 	if (component_len == 0) {
1199bc421551SDag-Erling Smørgrav 	  if (!*name)
1200bc421551SDag-Erling Smørgrav 	    error(_("empty file name"));
1201bc421551SDag-Erling Smørgrav 	  else
1202bc421551SDag-Erling Smørgrav 	    error(_(component == name
1203bc421551SDag-Erling Smørgrav 		     ? "file name '%s' begins with '/'"
1204bc421551SDag-Erling Smørgrav 		     : *component_end
1205bc421551SDag-Erling Smørgrav 		     ? "file name '%s' contains '//'"
1206bc421551SDag-Erling Smørgrav 		     : "file name '%s' ends with '/'"),
1207bc421551SDag-Erling Smørgrav 		   name);
1208bc421551SDag-Erling Smørgrav 	  return false;
1209bc421551SDag-Erling Smørgrav 	}
1210bc421551SDag-Erling Smørgrav 	if (0 < component_len && component_len <= 2
1211bc421551SDag-Erling Smørgrav 	    && component[0] == '.' && component_end[-1] == '.') {
1212bc421551SDag-Erling Smørgrav 	  int len = component_len;
1213bc421551SDag-Erling Smørgrav 	  error(_("file name '%s' contains '%.*s' component"),
1214bc421551SDag-Erling Smørgrav 		name, len, component);
1215bc421551SDag-Erling Smørgrav 	  return false;
1216bc421551SDag-Erling Smørgrav 	}
1217bc421551SDag-Erling Smørgrav 	if (noise) {
1218bc421551SDag-Erling Smørgrav 	  if (0 < component_len && component[0] == '-')
1219bc421551SDag-Erling Smørgrav 	    warning(_("file name '%s' component contains leading '-'"),
1220bc421551SDag-Erling Smørgrav 		    name);
1221bc421551SDag-Erling Smørgrav 	  if (component_len_max < component_len)
1222bc421551SDag-Erling Smørgrav 	    warning(_("file name '%s' contains overlength component"
1223bc421551SDag-Erling Smørgrav 		      " '%.*s...'"),
1224bc421551SDag-Erling Smørgrav 		    name, component_len_max, component);
1225bc421551SDag-Erling Smørgrav 	}
1226bc421551SDag-Erling Smørgrav 	return true;
1227bc421551SDag-Erling Smørgrav }
1228bc421551SDag-Erling Smørgrav 
1229bc421551SDag-Erling Smørgrav static bool
1230bc421551SDag-Erling Smørgrav namecheck(const char *name)
1231bc421551SDag-Erling Smørgrav {
1232bc421551SDag-Erling Smørgrav 	register char const *cp;
1233bc421551SDag-Erling Smørgrav 
1234bc421551SDag-Erling Smørgrav 	/* Benign characters in a portable file name.  */
1235bc421551SDag-Erling Smørgrav 	static char const benign[] =
1236bc421551SDag-Erling Smørgrav 	  "-/_"
1237bc421551SDag-Erling Smørgrav 	  "abcdefghijklmnopqrstuvwxyz"
1238bc421551SDag-Erling Smørgrav 	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1239bc421551SDag-Erling Smørgrav 
1240bc421551SDag-Erling Smørgrav 	/* Non-control chars in the POSIX portable character set,
1241bc421551SDag-Erling Smørgrav 	   excluding the benign characters.  */
1242bc421551SDag-Erling Smørgrav 	static char const printable_and_not_benign[] =
1243bc421551SDag-Erling Smørgrav 	  " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
1244bc421551SDag-Erling Smørgrav 
1245bc421551SDag-Erling Smørgrav 	register char const *component = name;
1246bc421551SDag-Erling Smørgrav 	for (cp = name; *cp; cp++) {
1247bc421551SDag-Erling Smørgrav 		unsigned char c = *cp;
1248bc421551SDag-Erling Smørgrav 		if (noise && !strchr(benign, c)) {
1249bc421551SDag-Erling Smørgrav 			warning((strchr(printable_and_not_benign, c)
1250bc421551SDag-Erling Smørgrav 				 ? _("file name '%s' contains byte '%c'")
1251bc421551SDag-Erling Smørgrav 				 : _("file name '%s' contains byte '\\%o'")),
1252bc421551SDag-Erling Smørgrav 				name, c);
1253bc421551SDag-Erling Smørgrav 		}
1254bc421551SDag-Erling Smørgrav 		if (c == '/') {
1255bc421551SDag-Erling Smørgrav 			if (!componentcheck(name, component, cp))
1256bc421551SDag-Erling Smørgrav 			  return false;
1257bc421551SDag-Erling Smørgrav 			component = cp + 1;
1258bc421551SDag-Erling Smørgrav 		}
1259bc421551SDag-Erling Smørgrav 	}
1260bc421551SDag-Erling Smørgrav 	return componentcheck(name, component, cp);
1261bc421551SDag-Erling Smørgrav }
1262bc421551SDag-Erling Smørgrav 
1263bc421551SDag-Erling Smørgrav /* Return a random uint_fast64_t.  */
1264bc421551SDag-Erling Smørgrav static uint_fast64_t
1265bc421551SDag-Erling Smørgrav get_rand_u64(void)
1266bc421551SDag-Erling Smørgrav {
1267bc421551SDag-Erling Smørgrav #if HAVE_GETRANDOM
1268bc421551SDag-Erling Smørgrav   static uint_fast64_t entropy_buffer[max(1, 256 / sizeof(uint_fast64_t))];
1269bc421551SDag-Erling Smørgrav   static int nwords;
1270bc421551SDag-Erling Smørgrav   if (!nwords) {
1271bc421551SDag-Erling Smørgrav     ssize_t s;
1272bc421551SDag-Erling Smørgrav     do
1273bc421551SDag-Erling Smørgrav       s = getrandom(entropy_buffer, sizeof entropy_buffer, 0);
1274bc421551SDag-Erling Smørgrav     while (s < 0 && errno == EINTR);
1275bc421551SDag-Erling Smørgrav 
1276d5c85ac6SDag-Erling Smørgrav     if (s < 0)
1277d5c85ac6SDag-Erling Smørgrav       nwords = -1;
1278d5c85ac6SDag-Erling Smørgrav     else
1279d5c85ac6SDag-Erling Smørgrav       nwords = s / sizeof *entropy_buffer;
1280bc421551SDag-Erling Smørgrav   }
1281bc421551SDag-Erling Smørgrav   if (0 < nwords)
1282bc421551SDag-Erling Smørgrav     return entropy_buffer[--nwords];
1283bc421551SDag-Erling Smørgrav #endif
1284bc421551SDag-Erling Smørgrav 
1285bc421551SDag-Erling Smørgrav   /* getrandom didn't work, so fall back on portable code that is
1286bc421551SDag-Erling Smørgrav      not the best because the seed isn't cryptographically random and
1287bc421551SDag-Erling Smørgrav      'rand' might not be cryptographically secure.  */
1288bc421551SDag-Erling Smørgrav   {
1289bc421551SDag-Erling Smørgrav     static bool initialized;
1290bc421551SDag-Erling Smørgrav     if (!initialized) {
1291bc421551SDag-Erling Smørgrav       srand(time(NULL));
1292bc421551SDag-Erling Smørgrav       initialized = true;
1293bc421551SDag-Erling Smørgrav     }
1294bc421551SDag-Erling Smørgrav   }
1295bc421551SDag-Erling Smørgrav 
1296bc421551SDag-Erling Smørgrav   /* Return a random number if rand() yields a random number and in
1297bc421551SDag-Erling Smørgrav      the typical case where RAND_MAX is one less than a power of two.
1298bc421551SDag-Erling Smørgrav      In other cases this code yields a sort-of-random number.  */
1299bc421551SDag-Erling Smørgrav   {
1300bc421551SDag-Erling Smørgrav     uint_fast64_t rand_max = RAND_MAX,
1301bc421551SDag-Erling Smørgrav       nrand = rand_max < UINT_FAST64_MAX ? rand_max + 1 : 0,
1302bc421551SDag-Erling Smørgrav       rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1,
1303bc421551SDag-Erling Smørgrav       r = 0, rmax = 0;
1304bc421551SDag-Erling Smørgrav 
1305bc421551SDag-Erling Smørgrav     do {
1306bc421551SDag-Erling Smørgrav       uint_fast64_t rmax1 = rmax;
1307bc421551SDag-Erling Smørgrav       if (rmod) {
1308bc421551SDag-Erling Smørgrav 	/* Avoid signed integer overflow on theoretical platforms
1309bc421551SDag-Erling Smørgrav 	   where uint_fast64_t promotes to int.  */
1310bc421551SDag-Erling Smørgrav 	rmax1 %= rmod;
1311bc421551SDag-Erling Smørgrav 	r %= rmod;
1312bc421551SDag-Erling Smørgrav       }
1313bc421551SDag-Erling Smørgrav       rmax1 = nrand * rmax1 + rand_max;
1314bc421551SDag-Erling Smørgrav       r = nrand * r + rand();
1315bc421551SDag-Erling Smørgrav       rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX;
1316bc421551SDag-Erling Smørgrav     } while (rmax < UINT_FAST64_MAX);
1317bc421551SDag-Erling Smørgrav 
1318bc421551SDag-Erling Smørgrav     return r;
1319bc421551SDag-Erling Smørgrav   }
1320bc421551SDag-Erling Smørgrav }
1321bc421551SDag-Erling Smørgrav 
1322bc421551SDag-Erling Smørgrav /* Generate a randomish name in the same directory as *NAME.  If
1323bc421551SDag-Erling Smørgrav    *NAMEALLOC, put the name into *NAMEALLOC which is assumed to be
1324bc421551SDag-Erling Smørgrav    that returned by a previous call and is thus already almost set up
1325bc421551SDag-Erling Smørgrav    and equal to *NAME; otherwise, allocate a new name and put its
1326bc421551SDag-Erling Smørgrav    address into both *NAMEALLOC and *NAME.  */
1327bc421551SDag-Erling Smørgrav static void
1328bc421551SDag-Erling Smørgrav random_dirent(char const **name, char **namealloc)
1329bc421551SDag-Erling Smørgrav {
1330bc421551SDag-Erling Smørgrav   char const *src = *name;
1331bc421551SDag-Erling Smørgrav   char *dst = *namealloc;
1332bc421551SDag-Erling Smørgrav   static char const prefix[] = ".zic";
1333bc421551SDag-Erling Smørgrav   static char const alphabet[] =
1334bc421551SDag-Erling Smørgrav     "abcdefghijklmnopqrstuvwxyz"
1335bc421551SDag-Erling Smørgrav     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1336bc421551SDag-Erling Smørgrav     "0123456789";
1337bc421551SDag-Erling Smørgrav   enum { prefixlen = sizeof prefix - 1, alphabetlen = sizeof alphabet - 1 };
1338bc421551SDag-Erling Smørgrav   int suffixlen = 6;
1339bc421551SDag-Erling Smørgrav   char const *lastslash = strrchr(src, '/');
1340bc421551SDag-Erling Smørgrav   ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0;
1341bc421551SDag-Erling Smørgrav   int i;
1342bc421551SDag-Erling Smørgrav   uint_fast64_t r;
1343bc421551SDag-Erling Smørgrav   uint_fast64_t base = alphabetlen;
1344bc421551SDag-Erling Smørgrav 
1345bc421551SDag-Erling Smørgrav   /* BASE**6 */
1346bc421551SDag-Erling Smørgrav   uint_fast64_t base__6 = base * base * base * base * base * base;
1347bc421551SDag-Erling Smørgrav 
1348bc421551SDag-Erling Smørgrav   /* The largest uintmax_t that is a multiple of BASE**6.  Any random
1349bc421551SDag-Erling Smørgrav      uintmax_t value that is this value or greater, yields a biased
1350bc421551SDag-Erling Smørgrav      remainder when divided by BASE**6.  UNFAIR_MIN equals the
1351bc421551SDag-Erling Smørgrav      mathematical value of ((UINTMAX_MAX + 1) - (UINTMAX_MAX + 1) % BASE**6)
1352bc421551SDag-Erling Smørgrav      computed without overflow.  */
1353bc421551SDag-Erling Smørgrav   uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
1354bc421551SDag-Erling Smørgrav 
1355bc421551SDag-Erling Smørgrav   if (!dst) {
1356bc421551SDag-Erling Smørgrav     dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
1357bc421551SDag-Erling Smørgrav     memcpy(dst, src, dirlen);
1358bc421551SDag-Erling Smørgrav     memcpy(dst + dirlen, prefix, prefixlen);
1359bc421551SDag-Erling Smørgrav     dst[dirlen + prefixlen + suffixlen] = '\0';
1360bc421551SDag-Erling Smørgrav     *name = *namealloc = dst;
1361bc421551SDag-Erling Smørgrav   }
1362bc421551SDag-Erling Smørgrav 
1363bc421551SDag-Erling Smørgrav   do
1364bc421551SDag-Erling Smørgrav     r = get_rand_u64();
1365bc421551SDag-Erling Smørgrav   while (unfair_min <= r);
1366bc421551SDag-Erling Smørgrav 
1367bc421551SDag-Erling Smørgrav   for (i = 0; i < suffixlen; i++) {
1368bc421551SDag-Erling Smørgrav     dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
1369bc421551SDag-Erling Smørgrav     r /= alphabetlen;
1370bc421551SDag-Erling Smørgrav   }
1371bc421551SDag-Erling Smørgrav }
1372bc421551SDag-Erling Smørgrav 
1373bc421551SDag-Erling Smørgrav /* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
1374bc421551SDag-Erling Smørgrav    name of the temporary file that will eventually be renamed to
1375bc421551SDag-Erling Smørgrav    *OUTNAME.  Assign the temporary file's name to both *OUTNAME and
1376bc421551SDag-Erling Smørgrav    *TEMPNAME.  If *TEMPNAME is null, allocate the name of any such
1377bc421551SDag-Erling Smørgrav    temporary file; otherwise, reuse *TEMPNAME's storage, which is
1378bc421551SDag-Erling Smørgrav    already set up and only needs its trailing suffix updated.  */
1379bc421551SDag-Erling Smørgrav static FILE *
1380bc421551SDag-Erling Smørgrav open_outfile(char const **outname, char **tempname)
1381bc421551SDag-Erling Smørgrav {
1382bc421551SDag-Erling Smørgrav #if __STDC_VERSION__ < 201112
1383bc421551SDag-Erling Smørgrav   static char const fopen_mode[] = "wb";
1384bc421551SDag-Erling Smørgrav #else
1385bc421551SDag-Erling Smørgrav   static char const fopen_mode[] = "wbx";
1386bc421551SDag-Erling Smørgrav #endif
1387bc421551SDag-Erling Smørgrav 
1388bc421551SDag-Erling Smørgrav   FILE *fp;
1389bc421551SDag-Erling Smørgrav   bool dirs_made = false;
1390bc421551SDag-Erling Smørgrav   if (!*tempname)
1391bc421551SDag-Erling Smørgrav     random_dirent(outname, tempname);
1392bc421551SDag-Erling Smørgrav 
1393bc421551SDag-Erling Smørgrav   /*
1394bc421551SDag-Erling Smørgrav    * Remove old file, if any, to snap links.
1395bc421551SDag-Erling Smørgrav    */
1396bc421551SDag-Erling Smørgrav   if (remove(*outname) != 0 && errno != ENOENT && errno != EISDIR) {
1397bc421551SDag-Erling Smørgrav     fprintf(stderr, _("can't remove %s"), *outname);
1398bc421551SDag-Erling Smørgrav     exit(EXIT_FAILURE);
1399bc421551SDag-Erling Smørgrav   }
1400bc421551SDag-Erling Smørgrav 
1401bc421551SDag-Erling Smørgrav   while (! (fp = fopen(*outname, fopen_mode))) {
1402bc421551SDag-Erling Smørgrav     int fopen_errno = errno;
1403bc421551SDag-Erling Smørgrav     if (fopen_errno == ENOENT && !dirs_made) {
1404bc421551SDag-Erling Smørgrav       mkdirs(*outname, true);
1405bc421551SDag-Erling Smørgrav       dirs_made = true;
1406bc421551SDag-Erling Smørgrav     } else if (fopen_errno == EEXIST)
1407bc421551SDag-Erling Smørgrav       random_dirent(outname, tempname);
1408bc421551SDag-Erling Smørgrav     else {
1409bc421551SDag-Erling Smørgrav       fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
1410bc421551SDag-Erling Smørgrav 	      progname, directory, *outname, strerror(fopen_errno));
1411bc421551SDag-Erling Smørgrav       exit(EXIT_FAILURE);
1412bc421551SDag-Erling Smørgrav     }
1413bc421551SDag-Erling Smørgrav   }
1414bc421551SDag-Erling Smørgrav 
1415bc421551SDag-Erling Smørgrav   return fp;
1416bc421551SDag-Erling Smørgrav }
1417bc421551SDag-Erling Smørgrav 
1418bc421551SDag-Erling Smørgrav /* If TEMPNAME, the result is in the temporary file TEMPNAME even
1419bc421551SDag-Erling Smørgrav    though the user wanted it in NAME, so rename TEMPNAME to NAME.
1420bc421551SDag-Erling Smørgrav    Report an error and exit if there is trouble.  Also, free TEMPNAME.  */
1421bc421551SDag-Erling Smørgrav static void
1422bc421551SDag-Erling Smørgrav rename_dest(char *tempname, char const *name)
1423bc421551SDag-Erling Smørgrav {
1424bc421551SDag-Erling Smørgrav   if (tempname) {
1425bc421551SDag-Erling Smørgrav     if (rename(tempname, name) != 0) {
1426bc421551SDag-Erling Smørgrav       int rename_errno = errno;
14272aad7570SDag-Erling Smørgrav       (void)remove(tempname);
1428bc421551SDag-Erling Smørgrav       fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
1429bc421551SDag-Erling Smørgrav 	      progname, directory, name, strerror(rename_errno));
1430bc421551SDag-Erling Smørgrav       exit(EXIT_FAILURE);
1431bc421551SDag-Erling Smørgrav     }
1432bc421551SDag-Erling Smørgrav     free(tempname);
1433bc421551SDag-Erling Smørgrav   }
1434bc421551SDag-Erling Smørgrav }
1435bc421551SDag-Erling Smørgrav 
143646c59934SDag-Erling Smørgrav /* Create symlink contents suitable for symlinking TARGET to LINKNAME, as a
143746c59934SDag-Erling Smørgrav    freshly allocated string.  TARGET should be a relative file name, and
143846c59934SDag-Erling Smørgrav    is relative to the global variable DIRECTORY.  LINKNAME can be either
1439bc421551SDag-Erling Smørgrav    relative or absolute.  */
1440bc421551SDag-Erling Smørgrav static char *
1441bc421551SDag-Erling Smørgrav relname(char const *target, char const *linkname)
1442bc421551SDag-Erling Smørgrav {
1443bc421551SDag-Erling Smørgrav   size_t i, taillen, dir_len = 0, dotdots = 0;
144475411d15SDag-Erling Smørgrav   ptrdiff_t dotdotetcsize, linksize = INDEX_MAX;
1445bc421551SDag-Erling Smørgrav   char const *f = target;
1446bc421551SDag-Erling Smørgrav   char *result = NULL;
1447bc421551SDag-Erling Smørgrav   if (*linkname == '/') {
1448bc421551SDag-Erling Smørgrav     /* Make F absolute too.  */
1449bc421551SDag-Erling Smørgrav     size_t len = strlen(directory);
1450bc421551SDag-Erling Smørgrav     size_t lenslash = len + (len && directory[len - 1] != '/');
1451bc421551SDag-Erling Smørgrav     size_t targetsize = strlen(target) + 1;
1452bc421551SDag-Erling Smørgrav     linksize = size_sum(lenslash, targetsize);
1453bc421551SDag-Erling Smørgrav     f = result = emalloc(linksize);
1454bc421551SDag-Erling Smørgrav     memcpy(result, directory, len);
1455bc421551SDag-Erling Smørgrav     result[len] = '/';
1456bc421551SDag-Erling Smørgrav     memcpy(result + lenslash, target, targetsize);
1457bc421551SDag-Erling Smørgrav   }
1458bc421551SDag-Erling Smørgrav   for (i = 0; f[i] && f[i] == linkname[i]; i++)
1459bc421551SDag-Erling Smørgrav     if (f[i] == '/')
1460bc421551SDag-Erling Smørgrav       dir_len = i + 1;
1461bc421551SDag-Erling Smørgrav   for (; linkname[i]; i++)
1462bc421551SDag-Erling Smørgrav     dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
1463bc421551SDag-Erling Smørgrav   taillen = strlen(f + dir_len);
1464bc421551SDag-Erling Smørgrav   dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
1465bc421551SDag-Erling Smørgrav   if (dotdotetcsize <= linksize) {
1466bc421551SDag-Erling Smørgrav     if (!result)
1467bc421551SDag-Erling Smørgrav       result = emalloc(dotdotetcsize);
1468bc421551SDag-Erling Smørgrav     for (i = 0; i < dotdots; i++)
1469bc421551SDag-Erling Smørgrav       memcpy(result + 3 * i, "../", 3);
1470bc421551SDag-Erling Smørgrav     memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
1471bc421551SDag-Erling Smørgrav   }
1472bc421551SDag-Erling Smørgrav   return result;
1473bc421551SDag-Erling Smørgrav }
1474bc421551SDag-Erling Smørgrav 
147546c59934SDag-Erling Smørgrav /* Return true if A and B must have the same parent dir if A and B exist.
147646c59934SDag-Erling Smørgrav    Return false if this is not necessarily true (though it might be true).
147746c59934SDag-Erling Smørgrav    Keep it simple, and do not inspect the file system.  */
1478*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static bool
147946c59934SDag-Erling Smørgrav same_parent_dirs(char const *a, char const *b)
148046c59934SDag-Erling Smørgrav {
148146c59934SDag-Erling Smørgrav   for (; *a == *b; a++, b++)
148246c59934SDag-Erling Smørgrav     if (!*a)
148346c59934SDag-Erling Smørgrav       return true;
148446c59934SDag-Erling Smørgrav   return ! (strchr(a, '/') || strchr(b, '/'));
148546c59934SDag-Erling Smørgrav }
148646c59934SDag-Erling Smørgrav 
1487bc421551SDag-Erling Smørgrav static void
1488bc421551SDag-Erling Smørgrav dolink(char const *target, char const *linkname, bool staysymlink)
1489bc421551SDag-Erling Smørgrav {
1490bc421551SDag-Erling Smørgrav 	bool linkdirs_made = false;
1491bc421551SDag-Erling Smørgrav 	int link_errno;
1492bc421551SDag-Erling Smørgrav 	char *tempname = NULL;
1493bc421551SDag-Erling Smørgrav 	char const *outname = linkname;
149446c59934SDag-Erling Smørgrav 	int targetissym = -2, linknameissym = -2;
1495bc421551SDag-Erling Smørgrav 
1496bc421551SDag-Erling Smørgrav 	check_for_signal();
1497bc421551SDag-Erling Smørgrav 
1498bc421551SDag-Erling Smørgrav 	if (strcmp(target, "-") == 0) {
1499bc421551SDag-Erling Smørgrav 	  if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR)
1500bc421551SDag-Erling Smørgrav 	    return;
1501bc421551SDag-Erling Smørgrav 	  else {
1502bc421551SDag-Erling Smørgrav 	    char const *e = strerror(errno);
1503bc421551SDag-Erling Smørgrav 	    fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
1504bc421551SDag-Erling Smørgrav 		    progname, directory, linkname, e);
1505bc421551SDag-Erling Smørgrav 	    exit(EXIT_FAILURE);
1506bc421551SDag-Erling Smørgrav 	  }
1507bc421551SDag-Erling Smørgrav 	}
1508bc421551SDag-Erling Smørgrav 
1509bc421551SDag-Erling Smørgrav 	while (true) {
1510bc421551SDag-Erling Smørgrav 	  if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW)
1511bc421551SDag-Erling Smørgrav 	      == 0) {
1512bc421551SDag-Erling Smørgrav 	    link_errno = 0;
1513bc421551SDag-Erling Smørgrav 	    break;
1514bc421551SDag-Erling Smørgrav 	  }
1515bc421551SDag-Erling Smørgrav 	  link_errno = errno;
151646c59934SDag-Erling Smørgrav 	  /* Linux 2.6.16 and 2.6.17 mishandle AT_SYMLINK_FOLLOW.  */
151746c59934SDag-Erling Smørgrav 	  if (link_errno == EINVAL)
151846c59934SDag-Erling Smørgrav 	    link_errno = ENOTSUP;
151946c59934SDag-Erling Smørgrav #if HAVE_LINK
152046c59934SDag-Erling Smørgrav 	  /* If linkat is not supported, fall back on link(A, B).
152146c59934SDag-Erling Smørgrav 	     However, skip this if A is a relative symlink
152246c59934SDag-Erling Smørgrav 	     and A and B might not have the same parent directory.
152346c59934SDag-Erling Smørgrav 	     On some platforms link(A, B) does not follow a symlink A,
152446c59934SDag-Erling Smørgrav 	     and if A is relative it might misbehave elsewhere.  */
152546c59934SDag-Erling Smørgrav 	  if (link_errno == ENOTSUP
152646c59934SDag-Erling Smørgrav 	      && (same_parent_dirs(target, outname)
152746c59934SDag-Erling Smørgrav 		  || 0 <= itssymlink(target, &targetissym))) {
152846c59934SDag-Erling Smørgrav 	    if (link(target, outname) == 0) {
152946c59934SDag-Erling Smørgrav 	      link_errno = 0;
153046c59934SDag-Erling Smørgrav 	      break;
153146c59934SDag-Erling Smørgrav 	    }
153246c59934SDag-Erling Smørgrav 	    link_errno = errno;
153346c59934SDag-Erling Smørgrav 	  }
153446c59934SDag-Erling Smørgrav #endif
1535bc421551SDag-Erling Smørgrav 	  if (link_errno == EXDEV || link_errno == ENOTSUP)
1536bc421551SDag-Erling Smørgrav 	    break;
1537bc421551SDag-Erling Smørgrav 
1538bc421551SDag-Erling Smørgrav 	  if (link_errno == EEXIST) {
1539bc421551SDag-Erling Smørgrav 	    staysymlink &= !tempname;
1540bc421551SDag-Erling Smørgrav 	    random_dirent(&outname, &tempname);
154146c59934SDag-Erling Smørgrav 	    if (staysymlink && itssymlink(linkname, &linknameissym))
1542bc421551SDag-Erling Smørgrav 	      break;
1543bc421551SDag-Erling Smørgrav 	  } else if (link_errno == ENOENT && !linkdirs_made) {
1544bc421551SDag-Erling Smørgrav 	    mkdirs(linkname, true);
1545bc421551SDag-Erling Smørgrav 	    linkdirs_made = true;
1546bc421551SDag-Erling Smørgrav 	  } else {
1547bc421551SDag-Erling Smørgrav 	    fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
1548bc421551SDag-Erling Smørgrav 		    progname, directory, target, directory, outname,
1549bc421551SDag-Erling Smørgrav 		    strerror(link_errno));
1550bc421551SDag-Erling Smørgrav 	    exit(EXIT_FAILURE);
1551bc421551SDag-Erling Smørgrav 	  }
1552bc421551SDag-Erling Smørgrav 	}
1553bc421551SDag-Erling Smørgrav 	if (link_errno != 0) {
1554bc421551SDag-Erling Smørgrav 	  bool absolute = *target == '/';
1555bc421551SDag-Erling Smørgrav 	  char *linkalloc = absolute ? NULL : relname(target, linkname);
1556bc421551SDag-Erling Smørgrav 	  char const *contents = absolute ? target : linkalloc;
1557bc421551SDag-Erling Smørgrav 	  int symlink_errno;
1558bc421551SDag-Erling Smørgrav 
1559bc421551SDag-Erling Smørgrav 	  while (true) {
1560bc421551SDag-Erling Smørgrav 	    if (symlink(contents, outname) == 0) {
1561bc421551SDag-Erling Smørgrav 	      symlink_errno = 0;
1562bc421551SDag-Erling Smørgrav 	      break;
1563bc421551SDag-Erling Smørgrav 	    }
1564bc421551SDag-Erling Smørgrav 	    symlink_errno = errno;
1565bc421551SDag-Erling Smørgrav 	    if (symlink_errno == EEXIST)
1566bc421551SDag-Erling Smørgrav 	      random_dirent(&outname, &tempname);
1567bc421551SDag-Erling Smørgrav 	    else if (symlink_errno == ENOENT && !linkdirs_made) {
1568bc421551SDag-Erling Smørgrav 	      mkdirs(linkname, true);
1569bc421551SDag-Erling Smørgrav 	      linkdirs_made = true;
1570bc421551SDag-Erling Smørgrav 	    } else
1571bc421551SDag-Erling Smørgrav 	      break;
1572bc421551SDag-Erling Smørgrav 	  }
1573bc421551SDag-Erling Smørgrav 	  free(linkalloc);
1574bc421551SDag-Erling Smørgrav 	  if (symlink_errno == 0) {
1575bc421551SDag-Erling Smørgrav 	    if (link_errno != ENOTSUP && link_errno != EEXIST)
1576bc421551SDag-Erling Smørgrav 	      warning(_("symbolic link used because hard link failed: %s"),
1577bc421551SDag-Erling Smørgrav 		      strerror(link_errno));
1578bc421551SDag-Erling Smørgrav 	  } else {
1579bc421551SDag-Erling Smørgrav 	    FILE *fp, *tp;
1580bc421551SDag-Erling Smørgrav 	    int c;
1581bc421551SDag-Erling Smørgrav 	    fp = fopen(target, "rb");
1582bc421551SDag-Erling Smørgrav 	    if (!fp) {
1583bc421551SDag-Erling Smørgrav 	      char const *e = strerror(errno);
1584bc421551SDag-Erling Smørgrav 	      fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
1585bc421551SDag-Erling Smørgrav 		      progname, directory, target, e);
1586bc421551SDag-Erling Smørgrav 	      exit(EXIT_FAILURE);
1587bc421551SDag-Erling Smørgrav 	    }
1588bc421551SDag-Erling Smørgrav 	    tp = open_outfile(&outname, &tempname);
1589bc421551SDag-Erling Smørgrav 	    while ((c = getc(fp)) != EOF)
1590bc421551SDag-Erling Smørgrav 	      putc(c, tp);
1591bc421551SDag-Erling Smørgrav 	    close_file(tp, directory, linkname, tempname);
1592bc421551SDag-Erling Smørgrav 	    close_file(fp, directory, target, NULL);
1593bc421551SDag-Erling Smørgrav 	    if (link_errno != ENOTSUP)
1594bc421551SDag-Erling Smørgrav 	      warning(_("copy used because hard link failed: %s"),
1595bc421551SDag-Erling Smørgrav 		      strerror(link_errno));
1596bc421551SDag-Erling Smørgrav 	    else if (symlink_errno != ENOTSUP)
1597bc421551SDag-Erling Smørgrav 	      warning(_("copy used because symbolic link failed: %s"),
1598bc421551SDag-Erling Smørgrav 		      strerror(symlink_errno));
1599bc421551SDag-Erling Smørgrav 	  }
1600bc421551SDag-Erling Smørgrav 	}
1601bc421551SDag-Erling Smørgrav 	rename_dest(tempname, linkname);
1602bc421551SDag-Erling Smørgrav }
1603bc421551SDag-Erling Smørgrav 
160446c59934SDag-Erling Smørgrav /* Return 1 if NAME is an absolute symbolic link, -1 if it is relative,
160546c59934SDag-Erling Smørgrav    0 if it is not a symbolic link.  If *CACHE is not -2, it is the
160646c59934SDag-Erling Smørgrav    cached result of a previous call to this function with the same NAME.  */
160746c59934SDag-Erling Smørgrav static int
160846c59934SDag-Erling Smørgrav itssymlink(char const *name, int *cache)
1609bc421551SDag-Erling Smørgrav {
161046c59934SDag-Erling Smørgrav   if (*cache == -2) {
161146c59934SDag-Erling Smørgrav     char c = '\0';
161246c59934SDag-Erling Smørgrav     *cache = readlink(name, &c, 1) < 0 ? 0 : c == '/' ? 1 : -1;
161346c59934SDag-Erling Smørgrav   }
161446c59934SDag-Erling Smørgrav   return *cache;
1615bc421551SDag-Erling Smørgrav }
1616bc421551SDag-Erling Smørgrav 
1617bc421551SDag-Erling Smørgrav /*
1618bc421551SDag-Erling Smørgrav ** Associate sets of rules with zones.
1619bc421551SDag-Erling Smørgrav */
1620bc421551SDag-Erling Smørgrav 
1621bc421551SDag-Erling Smørgrav /*
1622bc421551SDag-Erling Smørgrav ** Sort by rule name.
1623bc421551SDag-Erling Smørgrav */
1624bc421551SDag-Erling Smørgrav 
1625bc421551SDag-Erling Smørgrav static int
1626bc421551SDag-Erling Smørgrav rcomp(const void *cp1, const void *cp2)
1627bc421551SDag-Erling Smørgrav {
1628bc421551SDag-Erling Smørgrav   struct rule const *r1 = cp1, *r2 = cp2;
1629bc421551SDag-Erling Smørgrav   return strcmp(r1->r_name, r2->r_name);
1630bc421551SDag-Erling Smørgrav }
1631bc421551SDag-Erling Smørgrav 
1632bc421551SDag-Erling Smørgrav static void
1633bc421551SDag-Erling Smørgrav associate(void)
1634bc421551SDag-Erling Smørgrav {
1635bc421551SDag-Erling Smørgrav 	register struct zone *	zp;
1636bc421551SDag-Erling Smørgrav 	register struct rule *	rp;
1637bc421551SDag-Erling Smørgrav 	register ptrdiff_t i, j, base, out;
1638bc421551SDag-Erling Smørgrav 
1639bc421551SDag-Erling Smørgrav 	if (1 < nrules) {
1640bc421551SDag-Erling Smørgrav 		qsort(rules, nrules, sizeof *rules, rcomp);
1641bc421551SDag-Erling Smørgrav 		for (i = 0; i < nrules - 1; ++i) {
1642bc421551SDag-Erling Smørgrav 			if (strcmp(rules[i].r_name,
1643bc421551SDag-Erling Smørgrav 				rules[i + 1].r_name) != 0)
1644bc421551SDag-Erling Smørgrav 					continue;
1645bc421551SDag-Erling Smørgrav 			if (rules[i].r_filenum == rules[i + 1].r_filenum)
1646bc421551SDag-Erling Smørgrav 					continue;
1647bc421551SDag-Erling Smørgrav 			eat(rules[i].r_filenum, rules[i].r_linenum);
1648bc421551SDag-Erling Smørgrav 			warning(_("same rule name in multiple files"));
1649bc421551SDag-Erling Smørgrav 			eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum);
1650bc421551SDag-Erling Smørgrav 			warning(_("same rule name in multiple files"));
1651bc421551SDag-Erling Smørgrav 			for (j = i + 2; j < nrules; ++j) {
1652bc421551SDag-Erling Smørgrav 				if (strcmp(rules[i].r_name,
1653bc421551SDag-Erling Smørgrav 					rules[j].r_name) != 0)
1654bc421551SDag-Erling Smørgrav 						break;
1655bc421551SDag-Erling Smørgrav 				if (rules[i].r_filenum == rules[j].r_filenum)
1656bc421551SDag-Erling Smørgrav 						continue;
1657bc421551SDag-Erling Smørgrav 				if (rules[i + 1].r_filenum
1658bc421551SDag-Erling Smørgrav 				    == rules[j].r_filenum)
1659bc421551SDag-Erling Smørgrav 						continue;
1660bc421551SDag-Erling Smørgrav 				break;
1661bc421551SDag-Erling Smørgrav 			}
1662bc421551SDag-Erling Smørgrav 			i = j - 1;
1663bc421551SDag-Erling Smørgrav 		}
1664bc421551SDag-Erling Smørgrav 	}
1665bc421551SDag-Erling Smørgrav 	for (i = 0; i < nzones; ++i) {
1666bc421551SDag-Erling Smørgrav 		zp = &zones[i];
1667bc421551SDag-Erling Smørgrav 		zp->z_rules = NULL;
1668bc421551SDag-Erling Smørgrav 		zp->z_nrules = 0;
1669bc421551SDag-Erling Smørgrav 	}
1670bc421551SDag-Erling Smørgrav 	for (base = 0; base < nrules; base = out) {
1671bc421551SDag-Erling Smørgrav 		rp = &rules[base];
1672bc421551SDag-Erling Smørgrav 		for (out = base + 1; out < nrules; ++out)
1673bc421551SDag-Erling Smørgrav 			if (strcmp(rp->r_name, rules[out].r_name) != 0)
1674bc421551SDag-Erling Smørgrav 				break;
1675bc421551SDag-Erling Smørgrav 		for (i = 0; i < nzones; ++i) {
1676bc421551SDag-Erling Smørgrav 			zp = &zones[i];
1677bc421551SDag-Erling Smørgrav 			if (strcmp(zp->z_rule, rp->r_name) != 0)
1678bc421551SDag-Erling Smørgrav 				continue;
1679bc421551SDag-Erling Smørgrav 			zp->z_rules = rp;
1680bc421551SDag-Erling Smørgrav 			zp->z_nrules = out - base;
1681bc421551SDag-Erling Smørgrav 		}
1682bc421551SDag-Erling Smørgrav 	}
1683bc421551SDag-Erling Smørgrav 	for (i = 0; i < nzones; ++i) {
1684bc421551SDag-Erling Smørgrav 		zp = &zones[i];
1685bc421551SDag-Erling Smørgrav 		if (zp->z_nrules == 0) {
1686bc421551SDag-Erling Smørgrav 			/*
1687bc421551SDag-Erling Smørgrav 			** Maybe we have a local standard time offset.
1688bc421551SDag-Erling Smørgrav 			*/
1689bc421551SDag-Erling Smørgrav 			eat(zp->z_filenum, zp->z_linenum);
1690bc421551SDag-Erling Smørgrav 			zp->z_save = getsave(zp->z_rule, &zp->z_isdst);
1691bc421551SDag-Erling Smørgrav 			/*
1692bc421551SDag-Erling Smørgrav 			** Note, though, that if there's no rule,
1693bc421551SDag-Erling Smørgrav 			** a '%s' in the format is a bad thing.
1694bc421551SDag-Erling Smørgrav 			*/
1695bc421551SDag-Erling Smørgrav 			if (zp->z_format_specifier == 's')
1696bc421551SDag-Erling Smørgrav 				error("%s", _("%s in ruleless zone"));
1697bc421551SDag-Erling Smørgrav 		}
1698bc421551SDag-Erling Smørgrav 	}
1699bc421551SDag-Erling Smørgrav 	if (errors)
1700bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
1701bc421551SDag-Erling Smørgrav }
1702bc421551SDag-Erling Smørgrav 
1703bc421551SDag-Erling Smørgrav /* Read a text line from FP into BUF, which is of size BUFSIZE.
1704bc421551SDag-Erling Smørgrav    Terminate it with a NUL byte instead of a newline.
1705bc421551SDag-Erling Smørgrav    Return true if successful, false if EOF.
1706bc421551SDag-Erling Smørgrav    On error, report the error and exit.  */
1707bc421551SDag-Erling Smørgrav static bool
1708bc421551SDag-Erling Smørgrav inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
1709bc421551SDag-Erling Smørgrav {
1710bc421551SDag-Erling Smørgrav   ptrdiff_t linelen = 0, ch;
1711bc421551SDag-Erling Smørgrav   while ((ch = getc(fp)) != '\n') {
1712bc421551SDag-Erling Smørgrav     if (ch < 0) {
1713bc421551SDag-Erling Smørgrav       if (ferror(fp)) {
1714bc421551SDag-Erling Smørgrav 	error(_("input error"));
1715bc421551SDag-Erling Smørgrav 	exit(EXIT_FAILURE);
1716bc421551SDag-Erling Smørgrav       }
1717bc421551SDag-Erling Smørgrav       if (linelen == 0)
1718bc421551SDag-Erling Smørgrav 	return false;
1719bc421551SDag-Erling Smørgrav       error(_("unterminated line"));
1720bc421551SDag-Erling Smørgrav       exit(EXIT_FAILURE);
1721bc421551SDag-Erling Smørgrav     }
1722bc421551SDag-Erling Smørgrav     if (!ch) {
1723bc421551SDag-Erling Smørgrav       error(_("NUL input byte"));
1724bc421551SDag-Erling Smørgrav       exit(EXIT_FAILURE);
1725bc421551SDag-Erling Smørgrav     }
1726bc421551SDag-Erling Smørgrav     buf[linelen++] = ch;
1727bc421551SDag-Erling Smørgrav     if (linelen == bufsize) {
1728bc421551SDag-Erling Smørgrav       error(_("line too long"));
1729bc421551SDag-Erling Smørgrav       exit(EXIT_FAILURE);
1730bc421551SDag-Erling Smørgrav     }
1731bc421551SDag-Erling Smørgrav   }
1732bc421551SDag-Erling Smørgrav   buf[linelen] = '\0';
1733bc421551SDag-Erling Smørgrav   return true;
1734bc421551SDag-Erling Smørgrav }
1735bc421551SDag-Erling Smørgrav 
1736bc421551SDag-Erling Smørgrav static void
1737bc421551SDag-Erling Smørgrav infile(int fnum, char const *name)
1738bc421551SDag-Erling Smørgrav {
1739bc421551SDag-Erling Smørgrav 	register FILE *			fp;
1740bc421551SDag-Erling Smørgrav 	register const struct lookup *	lp;
1741bc421551SDag-Erling Smørgrav 	register bool			wantcont;
1742bc421551SDag-Erling Smørgrav 	register lineno			num;
1743bc421551SDag-Erling Smørgrav 
1744bc421551SDag-Erling Smørgrav 	if (strcmp(name, "-") == 0) {
1745bc421551SDag-Erling Smørgrav 		fp = stdin;
1746bc421551SDag-Erling Smørgrav 	} else if ((fp = fopen(name, "r")) == NULL) {
1747bc421551SDag-Erling Smørgrav 		const char *e = strerror(errno);
1748bc421551SDag-Erling Smørgrav 
1749bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("%s: Can't open %s: %s\n"),
1750bc421551SDag-Erling Smørgrav 			progname, name, e);
1751bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
1752bc421551SDag-Erling Smørgrav 	}
1753bc421551SDag-Erling Smørgrav 	wantcont = false;
1754bc421551SDag-Erling Smørgrav 	for (num = 1; ; ++num) {
1755bc421551SDag-Erling Smørgrav 		enum { bufsize_bound
175675411d15SDag-Erling Smørgrav 		  = (min(INT_MAX, INDEX_MAX) / FORMAT_LEN_GROWTH_BOUND) };
1757bc421551SDag-Erling Smørgrav 		char buf[min(_POSIX2_LINE_MAX, bufsize_bound)];
1758bc421551SDag-Erling Smørgrav 		int nfields;
1759bc421551SDag-Erling Smørgrav 		char *fields[MAX_FIELDS];
1760bc421551SDag-Erling Smørgrav 		eat(fnum, num);
1761bc421551SDag-Erling Smørgrav 		if (!inputline(fp, buf, sizeof buf))
1762bc421551SDag-Erling Smørgrav 		  break;
1763bc421551SDag-Erling Smørgrav 		nfields = getfields(buf, fields,
1764bc421551SDag-Erling Smørgrav 				    sizeof fields / sizeof *fields);
1765bc421551SDag-Erling Smørgrav 		if (nfields == 0) {
1766bc421551SDag-Erling Smørgrav 			/* nothing to do */
1767bc421551SDag-Erling Smørgrav 		} else if (wantcont) {
1768bc421551SDag-Erling Smørgrav 			wantcont = inzcont(fields, nfields);
1769bc421551SDag-Erling Smørgrav 		} else {
1770bc421551SDag-Erling Smørgrav 			struct lookup const *line_codes
1771bc421551SDag-Erling Smørgrav 			  = fnum < 0 ? leap_line_codes : zi_line_codes;
1772bc421551SDag-Erling Smørgrav 			lp = byword(fields[0], line_codes);
1773bc421551SDag-Erling Smørgrav 			if (lp == NULL)
1774bc421551SDag-Erling Smørgrav 				error(_("input line of unknown type"));
1775bc421551SDag-Erling Smørgrav 			else switch (lp->l_value) {
1776bc421551SDag-Erling Smørgrav 				case LC_RULE:
1777bc421551SDag-Erling Smørgrav 					inrule(fields, nfields);
1778bc421551SDag-Erling Smørgrav 					wantcont = false;
1779bc421551SDag-Erling Smørgrav 					break;
1780bc421551SDag-Erling Smørgrav 				case LC_ZONE:
1781bc421551SDag-Erling Smørgrav 					wantcont = inzone(fields, nfields);
1782bc421551SDag-Erling Smørgrav 					break;
1783bc421551SDag-Erling Smørgrav 				case LC_LINK:
1784bc421551SDag-Erling Smørgrav 					inlink(fields, nfields);
1785bc421551SDag-Erling Smørgrav 					wantcont = false;
1786bc421551SDag-Erling Smørgrav 					break;
1787bc421551SDag-Erling Smørgrav 				case LC_LEAP:
1788bc421551SDag-Erling Smørgrav 					inleap(fields, nfields);
1789bc421551SDag-Erling Smørgrav 					wantcont = false;
1790bc421551SDag-Erling Smørgrav 					break;
1791bc421551SDag-Erling Smørgrav 				case LC_EXPIRES:
1792bc421551SDag-Erling Smørgrav 					inexpires(fields, nfields);
1793bc421551SDag-Erling Smørgrav 					wantcont = false;
1794bc421551SDag-Erling Smørgrav 					break;
1795bc421551SDag-Erling Smørgrav 				default: unreachable();
1796bc421551SDag-Erling Smørgrav 			}
1797bc421551SDag-Erling Smørgrav 		}
1798bc421551SDag-Erling Smørgrav 	}
1799bc421551SDag-Erling Smørgrav 	close_file(fp, NULL, filename(fnum), NULL);
1800bc421551SDag-Erling Smørgrav 	if (wantcont)
1801bc421551SDag-Erling Smørgrav 		error(_("expected continuation line not found"));
1802bc421551SDag-Erling Smørgrav }
1803bc421551SDag-Erling Smørgrav 
1804bc421551SDag-Erling Smørgrav /*
1805bc421551SDag-Erling Smørgrav ** Convert a string of one of the forms
1806bc421551SDag-Erling Smørgrav **	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
1807bc421551SDag-Erling Smørgrav ** into a number of seconds.
1808bc421551SDag-Erling Smørgrav ** A null string maps to zero.
1809bc421551SDag-Erling Smørgrav ** Call error with errstring and return zero on errors.
1810bc421551SDag-Erling Smørgrav */
1811bc421551SDag-Erling Smørgrav 
1812bc421551SDag-Erling Smørgrav static zic_t
1813bc421551SDag-Erling Smørgrav gethms(char const *string, char const *errstring)
1814bc421551SDag-Erling Smørgrav {
1815bc421551SDag-Erling Smørgrav 	zic_t	hh;
1816bc421551SDag-Erling Smørgrav 	int sign, mm = 0, ss = 0;
1817bc421551SDag-Erling Smørgrav 	char hhx, mmx, ssx, xr = '0', xs;
1818bc421551SDag-Erling Smørgrav 	int tenths = 0;
1819bc421551SDag-Erling Smørgrav 	bool ok = true;
1820bc421551SDag-Erling Smørgrav 
1821bc421551SDag-Erling Smørgrav 	if (string == NULL || *string == '\0')
1822bc421551SDag-Erling Smørgrav 		return 0;
1823bc421551SDag-Erling Smørgrav 	if (*string == '-') {
1824bc421551SDag-Erling Smørgrav 		sign = -1;
1825bc421551SDag-Erling Smørgrav 		++string;
1826bc421551SDag-Erling Smørgrav 	} else	sign = 1;
1827bc421551SDag-Erling Smørgrav 	switch (sscanf(string,
1828bc421551SDag-Erling Smørgrav 		       "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
1829bc421551SDag-Erling Smørgrav 		       &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) {
1830bc421551SDag-Erling Smørgrav 	  default: ok = false; break;
1831bc421551SDag-Erling Smørgrav 	  case 8:
1832bc421551SDag-Erling Smørgrav 	    ok = '0' <= xr && xr <= '9';
1833bc421551SDag-Erling Smørgrav 	    ATTRIBUTE_FALLTHROUGH;
1834bc421551SDag-Erling Smørgrav 	  case 7:
1835bc421551SDag-Erling Smørgrav 	    ok &= ssx == '.';
1836bc421551SDag-Erling Smørgrav 	    if (ok && noise)
1837bc421551SDag-Erling Smørgrav 	      warning(_("fractional seconds rejected by"
1838bc421551SDag-Erling Smørgrav 			" pre-2018 versions of zic"));
1839bc421551SDag-Erling Smørgrav 	    ATTRIBUTE_FALLTHROUGH;
1840bc421551SDag-Erling Smørgrav 	  case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH;
1841bc421551SDag-Erling Smørgrav 	  case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH;
1842bc421551SDag-Erling Smørgrav 	  case 1: break;
1843bc421551SDag-Erling Smørgrav 	}
1844bc421551SDag-Erling Smørgrav 	if (!ok) {
1845bc421551SDag-Erling Smørgrav 			error("%s", errstring);
1846bc421551SDag-Erling Smørgrav 			return 0;
1847bc421551SDag-Erling Smørgrav 	}
1848bc421551SDag-Erling Smørgrav 	if (hh < 0 ||
1849bc421551SDag-Erling Smørgrav 		mm < 0 || mm >= MINSPERHOUR ||
1850bc421551SDag-Erling Smørgrav 		ss < 0 || ss > SECSPERMIN) {
1851bc421551SDag-Erling Smørgrav 			error("%s", errstring);
1852bc421551SDag-Erling Smørgrav 			return 0;
1853bc421551SDag-Erling Smørgrav 	}
1854bc421551SDag-Erling Smørgrav 	if (ZIC_MAX / SECSPERHOUR < hh) {
1855bc421551SDag-Erling Smørgrav 		error(_("time overflow"));
1856bc421551SDag-Erling Smørgrav 		return 0;
1857bc421551SDag-Erling Smørgrav 	}
1858bc421551SDag-Erling Smørgrav 	ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even.  */
1859bc421551SDag-Erling Smørgrav 	if (noise && (hh > HOURSPERDAY ||
1860bc421551SDag-Erling Smørgrav 		(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
1861bc421551SDag-Erling Smørgrav warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
1862bc421551SDag-Erling Smørgrav 	return oadd(sign * hh * SECSPERHOUR,
1863bc421551SDag-Erling Smørgrav 		    sign * (mm * SECSPERMIN + ss));
1864bc421551SDag-Erling Smørgrav }
1865bc421551SDag-Erling Smørgrav 
1866bc421551SDag-Erling Smørgrav static zic_t
1867bc421551SDag-Erling Smørgrav getsave(char *field, bool *isdst)
1868bc421551SDag-Erling Smørgrav {
1869bc421551SDag-Erling Smørgrav   int dst = -1;
1870bc421551SDag-Erling Smørgrav   zic_t save;
1871bc421551SDag-Erling Smørgrav   ptrdiff_t fieldlen = strlen(field);
1872bc421551SDag-Erling Smørgrav   if (fieldlen != 0) {
1873bc421551SDag-Erling Smørgrav     char *ep = field + fieldlen - 1;
1874bc421551SDag-Erling Smørgrav     switch (*ep) {
1875bc421551SDag-Erling Smørgrav       case 'd': dst = 1; *ep = '\0'; break;
1876bc421551SDag-Erling Smørgrav       case 's': dst = 0; *ep = '\0'; break;
1877bc421551SDag-Erling Smørgrav     }
1878bc421551SDag-Erling Smørgrav   }
1879bc421551SDag-Erling Smørgrav   save = gethms(field, _("invalid saved time"));
1880bc421551SDag-Erling Smørgrav   *isdst = dst < 0 ? save != 0 : dst;
1881bc421551SDag-Erling Smørgrav   return save;
1882bc421551SDag-Erling Smørgrav }
1883bc421551SDag-Erling Smørgrav 
1884bc421551SDag-Erling Smørgrav static void
1885bc421551SDag-Erling Smørgrav inrule(char **fields, int nfields)
1886bc421551SDag-Erling Smørgrav {
188755572cffSDag-Erling Smørgrav 	struct rule r = { 0 };
1888bc421551SDag-Erling Smørgrav 
1889bc421551SDag-Erling Smørgrav 	if (nfields != RULE_FIELDS) {
1890bc421551SDag-Erling Smørgrav 		error(_("wrong number of fields on Rule line"));
1891bc421551SDag-Erling Smørgrav 		return;
1892bc421551SDag-Erling Smørgrav 	}
1893bc421551SDag-Erling Smørgrav 	switch (*fields[RF_NAME]) {
1894bc421551SDag-Erling Smørgrav 	  case '\0':
1895bc421551SDag-Erling Smørgrav 	  case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
1896bc421551SDag-Erling Smørgrav 	  case '+': case '-':
1897bc421551SDag-Erling Smørgrav 	  case '0': case '1': case '2': case '3': case '4':
1898bc421551SDag-Erling Smørgrav 	  case '5': case '6': case '7': case '8': case '9':
1899bc421551SDag-Erling Smørgrav 		error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
1900bc421551SDag-Erling Smørgrav 		return;
1901bc421551SDag-Erling Smørgrav 	}
1902bc421551SDag-Erling Smørgrav 	r.r_filenum = filenum;
1903bc421551SDag-Erling Smørgrav 	r.r_linenum = linenum;
1904bc421551SDag-Erling Smørgrav 	r.r_save = getsave(fields[RF_SAVE], &r.r_isdst);
1905bc421551SDag-Erling Smørgrav 	if (!rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR],
1906bc421551SDag-Erling Smørgrav 		     fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
1907bc421551SDag-Erling Smørgrav 		     fields[RF_TOD]))
1908bc421551SDag-Erling Smørgrav 	  return;
1909bc421551SDag-Erling Smørgrav 	r.r_name = estrdup(fields[RF_NAME]);
1910bc421551SDag-Erling Smørgrav 	r.r_abbrvar = estrdup(fields[RF_ABBRVAR]);
1911bc421551SDag-Erling Smørgrav 	if (max_abbrvar_len < strlen(r.r_abbrvar))
1912bc421551SDag-Erling Smørgrav 		max_abbrvar_len = strlen(r.r_abbrvar);
1913bc421551SDag-Erling Smørgrav 	rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
1914bc421551SDag-Erling Smørgrav 	rules[nrules++] = r;
1915bc421551SDag-Erling Smørgrav }
1916bc421551SDag-Erling Smørgrav 
1917bc421551SDag-Erling Smørgrav static bool
1918bc421551SDag-Erling Smørgrav inzone(char **fields, int nfields)
1919bc421551SDag-Erling Smørgrav {
1920bc421551SDag-Erling Smørgrav 	register ptrdiff_t i;
1921bc421551SDag-Erling Smørgrav 
1922bc421551SDag-Erling Smørgrav 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
1923bc421551SDag-Erling Smørgrav 		error(_("wrong number of fields on Zone line"));
1924bc421551SDag-Erling Smørgrav 		return false;
1925bc421551SDag-Erling Smørgrav 	}
1926bc421551SDag-Erling Smørgrav 	if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
192746c59934SDag-Erling Smørgrav 	  error(_("\"Zone %s\" line and -l option are mutually exclusive"),
1928bc421551SDag-Erling Smørgrav 		tzdefault);
1929bc421551SDag-Erling Smørgrav 	  return false;
1930bc421551SDag-Erling Smørgrav 	}
1931bc421551SDag-Erling Smørgrav 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
193246c59934SDag-Erling Smørgrav 	  error(_("\"Zone %s\" line and -p option are mutually exclusive"),
1933bc421551SDag-Erling Smørgrav 		TZDEFRULES);
1934bc421551SDag-Erling Smørgrav 	  return false;
1935bc421551SDag-Erling Smørgrav 	}
1936bc421551SDag-Erling Smørgrav 	for (i = 0; i < nzones; ++i)
1937bc421551SDag-Erling Smørgrav 		if (zones[i].z_name != NULL &&
1938bc421551SDag-Erling Smørgrav 			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
1939bc421551SDag-Erling Smørgrav 				error(_("duplicate zone name %s"
1940bc421551SDag-Erling Smørgrav 					" (file \"%s\", line %"PRIdMAX")"),
1941bc421551SDag-Erling Smørgrav 				      fields[ZF_NAME],
1942bc421551SDag-Erling Smørgrav 				      filename(zones[i].z_filenum),
1943bc421551SDag-Erling Smørgrav 				      zones[i].z_linenum);
1944bc421551SDag-Erling Smørgrav 				return false;
1945bc421551SDag-Erling Smørgrav 		}
1946bc421551SDag-Erling Smørgrav 	return inzsub(fields, nfields, false);
1947bc421551SDag-Erling Smørgrav }
1948bc421551SDag-Erling Smørgrav 
1949bc421551SDag-Erling Smørgrav static bool
1950bc421551SDag-Erling Smørgrav inzcont(char **fields, int nfields)
1951bc421551SDag-Erling Smørgrav {
1952bc421551SDag-Erling Smørgrav 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
1953bc421551SDag-Erling Smørgrav 		error(_("wrong number of fields on Zone continuation line"));
1954bc421551SDag-Erling Smørgrav 		return false;
1955bc421551SDag-Erling Smørgrav 	}
1956bc421551SDag-Erling Smørgrav 	return inzsub(fields, nfields, true);
1957bc421551SDag-Erling Smørgrav }
1958bc421551SDag-Erling Smørgrav 
1959bc421551SDag-Erling Smørgrav static bool
1960bc421551SDag-Erling Smørgrav inzsub(char **fields, int nfields, bool iscont)
1961bc421551SDag-Erling Smørgrav {
1962bc421551SDag-Erling Smørgrav 	register char *		cp;
1963bc421551SDag-Erling Smørgrav 	char *			cp1;
196455572cffSDag-Erling Smørgrav 	struct zone		z = { 0 };
1965bc421551SDag-Erling Smørgrav 	int format_len;
1966bc421551SDag-Erling Smørgrav 	register int		i_stdoff, i_rule, i_format;
1967bc421551SDag-Erling Smørgrav 	register int		i_untilyear, i_untilmonth;
1968bc421551SDag-Erling Smørgrav 	register int		i_untilday, i_untiltime;
1969bc421551SDag-Erling Smørgrav 	register bool		hasuntil;
1970bc421551SDag-Erling Smørgrav 
1971bc421551SDag-Erling Smørgrav 	if (iscont) {
1972bc421551SDag-Erling Smørgrav 		i_stdoff = ZFC_STDOFF;
1973bc421551SDag-Erling Smørgrav 		i_rule = ZFC_RULE;
1974bc421551SDag-Erling Smørgrav 		i_format = ZFC_FORMAT;
1975bc421551SDag-Erling Smørgrav 		i_untilyear = ZFC_TILYEAR;
1976bc421551SDag-Erling Smørgrav 		i_untilmonth = ZFC_TILMONTH;
1977bc421551SDag-Erling Smørgrav 		i_untilday = ZFC_TILDAY;
1978bc421551SDag-Erling Smørgrav 		i_untiltime = ZFC_TILTIME;
1979bc421551SDag-Erling Smørgrav 	} else if (!namecheck(fields[ZF_NAME]))
1980bc421551SDag-Erling Smørgrav 		return false;
1981bc421551SDag-Erling Smørgrav 	else {
1982bc421551SDag-Erling Smørgrav 		i_stdoff = ZF_STDOFF;
1983bc421551SDag-Erling Smørgrav 		i_rule = ZF_RULE;
1984bc421551SDag-Erling Smørgrav 		i_format = ZF_FORMAT;
1985bc421551SDag-Erling Smørgrav 		i_untilyear = ZF_TILYEAR;
1986bc421551SDag-Erling Smørgrav 		i_untilmonth = ZF_TILMONTH;
1987bc421551SDag-Erling Smørgrav 		i_untilday = ZF_TILDAY;
1988bc421551SDag-Erling Smørgrav 		i_untiltime = ZF_TILTIME;
1989bc421551SDag-Erling Smørgrav 	}
1990bc421551SDag-Erling Smørgrav 	z.z_filenum = filenum;
1991bc421551SDag-Erling Smørgrav 	z.z_linenum = linenum;
1992bc421551SDag-Erling Smørgrav 	z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
1993bc421551SDag-Erling Smørgrav 	if ((cp = strchr(fields[i_format], '%')) != 0) {
1994bc421551SDag-Erling Smørgrav 		if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
1995bc421551SDag-Erling Smørgrav 		    || strchr(fields[i_format], '/')) {
1996bc421551SDag-Erling Smørgrav 			error(_("invalid abbreviation format"));
1997bc421551SDag-Erling Smørgrav 			return false;
1998bc421551SDag-Erling Smørgrav 		}
1999bc421551SDag-Erling Smørgrav 	}
2000bc421551SDag-Erling Smørgrav 	z.z_format_specifier = cp ? *cp : '\0';
2001bc421551SDag-Erling Smørgrav 	format_len = strlen(fields[i_format]);
2002bc421551SDag-Erling Smørgrav 	if (max_format_len < format_len)
2003bc421551SDag-Erling Smørgrav 	  max_format_len = format_len;
2004bc421551SDag-Erling Smørgrav 	hasuntil = nfields > i_untilyear;
2005bc421551SDag-Erling Smørgrav 	if (hasuntil) {
2006bc421551SDag-Erling Smørgrav 		z.z_untilrule.r_filenum = filenum;
2007bc421551SDag-Erling Smørgrav 		z.z_untilrule.r_linenum = linenum;
2008bc421551SDag-Erling Smørgrav 		if (!rulesub(
2009bc421551SDag-Erling Smørgrav 			&z.z_untilrule,
2010bc421551SDag-Erling Smørgrav 			fields[i_untilyear],
2011bc421551SDag-Erling Smørgrav 			"only",
2012bc421551SDag-Erling Smørgrav 			"",
2013bc421551SDag-Erling Smørgrav 			(nfields > i_untilmonth) ?
2014bc421551SDag-Erling Smørgrav 			fields[i_untilmonth] : "Jan",
2015bc421551SDag-Erling Smørgrav 			(nfields > i_untilday) ? fields[i_untilday] : "1",
2016bc421551SDag-Erling Smørgrav 			(nfields > i_untiltime) ? fields[i_untiltime] : "0"))
2017bc421551SDag-Erling Smørgrav 		  return false;
2018bc421551SDag-Erling Smørgrav 		z.z_untiltime = rpytime(&z.z_untilrule,
2019bc421551SDag-Erling Smørgrav 			z.z_untilrule.r_loyear);
2020bc421551SDag-Erling Smørgrav 		if (iscont && nzones > 0 &&
2021bc421551SDag-Erling Smørgrav 			z.z_untiltime > min_time &&
2022bc421551SDag-Erling Smørgrav 			z.z_untiltime < max_time &&
2023bc421551SDag-Erling Smørgrav 			zones[nzones - 1].z_untiltime > min_time &&
2024bc421551SDag-Erling Smørgrav 			zones[nzones - 1].z_untiltime < max_time &&
2025bc421551SDag-Erling Smørgrav 			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
202646c59934SDag-Erling Smørgrav 		  error(_("Zone continuation line end time is"
202746c59934SDag-Erling Smørgrav 			  " not after end time of previous line"));
2028bc421551SDag-Erling Smørgrav 		  return false;
2029bc421551SDag-Erling Smørgrav 		}
2030bc421551SDag-Erling Smørgrav 	}
2031bc421551SDag-Erling Smørgrav 	z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]);
2032bc421551SDag-Erling Smørgrav 	z.z_rule = estrdup(fields[i_rule]);
2033bc421551SDag-Erling Smørgrav 	z.z_format = cp1 = estrdup(fields[i_format]);
2034bc421551SDag-Erling Smørgrav 	if (z.z_format_specifier == 'z') {
2035bc421551SDag-Erling Smørgrav 	  cp1[cp - fields[i_format]] = 's';
2036bc421551SDag-Erling Smørgrav 	  if (noise)
2037bc421551SDag-Erling Smørgrav 	    warning(_("format '%s' not handled by pre-2015 versions of zic"),
2038bc421551SDag-Erling Smørgrav 		    fields[i_format]);
2039bc421551SDag-Erling Smørgrav 	}
2040bc421551SDag-Erling Smørgrav 	zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
2041bc421551SDag-Erling Smørgrav 	zones[nzones++] = z;
2042bc421551SDag-Erling Smørgrav 	/*
2043bc421551SDag-Erling Smørgrav 	** If there was an UNTIL field on this line,
2044bc421551SDag-Erling Smørgrav 	** there's more information about the zone on the next line.
2045bc421551SDag-Erling Smørgrav 	*/
2046bc421551SDag-Erling Smørgrav 	return hasuntil;
2047bc421551SDag-Erling Smørgrav }
2048bc421551SDag-Erling Smørgrav 
2049bc421551SDag-Erling Smørgrav static zic_t
2050bc421551SDag-Erling Smørgrav getleapdatetime(char **fields, bool expire_line)
2051bc421551SDag-Erling Smørgrav {
2052bc421551SDag-Erling Smørgrav 	register const char *		cp;
2053bc421551SDag-Erling Smørgrav 	register const struct lookup *	lp;
2054bc421551SDag-Erling Smørgrav 	register zic_t			i, j;
2055bc421551SDag-Erling Smørgrav 	zic_t				year;
2056bc421551SDag-Erling Smørgrav 	int				month, day;
2057bc421551SDag-Erling Smørgrav 	zic_t				dayoff, tod;
2058bc421551SDag-Erling Smørgrav 	zic_t				t;
2059bc421551SDag-Erling Smørgrav 	char xs;
2060bc421551SDag-Erling Smørgrav 
2061bc421551SDag-Erling Smørgrav 	dayoff = 0;
2062bc421551SDag-Erling Smørgrav 	cp = fields[LP_YEAR];
2063bc421551SDag-Erling Smørgrav 	if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) {
2064bc421551SDag-Erling Smørgrav 		/*
2065bc421551SDag-Erling Smørgrav 		** Leapin' Lizards!
2066bc421551SDag-Erling Smørgrav 		*/
2067bc421551SDag-Erling Smørgrav 		error(_("invalid leaping year"));
2068bc421551SDag-Erling Smørgrav 		return -1;
2069bc421551SDag-Erling Smørgrav 	}
2070bc421551SDag-Erling Smørgrav 	if (!expire_line) {
2071bc421551SDag-Erling Smørgrav 	    if (!leapseen || leapmaxyear < year)
2072bc421551SDag-Erling Smørgrav 		leapmaxyear = year;
2073bc421551SDag-Erling Smørgrav 	    if (!leapseen || leapminyear > year)
2074bc421551SDag-Erling Smørgrav 		leapminyear = year;
2075bc421551SDag-Erling Smørgrav 	    leapseen = true;
2076bc421551SDag-Erling Smørgrav 	}
2077bc421551SDag-Erling Smørgrav 	j = EPOCH_YEAR;
2078bc421551SDag-Erling Smørgrav 	while (j != year) {
2079bc421551SDag-Erling Smørgrav 		if (year > j) {
2080bc421551SDag-Erling Smørgrav 			i = len_years[isleap(j)];
2081bc421551SDag-Erling Smørgrav 			++j;
2082bc421551SDag-Erling Smørgrav 		} else {
2083bc421551SDag-Erling Smørgrav 			--j;
2084bc421551SDag-Erling Smørgrav 			i = -len_years[isleap(j)];
2085bc421551SDag-Erling Smørgrav 		}
2086bc421551SDag-Erling Smørgrav 		dayoff = oadd(dayoff, i);
2087bc421551SDag-Erling Smørgrav 	}
2088bc421551SDag-Erling Smørgrav 	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
2089bc421551SDag-Erling Smørgrav 		error(_("invalid month name"));
2090bc421551SDag-Erling Smørgrav 		return -1;
2091bc421551SDag-Erling Smørgrav 	}
2092bc421551SDag-Erling Smørgrav 	month = lp->l_value;
2093bc421551SDag-Erling Smørgrav 	j = TM_JANUARY;
2094bc421551SDag-Erling Smørgrav 	while (j != month) {
2095bc421551SDag-Erling Smørgrav 		i = len_months[isleap(year)][j];
2096bc421551SDag-Erling Smørgrav 		dayoff = oadd(dayoff, i);
2097bc421551SDag-Erling Smørgrav 		++j;
2098bc421551SDag-Erling Smørgrav 	}
2099bc421551SDag-Erling Smørgrav 	cp = fields[LP_DAY];
2100bc421551SDag-Erling Smørgrav 	if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
2101bc421551SDag-Erling Smørgrav 		day <= 0 || day > len_months[isleap(year)][month]) {
2102bc421551SDag-Erling Smørgrav 			error(_("invalid day of month"));
2103bc421551SDag-Erling Smørgrav 			return -1;
2104bc421551SDag-Erling Smørgrav 	}
2105bc421551SDag-Erling Smørgrav 	dayoff = oadd(dayoff, day - 1);
2106bc421551SDag-Erling Smørgrav 	if (dayoff < min_time / SECSPERDAY) {
2107bc421551SDag-Erling Smørgrav 		error(_("time too small"));
2108bc421551SDag-Erling Smørgrav 		return -1;
2109bc421551SDag-Erling Smørgrav 	}
2110bc421551SDag-Erling Smørgrav 	if (dayoff > max_time / SECSPERDAY) {
2111bc421551SDag-Erling Smørgrav 		error(_("time too large"));
2112bc421551SDag-Erling Smørgrav 		return -1;
2113bc421551SDag-Erling Smørgrav 	}
2114bc421551SDag-Erling Smørgrav 	t = dayoff * SECSPERDAY;
2115bc421551SDag-Erling Smørgrav 	tod = gethms(fields[LP_TIME], _("invalid time of day"));
2116bc421551SDag-Erling Smørgrav 	t = tadd(t, tod);
2117bc421551SDag-Erling Smørgrav 	if (t < 0)
2118bc421551SDag-Erling Smørgrav 	  error(_("leap second precedes Epoch"));
2119bc421551SDag-Erling Smørgrav 	return t;
2120bc421551SDag-Erling Smørgrav }
2121bc421551SDag-Erling Smørgrav 
2122bc421551SDag-Erling Smørgrav static void
2123bc421551SDag-Erling Smørgrav inleap(char **fields, int nfields)
2124bc421551SDag-Erling Smørgrav {
2125bc421551SDag-Erling Smørgrav   if (nfields != LEAP_FIELDS)
2126bc421551SDag-Erling Smørgrav     error(_("wrong number of fields on Leap line"));
2127bc421551SDag-Erling Smørgrav   else {
2128bc421551SDag-Erling Smørgrav     zic_t t = getleapdatetime(fields, false);
2129bc421551SDag-Erling Smørgrav     if (0 <= t) {
2130bc421551SDag-Erling Smørgrav       struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
2131bc421551SDag-Erling Smørgrav       if (!lp)
2132bc421551SDag-Erling Smørgrav 	error(_("invalid Rolling/Stationary field on Leap line"));
2133bc421551SDag-Erling Smørgrav       else {
2134bc421551SDag-Erling Smørgrav 	int correction = 0;
2135bc421551SDag-Erling Smørgrav 	if (!fields[LP_CORR][0]) /* infile() turns "-" into "".  */
2136bc421551SDag-Erling Smørgrav 	  correction = -1;
2137bc421551SDag-Erling Smørgrav 	else if (strcmp(fields[LP_CORR], "+") == 0)
2138bc421551SDag-Erling Smørgrav 	  correction = 1;
2139bc421551SDag-Erling Smørgrav 	else
2140bc421551SDag-Erling Smørgrav 	  error(_("invalid CORRECTION field on Leap line"));
2141bc421551SDag-Erling Smørgrav 	if (correction)
2142bc421551SDag-Erling Smørgrav 	  leapadd(t, correction, lp->l_value);
2143bc421551SDag-Erling Smørgrav       }
2144bc421551SDag-Erling Smørgrav     }
2145bc421551SDag-Erling Smørgrav   }
2146bc421551SDag-Erling Smørgrav }
2147bc421551SDag-Erling Smørgrav 
2148bc421551SDag-Erling Smørgrav static void
2149bc421551SDag-Erling Smørgrav inexpires(char **fields, int nfields)
2150bc421551SDag-Erling Smørgrav {
2151bc421551SDag-Erling Smørgrav   if (nfields != EXPIRES_FIELDS)
2152bc421551SDag-Erling Smørgrav     error(_("wrong number of fields on Expires line"));
2153bc421551SDag-Erling Smørgrav   else if (0 <= leapexpires)
2154bc421551SDag-Erling Smørgrav     error(_("multiple Expires lines"));
2155bc421551SDag-Erling Smørgrav   else
2156bc421551SDag-Erling Smørgrav     leapexpires = getleapdatetime(fields, true);
2157bc421551SDag-Erling Smørgrav }
2158bc421551SDag-Erling Smørgrav 
2159bc421551SDag-Erling Smørgrav static void
2160bc421551SDag-Erling Smørgrav inlink(char **fields, int nfields)
2161bc421551SDag-Erling Smørgrav {
2162bc421551SDag-Erling Smørgrav 	struct link	l;
2163bc421551SDag-Erling Smørgrav 
2164bc421551SDag-Erling Smørgrav 	if (nfields != LINK_FIELDS) {
2165bc421551SDag-Erling Smørgrav 		error(_("wrong number of fields on Link line"));
2166bc421551SDag-Erling Smørgrav 		return;
2167bc421551SDag-Erling Smørgrav 	}
2168bc421551SDag-Erling Smørgrav 	if (*fields[LF_TARGET] == '\0') {
2169bc421551SDag-Erling Smørgrav 		error(_("blank TARGET field on Link line"));
2170bc421551SDag-Erling Smørgrav 		return;
2171bc421551SDag-Erling Smørgrav 	}
2172bc421551SDag-Erling Smørgrav 	if (! namecheck(fields[LF_LINKNAME]))
2173bc421551SDag-Erling Smørgrav 	  return;
2174bc421551SDag-Erling Smørgrav 	l.l_filenum = filenum;
2175bc421551SDag-Erling Smørgrav 	l.l_linenum = linenum;
2176bc421551SDag-Erling Smørgrav 	l.l_target = estrdup(fields[LF_TARGET]);
2177bc421551SDag-Erling Smørgrav 	l.l_linkname = estrdup(fields[LF_LINKNAME]);
2178bc421551SDag-Erling Smørgrav 	links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
2179bc421551SDag-Erling Smørgrav 	links[nlinks++] = l;
2180bc421551SDag-Erling Smørgrav }
2181bc421551SDag-Erling Smørgrav 
2182bc421551SDag-Erling Smørgrav static bool
2183bc421551SDag-Erling Smørgrav rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
2184bc421551SDag-Erling Smørgrav 	const char *typep, const char *monthp, const char *dayp,
2185bc421551SDag-Erling Smørgrav 	const char *timep)
2186bc421551SDag-Erling Smørgrav {
2187bc421551SDag-Erling Smørgrav 	register const struct lookup *	lp;
2188bc421551SDag-Erling Smørgrav 	register const char *		cp;
2189bc421551SDag-Erling Smørgrav 	register char *			dp;
2190bc421551SDag-Erling Smørgrav 	register char *			ep;
2191bc421551SDag-Erling Smørgrav 	char xs;
2192bc421551SDag-Erling Smørgrav 
2193bc421551SDag-Erling Smørgrav 	if ((lp = byword(monthp, mon_names)) == NULL) {
2194bc421551SDag-Erling Smørgrav 		error(_("invalid month name"));
2195bc421551SDag-Erling Smørgrav 		return false;
2196bc421551SDag-Erling Smørgrav 	}
2197bc421551SDag-Erling Smørgrav 	rp->r_month = lp->l_value;
2198bc421551SDag-Erling Smørgrav 	rp->r_todisstd = false;
2199bc421551SDag-Erling Smørgrav 	rp->r_todisut = false;
2200bc421551SDag-Erling Smørgrav 	dp = estrdup(timep);
2201bc421551SDag-Erling Smørgrav 	if (*dp != '\0') {
2202bc421551SDag-Erling Smørgrav 		ep = dp + strlen(dp) - 1;
2203bc421551SDag-Erling Smørgrav 		switch (lowerit(*ep)) {
2204bc421551SDag-Erling Smørgrav 			case 's':	/* Standard */
2205bc421551SDag-Erling Smørgrav 				rp->r_todisstd = true;
2206bc421551SDag-Erling Smørgrav 				rp->r_todisut = false;
2207bc421551SDag-Erling Smørgrav 				*ep = '\0';
2208bc421551SDag-Erling Smørgrav 				break;
2209bc421551SDag-Erling Smørgrav 			case 'w':	/* Wall */
2210bc421551SDag-Erling Smørgrav 				rp->r_todisstd = false;
2211bc421551SDag-Erling Smørgrav 				rp->r_todisut = false;
2212bc421551SDag-Erling Smørgrav 				*ep = '\0';
2213bc421551SDag-Erling Smørgrav 				break;
2214bc421551SDag-Erling Smørgrav 			case 'g':	/* Greenwich */
2215bc421551SDag-Erling Smørgrav 			case 'u':	/* Universal */
2216bc421551SDag-Erling Smørgrav 			case 'z':	/* Zulu */
2217bc421551SDag-Erling Smørgrav 				rp->r_todisstd = true;
2218bc421551SDag-Erling Smørgrav 				rp->r_todisut = true;
2219bc421551SDag-Erling Smørgrav 				*ep = '\0';
2220bc421551SDag-Erling Smørgrav 				break;
2221bc421551SDag-Erling Smørgrav 		}
2222bc421551SDag-Erling Smørgrav 	}
2223bc421551SDag-Erling Smørgrav 	rp->r_tod = gethms(dp, _("invalid time of day"));
2224bc421551SDag-Erling Smørgrav 	free(dp);
2225bc421551SDag-Erling Smørgrav 	/*
2226bc421551SDag-Erling Smørgrav 	** Year work.
2227bc421551SDag-Erling Smørgrav 	*/
2228bc421551SDag-Erling Smørgrav 	cp = loyearp;
2229bc421551SDag-Erling Smørgrav 	lp = byword(cp, begin_years);
223046c59934SDag-Erling Smørgrav 	if (lp) switch (lp->l_value) {
2231bc421551SDag-Erling Smørgrav 		case YR_MINIMUM:
223246c59934SDag-Erling Smørgrav 			warning(_("FROM year \"%s\" is obsolete;"
223346c59934SDag-Erling Smørgrav 				  " treated as %d"),
223446c59934SDag-Erling Smørgrav 				cp, YEAR_32BIT_MIN - 1);
223546c59934SDag-Erling Smørgrav 			rp->r_loyear = YEAR_32BIT_MIN - 1;
2236bc421551SDag-Erling Smørgrav 			break;
2237bc421551SDag-Erling Smørgrav 		default: unreachable();
2238bc421551SDag-Erling Smørgrav 	} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
2239bc421551SDag-Erling Smørgrav 		error(_("invalid starting year"));
2240bc421551SDag-Erling Smørgrav 		return false;
2241bc421551SDag-Erling Smørgrav 	}
2242bc421551SDag-Erling Smørgrav 	cp = hiyearp;
2243bc421551SDag-Erling Smørgrav 	lp = byword(cp, end_years);
2244bc421551SDag-Erling Smørgrav 	rp->r_hiwasnum = lp == NULL;
2245bc421551SDag-Erling Smørgrav 	if (!rp->r_hiwasnum) switch (lp->l_value) {
2246bc421551SDag-Erling Smørgrav 		case YR_MAXIMUM:
2247bc421551SDag-Erling Smørgrav 			rp->r_hiyear = ZIC_MAX;
2248bc421551SDag-Erling Smørgrav 			break;
2249bc421551SDag-Erling Smørgrav 		case YR_ONLY:
2250bc421551SDag-Erling Smørgrav 			rp->r_hiyear = rp->r_loyear;
2251bc421551SDag-Erling Smørgrav 			break;
2252bc421551SDag-Erling Smørgrav 		default: unreachable();
2253bc421551SDag-Erling Smørgrav 	} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
2254bc421551SDag-Erling Smørgrav 		error(_("invalid ending year"));
2255bc421551SDag-Erling Smørgrav 		return false;
2256bc421551SDag-Erling Smørgrav 	}
2257bc421551SDag-Erling Smørgrav 	if (rp->r_loyear > rp->r_hiyear) {
2258bc421551SDag-Erling Smørgrav 		error(_("starting year greater than ending year"));
2259bc421551SDag-Erling Smørgrav 		return false;
2260bc421551SDag-Erling Smørgrav 	}
2261bc421551SDag-Erling Smørgrav 	if (*typep != '\0') {
2262bc421551SDag-Erling Smørgrav 		error(_("year type \"%s\" is unsupported; use \"-\" instead"),
2263bc421551SDag-Erling Smørgrav 			typep);
2264bc421551SDag-Erling Smørgrav 		return false;
2265bc421551SDag-Erling Smørgrav 	}
2266bc421551SDag-Erling Smørgrav 	/*
2267bc421551SDag-Erling Smørgrav 	** Day work.
2268bc421551SDag-Erling Smørgrav 	** Accept things such as:
2269bc421551SDag-Erling Smørgrav 	**	1
2270bc421551SDag-Erling Smørgrav 	**	lastSunday
2271bc421551SDag-Erling Smørgrav 	**	last-Sunday (undocumented; warn about this)
2272bc421551SDag-Erling Smørgrav 	**	Sun<=20
2273bc421551SDag-Erling Smørgrav 	**	Sun>=7
2274bc421551SDag-Erling Smørgrav 	*/
2275bc421551SDag-Erling Smørgrav 	dp = estrdup(dayp);
2276bc421551SDag-Erling Smørgrav 	if ((lp = byword(dp, lasts)) != NULL) {
2277bc421551SDag-Erling Smørgrav 		rp->r_dycode = DC_DOWLEQ;
2278bc421551SDag-Erling Smørgrav 		rp->r_wday = lp->l_value;
2279bc421551SDag-Erling Smørgrav 		rp->r_dayofmonth = len_months[1][rp->r_month];
2280bc421551SDag-Erling Smørgrav 	} else {
2281bc421551SDag-Erling Smørgrav 		if ((ep = strchr(dp, '<')) != 0)
2282bc421551SDag-Erling Smørgrav 			rp->r_dycode = DC_DOWLEQ;
2283bc421551SDag-Erling Smørgrav 		else if ((ep = strchr(dp, '>')) != 0)
2284bc421551SDag-Erling Smørgrav 			rp->r_dycode = DC_DOWGEQ;
2285bc421551SDag-Erling Smørgrav 		else {
2286bc421551SDag-Erling Smørgrav 			ep = dp;
2287bc421551SDag-Erling Smørgrav 			rp->r_dycode = DC_DOM;
2288bc421551SDag-Erling Smørgrav 		}
2289bc421551SDag-Erling Smørgrav 		if (rp->r_dycode != DC_DOM) {
2290bc421551SDag-Erling Smørgrav 			*ep++ = 0;
2291bc421551SDag-Erling Smørgrav 			if (*ep++ != '=') {
2292bc421551SDag-Erling Smørgrav 				error(_("invalid day of month"));
2293bc421551SDag-Erling Smørgrav 				free(dp);
2294bc421551SDag-Erling Smørgrav 				return false;
2295bc421551SDag-Erling Smørgrav 			}
2296bc421551SDag-Erling Smørgrav 			if ((lp = byword(dp, wday_names)) == NULL) {
2297bc421551SDag-Erling Smørgrav 				error(_("invalid weekday name"));
2298bc421551SDag-Erling Smørgrav 				free(dp);
2299bc421551SDag-Erling Smørgrav 				return false;
2300bc421551SDag-Erling Smørgrav 			}
2301bc421551SDag-Erling Smørgrav 			rp->r_wday = lp->l_value;
2302bc421551SDag-Erling Smørgrav 		}
2303bc421551SDag-Erling Smørgrav 		if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
2304bc421551SDag-Erling Smørgrav 			rp->r_dayofmonth <= 0 ||
2305bc421551SDag-Erling Smørgrav 			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
2306bc421551SDag-Erling Smørgrav 				error(_("invalid day of month"));
2307bc421551SDag-Erling Smørgrav 				free(dp);
2308bc421551SDag-Erling Smørgrav 				return false;
2309bc421551SDag-Erling Smørgrav 		}
2310bc421551SDag-Erling Smørgrav 	}
2311bc421551SDag-Erling Smørgrav 	free(dp);
2312bc421551SDag-Erling Smørgrav 	return true;
2313bc421551SDag-Erling Smørgrav }
2314bc421551SDag-Erling Smørgrav 
2315bc421551SDag-Erling Smørgrav static void
2316bc421551SDag-Erling Smørgrav convert(uint_fast32_t val, char *buf)
2317bc421551SDag-Erling Smørgrav {
2318bc421551SDag-Erling Smørgrav 	register int	i;
2319bc421551SDag-Erling Smørgrav 	register int	shift;
2320bc421551SDag-Erling Smørgrav 	unsigned char *const b = (unsigned char *) buf;
2321bc421551SDag-Erling Smørgrav 
2322bc421551SDag-Erling Smørgrav 	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
2323bc421551SDag-Erling Smørgrav 	  b[i] = (val >> shift) & 0xff;
2324bc421551SDag-Erling Smørgrav }
2325bc421551SDag-Erling Smørgrav 
2326bc421551SDag-Erling Smørgrav static void
2327bc421551SDag-Erling Smørgrav convert64(uint_fast64_t val, char *buf)
2328bc421551SDag-Erling Smørgrav {
2329bc421551SDag-Erling Smørgrav 	register int	i;
2330bc421551SDag-Erling Smørgrav 	register int	shift;
2331bc421551SDag-Erling Smørgrav 	unsigned char *const b = (unsigned char *) buf;
2332bc421551SDag-Erling Smørgrav 
2333bc421551SDag-Erling Smørgrav 	for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
2334bc421551SDag-Erling Smørgrav 	  b[i] = (val >> shift) & 0xff;
2335bc421551SDag-Erling Smørgrav }
2336bc421551SDag-Erling Smørgrav 
2337bc421551SDag-Erling Smørgrav static void
2338bc421551SDag-Erling Smørgrav puttzcode(zic_t val, FILE *fp)
2339bc421551SDag-Erling Smørgrav {
2340bc421551SDag-Erling Smørgrav 	char	buf[4];
2341bc421551SDag-Erling Smørgrav 
2342bc421551SDag-Erling Smørgrav 	convert(val, buf);
2343bc421551SDag-Erling Smørgrav 	fwrite(buf, sizeof buf, 1, fp);
2344bc421551SDag-Erling Smørgrav }
2345bc421551SDag-Erling Smørgrav 
2346bc421551SDag-Erling Smørgrav static void
2347bc421551SDag-Erling Smørgrav puttzcodepass(zic_t val, FILE *fp, int pass)
2348bc421551SDag-Erling Smørgrav {
2349bc421551SDag-Erling Smørgrav   if (pass == 1)
2350bc421551SDag-Erling Smørgrav     puttzcode(val, fp);
2351bc421551SDag-Erling Smørgrav   else {
2352bc421551SDag-Erling Smørgrav 	char	buf[8];
2353bc421551SDag-Erling Smørgrav 
2354bc421551SDag-Erling Smørgrav 	convert64(val, buf);
2355bc421551SDag-Erling Smørgrav 	fwrite(buf, sizeof buf, 1, fp);
2356bc421551SDag-Erling Smørgrav   }
2357bc421551SDag-Erling Smørgrav }
2358bc421551SDag-Erling Smørgrav 
2359bc421551SDag-Erling Smørgrav static int
2360bc421551SDag-Erling Smørgrav atcomp(const void *avp, const void *bvp)
2361bc421551SDag-Erling Smørgrav {
2362bc421551SDag-Erling Smørgrav   struct attype const *ap = avp, *bp = bvp;
2363bc421551SDag-Erling Smørgrav   zic_t a = ap->at, b = bp->at;
2364bc421551SDag-Erling Smørgrav   return a < b ? -1 : a > b;
2365bc421551SDag-Erling Smørgrav }
2366bc421551SDag-Erling Smørgrav 
2367bc421551SDag-Erling Smørgrav struct timerange {
2368bc421551SDag-Erling Smørgrav   int defaulttype;
2369bc421551SDag-Erling Smørgrav   ptrdiff_t base, count;
2370bc421551SDag-Erling Smørgrav   int leapbase, leapcount;
2371bc421551SDag-Erling Smørgrav   bool leapexpiry;
2372bc421551SDag-Erling Smørgrav };
2373bc421551SDag-Erling Smørgrav 
2374bc421551SDag-Erling Smørgrav static struct timerange
2375bc421551SDag-Erling Smørgrav limitrange(struct timerange r, zic_t lo, zic_t hi,
2376bc421551SDag-Erling Smørgrav 	   zic_t const *ats, unsigned char const *types)
2377bc421551SDag-Erling Smørgrav {
2378bc421551SDag-Erling Smørgrav   /* Omit ordinary transitions < LO.  */
2379bc421551SDag-Erling Smørgrav   while (0 < r.count && ats[r.base] < lo) {
2380bc421551SDag-Erling Smørgrav     r.defaulttype = types[r.base];
2381bc421551SDag-Erling Smørgrav     r.count--;
2382bc421551SDag-Erling Smørgrav     r.base++;
2383bc421551SDag-Erling Smørgrav   }
2384bc421551SDag-Erling Smørgrav 
2385bc421551SDag-Erling Smørgrav   /* Omit as many initial leap seconds as possible, such that the
2386bc421551SDag-Erling Smørgrav      first leap second in the truncated list is <= LO, and is a
2387bc421551SDag-Erling Smørgrav      positive leap second if and only if it has a positive correction.
2388bc421551SDag-Erling Smørgrav      This supports common TZif readers that assume that the first leap
2389bc421551SDag-Erling Smørgrav      second is positive if and only if its correction is positive.  */
2390bc421551SDag-Erling Smørgrav   while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) {
2391bc421551SDag-Erling Smørgrav     r.leapcount--;
2392bc421551SDag-Erling Smørgrav     r.leapbase++;
2393bc421551SDag-Erling Smørgrav   }
2394bc421551SDag-Erling Smørgrav   while (0 < r.leapbase
2395bc421551SDag-Erling Smørgrav 	 && ((corr[r.leapbase - 1] < corr[r.leapbase])
2396bc421551SDag-Erling Smørgrav 	     != (0 < corr[r.leapbase]))) {
2397bc421551SDag-Erling Smørgrav     r.leapcount++;
2398bc421551SDag-Erling Smørgrav     r.leapbase--;
2399bc421551SDag-Erling Smørgrav   }
2400bc421551SDag-Erling Smørgrav 
2401bc421551SDag-Erling Smørgrav 
2402bc421551SDag-Erling Smørgrav   /* Omit ordinary and leap second transitions greater than HI + 1.  */
2403bc421551SDag-Erling Smørgrav   if (hi < max_time) {
2404bc421551SDag-Erling Smørgrav     while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
2405bc421551SDag-Erling Smørgrav       r.count--;
2406bc421551SDag-Erling Smørgrav     while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
2407bc421551SDag-Erling Smørgrav       r.leapcount--;
2408bc421551SDag-Erling Smørgrav   }
2409bc421551SDag-Erling Smørgrav 
2410bc421551SDag-Erling Smørgrav   /* Determine whether to append an expiration to the leap second table.  */
2411bc421551SDag-Erling Smørgrav   r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
2412bc421551SDag-Erling Smørgrav 
2413bc421551SDag-Erling Smørgrav   return r;
2414bc421551SDag-Erling Smørgrav }
2415bc421551SDag-Erling Smørgrav 
2416bc421551SDag-Erling Smørgrav static void
2417bc421551SDag-Erling Smørgrav writezone(const char *const name, const char *const string, char version,
2418bc421551SDag-Erling Smørgrav 	  int defaulttype)
2419bc421551SDag-Erling Smørgrav {
2420bc421551SDag-Erling Smørgrav 	register FILE *			fp;
2421bc421551SDag-Erling Smørgrav 	register ptrdiff_t		i, j;
2422bc421551SDag-Erling Smørgrav 	register size_t			u;
2423bc421551SDag-Erling Smørgrav 	register int			pass;
2424bc421551SDag-Erling Smørgrav 	char *tempname = NULL;
2425bc421551SDag-Erling Smørgrav 	char const *outname = name;
2426bc421551SDag-Erling Smørgrav 
2427bc421551SDag-Erling Smørgrav 	/* Allocate the ATS and TYPES arrays via a single malloc,
2428bc421551SDag-Erling Smørgrav 	   as this is a bit faster.  Do not malloc(0) if !timecnt,
2429bc421551SDag-Erling Smørgrav 	   as that might return NULL even on success.  */
2430bc421551SDag-Erling Smørgrav 	zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt,
2431bc421551SDag-Erling Smørgrav 						   sizeof *ats + 1),
2432bc421551SDag-Erling Smørgrav 				      alignof(zic_t)));
2433bc421551SDag-Erling Smørgrav 	void *typesptr = ats + timecnt;
2434bc421551SDag-Erling Smørgrav 	unsigned char *types = typesptr;
2435bc421551SDag-Erling Smørgrav 	struct timerange rangeall = {0}, range32, range64;
2436bc421551SDag-Erling Smørgrav 
2437bc421551SDag-Erling Smørgrav 	/*
2438bc421551SDag-Erling Smørgrav 	** Sort.
2439bc421551SDag-Erling Smørgrav 	*/
2440bc421551SDag-Erling Smørgrav 	if (timecnt > 1)
2441bc421551SDag-Erling Smørgrav 		qsort(attypes, timecnt, sizeof *attypes, atcomp);
2442bc421551SDag-Erling Smørgrav 	/*
2443bc421551SDag-Erling Smørgrav 	** Optimize.
2444bc421551SDag-Erling Smørgrav 	*/
2445bc421551SDag-Erling Smørgrav 	{
2446bc421551SDag-Erling Smørgrav 		ptrdiff_t fromi, toi;
2447bc421551SDag-Erling Smørgrav 
2448bc421551SDag-Erling Smørgrav 		toi = 0;
2449bc421551SDag-Erling Smørgrav 		fromi = 0;
2450bc421551SDag-Erling Smørgrav 		for ( ; fromi < timecnt; ++fromi) {
2451bc421551SDag-Erling Smørgrav 			if (toi != 0
2452bc421551SDag-Erling Smørgrav 			    && ((attypes[fromi].at
2453bc421551SDag-Erling Smørgrav 				 + utoffs[attypes[toi - 1].type])
2454bc421551SDag-Erling Smørgrav 				<= (attypes[toi - 1].at
2455bc421551SDag-Erling Smørgrav 				    + utoffs[toi == 1 ? 0
2456bc421551SDag-Erling Smørgrav 					     : attypes[toi - 2].type]))) {
2457bc421551SDag-Erling Smørgrav 					attypes[toi - 1].type =
2458bc421551SDag-Erling Smørgrav 						attypes[fromi].type;
2459bc421551SDag-Erling Smørgrav 					continue;
2460bc421551SDag-Erling Smørgrav 			}
2461bc421551SDag-Erling Smørgrav 			if (toi == 0
2462bc421551SDag-Erling Smørgrav 			    || attypes[fromi].dontmerge
2463bc421551SDag-Erling Smørgrav 			    || (utoffs[attypes[toi - 1].type]
2464bc421551SDag-Erling Smørgrav 				!= utoffs[attypes[fromi].type])
2465bc421551SDag-Erling Smørgrav 			    || (isdsts[attypes[toi - 1].type]
2466bc421551SDag-Erling Smørgrav 				!= isdsts[attypes[fromi].type])
2467bc421551SDag-Erling Smørgrav 			    || (desigidx[attypes[toi - 1].type]
2468bc421551SDag-Erling Smørgrav 				!= desigidx[attypes[fromi].type]))
2469bc421551SDag-Erling Smørgrav 					attypes[toi++] = attypes[fromi];
2470bc421551SDag-Erling Smørgrav 		}
2471bc421551SDag-Erling Smørgrav 		timecnt = toi;
2472bc421551SDag-Erling Smørgrav 	}
2473bc421551SDag-Erling Smørgrav 
2474bc421551SDag-Erling Smørgrav 	if (noise && timecnt > 1200) {
2475bc421551SDag-Erling Smørgrav 	  if (timecnt > TZ_MAX_TIMES)
2476bc421551SDag-Erling Smørgrav 		warning(_("reference clients mishandle"
2477bc421551SDag-Erling Smørgrav 			  " more than %d transition times"),
2478bc421551SDag-Erling Smørgrav 			TZ_MAX_TIMES);
2479bc421551SDag-Erling Smørgrav 	  else
2480bc421551SDag-Erling Smørgrav 		warning(_("pre-2014 clients may mishandle"
2481bc421551SDag-Erling Smørgrav 			  " more than 1200 transition times"));
2482bc421551SDag-Erling Smørgrav 	}
2483bc421551SDag-Erling Smørgrav 	/*
2484bc421551SDag-Erling Smørgrav 	** Transfer.
2485bc421551SDag-Erling Smørgrav 	*/
2486bc421551SDag-Erling Smørgrav 	for (i = 0; i < timecnt; ++i) {
2487bc421551SDag-Erling Smørgrav 		ats[i] = attypes[i].at;
2488bc421551SDag-Erling Smørgrav 		types[i] = attypes[i].type;
2489bc421551SDag-Erling Smørgrav 	}
2490bc421551SDag-Erling Smørgrav 
2491bc421551SDag-Erling Smørgrav 	/*
2492bc421551SDag-Erling Smørgrav 	** Correct for leap seconds.
2493bc421551SDag-Erling Smørgrav 	*/
2494bc421551SDag-Erling Smørgrav 	for (i = 0; i < timecnt; ++i) {
2495bc421551SDag-Erling Smørgrav 		j = leapcnt;
2496bc421551SDag-Erling Smørgrav 		while (--j >= 0)
2497bc421551SDag-Erling Smørgrav 			if (ats[i] > trans[j] - corr[j]) {
2498bc421551SDag-Erling Smørgrav 				ats[i] = tadd(ats[i], corr[j]);
2499bc421551SDag-Erling Smørgrav 				break;
2500bc421551SDag-Erling Smørgrav 			}
2501bc421551SDag-Erling Smørgrav 	}
2502bc421551SDag-Erling Smørgrav 
2503bc421551SDag-Erling Smørgrav 	rangeall.defaulttype = defaulttype;
2504bc421551SDag-Erling Smørgrav 	rangeall.count = timecnt;
2505bc421551SDag-Erling Smørgrav 	rangeall.leapcount = leapcnt;
2506bc421551SDag-Erling Smørgrav 	range64 = limitrange(rangeall, lo_time,
2507bc421551SDag-Erling Smørgrav 			     max(hi_time,
2508bc421551SDag-Erling Smørgrav 				 redundant_time - (ZIC_MIN < redundant_time)),
2509bc421551SDag-Erling Smørgrav 			     ats, types);
2510bc421551SDag-Erling Smørgrav 	range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
2511bc421551SDag-Erling Smørgrav 
2512bc421551SDag-Erling Smørgrav 	/* TZif version 4 is needed if a no-op transition is appended to
2513bc421551SDag-Erling Smørgrav 	   indicate the expiration of the leap second table, or if the first
2514bc421551SDag-Erling Smørgrav 	   leap second transition is not to a +1 or -1 correction.  */
2515bc421551SDag-Erling Smørgrav 	for (pass = 1; pass <= 2; pass++) {
2516bc421551SDag-Erling Smørgrav 	  struct timerange const *r = pass == 1 ? &range32 : &range64;
2517bc421551SDag-Erling Smørgrav 	  if (pass == 1 && !want_bloat())
2518bc421551SDag-Erling Smørgrav 	    continue;
2519bc421551SDag-Erling Smørgrav 	  if (r->leapexpiry) {
2520bc421551SDag-Erling Smørgrav 	    if (noise)
2521bc421551SDag-Erling Smørgrav 	      warning(_("%s: pre-2021b clients may mishandle"
2522bc421551SDag-Erling Smørgrav 			" leap second expiry"),
2523bc421551SDag-Erling Smørgrav 		      name);
2524bc421551SDag-Erling Smørgrav 	    version = '4';
2525bc421551SDag-Erling Smørgrav 	  }
2526bc421551SDag-Erling Smørgrav 	  if (0 < r->leapcount
2527bc421551SDag-Erling Smørgrav 	      && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) {
2528bc421551SDag-Erling Smørgrav 	    if (noise)
2529bc421551SDag-Erling Smørgrav 	      warning(_("%s: pre-2021b clients may mishandle"
2530bc421551SDag-Erling Smørgrav 			" leap second table truncation"),
2531bc421551SDag-Erling Smørgrav 		      name);
2532bc421551SDag-Erling Smørgrav 	    version = '4';
2533bc421551SDag-Erling Smørgrav 	  }
2534bc421551SDag-Erling Smørgrav 	  if (version == '4')
2535bc421551SDag-Erling Smørgrav 	    break;
2536bc421551SDag-Erling Smørgrav 	}
2537bc421551SDag-Erling Smørgrav 
2538bc421551SDag-Erling Smørgrav 	fp = open_outfile(&outname, &tempname);
2539bc421551SDag-Erling Smørgrav 
2540bc421551SDag-Erling Smørgrav 	for (pass = 1; pass <= 2; ++pass) {
2541bc421551SDag-Erling Smørgrav 		register ptrdiff_t thistimei, thistimecnt, thistimelim;
2542bc421551SDag-Erling Smørgrav 		register int	thisleapi, thisleapcnt, thisleaplim;
2543bc421551SDag-Erling Smørgrav 		struct tzhead tzh;
2544bc421551SDag-Erling Smørgrav 		int pretranstype = -1, thisdefaulttype;
2545bc421551SDag-Erling Smørgrav 		bool locut, hicut, thisleapexpiry;
2546bc421551SDag-Erling Smørgrav 		zic_t lo, thismin, thismax;
2547bc421551SDag-Erling Smørgrav 		int old0;
2548bc421551SDag-Erling Smørgrav 		char		omittype[TZ_MAX_TYPES];
2549bc421551SDag-Erling Smørgrav 		int		typemap[TZ_MAX_TYPES];
2550bc421551SDag-Erling Smørgrav 		int		thistypecnt, stdcnt, utcnt;
2551bc421551SDag-Erling Smørgrav 		char		thischars[TZ_MAX_CHARS];
2552bc421551SDag-Erling Smørgrav 		int		thischarcnt;
2553bc421551SDag-Erling Smørgrav 		bool		toomanytimes;
2554bc421551SDag-Erling Smørgrav 		int		indmap[TZ_MAX_CHARS];
2555bc421551SDag-Erling Smørgrav 
2556bc421551SDag-Erling Smørgrav 		if (pass == 1) {
2557bc421551SDag-Erling Smørgrav 			thisdefaulttype = range32.defaulttype;
2558bc421551SDag-Erling Smørgrav 			thistimei = range32.base;
2559bc421551SDag-Erling Smørgrav 			thistimecnt = range32.count;
2560bc421551SDag-Erling Smørgrav 			toomanytimes = thistimecnt >> 31 >> 1 != 0;
2561bc421551SDag-Erling Smørgrav 			thisleapi = range32.leapbase;
2562bc421551SDag-Erling Smørgrav 			thisleapcnt = range32.leapcount;
2563bc421551SDag-Erling Smørgrav 			thisleapexpiry = range32.leapexpiry;
2564bc421551SDag-Erling Smørgrav 			thismin = ZIC32_MIN;
2565bc421551SDag-Erling Smørgrav 			thismax = ZIC32_MAX;
2566bc421551SDag-Erling Smørgrav 		} else {
2567bc421551SDag-Erling Smørgrav 			thisdefaulttype = range64.defaulttype;
2568bc421551SDag-Erling Smørgrav 			thistimei = range64.base;
2569bc421551SDag-Erling Smørgrav 			thistimecnt = range64.count;
2570bc421551SDag-Erling Smørgrav 			toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
2571bc421551SDag-Erling Smørgrav 			thisleapi = range64.leapbase;
2572bc421551SDag-Erling Smørgrav 			thisleapcnt = range64.leapcount;
2573bc421551SDag-Erling Smørgrav 			thisleapexpiry = range64.leapexpiry;
2574bc421551SDag-Erling Smørgrav 			thismin = min_time;
2575bc421551SDag-Erling Smørgrav 			thismax = max_time;
2576bc421551SDag-Erling Smørgrav 		}
2577bc421551SDag-Erling Smørgrav 		if (toomanytimes)
2578bc421551SDag-Erling Smørgrav 		  error(_("too many transition times"));
2579bc421551SDag-Erling Smørgrav 
2580bc421551SDag-Erling Smørgrav 		locut = thismin < lo_time && lo_time <= thismax;
2581bc421551SDag-Erling Smørgrav 		hicut = thismin <= hi_time && hi_time < thismax;
2582bc421551SDag-Erling Smørgrav 		thistimelim = thistimei + thistimecnt;
2583bc421551SDag-Erling Smørgrav 		memset(omittype, true, typecnt);
2584bc421551SDag-Erling Smørgrav 
2585bc421551SDag-Erling Smørgrav 		/* Determine whether to output a transition before the first
2586bc421551SDag-Erling Smørgrav 		   transition in range.  This is needed when the output is
2587bc421551SDag-Erling Smørgrav 		   truncated at the start, and is also useful when catering to
2588bc421551SDag-Erling Smørgrav 		   buggy 32-bit clients that do not use time type 0 for
2589bc421551SDag-Erling Smørgrav 		   timestamps before the first transition.  */
2590bc421551SDag-Erling Smørgrav 		if ((locut || (pass == 1 && thistimei))
2591bc421551SDag-Erling Smørgrav 		    && ! (thistimecnt && ats[thistimei] == lo_time)) {
2592bc421551SDag-Erling Smørgrav 		  pretranstype = thisdefaulttype;
2593bc421551SDag-Erling Smørgrav 		  omittype[pretranstype] = false;
2594bc421551SDag-Erling Smørgrav 		}
2595bc421551SDag-Erling Smørgrav 
2596bc421551SDag-Erling Smørgrav 		/* Arguably the default time type in the 32-bit data
2597bc421551SDag-Erling Smørgrav 		   should be range32.defaulttype, which is suited for
2598bc421551SDag-Erling Smørgrav 		   timestamps just before ZIC32_MIN.  However, zic
2599bc421551SDag-Erling Smørgrav 		   traditionally used the time type of the indefinite
2600bc421551SDag-Erling Smørgrav 		   past instead.  Internet RFC 8532 says readers should
2601bc421551SDag-Erling Smørgrav 		   ignore 32-bit data, so this discrepancy matters only
2602bc421551SDag-Erling Smørgrav 		   to obsolete readers where the traditional type might
2603bc421551SDag-Erling Smørgrav 		   be more appropriate even if it's "wrong".  So, use
2604bc421551SDag-Erling Smørgrav 		   the historical zic value, unless -r specifies a low
2605bc421551SDag-Erling Smørgrav 		   cutoff that excludes some 32-bit timestamps.  */
2606bc421551SDag-Erling Smørgrav 		if (pass == 1 && lo_time <= thismin)
2607bc421551SDag-Erling Smørgrav 		  thisdefaulttype = range64.defaulttype;
2608bc421551SDag-Erling Smørgrav 
2609bc421551SDag-Erling Smørgrav 		if (locut)
2610bc421551SDag-Erling Smørgrav 		  thisdefaulttype = unspecifiedtype;
2611bc421551SDag-Erling Smørgrav 		omittype[thisdefaulttype] = false;
2612bc421551SDag-Erling Smørgrav 		for (i = thistimei; i < thistimelim; i++)
2613bc421551SDag-Erling Smørgrav 		  omittype[types[i]] = false;
2614bc421551SDag-Erling Smørgrav 		if (hicut)
2615bc421551SDag-Erling Smørgrav 		  omittype[unspecifiedtype] = false;
2616bc421551SDag-Erling Smørgrav 
2617bc421551SDag-Erling Smørgrav 		/* Reorder types to make THISDEFAULTTYPE type 0.
2618bc421551SDag-Erling Smørgrav 		   Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that
2619bc421551SDag-Erling Smørgrav 		   THISDEFAULTTYPE appears as type 0 in the output instead
2620bc421551SDag-Erling Smørgrav 		   of OLD0.  TYPEMAP also omits unused types.  */
2621bc421551SDag-Erling Smørgrav 		old0 = strlen(omittype);
2622bc421551SDag-Erling Smørgrav 
2623bc421551SDag-Erling Smørgrav #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
2624bc421551SDag-Erling Smørgrav 		/*
2625bc421551SDag-Erling Smørgrav 		** For some pre-2011 systems: if the last-to-be-written
2626bc421551SDag-Erling Smørgrav 		** standard (or daylight) type has an offset different from the
2627bc421551SDag-Erling Smørgrav 		** most recently used offset,
2628bc421551SDag-Erling Smørgrav 		** append an (unused) copy of the most recently used type
2629bc421551SDag-Erling Smørgrav 		** (to help get global "altzone" and "timezone" variables
2630bc421551SDag-Erling Smørgrav 		** set correctly).
2631bc421551SDag-Erling Smørgrav 		*/
2632bc421551SDag-Erling Smørgrav 		if (want_bloat()) {
2633bc421551SDag-Erling Smørgrav 			register int	mrudst, mrustd, hidst, histd, type;
2634bc421551SDag-Erling Smørgrav 
2635bc421551SDag-Erling Smørgrav 			hidst = histd = mrudst = mrustd = -1;
2636bc421551SDag-Erling Smørgrav 			if (0 <= pretranstype) {
2637bc421551SDag-Erling Smørgrav 			  if (isdsts[pretranstype])
2638bc421551SDag-Erling Smørgrav 			    mrudst = pretranstype;
2639bc421551SDag-Erling Smørgrav 			  else
2640bc421551SDag-Erling Smørgrav 			    mrustd = pretranstype;
2641bc421551SDag-Erling Smørgrav 			}
2642bc421551SDag-Erling Smørgrav 			for (i = thistimei; i < thistimelim; i++)
2643bc421551SDag-Erling Smørgrav 				if (isdsts[types[i]])
2644bc421551SDag-Erling Smørgrav 					mrudst = types[i];
2645bc421551SDag-Erling Smørgrav 				else	mrustd = types[i];
2646bc421551SDag-Erling Smørgrav 			for (i = old0; i < typecnt; i++) {
2647bc421551SDag-Erling Smørgrav 			  int h = (i == old0 ? thisdefaulttype
2648bc421551SDag-Erling Smørgrav 				   : i == thisdefaulttype ? old0 : i);
2649bc421551SDag-Erling Smørgrav 			  if (!omittype[h]) {
2650bc421551SDag-Erling Smørgrav 			    if (isdsts[h])
2651bc421551SDag-Erling Smørgrav 			      hidst = i;
2652bc421551SDag-Erling Smørgrav 			    else
2653bc421551SDag-Erling Smørgrav 			      histd = i;
2654bc421551SDag-Erling Smørgrav 			  }
2655bc421551SDag-Erling Smørgrav 			}
2656bc421551SDag-Erling Smørgrav 			if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
2657bc421551SDag-Erling Smørgrav 				utoffs[hidst] != utoffs[mrudst]) {
2658bc421551SDag-Erling Smørgrav 					isdsts[mrudst] = -1;
2659bc421551SDag-Erling Smørgrav 					type = addtype(utoffs[mrudst],
2660bc421551SDag-Erling Smørgrav 						&chars[desigidx[mrudst]],
2661bc421551SDag-Erling Smørgrav 						true,
2662bc421551SDag-Erling Smørgrav 						ttisstds[mrudst],
2663bc421551SDag-Erling Smørgrav 						ttisuts[mrudst]);
2664bc421551SDag-Erling Smørgrav 					isdsts[mrudst] = 1;
2665bc421551SDag-Erling Smørgrav 					omittype[type] = false;
2666bc421551SDag-Erling Smørgrav 			}
2667bc421551SDag-Erling Smørgrav 			if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
2668bc421551SDag-Erling Smørgrav 				utoffs[histd] != utoffs[mrustd]) {
2669bc421551SDag-Erling Smørgrav 					isdsts[mrustd] = -1;
2670bc421551SDag-Erling Smørgrav 					type = addtype(utoffs[mrustd],
2671bc421551SDag-Erling Smørgrav 						&chars[desigidx[mrustd]],
2672bc421551SDag-Erling Smørgrav 						false,
2673bc421551SDag-Erling Smørgrav 						ttisstds[mrustd],
2674bc421551SDag-Erling Smørgrav 						ttisuts[mrustd]);
2675bc421551SDag-Erling Smørgrav 					isdsts[mrustd] = 0;
2676bc421551SDag-Erling Smørgrav 					omittype[type] = false;
2677bc421551SDag-Erling Smørgrav 			}
2678bc421551SDag-Erling Smørgrav 		}
2679bc421551SDag-Erling Smørgrav #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
2680bc421551SDag-Erling Smørgrav 		thistypecnt = 0;
2681bc421551SDag-Erling Smørgrav 		for (i = old0; i < typecnt; i++)
2682bc421551SDag-Erling Smørgrav 		  if (!omittype[i])
2683bc421551SDag-Erling Smørgrav 		    typemap[i == old0 ? thisdefaulttype
2684bc421551SDag-Erling Smørgrav 			    : i == thisdefaulttype ? old0 : i]
2685bc421551SDag-Erling Smørgrav 		      = thistypecnt++;
2686bc421551SDag-Erling Smørgrav 
2687bc421551SDag-Erling Smørgrav 		for (u = 0; u < sizeof indmap / sizeof indmap[0]; ++u)
2688bc421551SDag-Erling Smørgrav 			indmap[u] = -1;
2689bc421551SDag-Erling Smørgrav 		thischarcnt = stdcnt = utcnt = 0;
2690bc421551SDag-Erling Smørgrav 		for (i = old0; i < typecnt; i++) {
2691bc421551SDag-Erling Smørgrav 			register char *	thisabbr;
2692bc421551SDag-Erling Smørgrav 
2693bc421551SDag-Erling Smørgrav 			if (omittype[i])
2694bc421551SDag-Erling Smørgrav 				continue;
2695bc421551SDag-Erling Smørgrav 			if (ttisstds[i])
2696bc421551SDag-Erling Smørgrav 			  stdcnt = thistypecnt;
2697bc421551SDag-Erling Smørgrav 			if (ttisuts[i])
2698bc421551SDag-Erling Smørgrav 			  utcnt = thistypecnt;
2699bc421551SDag-Erling Smørgrav 			if (indmap[desigidx[i]] >= 0)
2700bc421551SDag-Erling Smørgrav 				continue;
2701bc421551SDag-Erling Smørgrav 			thisabbr = &chars[desigidx[i]];
2702bc421551SDag-Erling Smørgrav 			for (j = 0; j < thischarcnt; ++j)
2703bc421551SDag-Erling Smørgrav 				if (strcmp(&thischars[j], thisabbr) == 0)
2704bc421551SDag-Erling Smørgrav 					break;
2705bc421551SDag-Erling Smørgrav 			if (j == thischarcnt) {
2706bc421551SDag-Erling Smørgrav 				strcpy(&thischars[thischarcnt], thisabbr);
2707bc421551SDag-Erling Smørgrav 				thischarcnt += strlen(thisabbr) + 1;
2708bc421551SDag-Erling Smørgrav 			}
2709bc421551SDag-Erling Smørgrav 			indmap[desigidx[i]] = j;
2710bc421551SDag-Erling Smørgrav 		}
2711bc421551SDag-Erling Smørgrav 		if (pass == 1 && !want_bloat()) {
2712bc421551SDag-Erling Smørgrav 		  hicut = thisleapexpiry = false;
2713bc421551SDag-Erling Smørgrav 		  pretranstype = -1;
2714bc421551SDag-Erling Smørgrav 		  thistimecnt = thisleapcnt = 0;
2715bc421551SDag-Erling Smørgrav 		  thistypecnt = thischarcnt = 1;
2716bc421551SDag-Erling Smørgrav 		}
2717bc421551SDag-Erling Smørgrav #define DO(field)	fwrite(tzh.field, sizeof tzh.field, 1, fp)
2718bc421551SDag-Erling Smørgrav 		memset(&tzh, 0, sizeof tzh);
2719bc421551SDag-Erling Smørgrav 		memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
2720bc421551SDag-Erling Smørgrav 		tzh.tzh_version[0] = version;
2721bc421551SDag-Erling Smørgrav 		convert(utcnt, tzh.tzh_ttisutcnt);
2722bc421551SDag-Erling Smørgrav 		convert(stdcnt, tzh.tzh_ttisstdcnt);
2723bc421551SDag-Erling Smørgrav 		convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
2724bc421551SDag-Erling Smørgrav 		convert((0 <= pretranstype) + thistimecnt + hicut,
2725bc421551SDag-Erling Smørgrav 			tzh.tzh_timecnt);
2726bc421551SDag-Erling Smørgrav 		convert(thistypecnt, tzh.tzh_typecnt);
2727bc421551SDag-Erling Smørgrav 		convert(thischarcnt, tzh.tzh_charcnt);
2728bc421551SDag-Erling Smørgrav 		DO(tzh_magic);
2729bc421551SDag-Erling Smørgrav 		DO(tzh_version);
2730bc421551SDag-Erling Smørgrav 		DO(tzh_reserved);
2731bc421551SDag-Erling Smørgrav 		DO(tzh_ttisutcnt);
2732bc421551SDag-Erling Smørgrav 		DO(tzh_ttisstdcnt);
2733bc421551SDag-Erling Smørgrav 		DO(tzh_leapcnt);
2734bc421551SDag-Erling Smørgrav 		DO(tzh_timecnt);
2735bc421551SDag-Erling Smørgrav 		DO(tzh_typecnt);
2736bc421551SDag-Erling Smørgrav 		DO(tzh_charcnt);
2737bc421551SDag-Erling Smørgrav #undef DO
2738bc421551SDag-Erling Smørgrav 		if (pass == 1 && !want_bloat()) {
2739bc421551SDag-Erling Smørgrav 		  /* Output a minimal data block with just one time type.  */
2740bc421551SDag-Erling Smørgrav 		  puttzcode(0, fp);	/* utoff */
2741bc421551SDag-Erling Smørgrav 		  putc(0, fp);		/* dst */
2742bc421551SDag-Erling Smørgrav 		  putc(0, fp);		/* index of abbreviation */
2743bc421551SDag-Erling Smørgrav 		  putc(0, fp);		/* empty-string abbreviation */
2744bc421551SDag-Erling Smørgrav 		  continue;
2745bc421551SDag-Erling Smørgrav 		}
2746bc421551SDag-Erling Smørgrav 
2747bc421551SDag-Erling Smørgrav 		/* Output a LO_TIME transition if needed; see limitrange.
2748bc421551SDag-Erling Smørgrav 		   But do not go below the minimum representable value
2749bc421551SDag-Erling Smørgrav 		   for this pass.  */
2750bc421551SDag-Erling Smørgrav 		lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
2751bc421551SDag-Erling Smørgrav 
2752bc421551SDag-Erling Smørgrav 		if (0 <= pretranstype)
2753bc421551SDag-Erling Smørgrav 		  puttzcodepass(lo, fp, pass);
2754bc421551SDag-Erling Smørgrav 		for (i = thistimei; i < thistimelim; ++i) {
2755bc421551SDag-Erling Smørgrav 		  puttzcodepass(ats[i], fp, pass);
2756bc421551SDag-Erling Smørgrav 		}
2757bc421551SDag-Erling Smørgrav 		if (hicut)
2758bc421551SDag-Erling Smørgrav 		  puttzcodepass(hi_time + 1, fp, pass);
2759bc421551SDag-Erling Smørgrav 		if (0 <= pretranstype)
2760bc421551SDag-Erling Smørgrav 		  putc(typemap[pretranstype], fp);
2761bc421551SDag-Erling Smørgrav 		for (i = thistimei; i < thistimelim; i++)
2762bc421551SDag-Erling Smørgrav 		  putc(typemap[types[i]], fp);
2763bc421551SDag-Erling Smørgrav 		if (hicut)
2764bc421551SDag-Erling Smørgrav 		  putc(typemap[unspecifiedtype], fp);
2765bc421551SDag-Erling Smørgrav 
2766bc421551SDag-Erling Smørgrav 		for (i = old0; i < typecnt; i++) {
2767bc421551SDag-Erling Smørgrav 		  int h = (i == old0 ? thisdefaulttype
2768bc421551SDag-Erling Smørgrav 			   : i == thisdefaulttype ? old0 : i);
2769bc421551SDag-Erling Smørgrav 		  if (!omittype[h]) {
2770bc421551SDag-Erling Smørgrav 		    puttzcode(utoffs[h], fp);
2771bc421551SDag-Erling Smørgrav 		    putc(isdsts[h], fp);
2772bc421551SDag-Erling Smørgrav 		    putc(indmap[desigidx[h]], fp);
2773bc421551SDag-Erling Smørgrav 		  }
2774bc421551SDag-Erling Smørgrav 		}
2775bc421551SDag-Erling Smørgrav 		if (thischarcnt != 0)
2776bc421551SDag-Erling Smørgrav 			fwrite(thischars, sizeof thischars[0],
2777bc421551SDag-Erling Smørgrav 				      thischarcnt, fp);
2778bc421551SDag-Erling Smørgrav 		thisleaplim = thisleapi + thisleapcnt;
2779bc421551SDag-Erling Smørgrav 		for (i = thisleapi; i < thisleaplim; ++i) {
2780bc421551SDag-Erling Smørgrav 			register zic_t	todo;
2781bc421551SDag-Erling Smørgrav 
2782bc421551SDag-Erling Smørgrav 			if (roll[i]) {
2783bc421551SDag-Erling Smørgrav 				if (timecnt == 0 || trans[i] < ats[0]) {
2784bc421551SDag-Erling Smørgrav 					j = 0;
2785bc421551SDag-Erling Smørgrav 					while (isdsts[j])
2786bc421551SDag-Erling Smørgrav 						if (++j >= typecnt) {
2787bc421551SDag-Erling Smørgrav 							j = 0;
2788bc421551SDag-Erling Smørgrav 							break;
2789bc421551SDag-Erling Smørgrav 						}
2790bc421551SDag-Erling Smørgrav 				} else {
2791bc421551SDag-Erling Smørgrav 					j = 1;
2792bc421551SDag-Erling Smørgrav 					while (j < timecnt &&
2793bc421551SDag-Erling Smørgrav 						trans[i] >= ats[j])
2794bc421551SDag-Erling Smørgrav 							++j;
2795bc421551SDag-Erling Smørgrav 					j = types[j - 1];
2796bc421551SDag-Erling Smørgrav 				}
2797bc421551SDag-Erling Smørgrav 				todo = tadd(trans[i], -utoffs[j]);
2798bc421551SDag-Erling Smørgrav 			} else	todo = trans[i];
2799bc421551SDag-Erling Smørgrav 			puttzcodepass(todo, fp, pass);
2800bc421551SDag-Erling Smørgrav 			puttzcode(corr[i], fp);
2801bc421551SDag-Erling Smørgrav 		}
2802bc421551SDag-Erling Smørgrav 		if (thisleapexpiry) {
2803bc421551SDag-Erling Smørgrav 		  /* Append a no-op leap correction indicating when the leap
2804bc421551SDag-Erling Smørgrav 		     second table expires.  Although this does not conform to
2805bc421551SDag-Erling Smørgrav 		     Internet RFC 8536, most clients seem to accept this and
2806bc421551SDag-Erling Smørgrav 		     the plan is to amend the RFC to allow this in version 4
2807bc421551SDag-Erling Smørgrav 		     TZif files.  */
2808bc421551SDag-Erling Smørgrav 		  puttzcodepass(leapexpires, fp, pass);
2809bc421551SDag-Erling Smørgrav 		  puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp);
2810bc421551SDag-Erling Smørgrav 		}
2811bc421551SDag-Erling Smørgrav 		if (stdcnt != 0)
2812bc421551SDag-Erling Smørgrav 		  for (i = old0; i < typecnt; i++)
2813bc421551SDag-Erling Smørgrav 			if (!omittype[i])
2814bc421551SDag-Erling Smørgrav 				putc(ttisstds[i], fp);
2815bc421551SDag-Erling Smørgrav 		if (utcnt != 0)
2816bc421551SDag-Erling Smørgrav 		  for (i = old0; i < typecnt; i++)
2817bc421551SDag-Erling Smørgrav 			if (!omittype[i])
2818bc421551SDag-Erling Smørgrav 				putc(ttisuts[i], fp);
2819bc421551SDag-Erling Smørgrav 	}
2820bc421551SDag-Erling Smørgrav 	fprintf(fp, "\n%s\n", string);
2821bc421551SDag-Erling Smørgrav 	close_file(fp, directory, name, tempname);
2822bc421551SDag-Erling Smørgrav 	if (chmod(tempname, mflag) < 0) {
2823bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("cannot change mode of %s to %03o"),
2824bc421551SDag-Erling Smørgrav 		    tempname, (unsigned)mflag);
2825bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
2826bc421551SDag-Erling Smørgrav 	}
2827bc421551SDag-Erling Smørgrav 	if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
2828bc421551SDag-Erling Smørgrav 	    && chown(tempname, uflag, gflag) < 0) {
2829bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("cannot change ownership of %s"),
2830bc421551SDag-Erling Smørgrav 		    tempname);
2831bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
2832bc421551SDag-Erling Smørgrav 	}
2833bc421551SDag-Erling Smørgrav 	rename_dest(tempname, name);
2834bc421551SDag-Erling Smørgrav 	free(ats);
2835bc421551SDag-Erling Smørgrav }
2836bc421551SDag-Erling Smørgrav 
2837bc421551SDag-Erling Smørgrav static char const *
2838bc421551SDag-Erling Smørgrav abbroffset(char *buf, zic_t offset)
2839bc421551SDag-Erling Smørgrav {
2840bc421551SDag-Erling Smørgrav   char sign = '+';
2841bc421551SDag-Erling Smørgrav   int seconds, minutes;
2842bc421551SDag-Erling Smørgrav 
2843bc421551SDag-Erling Smørgrav   if (offset < 0) {
2844bc421551SDag-Erling Smørgrav     offset = -offset;
2845bc421551SDag-Erling Smørgrav     sign = '-';
2846bc421551SDag-Erling Smørgrav   }
2847bc421551SDag-Erling Smørgrav 
2848bc421551SDag-Erling Smørgrav   seconds = offset % SECSPERMIN;
2849bc421551SDag-Erling Smørgrav   offset /= SECSPERMIN;
2850bc421551SDag-Erling Smørgrav   minutes = offset % MINSPERHOUR;
2851bc421551SDag-Erling Smørgrav   offset /= MINSPERHOUR;
2852bc421551SDag-Erling Smørgrav   if (100 <= offset) {
2853bc421551SDag-Erling Smørgrav     error(_("%%z UT offset magnitude exceeds 99:59:59"));
2854bc421551SDag-Erling Smørgrav     return "%z";
2855bc421551SDag-Erling Smørgrav   } else {
2856bc421551SDag-Erling Smørgrav     char *p = buf;
2857bc421551SDag-Erling Smørgrav     *p++ = sign;
2858bc421551SDag-Erling Smørgrav     *p++ = '0' + offset / 10;
2859bc421551SDag-Erling Smørgrav     *p++ = '0' + offset % 10;
2860bc421551SDag-Erling Smørgrav     if (minutes | seconds) {
2861bc421551SDag-Erling Smørgrav       *p++ = '0' + minutes / 10;
2862bc421551SDag-Erling Smørgrav       *p++ = '0' + minutes % 10;
2863bc421551SDag-Erling Smørgrav       if (seconds) {
2864bc421551SDag-Erling Smørgrav 	*p++ = '0' + seconds / 10;
2865bc421551SDag-Erling Smørgrav 	*p++ = '0' + seconds % 10;
2866bc421551SDag-Erling Smørgrav       }
2867bc421551SDag-Erling Smørgrav     }
2868bc421551SDag-Erling Smørgrav     *p = '\0';
2869bc421551SDag-Erling Smørgrav     return buf;
2870bc421551SDag-Erling Smørgrav   }
2871bc421551SDag-Erling Smørgrav }
2872bc421551SDag-Erling Smørgrav 
2873bc421551SDag-Erling Smørgrav static char const disable_percent_s[] = "";
2874bc421551SDag-Erling Smørgrav 
2875bc421551SDag-Erling Smørgrav static ptrdiff_t
2876bc421551SDag-Erling Smørgrav doabbr(char *abbr, struct zone const *zp, char const *letters,
2877bc421551SDag-Erling Smørgrav        bool isdst, zic_t save, bool doquotes)
2878bc421551SDag-Erling Smørgrav {
2879bc421551SDag-Erling Smørgrav 	register char *	cp;
2880bc421551SDag-Erling Smørgrav 	register char *	slashp;
2881bc421551SDag-Erling Smørgrav 	ptrdiff_t len;
2882bc421551SDag-Erling Smørgrav 	char const *format = zp->z_format;
2883bc421551SDag-Erling Smørgrav 
2884bc421551SDag-Erling Smørgrav 	slashp = strchr(format, '/');
2885bc421551SDag-Erling Smørgrav 	if (slashp == NULL) {
2886bc421551SDag-Erling Smørgrav 	  char letterbuf[PERCENT_Z_LEN_BOUND + 1];
2887bc421551SDag-Erling Smørgrav 	  if (zp->z_format_specifier == 'z')
2888bc421551SDag-Erling Smørgrav 	    letters = abbroffset(letterbuf, zp->z_stdoff + save);
2889bc421551SDag-Erling Smørgrav 	  else if (!letters)
2890bc421551SDag-Erling Smørgrav 	    letters = "%s";
2891bc421551SDag-Erling Smørgrav 	  else if (letters == disable_percent_s)
2892bc421551SDag-Erling Smørgrav 	    return 0;
2893bc421551SDag-Erling Smørgrav 	  sprintf(abbr, format, letters);
2894bc421551SDag-Erling Smørgrav 	} else if (isdst) {
2895bc421551SDag-Erling Smørgrav 		strcpy(abbr, slashp + 1);
2896bc421551SDag-Erling Smørgrav 	} else {
2897bc421551SDag-Erling Smørgrav 		memcpy(abbr, format, slashp - format);
2898bc421551SDag-Erling Smørgrav 		abbr[slashp - format] = '\0';
2899bc421551SDag-Erling Smørgrav 	}
2900bc421551SDag-Erling Smørgrav 	len = strlen(abbr);
2901bc421551SDag-Erling Smørgrav 	if (!doquotes)
2902bc421551SDag-Erling Smørgrav 		return len;
2903bc421551SDag-Erling Smørgrav 	for (cp = abbr; is_alpha(*cp); cp++)
2904bc421551SDag-Erling Smørgrav 		continue;
2905bc421551SDag-Erling Smørgrav 	if (len > 0 && *cp == '\0')
2906bc421551SDag-Erling Smørgrav 		return len;
2907bc421551SDag-Erling Smørgrav 	abbr[len + 2] = '\0';
2908bc421551SDag-Erling Smørgrav 	abbr[len + 1] = '>';
2909bc421551SDag-Erling Smørgrav 	memmove(abbr + 1, abbr, len);
2910bc421551SDag-Erling Smørgrav 	abbr[0] = '<';
2911bc421551SDag-Erling Smørgrav 	return len + 2;
2912bc421551SDag-Erling Smørgrav }
2913bc421551SDag-Erling Smørgrav 
2914bc421551SDag-Erling Smørgrav static void
2915bc421551SDag-Erling Smørgrav updateminmax(const zic_t x)
2916bc421551SDag-Erling Smørgrav {
2917bc421551SDag-Erling Smørgrav 	if (min_year > x)
2918bc421551SDag-Erling Smørgrav 		min_year = x;
2919bc421551SDag-Erling Smørgrav 	if (max_year < x)
2920bc421551SDag-Erling Smørgrav 		max_year = x;
2921bc421551SDag-Erling Smørgrav }
2922bc421551SDag-Erling Smørgrav 
2923bc421551SDag-Erling Smørgrav static int
2924bc421551SDag-Erling Smørgrav stringoffset(char *result, zic_t offset)
2925bc421551SDag-Erling Smørgrav {
2926bc421551SDag-Erling Smørgrav 	register int	hours;
2927bc421551SDag-Erling Smørgrav 	register int	minutes;
2928bc421551SDag-Erling Smørgrav 	register int	seconds;
2929bc421551SDag-Erling Smørgrav 	bool negative = offset < 0;
2930bc421551SDag-Erling Smørgrav 	int len = negative;
2931bc421551SDag-Erling Smørgrav 
2932bc421551SDag-Erling Smørgrav 	if (negative) {
2933bc421551SDag-Erling Smørgrav 		offset = -offset;
2934bc421551SDag-Erling Smørgrav 		result[0] = '-';
2935bc421551SDag-Erling Smørgrav 	}
2936bc421551SDag-Erling Smørgrav 	seconds = offset % SECSPERMIN;
2937bc421551SDag-Erling Smørgrav 	offset /= SECSPERMIN;
2938bc421551SDag-Erling Smørgrav 	minutes = offset % MINSPERHOUR;
2939bc421551SDag-Erling Smørgrav 	offset /= MINSPERHOUR;
2940bc421551SDag-Erling Smørgrav 	hours = offset;
2941bc421551SDag-Erling Smørgrav 	if (hours >= HOURSPERDAY * DAYSPERWEEK) {
2942bc421551SDag-Erling Smørgrav 		result[0] = '\0';
2943bc421551SDag-Erling Smørgrav 		return 0;
2944bc421551SDag-Erling Smørgrav 	}
2945bc421551SDag-Erling Smørgrav 	len += sprintf(result + len, "%d", hours);
2946bc421551SDag-Erling Smørgrav 	if (minutes != 0 || seconds != 0) {
2947bc421551SDag-Erling Smørgrav 		len += sprintf(result + len, ":%02d", minutes);
2948bc421551SDag-Erling Smørgrav 		if (seconds != 0)
2949bc421551SDag-Erling Smørgrav 			len += sprintf(result + len, ":%02d", seconds);
2950bc421551SDag-Erling Smørgrav 	}
2951bc421551SDag-Erling Smørgrav 	return len;
2952bc421551SDag-Erling Smørgrav }
2953bc421551SDag-Erling Smørgrav 
2954bc421551SDag-Erling Smørgrav static int
2955bc421551SDag-Erling Smørgrav stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff)
2956bc421551SDag-Erling Smørgrav {
2957bc421551SDag-Erling Smørgrav 	register zic_t	tod = rp->r_tod;
2958bc421551SDag-Erling Smørgrav 	register int	compat = 0;
2959bc421551SDag-Erling Smørgrav 
2960bc421551SDag-Erling Smørgrav 	if (rp->r_dycode == DC_DOM) {
2961bc421551SDag-Erling Smørgrav 		register int	month, total;
2962bc421551SDag-Erling Smørgrav 
2963bc421551SDag-Erling Smørgrav 		if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
2964bc421551SDag-Erling Smørgrav 			return -1;
2965bc421551SDag-Erling Smørgrav 		total = 0;
2966bc421551SDag-Erling Smørgrav 		for (month = 0; month < rp->r_month; ++month)
2967bc421551SDag-Erling Smørgrav 			total += len_months[0][month];
2968bc421551SDag-Erling Smørgrav 		/* Omit the "J" in Jan and Feb, as that's shorter.  */
2969bc421551SDag-Erling Smørgrav 		if (rp->r_month <= 1)
2970bc421551SDag-Erling Smørgrav 		  result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
2971bc421551SDag-Erling Smørgrav 		else
2972bc421551SDag-Erling Smørgrav 		  result += sprintf(result, "J%d", total + rp->r_dayofmonth);
2973bc421551SDag-Erling Smørgrav 	} else {
2974bc421551SDag-Erling Smørgrav 		register int	week;
2975bc421551SDag-Erling Smørgrav 		register int	wday = rp->r_wday;
2976bc421551SDag-Erling Smørgrav 		register int	wdayoff;
2977bc421551SDag-Erling Smørgrav 
2978bc421551SDag-Erling Smørgrav 		if (rp->r_dycode == DC_DOWGEQ) {
2979bc421551SDag-Erling Smørgrav 			wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
2980bc421551SDag-Erling Smørgrav 			if (wdayoff)
2981bc421551SDag-Erling Smørgrav 				compat = 2013;
2982bc421551SDag-Erling Smørgrav 			wday -= wdayoff;
2983bc421551SDag-Erling Smørgrav 			tod += wdayoff * SECSPERDAY;
2984bc421551SDag-Erling Smørgrav 			week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
2985bc421551SDag-Erling Smørgrav 		} else if (rp->r_dycode == DC_DOWLEQ) {
2986bc421551SDag-Erling Smørgrav 			if (rp->r_dayofmonth == len_months[1][rp->r_month])
2987bc421551SDag-Erling Smørgrav 				week = 5;
2988bc421551SDag-Erling Smørgrav 			else {
2989bc421551SDag-Erling Smørgrav 				wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
2990bc421551SDag-Erling Smørgrav 				if (wdayoff)
2991bc421551SDag-Erling Smørgrav 					compat = 2013;
2992bc421551SDag-Erling Smørgrav 				wday -= wdayoff;
2993bc421551SDag-Erling Smørgrav 				tod += wdayoff * SECSPERDAY;
2994bc421551SDag-Erling Smørgrav 				week = rp->r_dayofmonth / DAYSPERWEEK;
2995bc421551SDag-Erling Smørgrav 			}
2996bc421551SDag-Erling Smørgrav 		} else	return -1;	/* "cannot happen" */
2997bc421551SDag-Erling Smørgrav 		if (wday < 0)
2998bc421551SDag-Erling Smørgrav 			wday += DAYSPERWEEK;
2999bc421551SDag-Erling Smørgrav 		result += sprintf(result, "M%d.%d.%d",
3000bc421551SDag-Erling Smørgrav 				  rp->r_month + 1, week, wday);
3001bc421551SDag-Erling Smørgrav 	}
3002bc421551SDag-Erling Smørgrav 	if (rp->r_todisut)
3003bc421551SDag-Erling Smørgrav 	  tod += stdoff;
3004bc421551SDag-Erling Smørgrav 	if (rp->r_todisstd && !rp->r_isdst)
3005bc421551SDag-Erling Smørgrav 	  tod += save;
3006bc421551SDag-Erling Smørgrav 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
3007bc421551SDag-Erling Smørgrav 		*result++ = '/';
3008bc421551SDag-Erling Smørgrav 		if (! stringoffset(result, tod))
3009bc421551SDag-Erling Smørgrav 			return -1;
3010bc421551SDag-Erling Smørgrav 		if (tod < 0) {
3011bc421551SDag-Erling Smørgrav 			if (compat < 2013)
3012bc421551SDag-Erling Smørgrav 				compat = 2013;
3013bc421551SDag-Erling Smørgrav 		} else if (SECSPERDAY <= tod) {
3014bc421551SDag-Erling Smørgrav 			if (compat < 1994)
3015bc421551SDag-Erling Smørgrav 				compat = 1994;
3016bc421551SDag-Erling Smørgrav 		}
3017bc421551SDag-Erling Smørgrav 	}
3018bc421551SDag-Erling Smørgrav 	return compat;
3019bc421551SDag-Erling Smørgrav }
3020bc421551SDag-Erling Smørgrav 
3021bc421551SDag-Erling Smørgrav static int
3022bc421551SDag-Erling Smørgrav rule_cmp(struct rule const *a, struct rule const *b)
3023bc421551SDag-Erling Smørgrav {
3024bc421551SDag-Erling Smørgrav 	if (!a)
3025bc421551SDag-Erling Smørgrav 		return -!!b;
3026bc421551SDag-Erling Smørgrav 	if (!b)
3027bc421551SDag-Erling Smørgrav 		return 1;
3028bc421551SDag-Erling Smørgrav 	if (a->r_hiyear != b->r_hiyear)
3029bc421551SDag-Erling Smørgrav 		return a->r_hiyear < b->r_hiyear ? -1 : 1;
3030bc421551SDag-Erling Smørgrav 	if (a->r_hiyear == ZIC_MAX)
3031bc421551SDag-Erling Smørgrav 		return 0;
3032bc421551SDag-Erling Smørgrav 	if (a->r_month - b->r_month != 0)
3033bc421551SDag-Erling Smørgrav 		return a->r_month - b->r_month;
3034bc421551SDag-Erling Smørgrav 	return a->r_dayofmonth - b->r_dayofmonth;
3035bc421551SDag-Erling Smørgrav }
3036bc421551SDag-Erling Smørgrav 
3037*a979394aSDag-Erling Smørgrav /* Store into RESULT a proleptic TZ string that represent the future
303846c59934SDag-Erling Smørgrav    predictions for the zone ZPFIRST with ZONECOUNT entries.  Return a
303946c59934SDag-Erling Smørgrav    compatibility indicator (a TZDB release year) if successful, a
3040*a979394aSDag-Erling Smørgrav    negative integer if no such TZ string exists.  */
3041bc421551SDag-Erling Smørgrav static int
3042bc421551SDag-Erling Smørgrav stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
3043bc421551SDag-Erling Smørgrav {
3044bc421551SDag-Erling Smørgrav 	register const struct zone *	zp;
3045bc421551SDag-Erling Smørgrav 	register struct rule *		rp;
3046bc421551SDag-Erling Smørgrav 	register struct rule *		stdrp;
3047bc421551SDag-Erling Smørgrav 	register struct rule *		dstrp;
3048bc421551SDag-Erling Smørgrav 	register ptrdiff_t		i;
3049bc421551SDag-Erling Smørgrav 	register int			compat = 0;
3050bc421551SDag-Erling Smørgrav 	register int			c;
3051bc421551SDag-Erling Smørgrav 	int				offsetlen;
3052bc421551SDag-Erling Smørgrav 	struct rule			stdr, dstr;
3053bc421551SDag-Erling Smørgrav 	ptrdiff_t len;
3054bc421551SDag-Erling Smørgrav 	int dstcmp;
3055bc421551SDag-Erling Smørgrav 	struct rule *lastrp[2] = { NULL, NULL };
3056bc421551SDag-Erling Smørgrav 	struct zone zstr[2];
3057bc421551SDag-Erling Smørgrav 	struct zone const *stdzp;
3058bc421551SDag-Erling Smørgrav 	struct zone const *dstzp;
3059bc421551SDag-Erling Smørgrav 
3060bc421551SDag-Erling Smørgrav 	result[0] = '\0';
3061bc421551SDag-Erling Smørgrav 
3062bc421551SDag-Erling Smørgrav 	/* Internet RFC 8536 section 5.1 says to use an empty TZ string if
3063bc421551SDag-Erling Smørgrav 	   future timestamps are truncated.  */
3064bc421551SDag-Erling Smørgrav 	if (hi_time < max_time)
3065bc421551SDag-Erling Smørgrav 	  return -1;
3066bc421551SDag-Erling Smørgrav 
3067bc421551SDag-Erling Smørgrav 	zp = zpfirst + zonecount - 1;
3068bc421551SDag-Erling Smørgrav 	for (i = 0; i < zp->z_nrules; ++i) {
3069bc421551SDag-Erling Smørgrav 		struct rule **last;
3070bc421551SDag-Erling Smørgrav 		int cmp;
3071bc421551SDag-Erling Smørgrav 		rp = &zp->z_rules[i];
3072bc421551SDag-Erling Smørgrav 		last = &lastrp[rp->r_isdst];
3073bc421551SDag-Erling Smørgrav 		cmp = rule_cmp(*last, rp);
3074bc421551SDag-Erling Smørgrav 		if (cmp < 0)
3075bc421551SDag-Erling Smørgrav 		  *last = rp;
3076bc421551SDag-Erling Smørgrav 		else if (cmp == 0)
3077bc421551SDag-Erling Smørgrav 		  return -1;
3078bc421551SDag-Erling Smørgrav 	}
3079bc421551SDag-Erling Smørgrav 	stdrp = lastrp[false];
3080bc421551SDag-Erling Smørgrav 	dstrp = lastrp[true];
3081bc421551SDag-Erling Smørgrav 	dstcmp = zp->z_nrules ? rule_cmp(dstrp, stdrp) : zp->z_isdst ? 1 : -1;
3082bc421551SDag-Erling Smørgrav 	stdzp = dstzp = zp;
3083bc421551SDag-Erling Smørgrav 
3084bc421551SDag-Erling Smørgrav 	if (dstcmp < 0) {
3085bc421551SDag-Erling Smørgrav 	  /* Standard time all year.  */
3086bc421551SDag-Erling Smørgrav 	  dstrp = NULL;
3087bc421551SDag-Erling Smørgrav 	} else if (0 < dstcmp) {
3088bc421551SDag-Erling Smørgrav 	  /* DST all year.  Use an abbreviation like
3089bc421551SDag-Erling Smørgrav 	     "XXX3EDT4,0/0,J365/23" for EDT (-04) all year.  */
3090bc421551SDag-Erling Smørgrav 	  zic_t save = dstrp ? dstrp->r_save : zp->z_save;
3091bc421551SDag-Erling Smørgrav 	  if (0 <= save)
3092bc421551SDag-Erling Smørgrav 	    {
3093bc421551SDag-Erling Smørgrav 	      /* Positive DST, the typical case for all-year DST.
3094bc421551SDag-Erling Smørgrav 		 Fake a timezone with negative DST.  */
3095bc421551SDag-Erling Smørgrav 	      stdzp = &zstr[0];
3096bc421551SDag-Erling Smørgrav 	      dstzp = &zstr[1];
3097bc421551SDag-Erling Smørgrav 	      zstr[0].z_stdoff = zp->z_stdoff + 2 * save;
3098bc421551SDag-Erling Smørgrav 	      zstr[0].z_format = "XXX";  /* Any 3 letters will do.  */
3099bc421551SDag-Erling Smørgrav 	      zstr[0].z_format_specifier = 0;
3100bc421551SDag-Erling Smørgrav 	      zstr[1].z_stdoff = zstr[0].z_stdoff;
3101bc421551SDag-Erling Smørgrav 	      zstr[1].z_format = zp->z_format;
3102bc421551SDag-Erling Smørgrav 	      zstr[1].z_format_specifier = zp->z_format_specifier;
3103bc421551SDag-Erling Smørgrav 	    }
3104bc421551SDag-Erling Smørgrav 	  dstr.r_month = TM_JANUARY;
3105bc421551SDag-Erling Smørgrav 	  dstr.r_dycode = DC_DOM;
3106bc421551SDag-Erling Smørgrav 	  dstr.r_dayofmonth = 1;
3107bc421551SDag-Erling Smørgrav 	  dstr.r_tod = 0;
3108bc421551SDag-Erling Smørgrav 	  dstr.r_todisstd = dstr.r_todisut = false;
3109bc421551SDag-Erling Smørgrav 	  dstr.r_isdst = true;
3110bc421551SDag-Erling Smørgrav 	  dstr.r_save = save < 0 ? save : -save;
3111bc421551SDag-Erling Smørgrav 	  dstr.r_abbrvar = dstrp ? dstrp->r_abbrvar : NULL;
3112bc421551SDag-Erling Smørgrav 	  stdr.r_month = TM_DECEMBER;
3113bc421551SDag-Erling Smørgrav 	  stdr.r_dycode = DC_DOM;
3114bc421551SDag-Erling Smørgrav 	  stdr.r_dayofmonth = 31;
3115bc421551SDag-Erling Smørgrav 	  stdr.r_tod = SECSPERDAY + dstr.r_save;
3116bc421551SDag-Erling Smørgrav 	  stdr.r_todisstd = stdr.r_todisut = false;
3117bc421551SDag-Erling Smørgrav 	  stdr.r_isdst = false;
3118bc421551SDag-Erling Smørgrav 	  stdr.r_save = 0;
3119bc421551SDag-Erling Smørgrav 	  stdr.r_abbrvar = save < 0 && stdrp ? stdrp->r_abbrvar : NULL;
3120bc421551SDag-Erling Smørgrav 	  dstrp = &dstr;
3121bc421551SDag-Erling Smørgrav 	  stdrp = &stdr;
3122bc421551SDag-Erling Smørgrav 	}
3123bc421551SDag-Erling Smørgrav 	len = doabbr(result, stdzp, stdrp ? stdrp->r_abbrvar : NULL,
3124bc421551SDag-Erling Smørgrav 		     false, 0, true);
3125bc421551SDag-Erling Smørgrav 	offsetlen = stringoffset(result + len, - stdzp->z_stdoff);
3126bc421551SDag-Erling Smørgrav 	if (! offsetlen) {
3127bc421551SDag-Erling Smørgrav 		result[0] = '\0';
3128bc421551SDag-Erling Smørgrav 		return -1;
3129bc421551SDag-Erling Smørgrav 	}
3130bc421551SDag-Erling Smørgrav 	len += offsetlen;
3131bc421551SDag-Erling Smørgrav 	if (dstrp == NULL)
3132bc421551SDag-Erling Smørgrav 		return compat;
3133bc421551SDag-Erling Smørgrav 	len += doabbr(result + len, dstzp, dstrp->r_abbrvar,
3134bc421551SDag-Erling Smørgrav 		      dstrp->r_isdst, dstrp->r_save, true);
3135bc421551SDag-Erling Smørgrav 	if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) {
3136bc421551SDag-Erling Smørgrav 	  offsetlen = stringoffset(result + len,
3137bc421551SDag-Erling Smørgrav 				   - (dstzp->z_stdoff + dstrp->r_save));
3138bc421551SDag-Erling Smørgrav 	  if (! offsetlen) {
3139bc421551SDag-Erling Smørgrav 	    result[0] = '\0';
3140bc421551SDag-Erling Smørgrav 	    return -1;
3141bc421551SDag-Erling Smørgrav 	  }
3142bc421551SDag-Erling Smørgrav 	  len += offsetlen;
3143bc421551SDag-Erling Smørgrav 	}
3144bc421551SDag-Erling Smørgrav 	result[len++] = ',';
3145bc421551SDag-Erling Smørgrav 	c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff);
3146bc421551SDag-Erling Smørgrav 	if (c < 0) {
3147bc421551SDag-Erling Smørgrav 		result[0] = '\0';
3148bc421551SDag-Erling Smørgrav 		return -1;
3149bc421551SDag-Erling Smørgrav 	}
3150bc421551SDag-Erling Smørgrav 	if (compat < c)
3151bc421551SDag-Erling Smørgrav 		compat = c;
3152bc421551SDag-Erling Smørgrav 	len += strlen(result + len);
3153bc421551SDag-Erling Smørgrav 	result[len++] = ',';
3154bc421551SDag-Erling Smørgrav 	c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff);
3155bc421551SDag-Erling Smørgrav 	if (c < 0) {
3156bc421551SDag-Erling Smørgrav 		result[0] = '\0';
3157bc421551SDag-Erling Smørgrav 		return -1;
3158bc421551SDag-Erling Smørgrav 	}
3159bc421551SDag-Erling Smørgrav 	if (compat < c)
3160bc421551SDag-Erling Smørgrav 		compat = c;
3161bc421551SDag-Erling Smørgrav 	return compat;
3162bc421551SDag-Erling Smørgrav }
3163bc421551SDag-Erling Smørgrav 
3164bc421551SDag-Erling Smørgrav static void
3165bc421551SDag-Erling Smørgrav outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
3166bc421551SDag-Erling Smørgrav {
3167bc421551SDag-Erling Smørgrav 	register ptrdiff_t		i, j;
3168bc421551SDag-Erling Smørgrav 	register zic_t			starttime, untiltime;
3169bc421551SDag-Erling Smørgrav 	register bool			startttisstd;
3170bc421551SDag-Erling Smørgrav 	register bool			startttisut;
3171bc421551SDag-Erling Smørgrav 	register char *			startbuf;
3172bc421551SDag-Erling Smørgrav 	register char *			ab;
3173bc421551SDag-Erling Smørgrav 	register char *			envvar;
3174bc421551SDag-Erling Smørgrav 	register int			max_abbr_len;
3175bc421551SDag-Erling Smørgrav 	register int			max_envvar_len;
3176bc421551SDag-Erling Smørgrav 	register int			compat;
3177bc421551SDag-Erling Smørgrav 	register bool			do_extend;
3178bc421551SDag-Erling Smørgrav 	register char			version;
317946c59934SDag-Erling Smørgrav 	zic_t nonTZlimtime = ZIC_MIN;
318046c59934SDag-Erling Smørgrav 	int nonTZlimtype = -1;
3181bc421551SDag-Erling Smørgrav 	zic_t max_year0;
3182bc421551SDag-Erling Smørgrav 	int defaulttype = -1;
3183bc421551SDag-Erling Smørgrav 
3184bc421551SDag-Erling Smørgrav 	check_for_signal();
3185bc421551SDag-Erling Smørgrav 
3186bc421551SDag-Erling Smørgrav 	/* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND.  */
3187bc421551SDag-Erling Smørgrav 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
3188bc421551SDag-Erling Smørgrav 	max_envvar_len = 2 * max_abbr_len + 5 * 9;
3189bc421551SDag-Erling Smørgrav 
3190bc421551SDag-Erling Smørgrav 	startbuf = emalloc(max_abbr_len + 1);
3191bc421551SDag-Erling Smørgrav 	ab = emalloc(max_abbr_len + 1);
3192bc421551SDag-Erling Smørgrav 	envvar = emalloc(max_envvar_len + 1);
3193bc421551SDag-Erling Smørgrav 	INITIALIZE(untiltime);
3194bc421551SDag-Erling Smørgrav 	INITIALIZE(starttime);
3195bc421551SDag-Erling Smørgrav 	/*
3196bc421551SDag-Erling Smørgrav 	** Now. . .finally. . .generate some useful data!
3197bc421551SDag-Erling Smørgrav 	*/
3198bc421551SDag-Erling Smørgrav 	timecnt = 0;
3199bc421551SDag-Erling Smørgrav 	typecnt = 0;
3200bc421551SDag-Erling Smørgrav 	charcnt = 0;
3201bc421551SDag-Erling Smørgrav 	/*
3202bc421551SDag-Erling Smørgrav 	** Thanks to Earl Chew
3203bc421551SDag-Erling Smørgrav 	** for noting the need to unconditionally initialize startttisstd.
3204bc421551SDag-Erling Smørgrav 	*/
3205bc421551SDag-Erling Smørgrav 	startttisstd = false;
3206bc421551SDag-Erling Smørgrav 	startttisut = false;
3207bc421551SDag-Erling Smørgrav 	min_year = max_year = EPOCH_YEAR;
3208bc421551SDag-Erling Smørgrav 	if (leapseen) {
3209bc421551SDag-Erling Smørgrav 		updateminmax(leapminyear);
3210bc421551SDag-Erling Smørgrav 		updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
3211bc421551SDag-Erling Smørgrav 	}
3212bc421551SDag-Erling Smørgrav 	for (i = 0; i < zonecount; ++i) {
3213bc421551SDag-Erling Smørgrav 		struct zone const *zp = &zpfirst[i];
3214bc421551SDag-Erling Smørgrav 		if (i < zonecount - 1)
3215bc421551SDag-Erling Smørgrav 			updateminmax(zp->z_untilrule.r_loyear);
3216bc421551SDag-Erling Smørgrav 		for (j = 0; j < zp->z_nrules; ++j) {
3217bc421551SDag-Erling Smørgrav 			struct rule *rp = &zp->z_rules[j];
3218bc421551SDag-Erling Smørgrav 			updateminmax(rp->r_loyear);
3219bc421551SDag-Erling Smørgrav 			if (rp->r_hiwasnum)
3220bc421551SDag-Erling Smørgrav 				updateminmax(rp->r_hiyear);
3221bc421551SDag-Erling Smørgrav 		}
3222bc421551SDag-Erling Smørgrav 	}
3223bc421551SDag-Erling Smørgrav 	/*
3224bc421551SDag-Erling Smørgrav 	** Generate lots of data if a rule can't cover all future times.
3225bc421551SDag-Erling Smørgrav 	*/
3226bc421551SDag-Erling Smørgrav 	compat = stringzone(envvar, zpfirst, zonecount);
3227bc421551SDag-Erling Smørgrav 	version = compat < 2013 ? '2' : '3';
3228bc421551SDag-Erling Smørgrav 	do_extend = compat < 0;
3229bc421551SDag-Erling Smørgrav 	if (noise) {
3230bc421551SDag-Erling Smørgrav 		if (!*envvar)
3231bc421551SDag-Erling Smørgrav 			warning("%s %s",
3232*a979394aSDag-Erling Smørgrav 				_("no proleptic TZ string for zone"),
3233bc421551SDag-Erling Smørgrav 				zpfirst->z_name);
3234bc421551SDag-Erling Smørgrav 		else if (compat != 0) {
3235bc421551SDag-Erling Smørgrav 			/* Circa-COMPAT clients, and earlier clients, might
3236bc421551SDag-Erling Smørgrav 			   not work for this zone when given dates before
3237bc421551SDag-Erling Smørgrav 			   1970 or after 2038.  */
3238bc421551SDag-Erling Smørgrav 			warning(_("%s: pre-%d clients may mishandle"
3239bc421551SDag-Erling Smørgrav 				  " distant timestamps"),
3240bc421551SDag-Erling Smørgrav 				zpfirst->z_name, compat);
3241bc421551SDag-Erling Smørgrav 		}
3242bc421551SDag-Erling Smørgrav 	}
3243bc421551SDag-Erling Smørgrav 	if (do_extend) {
3244bc421551SDag-Erling Smørgrav 		if (min_year >= ZIC_MIN + years_of_observations)
3245bc421551SDag-Erling Smørgrav 			min_year -= years_of_observations;
3246bc421551SDag-Erling Smørgrav 		else	min_year = ZIC_MIN;
3247bc421551SDag-Erling Smørgrav 		if (max_year <= ZIC_MAX - years_of_observations)
3248bc421551SDag-Erling Smørgrav 			max_year += years_of_observations;
3249bc421551SDag-Erling Smørgrav 		else	max_year = ZIC_MAX;
3250bc421551SDag-Erling Smørgrav 	}
3251bc421551SDag-Erling Smørgrav 	max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
3252bc421551SDag-Erling Smørgrav 				  + EPOCH_YEAR + 1));
3253bc421551SDag-Erling Smørgrav 	max_year0 = max_year;
3254bc421551SDag-Erling Smørgrav 	if (want_bloat()) {
3255bc421551SDag-Erling Smørgrav 	  /* For the benefit of older systems,
3256bc421551SDag-Erling Smørgrav 	     generate data from 1900 through 2038.  */
325746c59934SDag-Erling Smørgrav 	  if (min_year > YEAR_32BIT_MIN - 1)
325846c59934SDag-Erling Smørgrav 		min_year = YEAR_32BIT_MIN - 1;
325946c59934SDag-Erling Smørgrav 	  if (max_year < YEAR_32BIT_MAX)
326046c59934SDag-Erling Smørgrav 		max_year = YEAR_32BIT_MAX;
3261bc421551SDag-Erling Smørgrav 	}
3262bc421551SDag-Erling Smørgrav 
3263bc421551SDag-Erling Smørgrav 	if (min_time < lo_time || hi_time < max_time)
3264bc421551SDag-Erling Smørgrav 	  unspecifiedtype = addtype(0, "-00", false, false, false);
3265bc421551SDag-Erling Smørgrav 
3266bc421551SDag-Erling Smørgrav 	for (i = 0; i < zonecount; ++i) {
3267bc421551SDag-Erling Smørgrav 		/*
3268bc421551SDag-Erling Smørgrav 		** A guess that may well be corrected later.
3269bc421551SDag-Erling Smørgrav 		*/
3270bc421551SDag-Erling Smørgrav 		zic_t save = 0;
3271bc421551SDag-Erling Smørgrav 		struct zone const *zp = &zpfirst[i];
3272bc421551SDag-Erling Smørgrav 		bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
3273bc421551SDag-Erling Smørgrav 		bool useuntil = i < (zonecount - 1);
3274bc421551SDag-Erling Smørgrav 		zic_t stdoff = zp->z_stdoff;
3275bc421551SDag-Erling Smørgrav 		zic_t startoff = stdoff;
3276bc421551SDag-Erling Smørgrav 		if (useuntil && zp->z_untiltime <= min_time)
3277bc421551SDag-Erling Smørgrav 			continue;
3278bc421551SDag-Erling Smørgrav 		eat(zp->z_filenum, zp->z_linenum);
3279bc421551SDag-Erling Smørgrav 		*startbuf = '\0';
3280bc421551SDag-Erling Smørgrav 		if (zp->z_nrules == 0) {
3281bc421551SDag-Erling Smørgrav 			int type;
3282bc421551SDag-Erling Smørgrav 			save = zp->z_save;
3283bc421551SDag-Erling Smørgrav 			doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
3284bc421551SDag-Erling Smørgrav 			type = addtype(oadd(zp->z_stdoff, save),
3285bc421551SDag-Erling Smørgrav 				startbuf, zp->z_isdst, startttisstd,
3286bc421551SDag-Erling Smørgrav 				startttisut);
3287bc421551SDag-Erling Smørgrav 			if (usestart) {
3288bc421551SDag-Erling Smørgrav 				addtt(starttime, type);
328946c59934SDag-Erling Smørgrav 				if (useuntil && nonTZlimtime < starttime) {
329046c59934SDag-Erling Smørgrav 				  nonTZlimtime = starttime;
329146c59934SDag-Erling Smørgrav 				  nonTZlimtype = type;
329246c59934SDag-Erling Smørgrav 				}
3293bc421551SDag-Erling Smørgrav 				usestart = false;
3294bc421551SDag-Erling Smørgrav 			} else
3295bc421551SDag-Erling Smørgrav 				defaulttype = type;
3296bc421551SDag-Erling Smørgrav 		} else {
3297bc421551SDag-Erling Smørgrav 		  zic_t year;
3298bc421551SDag-Erling Smørgrav 		  for (year = min_year; year <= max_year; ++year) {
3299bc421551SDag-Erling Smørgrav 			if (useuntil && year > zp->z_untilrule.r_hiyear)
3300bc421551SDag-Erling Smørgrav 				break;
3301bc421551SDag-Erling Smørgrav 			/*
3302bc421551SDag-Erling Smørgrav 			** Mark which rules to do in the current year.
3303bc421551SDag-Erling Smørgrav 			** For those to do, calculate rpytime(rp, year);
3304bc421551SDag-Erling Smørgrav 			** The former TYPE field was also considered here.
3305bc421551SDag-Erling Smørgrav 			*/
3306bc421551SDag-Erling Smørgrav 			for (j = 0; j < zp->z_nrules; ++j) {
3307bc421551SDag-Erling Smørgrav 				zic_t one = 1;
3308bc421551SDag-Erling Smørgrav 				zic_t y2038_boundary = one << 31;
3309bc421551SDag-Erling Smørgrav 				struct rule *rp = &zp->z_rules[j];
3310bc421551SDag-Erling Smørgrav 				eats(zp->z_filenum, zp->z_linenum,
3311bc421551SDag-Erling Smørgrav 				     rp->r_filenum, rp->r_linenum);
3312bc421551SDag-Erling Smørgrav 				rp->r_todo = year >= rp->r_loyear &&
3313bc421551SDag-Erling Smørgrav 						year <= rp->r_hiyear;
3314bc421551SDag-Erling Smørgrav 				if (rp->r_todo) {
3315bc421551SDag-Erling Smørgrav 					rp->r_temp = rpytime(rp, year);
3316bc421551SDag-Erling Smørgrav 					rp->r_todo
3317bc421551SDag-Erling Smørgrav 					  = (rp->r_temp < y2038_boundary
3318bc421551SDag-Erling Smørgrav 					     || year <= max_year0);
3319bc421551SDag-Erling Smørgrav 				}
3320bc421551SDag-Erling Smørgrav 			}
3321bc421551SDag-Erling Smørgrav 			for ( ; ; ) {
3322bc421551SDag-Erling Smørgrav 				register ptrdiff_t k;
3323bc421551SDag-Erling Smørgrav 				register zic_t	jtime, ktime;
3324bc421551SDag-Erling Smørgrav 				register zic_t	offset;
3325bc421551SDag-Erling Smørgrav 				struct rule *rp;
3326bc421551SDag-Erling Smørgrav 				int type;
3327bc421551SDag-Erling Smørgrav 
3328bc421551SDag-Erling Smørgrav 				INITIALIZE(ktime);
3329bc421551SDag-Erling Smørgrav 				if (useuntil) {
3330bc421551SDag-Erling Smørgrav 					/*
3331bc421551SDag-Erling Smørgrav 					** Turn untiltime into UT
3332bc421551SDag-Erling Smørgrav 					** assuming the current stdoff and
3333bc421551SDag-Erling Smørgrav 					** save values.
3334bc421551SDag-Erling Smørgrav 					*/
3335bc421551SDag-Erling Smørgrav 					untiltime = zp->z_untiltime;
3336bc421551SDag-Erling Smørgrav 					if (!zp->z_untilrule.r_todisut)
3337bc421551SDag-Erling Smørgrav 						untiltime = tadd(untiltime,
3338bc421551SDag-Erling Smørgrav 								 -stdoff);
3339bc421551SDag-Erling Smørgrav 					if (!zp->z_untilrule.r_todisstd)
3340bc421551SDag-Erling Smørgrav 						untiltime = tadd(untiltime,
3341bc421551SDag-Erling Smørgrav 								 -save);
3342bc421551SDag-Erling Smørgrav 				}
3343bc421551SDag-Erling Smørgrav 				/*
3344bc421551SDag-Erling Smørgrav 				** Find the rule (of those to do, if any)
3345bc421551SDag-Erling Smørgrav 				** that takes effect earliest in the year.
3346bc421551SDag-Erling Smørgrav 				*/
3347bc421551SDag-Erling Smørgrav 				k = -1;
3348bc421551SDag-Erling Smørgrav 				for (j = 0; j < zp->z_nrules; ++j) {
3349bc421551SDag-Erling Smørgrav 					struct rule *r = &zp->z_rules[j];
3350bc421551SDag-Erling Smørgrav 					if (!r->r_todo)
3351bc421551SDag-Erling Smørgrav 						continue;
3352bc421551SDag-Erling Smørgrav 					eats(zp->z_filenum, zp->z_linenum,
3353bc421551SDag-Erling Smørgrav 					     r->r_filenum, r->r_linenum);
3354bc421551SDag-Erling Smørgrav 					offset = r->r_todisut ? 0 : stdoff;
3355bc421551SDag-Erling Smørgrav 					if (!r->r_todisstd)
3356bc421551SDag-Erling Smørgrav 						offset = oadd(offset, save);
3357bc421551SDag-Erling Smørgrav 					jtime = r->r_temp;
3358bc421551SDag-Erling Smørgrav 					if (jtime == min_time ||
3359bc421551SDag-Erling Smørgrav 						jtime == max_time)
3360bc421551SDag-Erling Smørgrav 							continue;
3361bc421551SDag-Erling Smørgrav 					jtime = tadd(jtime, -offset);
3362bc421551SDag-Erling Smørgrav 					if (k < 0 || jtime < ktime) {
3363bc421551SDag-Erling Smørgrav 						k = j;
3364bc421551SDag-Erling Smørgrav 						ktime = jtime;
3365bc421551SDag-Erling Smørgrav 					} else if (jtime == ktime) {
3366bc421551SDag-Erling Smørgrav 					  char const *dup_rules_msg =
3367bc421551SDag-Erling Smørgrav 					    _("two rules for same instant");
3368bc421551SDag-Erling Smørgrav 					  eats(zp->z_filenum, zp->z_linenum,
3369bc421551SDag-Erling Smørgrav 					       r->r_filenum, r->r_linenum);
3370bc421551SDag-Erling Smørgrav 					  warning("%s", dup_rules_msg);
3371bc421551SDag-Erling Smørgrav 					  r = &zp->z_rules[k];
3372bc421551SDag-Erling Smørgrav 					  eats(zp->z_filenum, zp->z_linenum,
3373bc421551SDag-Erling Smørgrav 					       r->r_filenum, r->r_linenum);
3374bc421551SDag-Erling Smørgrav 					  error("%s", dup_rules_msg);
3375bc421551SDag-Erling Smørgrav 					}
3376bc421551SDag-Erling Smørgrav 				}
3377bc421551SDag-Erling Smørgrav 				if (k < 0)
3378bc421551SDag-Erling Smørgrav 					break;	/* go on to next year */
3379bc421551SDag-Erling Smørgrav 				rp = &zp->z_rules[k];
3380bc421551SDag-Erling Smørgrav 				rp->r_todo = false;
3381bc421551SDag-Erling Smørgrav 				if (useuntil && ktime >= untiltime) {
3382bc421551SDag-Erling Smørgrav 					if (!*startbuf
3383bc421551SDag-Erling Smørgrav 					    && (oadd(zp->z_stdoff, rp->r_save)
3384bc421551SDag-Erling Smørgrav 						== startoff))
3385bc421551SDag-Erling Smørgrav 					  doabbr(startbuf, zp, rp->r_abbrvar,
3386bc421551SDag-Erling Smørgrav 						 rp->r_isdst, rp->r_save,
3387bc421551SDag-Erling Smørgrav 						 false);
3388bc421551SDag-Erling Smørgrav 					break;
3389bc421551SDag-Erling Smørgrav 				}
3390bc421551SDag-Erling Smørgrav 				save = rp->r_save;
3391bc421551SDag-Erling Smørgrav 				if (usestart && ktime == starttime)
3392bc421551SDag-Erling Smørgrav 					usestart = false;
3393bc421551SDag-Erling Smørgrav 				if (usestart) {
3394bc421551SDag-Erling Smørgrav 					if (ktime < starttime) {
3395bc421551SDag-Erling Smørgrav 						startoff = oadd(zp->z_stdoff,
3396bc421551SDag-Erling Smørgrav 								save);
3397bc421551SDag-Erling Smørgrav 						doabbr(startbuf, zp,
3398bc421551SDag-Erling Smørgrav 							rp->r_abbrvar,
3399bc421551SDag-Erling Smørgrav 							rp->r_isdst,
3400bc421551SDag-Erling Smørgrav 							rp->r_save,
3401bc421551SDag-Erling Smørgrav 							false);
3402bc421551SDag-Erling Smørgrav 						continue;
3403bc421551SDag-Erling Smørgrav 					}
3404bc421551SDag-Erling Smørgrav 					if (*startbuf == '\0'
3405bc421551SDag-Erling Smørgrav 					    && startoff == oadd(zp->z_stdoff,
3406bc421551SDag-Erling Smørgrav 								save)) {
3407bc421551SDag-Erling Smørgrav 							doabbr(startbuf,
3408bc421551SDag-Erling Smørgrav 								zp,
3409bc421551SDag-Erling Smørgrav 								rp->r_abbrvar,
3410bc421551SDag-Erling Smørgrav 								rp->r_isdst,
3411bc421551SDag-Erling Smørgrav 								rp->r_save,
3412bc421551SDag-Erling Smørgrav 								false);
3413bc421551SDag-Erling Smørgrav 					}
3414bc421551SDag-Erling Smørgrav 				}
3415bc421551SDag-Erling Smørgrav 				eats(zp->z_filenum, zp->z_linenum,
3416bc421551SDag-Erling Smørgrav 				     rp->r_filenum, rp->r_linenum);
3417bc421551SDag-Erling Smørgrav 				doabbr(ab, zp, rp->r_abbrvar,
3418bc421551SDag-Erling Smørgrav 				       rp->r_isdst, rp->r_save, false);
3419bc421551SDag-Erling Smørgrav 				offset = oadd(zp->z_stdoff, rp->r_save);
3420bc421551SDag-Erling Smørgrav 				type = addtype(offset, ab, rp->r_isdst,
3421bc421551SDag-Erling Smørgrav 					rp->r_todisstd, rp->r_todisut);
3422bc421551SDag-Erling Smørgrav 				if (defaulttype < 0 && !rp->r_isdst)
3423bc421551SDag-Erling Smørgrav 				  defaulttype = type;
3424bc421551SDag-Erling Smørgrav 				addtt(ktime, type);
342546c59934SDag-Erling Smørgrav 				if (nonTZlimtime < ktime
342646c59934SDag-Erling Smørgrav 				    && (useuntil || rp->r_hiyear != ZIC_MAX)) {
342746c59934SDag-Erling Smørgrav 				  nonTZlimtime = ktime;
342846c59934SDag-Erling Smørgrav 				  nonTZlimtype = type;
342946c59934SDag-Erling Smørgrav 				}
3430bc421551SDag-Erling Smørgrav 			}
3431bc421551SDag-Erling Smørgrav 		  }
3432bc421551SDag-Erling Smørgrav 		}
3433bc421551SDag-Erling Smørgrav 		if (usestart) {
3434bc421551SDag-Erling Smørgrav 			bool isdst = startoff != zp->z_stdoff;
3435bc421551SDag-Erling Smørgrav 			if (*startbuf == '\0' && zp->z_format)
3436bc421551SDag-Erling Smørgrav 			  doabbr(startbuf, zp, disable_percent_s,
3437bc421551SDag-Erling Smørgrav 				 isdst, save, false);
3438bc421551SDag-Erling Smørgrav 			eat(zp->z_filenum, zp->z_linenum);
3439bc421551SDag-Erling Smørgrav 			if (*startbuf == '\0')
344046c59934SDag-Erling Smørgrav 			  error(_("can't determine time zone abbreviation"
344146c59934SDag-Erling Smørgrav 				  " to use just after until time"));
3442bc421551SDag-Erling Smørgrav 			else {
3443bc421551SDag-Erling Smørgrav 			  int type = addtype(startoff, startbuf, isdst,
3444bc421551SDag-Erling Smørgrav 					     startttisstd, startttisut);
3445bc421551SDag-Erling Smørgrav 			  if (defaulttype < 0 && !isdst)
3446bc421551SDag-Erling Smørgrav 			    defaulttype = type;
3447bc421551SDag-Erling Smørgrav 			  addtt(starttime, type);
3448bc421551SDag-Erling Smørgrav 			}
3449bc421551SDag-Erling Smørgrav 		}
3450bc421551SDag-Erling Smørgrav 		/*
3451bc421551SDag-Erling Smørgrav 		** Now we may get to set starttime for the next zone line.
3452bc421551SDag-Erling Smørgrav 		*/
3453bc421551SDag-Erling Smørgrav 		if (useuntil) {
3454bc421551SDag-Erling Smørgrav 			startttisstd = zp->z_untilrule.r_todisstd;
3455bc421551SDag-Erling Smørgrav 			startttisut = zp->z_untilrule.r_todisut;
3456bc421551SDag-Erling Smørgrav 			starttime = zp->z_untiltime;
3457bc421551SDag-Erling Smørgrav 			if (!startttisstd)
3458bc421551SDag-Erling Smørgrav 			  starttime = tadd(starttime, -save);
3459bc421551SDag-Erling Smørgrav 			if (!startttisut)
3460bc421551SDag-Erling Smørgrav 			  starttime = tadd(starttime, -stdoff);
3461bc421551SDag-Erling Smørgrav 		}
3462bc421551SDag-Erling Smørgrav 	}
3463bc421551SDag-Erling Smørgrav 	if (defaulttype < 0)
3464bc421551SDag-Erling Smørgrav 	  defaulttype = 0;
346546c59934SDag-Erling Smørgrav 	if (!do_extend && !want_bloat()) {
346646c59934SDag-Erling Smørgrav 	  /* Keep trailing transitions that are no greater than this.  */
346746c59934SDag-Erling Smørgrav 	  zic_t keep_at_max;
346846c59934SDag-Erling Smørgrav 
346946c59934SDag-Erling Smørgrav 	  /* The earliest transition into a time governed by the TZ string.  */
347046c59934SDag-Erling Smørgrav 	  zic_t TZstarttime = ZIC_MAX;
347146c59934SDag-Erling Smørgrav 	  for (i = 0; i < timecnt; i++) {
347246c59934SDag-Erling Smørgrav 	    zic_t at = attypes[i].at;
347346c59934SDag-Erling Smørgrav 	    if (nonTZlimtime < at && at < TZstarttime)
347446c59934SDag-Erling Smørgrav 	      TZstarttime = at;
347546c59934SDag-Erling Smørgrav 	  }
347646c59934SDag-Erling Smørgrav 	  if (TZstarttime == ZIC_MAX)
347746c59934SDag-Erling Smørgrav 	    TZstarttime = nonTZlimtime;
347846c59934SDag-Erling Smørgrav 
347946c59934SDag-Erling Smørgrav 	  /* Omit trailing transitions deducible from the TZ string,
348046c59934SDag-Erling Smørgrav 	     and not needed for -r or -R.  */
348146c59934SDag-Erling Smørgrav 	  keep_at_max = max(TZstarttime, redundant_time);
348246c59934SDag-Erling Smørgrav 	  for (i = j = 0; i < timecnt; i++)
348346c59934SDag-Erling Smørgrav 	    if (attypes[i].at <= keep_at_max) {
348446c59934SDag-Erling Smørgrav 	      attypes[j].at = attypes[i].at;
348546c59934SDag-Erling Smørgrav 	      attypes[j].dontmerge = (attypes[i].at == TZstarttime
348646c59934SDag-Erling Smørgrav 				      && (nonTZlimtype != attypes[i].type
348746c59934SDag-Erling Smørgrav 					  || strchr(envvar, ',')));
348846c59934SDag-Erling Smørgrav 	      attypes[j].type = attypes[i].type;
348946c59934SDag-Erling Smørgrav 	      j++;
349046c59934SDag-Erling Smørgrav 	    }
349146c59934SDag-Erling Smørgrav 	  timecnt = j;
349246c59934SDag-Erling Smørgrav 	}
3493bc421551SDag-Erling Smørgrav 	if (do_extend) {
3494bc421551SDag-Erling Smørgrav 		/*
349546c59934SDag-Erling Smørgrav 		** If we're extending the explicitly listed observations for
3496*a979394aSDag-Erling Smørgrav 		** 400 years because we can't fill the proleptic TZ field,
3497bc421551SDag-Erling Smørgrav 		** check whether we actually ended up explicitly listing
3498bc421551SDag-Erling Smørgrav 		** observations through that period.  If there aren't any
3499bc421551SDag-Erling Smørgrav 		** near the end of the 400-year period, add a redundant
3500bc421551SDag-Erling Smørgrav 		** one at the end of the final year, to make it clear
3501bc421551SDag-Erling Smørgrav 		** that we are claiming to have definite knowledge of
3502bc421551SDag-Erling Smørgrav 		** the lack of transitions up to that point.
3503bc421551SDag-Erling Smørgrav 		*/
3504bc421551SDag-Erling Smørgrav 		struct rule xr;
3505bc421551SDag-Erling Smørgrav 		struct attype *lastat;
3506bc421551SDag-Erling Smørgrav 		xr.r_month = TM_JANUARY;
3507bc421551SDag-Erling Smørgrav 		xr.r_dycode = DC_DOM;
3508bc421551SDag-Erling Smørgrav 		xr.r_dayofmonth = 1;
3509bc421551SDag-Erling Smørgrav 		xr.r_tod = 0;
3510bc421551SDag-Erling Smørgrav 		for (lastat = attypes, i = 1; i < timecnt; i++)
3511bc421551SDag-Erling Smørgrav 			if (attypes[i].at > lastat->at)
3512bc421551SDag-Erling Smørgrav 				lastat = &attypes[i];
3513bc421551SDag-Erling Smørgrav 		if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) {
3514bc421551SDag-Erling Smørgrav 			addtt(rpytime(&xr, max_year + 1),
3515bc421551SDag-Erling Smørgrav 			      lastat ? lastat->type : defaulttype);
3516bc421551SDag-Erling Smørgrav 			attypes[timecnt - 1].dontmerge = true;
3517bc421551SDag-Erling Smørgrav 		}
3518bc421551SDag-Erling Smørgrav 	}
3519bc421551SDag-Erling Smørgrav 	writezone(zpfirst->z_name, envvar, version, defaulttype);
3520bc421551SDag-Erling Smørgrav 	free(startbuf);
3521bc421551SDag-Erling Smørgrav 	free(ab);
3522bc421551SDag-Erling Smørgrav 	free(envvar);
3523bc421551SDag-Erling Smørgrav }
3524bc421551SDag-Erling Smørgrav 
3525bc421551SDag-Erling Smørgrav static void
3526bc421551SDag-Erling Smørgrav addtt(zic_t starttime, int type)
3527bc421551SDag-Erling Smørgrav {
3528bc421551SDag-Erling Smørgrav 	attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
3529bc421551SDag-Erling Smørgrav 	attypes[timecnt].at = starttime;
3530bc421551SDag-Erling Smørgrav 	attypes[timecnt].dontmerge = false;
3531bc421551SDag-Erling Smørgrav 	attypes[timecnt].type = type;
3532bc421551SDag-Erling Smørgrav 	++timecnt;
3533bc421551SDag-Erling Smørgrav }
3534bc421551SDag-Erling Smørgrav 
3535bc421551SDag-Erling Smørgrav static int
3536bc421551SDag-Erling Smørgrav addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
3537bc421551SDag-Erling Smørgrav {
3538bc421551SDag-Erling Smørgrav 	register int	i, j;
3539bc421551SDag-Erling Smørgrav 
3540bc421551SDag-Erling Smørgrav 	if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) {
3541bc421551SDag-Erling Smørgrav 		error(_("UT offset out of range"));
3542bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
3543bc421551SDag-Erling Smørgrav 	}
3544bc421551SDag-Erling Smørgrav 	if (!want_bloat())
3545bc421551SDag-Erling Smørgrav 	  ttisstd = ttisut = false;
3546bc421551SDag-Erling Smørgrav 
3547bc421551SDag-Erling Smørgrav 	for (j = 0; j < charcnt; ++j)
3548bc421551SDag-Erling Smørgrav 		if (strcmp(&chars[j], abbr) == 0)
3549bc421551SDag-Erling Smørgrav 			break;
3550bc421551SDag-Erling Smørgrav 	if (j == charcnt)
3551bc421551SDag-Erling Smørgrav 		newabbr(abbr);
3552bc421551SDag-Erling Smørgrav 	else {
3553bc421551SDag-Erling Smørgrav 	  /* If there's already an entry, return its index.  */
3554bc421551SDag-Erling Smørgrav 	  for (i = 0; i < typecnt; i++)
3555bc421551SDag-Erling Smørgrav 	    if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
3556bc421551SDag-Erling Smørgrav 		&& ttisstd == ttisstds[i] && ttisut == ttisuts[i])
3557bc421551SDag-Erling Smørgrav 	      return i;
3558bc421551SDag-Erling Smørgrav 	}
3559bc421551SDag-Erling Smørgrav 	/*
3560bc421551SDag-Erling Smørgrav 	** There isn't one; add a new one, unless there are already too
3561bc421551SDag-Erling Smørgrav 	** many.
3562bc421551SDag-Erling Smørgrav 	*/
3563bc421551SDag-Erling Smørgrav 	if (typecnt >= TZ_MAX_TYPES) {
3564bc421551SDag-Erling Smørgrav 		error(_("too many local time types"));
3565bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
3566bc421551SDag-Erling Smørgrav 	}
3567bc421551SDag-Erling Smørgrav 	i = typecnt++;
3568bc421551SDag-Erling Smørgrav 	utoffs[i] = utoff;
3569bc421551SDag-Erling Smørgrav 	isdsts[i] = isdst;
3570bc421551SDag-Erling Smørgrav 	ttisstds[i] = ttisstd;
3571bc421551SDag-Erling Smørgrav 	ttisuts[i] = ttisut;
3572bc421551SDag-Erling Smørgrav 	desigidx[i] = j;
3573bc421551SDag-Erling Smørgrav 	return i;
3574bc421551SDag-Erling Smørgrav }
3575bc421551SDag-Erling Smørgrav 
3576bc421551SDag-Erling Smørgrav static void
3577bc421551SDag-Erling Smørgrav leapadd(zic_t t, int correction, int rolling)
3578bc421551SDag-Erling Smørgrav {
3579bc421551SDag-Erling Smørgrav 	register int i;
3580bc421551SDag-Erling Smørgrav 
3581bc421551SDag-Erling Smørgrav 	if (TZ_MAX_LEAPS <= leapcnt) {
3582bc421551SDag-Erling Smørgrav 		error(_("too many leap seconds"));
3583bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
3584bc421551SDag-Erling Smørgrav 	}
3585bc421551SDag-Erling Smørgrav 	if (rolling && (lo_time != min_time || hi_time != max_time)) {
3586bc421551SDag-Erling Smørgrav 	  error(_("Rolling leap seconds not supported with -r"));
3587bc421551SDag-Erling Smørgrav 	  exit(EXIT_FAILURE);
3588bc421551SDag-Erling Smørgrav 	}
3589bc421551SDag-Erling Smørgrav 	for (i = 0; i < leapcnt; ++i)
3590bc421551SDag-Erling Smørgrav 		if (t <= trans[i])
3591bc421551SDag-Erling Smørgrav 			break;
3592bc421551SDag-Erling Smørgrav 	memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
3593bc421551SDag-Erling Smørgrav 	memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
3594bc421551SDag-Erling Smørgrav 	memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
3595bc421551SDag-Erling Smørgrav 	trans[i] = t;
3596bc421551SDag-Erling Smørgrav 	corr[i] = correction;
3597bc421551SDag-Erling Smørgrav 	roll[i] = rolling;
3598bc421551SDag-Erling Smørgrav 	++leapcnt;
3599bc421551SDag-Erling Smørgrav }
3600bc421551SDag-Erling Smørgrav 
3601bc421551SDag-Erling Smørgrav static void
3602bc421551SDag-Erling Smørgrav adjleap(void)
3603bc421551SDag-Erling Smørgrav {
3604bc421551SDag-Erling Smørgrav 	register int	i;
3605bc421551SDag-Erling Smørgrav 	register zic_t	last = 0;
3606bc421551SDag-Erling Smørgrav 	register zic_t	prevtrans = 0;
3607bc421551SDag-Erling Smørgrav 
3608bc421551SDag-Erling Smørgrav 	/*
3609bc421551SDag-Erling Smørgrav 	** propagate leap seconds forward
3610bc421551SDag-Erling Smørgrav 	*/
3611bc421551SDag-Erling Smørgrav 	for (i = 0; i < leapcnt; ++i) {
3612bc421551SDag-Erling Smørgrav 		if (trans[i] - prevtrans < 28 * SECSPERDAY) {
3613bc421551SDag-Erling Smørgrav 		  error(_("Leap seconds too close together"));
3614bc421551SDag-Erling Smørgrav 		  exit(EXIT_FAILURE);
3615bc421551SDag-Erling Smørgrav 		}
3616bc421551SDag-Erling Smørgrav 		prevtrans = trans[i];
3617bc421551SDag-Erling Smørgrav 		trans[i] = tadd(trans[i], last);
3618bc421551SDag-Erling Smørgrav 		last = corr[i] += last;
3619bc421551SDag-Erling Smørgrav 	}
3620bc421551SDag-Erling Smørgrav 
3621bc421551SDag-Erling Smørgrav 	if (0 <= leapexpires) {
3622bc421551SDag-Erling Smørgrav 	  leapexpires = oadd(leapexpires, last);
3623bc421551SDag-Erling Smørgrav 	  if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) {
3624bc421551SDag-Erling Smørgrav 	    error(_("last Leap time does not precede Expires time"));
3625bc421551SDag-Erling Smørgrav 	    exit(EXIT_FAILURE);
3626bc421551SDag-Erling Smørgrav 	  }
3627bc421551SDag-Erling Smørgrav 	}
3628bc421551SDag-Erling Smørgrav }
3629bc421551SDag-Erling Smørgrav 
3630bc421551SDag-Erling Smørgrav /* Is A a space character in the C locale?  */
3631bc421551SDag-Erling Smørgrav static bool
3632bc421551SDag-Erling Smørgrav is_space(char a)
3633bc421551SDag-Erling Smørgrav {
3634bc421551SDag-Erling Smørgrav 	switch (a) {
3635bc421551SDag-Erling Smørgrav 	  default:
3636bc421551SDag-Erling Smørgrav 		return false;
3637bc421551SDag-Erling Smørgrav 	  case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
3638bc421551SDag-Erling Smørgrav 		return true;
3639bc421551SDag-Erling Smørgrav 	}
3640bc421551SDag-Erling Smørgrav }
3641bc421551SDag-Erling Smørgrav 
3642bc421551SDag-Erling Smørgrav /* Is A an alphabetic character in the C locale?  */
3643bc421551SDag-Erling Smørgrav static bool
3644bc421551SDag-Erling Smørgrav is_alpha(char a)
3645bc421551SDag-Erling Smørgrav {
3646bc421551SDag-Erling Smørgrav 	switch (a) {
3647bc421551SDag-Erling Smørgrav 	  default:
3648bc421551SDag-Erling Smørgrav 		return false;
3649bc421551SDag-Erling Smørgrav 	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
3650bc421551SDag-Erling Smørgrav 	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
3651bc421551SDag-Erling Smørgrav 	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
3652bc421551SDag-Erling Smørgrav 	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
3653bc421551SDag-Erling Smørgrav 	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
3654bc421551SDag-Erling Smørgrav 	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
3655bc421551SDag-Erling Smørgrav 	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
3656bc421551SDag-Erling Smørgrav 	  case 'v': case 'w': case 'x': case 'y': case 'z':
3657bc421551SDag-Erling Smørgrav 		return true;
3658bc421551SDag-Erling Smørgrav 	}
3659bc421551SDag-Erling Smørgrav }
3660bc421551SDag-Erling Smørgrav 
3661bc421551SDag-Erling Smørgrav /* If A is an uppercase character in the C locale, return its lowercase
3662bc421551SDag-Erling Smørgrav    counterpart.  Otherwise, return A.  */
3663bc421551SDag-Erling Smørgrav static char
3664bc421551SDag-Erling Smørgrav lowerit(char a)
3665bc421551SDag-Erling Smørgrav {
3666bc421551SDag-Erling Smørgrav 	switch (a) {
3667bc421551SDag-Erling Smørgrav 	  default: return a;
3668bc421551SDag-Erling Smørgrav 	  case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
3669bc421551SDag-Erling Smørgrav 	  case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
3670bc421551SDag-Erling Smørgrav 	  case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
3671bc421551SDag-Erling Smørgrav 	  case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
3672bc421551SDag-Erling Smørgrav 	  case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
3673bc421551SDag-Erling Smørgrav 	  case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
3674bc421551SDag-Erling Smørgrav 	  case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
3675bc421551SDag-Erling Smørgrav 	  case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
3676bc421551SDag-Erling Smørgrav 	  case 'Y': return 'y'; case 'Z': return 'z';
3677bc421551SDag-Erling Smørgrav 	}
3678bc421551SDag-Erling Smørgrav }
3679bc421551SDag-Erling Smørgrav 
3680bc421551SDag-Erling Smørgrav /* case-insensitive equality */
3681*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static bool
3682bc421551SDag-Erling Smørgrav ciequal(register const char *ap, register const char *bp)
3683bc421551SDag-Erling Smørgrav {
3684bc421551SDag-Erling Smørgrav 	while (lowerit(*ap) == lowerit(*bp++))
3685bc421551SDag-Erling Smørgrav 		if (*ap++ == '\0')
3686bc421551SDag-Erling Smørgrav 			return true;
3687bc421551SDag-Erling Smørgrav 	return false;
3688bc421551SDag-Erling Smørgrav }
3689bc421551SDag-Erling Smørgrav 
3690*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static bool
3691bc421551SDag-Erling Smørgrav itsabbr(register const char *abbr, register const char *word)
3692bc421551SDag-Erling Smørgrav {
3693bc421551SDag-Erling Smørgrav 	if (lowerit(*abbr) != lowerit(*word))
3694bc421551SDag-Erling Smørgrav 		return false;
3695bc421551SDag-Erling Smørgrav 	++word;
3696bc421551SDag-Erling Smørgrav 	while (*++abbr != '\0')
3697bc421551SDag-Erling Smørgrav 		do {
3698bc421551SDag-Erling Smørgrav 			if (*word == '\0')
3699bc421551SDag-Erling Smørgrav 				return false;
3700bc421551SDag-Erling Smørgrav 		} while (lowerit(*word++) != lowerit(*abbr));
3701bc421551SDag-Erling Smørgrav 	return true;
3702bc421551SDag-Erling Smørgrav }
3703bc421551SDag-Erling Smørgrav 
3704bc421551SDag-Erling Smørgrav /* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case.  */
3705bc421551SDag-Erling Smørgrav 
3706*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static bool
3707bc421551SDag-Erling Smørgrav ciprefix(char const *abbr, char const *word)
3708bc421551SDag-Erling Smørgrav {
3709bc421551SDag-Erling Smørgrav   do
3710bc421551SDag-Erling Smørgrav     if (!*abbr)
3711bc421551SDag-Erling Smørgrav       return true;
3712bc421551SDag-Erling Smørgrav   while (lowerit(*abbr++) == lowerit(*word++));
3713bc421551SDag-Erling Smørgrav 
3714bc421551SDag-Erling Smørgrav   return false;
3715bc421551SDag-Erling Smørgrav }
3716bc421551SDag-Erling Smørgrav 
3717bc421551SDag-Erling Smørgrav static const struct lookup *
3718bc421551SDag-Erling Smørgrav byword(const char *word, const struct lookup *table)
3719bc421551SDag-Erling Smørgrav {
3720bc421551SDag-Erling Smørgrav 	register const struct lookup *	foundlp;
3721bc421551SDag-Erling Smørgrav 	register const struct lookup *	lp;
3722bc421551SDag-Erling Smørgrav 
3723bc421551SDag-Erling Smørgrav 	if (word == NULL || table == NULL)
3724bc421551SDag-Erling Smørgrav 		return NULL;
3725bc421551SDag-Erling Smørgrav 
3726bc421551SDag-Erling Smørgrav 	/* If TABLE is LASTS and the word starts with "last" followed
3727bc421551SDag-Erling Smørgrav 	   by a non-'-', skip the "last" and look in WDAY_NAMES instead.
3728bc421551SDag-Erling Smørgrav 	   Warn about any usage of the undocumented prefix "last-".  */
3729bc421551SDag-Erling Smørgrav 	if (table == lasts && ciprefix("last", word) && word[4]) {
3730bc421551SDag-Erling Smørgrav 	  if (word[4] == '-')
3731bc421551SDag-Erling Smørgrav 	    warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
3732bc421551SDag-Erling Smørgrav 		    word, word + 5);
3733bc421551SDag-Erling Smørgrav 	  else {
3734bc421551SDag-Erling Smørgrav 	    word += 4;
3735bc421551SDag-Erling Smørgrav 	    table = wday_names;
3736bc421551SDag-Erling Smørgrav 	  }
3737bc421551SDag-Erling Smørgrav 	}
3738bc421551SDag-Erling Smørgrav 
3739bc421551SDag-Erling Smørgrav 	/*
3740bc421551SDag-Erling Smørgrav 	** Look for exact match.
3741bc421551SDag-Erling Smørgrav 	*/
3742bc421551SDag-Erling Smørgrav 	for (lp = table; lp->l_word != NULL; ++lp)
3743bc421551SDag-Erling Smørgrav 		if (ciequal(word, lp->l_word))
3744bc421551SDag-Erling Smørgrav 			return lp;
3745bc421551SDag-Erling Smørgrav 	/*
3746bc421551SDag-Erling Smørgrav 	** Look for inexact match.
3747bc421551SDag-Erling Smørgrav 	*/
3748bc421551SDag-Erling Smørgrav 	foundlp = NULL;
3749bc421551SDag-Erling Smørgrav 	for (lp = table; lp->l_word != NULL; ++lp)
3750bc421551SDag-Erling Smørgrav 		if (ciprefix(word, lp->l_word)) {
3751bc421551SDag-Erling Smørgrav 			if (foundlp == NULL)
3752bc421551SDag-Erling Smørgrav 				foundlp = lp;
3753bc421551SDag-Erling Smørgrav 			else	return NULL;	/* multiple inexact matches */
3754bc421551SDag-Erling Smørgrav 		}
3755bc421551SDag-Erling Smørgrav 
3756bc421551SDag-Erling Smørgrav 	if (foundlp && noise) {
3757bc421551SDag-Erling Smørgrav 	  /* Warn about any backward-compatibility issue with pre-2017c zic.  */
3758bc421551SDag-Erling Smørgrav 	  bool pre_2017c_match = false;
3759bc421551SDag-Erling Smørgrav 	  for (lp = table; lp->l_word; lp++)
3760bc421551SDag-Erling Smørgrav 	    if (itsabbr(word, lp->l_word)) {
3761bc421551SDag-Erling Smørgrav 	      if (pre_2017c_match) {
3762bc421551SDag-Erling Smørgrav 		warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
3763bc421551SDag-Erling Smørgrav 		break;
3764bc421551SDag-Erling Smørgrav 	      }
3765bc421551SDag-Erling Smørgrav 	      pre_2017c_match = true;
3766bc421551SDag-Erling Smørgrav 	    }
3767bc421551SDag-Erling Smørgrav 	}
3768bc421551SDag-Erling Smørgrav 
3769bc421551SDag-Erling Smørgrav 	return foundlp;
3770bc421551SDag-Erling Smørgrav }
3771bc421551SDag-Erling Smørgrav 
3772bc421551SDag-Erling Smørgrav static int
3773bc421551SDag-Erling Smørgrav getfields(char *cp, char **array, int arrayelts)
3774bc421551SDag-Erling Smørgrav {
3775bc421551SDag-Erling Smørgrav 	register char *		dp;
3776bc421551SDag-Erling Smørgrav 	register int		nsubs;
3777bc421551SDag-Erling Smørgrav 
3778bc421551SDag-Erling Smørgrav 	nsubs = 0;
3779bc421551SDag-Erling Smørgrav 	for ( ; ; ) {
3780bc421551SDag-Erling Smørgrav 		char *dstart;
3781bc421551SDag-Erling Smørgrav 		while (is_space(*cp))
3782bc421551SDag-Erling Smørgrav 				++cp;
3783bc421551SDag-Erling Smørgrav 		if (*cp == '\0' || *cp == '#')
3784bc421551SDag-Erling Smørgrav 			break;
3785bc421551SDag-Erling Smørgrav 		dstart = dp = cp;
3786bc421551SDag-Erling Smørgrav 		do {
3787bc421551SDag-Erling Smørgrav 			if ((*dp = *cp++) != '"')
3788bc421551SDag-Erling Smørgrav 				++dp;
3789bc421551SDag-Erling Smørgrav 			else while ((*dp = *cp++) != '"')
3790bc421551SDag-Erling Smørgrav 				if (*dp != '\0')
3791bc421551SDag-Erling Smørgrav 					++dp;
3792bc421551SDag-Erling Smørgrav 				else {
3793bc421551SDag-Erling Smørgrav 				  error(_("Odd number of quotation marks"));
3794bc421551SDag-Erling Smørgrav 				  exit(EXIT_FAILURE);
3795bc421551SDag-Erling Smørgrav 				}
3796bc421551SDag-Erling Smørgrav 		} while (*cp && *cp != '#' && !is_space(*cp));
3797bc421551SDag-Erling Smørgrav 		if (is_space(*cp))
3798bc421551SDag-Erling Smørgrav 			++cp;
3799bc421551SDag-Erling Smørgrav 		*dp = '\0';
3800bc421551SDag-Erling Smørgrav 		if (nsubs == arrayelts) {
3801bc421551SDag-Erling Smørgrav 		  error(_("Too many input fields"));
3802bc421551SDag-Erling Smørgrav 		  exit(EXIT_FAILURE);
3803bc421551SDag-Erling Smørgrav 		}
3804bc421551SDag-Erling Smørgrav 		array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
3805bc421551SDag-Erling Smørgrav 	}
3806bc421551SDag-Erling Smørgrav 	return nsubs;
3807bc421551SDag-Erling Smørgrav }
3808bc421551SDag-Erling Smørgrav 
380975411d15SDag-Erling Smørgrav ATTRIBUTE_NORETURN static void
3810bc421551SDag-Erling Smørgrav time_overflow(void)
3811bc421551SDag-Erling Smørgrav {
3812bc421551SDag-Erling Smørgrav   error(_("time overflow"));
3813bc421551SDag-Erling Smørgrav   exit(EXIT_FAILURE);
3814bc421551SDag-Erling Smørgrav }
3815bc421551SDag-Erling Smørgrav 
3816*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static zic_t
3817bc421551SDag-Erling Smørgrav oadd(zic_t t1, zic_t t2)
3818bc421551SDag-Erling Smørgrav {
3819bc421551SDag-Erling Smørgrav #ifdef ckd_add
3820bc421551SDag-Erling Smørgrav   zic_t sum;
3821bc421551SDag-Erling Smørgrav   if (!ckd_add(&sum, t1, t2))
3822bc421551SDag-Erling Smørgrav     return sum;
3823bc421551SDag-Erling Smørgrav #else
3824bc421551SDag-Erling Smørgrav   if (t1 < 0 ? ZIC_MIN - t1 <= t2 : t2 <= ZIC_MAX - t1)
3825bc421551SDag-Erling Smørgrav     return t1 + t2;
3826bc421551SDag-Erling Smørgrav #endif
3827bc421551SDag-Erling Smørgrav   time_overflow();
3828bc421551SDag-Erling Smørgrav }
3829bc421551SDag-Erling Smørgrav 
3830*a979394aSDag-Erling Smørgrav ATTRIBUTE_PURE_114833 static zic_t
3831bc421551SDag-Erling Smørgrav tadd(zic_t t1, zic_t t2)
3832bc421551SDag-Erling Smørgrav {
3833bc421551SDag-Erling Smørgrav #ifdef ckd_add
3834bc421551SDag-Erling Smørgrav   zic_t sum;
3835bc421551SDag-Erling Smørgrav   if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time)
3836bc421551SDag-Erling Smørgrav     return sum;
3837bc421551SDag-Erling Smørgrav #else
3838bc421551SDag-Erling Smørgrav   if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1)
3839bc421551SDag-Erling Smørgrav     return t1 + t2;
3840bc421551SDag-Erling Smørgrav #endif
3841bc421551SDag-Erling Smørgrav   if (t1 == min_time || t1 == max_time)
3842bc421551SDag-Erling Smørgrav     return t1;
3843bc421551SDag-Erling Smørgrav   time_overflow();
3844bc421551SDag-Erling Smørgrav }
3845bc421551SDag-Erling Smørgrav 
3846bc421551SDag-Erling Smørgrav /*
3847bc421551SDag-Erling Smørgrav ** Given a rule, and a year, compute the date (in seconds since January 1,
3848bc421551SDag-Erling Smørgrav ** 1970, 00:00 LOCAL time) in that year that the rule refers to.
3849bc421551SDag-Erling Smørgrav */
3850bc421551SDag-Erling Smørgrav 
3851bc421551SDag-Erling Smørgrav static zic_t
3852bc421551SDag-Erling Smørgrav rpytime(const struct rule *rp, zic_t wantedy)
3853bc421551SDag-Erling Smørgrav {
3854bc421551SDag-Erling Smørgrav 	register int	m, i;
3855bc421551SDag-Erling Smørgrav 	register zic_t	dayoff;			/* with a nod to Margaret O. */
3856bc421551SDag-Erling Smørgrav 	register zic_t	t, y;
3857bc421551SDag-Erling Smørgrav 	int yrem;
3858bc421551SDag-Erling Smørgrav 
3859bc421551SDag-Erling Smørgrav 	if (wantedy == ZIC_MIN)
3860bc421551SDag-Erling Smørgrav 		return min_time;
3861bc421551SDag-Erling Smørgrav 	if (wantedy == ZIC_MAX)
3862bc421551SDag-Erling Smørgrav 		return max_time;
3863bc421551SDag-Erling Smørgrav 	m = TM_JANUARY;
3864bc421551SDag-Erling Smørgrav 	y = EPOCH_YEAR;
3865bc421551SDag-Erling Smørgrav 
3866bc421551SDag-Erling Smørgrav 	/* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
3867bc421551SDag-Erling Smørgrav 	   sans overflow.  */
3868bc421551SDag-Erling Smørgrav 	yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
3869bc421551SDag-Erling Smørgrav 	dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
3870bc421551SDag-Erling Smørgrav 		   + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
3871bc421551SDag-Erling Smørgrav 		  * DAYSPERREPEAT);
3872bc421551SDag-Erling Smørgrav 	/* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow.  */
3873bc421551SDag-Erling Smørgrav 	wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
3874bc421551SDag-Erling Smørgrav 
3875bc421551SDag-Erling Smørgrav 	while (wantedy != y) {
3876bc421551SDag-Erling Smørgrav 		i = len_years[isleap(y)];
3877bc421551SDag-Erling Smørgrav 		dayoff = oadd(dayoff, i);
3878bc421551SDag-Erling Smørgrav 		y++;
3879bc421551SDag-Erling Smørgrav 	}
3880bc421551SDag-Erling Smørgrav 	while (m != rp->r_month) {
3881bc421551SDag-Erling Smørgrav 		i = len_months[isleap(y)][m];
3882bc421551SDag-Erling Smørgrav 		dayoff = oadd(dayoff, i);
3883bc421551SDag-Erling Smørgrav 		++m;
3884bc421551SDag-Erling Smørgrav 	}
3885bc421551SDag-Erling Smørgrav 	i = rp->r_dayofmonth;
3886bc421551SDag-Erling Smørgrav 	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
3887bc421551SDag-Erling Smørgrav 		if (rp->r_dycode == DC_DOWLEQ)
3888bc421551SDag-Erling Smørgrav 			--i;
3889bc421551SDag-Erling Smørgrav 		else {
3890bc421551SDag-Erling Smørgrav 			error(_("use of 2/29 in non leap-year"));
3891bc421551SDag-Erling Smørgrav 			exit(EXIT_FAILURE);
3892bc421551SDag-Erling Smørgrav 		}
3893bc421551SDag-Erling Smørgrav 	}
3894bc421551SDag-Erling Smørgrav 	--i;
3895bc421551SDag-Erling Smørgrav 	dayoff = oadd(dayoff, i);
3896bc421551SDag-Erling Smørgrav 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
3897bc421551SDag-Erling Smørgrav 		/*
3898bc421551SDag-Erling Smørgrav 		** Don't trust mod of negative numbers.
3899bc421551SDag-Erling Smørgrav 		*/
3900bc421551SDag-Erling Smørgrav 		zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK)
3901bc421551SDag-Erling Smørgrav 			      % DAYSPERWEEK);
3902bc421551SDag-Erling Smørgrav 		while (wday != rp->r_wday)
3903bc421551SDag-Erling Smørgrav 			if (rp->r_dycode == DC_DOWGEQ) {
3904bc421551SDag-Erling Smørgrav 				dayoff = oadd(dayoff, 1);
3905bc421551SDag-Erling Smørgrav 				if (++wday >= DAYSPERWEEK)
3906bc421551SDag-Erling Smørgrav 					wday = 0;
3907bc421551SDag-Erling Smørgrav 				++i;
3908bc421551SDag-Erling Smørgrav 			} else {
3909bc421551SDag-Erling Smørgrav 				dayoff = oadd(dayoff, -1);
3910bc421551SDag-Erling Smørgrav 				if (--wday < 0)
3911bc421551SDag-Erling Smørgrav 					wday = DAYSPERWEEK - 1;
3912bc421551SDag-Erling Smørgrav 				--i;
3913bc421551SDag-Erling Smørgrav 			}
3914bc421551SDag-Erling Smørgrav 		if (i < 0 || i >= len_months[isleap(y)][m]) {
3915bc421551SDag-Erling Smørgrav 			if (noise)
3916bc421551SDag-Erling Smørgrav 				warning(_("rule goes past start/end of month; \
3917bc421551SDag-Erling Smørgrav will not work with pre-2004 versions of zic"));
3918bc421551SDag-Erling Smørgrav 		}
3919bc421551SDag-Erling Smørgrav 	}
3920bc421551SDag-Erling Smørgrav 	if (dayoff < min_time / SECSPERDAY)
3921bc421551SDag-Erling Smørgrav 		return min_time;
3922bc421551SDag-Erling Smørgrav 	if (dayoff > max_time / SECSPERDAY)
3923bc421551SDag-Erling Smørgrav 		return max_time;
3924bc421551SDag-Erling Smørgrav 	t = (zic_t) dayoff * SECSPERDAY;
3925bc421551SDag-Erling Smørgrav 	return tadd(t, rp->r_tod);
3926bc421551SDag-Erling Smørgrav }
3927bc421551SDag-Erling Smørgrav 
3928bc421551SDag-Erling Smørgrav static void
3929bc421551SDag-Erling Smørgrav newabbr(const char *string)
3930bc421551SDag-Erling Smørgrav {
3931bc421551SDag-Erling Smørgrav 	register int	i;
3932bc421551SDag-Erling Smørgrav 
3933bc421551SDag-Erling Smørgrav 	if (strcmp(string, GRANDPARENTED) != 0) {
3934bc421551SDag-Erling Smørgrav 		register const char *	cp;
3935bc421551SDag-Erling Smørgrav 		const char *		mp;
3936bc421551SDag-Erling Smørgrav 
3937bc421551SDag-Erling Smørgrav 		cp = string;
3938bc421551SDag-Erling Smørgrav 		mp = NULL;
3939bc421551SDag-Erling Smørgrav 		while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
3940bc421551SDag-Erling Smørgrav 		       || *cp == '-' || *cp == '+')
3941bc421551SDag-Erling Smørgrav 				++cp;
3942bc421551SDag-Erling Smørgrav 		if (noise && cp - string < 3)
3943bc421551SDag-Erling Smørgrav 		  mp = _("time zone abbreviation has fewer than 3 characters");
3944bc421551SDag-Erling Smørgrav 		if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
3945bc421551SDag-Erling Smørgrav 		  mp = _("time zone abbreviation has too many characters");
3946bc421551SDag-Erling Smørgrav 		if (*cp != '\0')
3947bc421551SDag-Erling Smørgrav mp = _("time zone abbreviation differs from POSIX standard");
3948bc421551SDag-Erling Smørgrav 		if (mp != NULL)
3949bc421551SDag-Erling Smørgrav 			warning("%s (%s)", mp, string);
3950bc421551SDag-Erling Smørgrav 	}
3951bc421551SDag-Erling Smørgrav 	i = strlen(string) + 1;
3952bc421551SDag-Erling Smørgrav 	if (charcnt + i > TZ_MAX_CHARS) {
3953bc421551SDag-Erling Smørgrav 		error(_("too many, or too long, time zone abbreviations"));
3954bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
3955bc421551SDag-Erling Smørgrav 	}
3956bc421551SDag-Erling Smørgrav 	strcpy(&chars[charcnt], string);
3957bc421551SDag-Erling Smørgrav 	charcnt += i;
3958bc421551SDag-Erling Smørgrav }
3959bc421551SDag-Erling Smørgrav 
3960bc421551SDag-Erling Smørgrav /* Ensure that the directories of ARGNAME exist, by making any missing
3961bc421551SDag-Erling Smørgrav    ones.  If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
3962bc421551SDag-Erling Smørgrav    do it for ARGNAME too.  Exit with failure if there is trouble.
3963bc421551SDag-Erling Smørgrav    Do not consider an existing file to be trouble.  */
3964bc421551SDag-Erling Smørgrav static void
3965bc421551SDag-Erling Smørgrav mkdirs(char const *argname, bool ancestors)
3966bc421551SDag-Erling Smørgrav {
39672aad7570SDag-Erling Smørgrav 	/*
39682aad7570SDag-Erling Smørgrav 	 * If -D was specified, do not create directories.  A subsequent
39692aad7570SDag-Erling Smørgrav 	 * file operation will fail and produce an appropriate error
39702aad7570SDag-Erling Smørgrav 	 * message.
39712aad7570SDag-Erling Smørgrav 	 */
39722aad7570SDag-Erling Smørgrav 	if (Dflag)
39732aad7570SDag-Erling Smørgrav 		return;
39742aad7570SDag-Erling Smørgrav 
3975bc421551SDag-Erling Smørgrav 	char *name = estrdup(argname);
3976bc421551SDag-Erling Smørgrav 	char *cp = name;
3977bc421551SDag-Erling Smørgrav 
3978bc421551SDag-Erling Smørgrav 	/* On MS-Windows systems, do not worry about drive letters or
3979bc421551SDag-Erling Smørgrav 	   backslashes, as this should suffice in practice.  Time zone
3980bc421551SDag-Erling Smørgrav 	   names do not use drive letters and backslashes.  If the -d
3981bc421551SDag-Erling Smørgrav 	   option of zic does not name an already-existing directory,
3982bc421551SDag-Erling Smørgrav 	   it can use slashes to separate the already-existing
3983bc421551SDag-Erling Smørgrav 	   ancestor prefix from the to-be-created subdirectories.  */
3984bc421551SDag-Erling Smørgrav 
3985bc421551SDag-Erling Smørgrav 	/* Do not mkdir a root directory, as it must exist.  */
3986bc421551SDag-Erling Smørgrav 	while (*cp == '/')
3987bc421551SDag-Erling Smørgrav 	  cp++;
3988bc421551SDag-Erling Smørgrav 
3989bc421551SDag-Erling Smørgrav 	while (cp && ((cp = strchr(cp, '/')) || !ancestors)) {
3990bc421551SDag-Erling Smørgrav 		if (cp)
3991bc421551SDag-Erling Smørgrav 		  *cp = '\0';
3992bc421551SDag-Erling Smørgrav 		/*
3993bc421551SDag-Erling Smørgrav 		** Try to create it.  It's OK if creation fails because
3994bc421551SDag-Erling Smørgrav 		** the directory already exists, perhaps because some
3995bc421551SDag-Erling Smørgrav 		** other process just created it.  For simplicity do
3996bc421551SDag-Erling Smørgrav 		** not check first whether it already exists, as that
3997bc421551SDag-Erling Smørgrav 		** is checked anyway if the mkdir fails.
3998bc421551SDag-Erling Smørgrav 		*/
3999bc421551SDag-Erling Smørgrav 		if (mkdir(name, MKDIR_UMASK) != 0) {
4000bc421551SDag-Erling Smørgrav 			/* Do not report an error if err == EEXIST, because
4001bc421551SDag-Erling Smørgrav 			   some other process might have made the directory
4002bc421551SDag-Erling Smørgrav 			   in the meantime.  Likewise for ENOSYS, because
4003bc421551SDag-Erling Smørgrav 			   Solaris 10 mkdir fails with ENOSYS if the
4004bc421551SDag-Erling Smørgrav 			   directory is an automounted mount point.
4005bc421551SDag-Erling Smørgrav 			   Likewise for EACCES, since mkdir can fail
4006bc421551SDag-Erling Smørgrav 			   with EACCES merely because the parent directory
4007bc421551SDag-Erling Smørgrav 			   is unwritable.  Likewise for most other error
4008bc421551SDag-Erling Smørgrav 			   numbers.  */
4009bc421551SDag-Erling Smørgrav 			int err = errno;
4010bc421551SDag-Erling Smørgrav 			if (err == ELOOP || err == ENAMETOOLONG
4011bc421551SDag-Erling Smørgrav 			    || err == ENOENT || err == ENOTDIR) {
4012bc421551SDag-Erling Smørgrav 				error(_("%s: Can't create directory %s: %s"),
4013bc421551SDag-Erling Smørgrav 				      progname, name, strerror(err));
4014bc421551SDag-Erling Smørgrav 				exit(EXIT_FAILURE);
4015bc421551SDag-Erling Smørgrav 			}
4016bc421551SDag-Erling Smørgrav 		}
4017bc421551SDag-Erling Smørgrav 		if (cp)
4018bc421551SDag-Erling Smørgrav 		  *cp++ = '/';
4019bc421551SDag-Erling Smørgrav 	}
4020bc421551SDag-Erling Smørgrav 	free(name);
4021bc421551SDag-Erling Smørgrav }
4022bc421551SDag-Erling Smørgrav 
4023bc421551SDag-Erling Smørgrav #include <grp.h>
4024bc421551SDag-Erling Smørgrav #include <pwd.h>
4025bc421551SDag-Erling Smørgrav 
4026bc421551SDag-Erling Smørgrav static void
4027d5c85ac6SDag-Erling Smørgrav setgroup(gid_t *flag, const char *name)
4028bc421551SDag-Erling Smørgrav {
4029bc421551SDag-Erling Smørgrav 	struct group *gr;
4030bc421551SDag-Erling Smørgrav 
4031bc421551SDag-Erling Smørgrav 	if (*flag != (gid_t)-1) {
4032bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("multiple -g flags specified"));
4033bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
4034bc421551SDag-Erling Smørgrav 	}
4035bc421551SDag-Erling Smørgrav 
4036bc421551SDag-Erling Smørgrav 	gr = getgrnam(name);
4037bc421551SDag-Erling Smørgrav 	if (gr == 0) {
4038bc421551SDag-Erling Smørgrav 		char *ep;
4039bc421551SDag-Erling Smørgrav 		unsigned long ul;
4040bc421551SDag-Erling Smørgrav 
4041bc421551SDag-Erling Smørgrav 		ul = strtoul(name, &ep, 10);
4042bc421551SDag-Erling Smørgrav 		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
4043bc421551SDag-Erling Smørgrav 			*flag = ul;
4044bc421551SDag-Erling Smørgrav 			return;
4045bc421551SDag-Erling Smørgrav 		}
4046bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("group `%s' not found"), name);
4047bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
4048bc421551SDag-Erling Smørgrav 	}
4049bc421551SDag-Erling Smørgrav 	*flag = gr->gr_gid;
4050bc421551SDag-Erling Smørgrav }
4051bc421551SDag-Erling Smørgrav 
4052bc421551SDag-Erling Smørgrav static void
4053d5c85ac6SDag-Erling Smørgrav setuser(uid_t *flag, const char *name)
4054bc421551SDag-Erling Smørgrav {
4055bc421551SDag-Erling Smørgrav 	struct passwd *pw;
4056bc421551SDag-Erling Smørgrav 
4057bc421551SDag-Erling Smørgrav 	if (*flag != (gid_t)-1) {
4058bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("multiple -u flags specified"));
4059bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
4060bc421551SDag-Erling Smørgrav 	}
4061bc421551SDag-Erling Smørgrav 
4062bc421551SDag-Erling Smørgrav 	pw = getpwnam(name);
4063bc421551SDag-Erling Smørgrav 	if (pw == 0) {
4064bc421551SDag-Erling Smørgrav 		char *ep;
4065bc421551SDag-Erling Smørgrav 		unsigned long ul;
4066bc421551SDag-Erling Smørgrav 
4067bc421551SDag-Erling Smørgrav 		ul = strtoul(name, &ep, 10);
4068bc421551SDag-Erling Smørgrav 		if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
4069bc421551SDag-Erling Smørgrav 			*flag = ul;
4070bc421551SDag-Erling Smørgrav 			return;
4071bc421551SDag-Erling Smørgrav 		}
4072bc421551SDag-Erling Smørgrav 		fprintf(stderr, _("user `%s' not found"), name);
4073bc421551SDag-Erling Smørgrav 		exit(EXIT_FAILURE);
4074bc421551SDag-Erling Smørgrav 	}
4075bc421551SDag-Erling Smørgrav 	*flag = pw->pw_uid;
4076bc421551SDag-Erling Smørgrav }
4077bc421551SDag-Erling Smørgrav 
4078bc421551SDag-Erling Smørgrav /*
4079bc421551SDag-Erling Smørgrav ** UNIX was a registered trademark of The Open Group in 2003.
4080bc421551SDag-Erling Smørgrav */
4081