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