Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
    35         kx /* Compile .zi time zone data into TZif binary files.  */
    35         kx 
    35         kx /*
    35         kx ** This file is in the public domain, so clarified as of
    35         kx ** 2006-07-17 by Arthur David Olson.
    35         kx */
    35         kx 
    35         kx /* Use the system 'time' function, instead of any private replacement.
    35         kx    This avoids creating an unnecessary dependency on localtime.c.  */
    35         kx #undef EPOCH_LOCAL
    35         kx #undef EPOCH_OFFSET
    35         kx #undef RESERVE_STD_EXT_IDS
    35         kx #undef time_tz
    35         kx 
    35         kx #include "version.h"
    35         kx #include "private.h"
    35         kx #include "tzfile.h"
    35         kx 
    35         kx #include <fcntl.h>
    35         kx #include <locale.h>
    35         kx #include <signal.h>
    35         kx #include <stdarg.h>
    35         kx #include <stdio.h>
    35         kx 
    35         kx typedef int_fast64_t	zic_t;
    35         kx static zic_t const
    35         kx   ZIC_MIN = INT_FAST64_MIN,
    35         kx   ZIC_MAX = INT_FAST64_MAX,
    35         kx   ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
    35         kx   ZIC32_MAX = 0x7fffffff;
    35         kx #define SCNdZIC SCNdFAST64
    35         kx 
    35         kx #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
    35         kx # define ZIC_MAX_ABBR_LEN_WO_WARN 6
    35         kx #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
    35         kx 
    35         kx /* An upper bound on how much a format might grow due to concatenation.  */
    35         kx enum { FORMAT_LEN_GROWTH_BOUND = 5 };
    35         kx 
    35         kx #ifdef HAVE_DIRECT_H
    35         kx # include <direct.h>
    35         kx # include <io.h>
    35         kx # undef mkdir
    35         kx # define mkdir(name, mode) _mkdir(name)
    35         kx #endif
    35         kx 
    35         kx #ifndef HAVE_GETRANDOM
    35         kx # ifdef __has_include
    35         kx #  if __has_include(<sys/random.h>)
    35         kx #   include <sys/random.h>
    35         kx #  endif
    35         kx # elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__)
    35         kx #  include <sys/random.h>
    35         kx # endif
    35         kx # define HAVE_GETRANDOM GRND_RANDOM
    35         kx #elif HAVE_GETRANDOM
    35         kx # include <sys/random.h>
    35         kx #endif
    35         kx 
    35         kx #if HAVE_SYS_STAT_H
    35         kx # include <sys/stat.h>
    35         kx #endif
    35         kx #ifdef S_IRUSR
    35         kx # define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
    35         kx #else
    35         kx # define MKDIR_UMASK 0755
    35         kx #endif
    35         kx 
    35         kx /* The minimum alignment of a type, for pre-C23 platforms.
    35         kx    The __SUNPRO_C test is because Oracle Developer Studio 12.6 lacks
    35         kx    <stdalign.h> even though __STDC_VERSION__ == 201112.  */
    35         kx #if __STDC_VERSION__ < 201112 || defined __SUNPRO_C
    35         kx # define alignof(type) offsetof(struct { char a; type b; }, b)
    35         kx #elif __STDC_VERSION__ < 202311
    35         kx # include <stdalign.h>
    35         kx #endif
    35         kx 
    35         kx /* The maximum length of a text line, including the trailing newline.  */
    35         kx #ifndef _POSIX2_LINE_MAX
    35         kx # define _POSIX2_LINE_MAX 2048
    35         kx #endif
    35         kx 
    35         kx /* The type for line numbers.  Use PRIdMAX to format them; formerly
    35         kx    there was also "#define PRIdLINENO PRIdMAX" and formats used
    35         kx    PRIdLINENO, but xgettext cannot grok that.  */
    35         kx typedef intmax_t lineno;
    35         kx 
    35         kx struct rule {
    35         kx 	int		r_filenum;
    35         kx 	lineno		r_linenum;
    35         kx 	const char *	r_name;
    35         kx 
    35         kx 	zic_t		r_loyear;	/* for example, 1986 */
    35         kx 	zic_t		r_hiyear;	/* for example, 1986 */
    35         kx 	bool		r_lowasnum;
    35         kx 	bool		r_hiwasnum;
    35         kx 
    35         kx 	int		r_month;	/* 0..11 */
    35         kx 
    35         kx 	int		r_dycode;	/* see below */
    35         kx 	int		r_dayofmonth;
    35         kx 	int		r_wday;
    35         kx 
    35         kx 	zic_t		r_tod;		/* time from midnight */
    35         kx 	bool		r_todisstd;	/* is r_tod standard time? */
    35         kx 	bool		r_todisut;	/* is r_tod UT? */
    35         kx 	bool		r_isdst;	/* is this daylight saving time? */
    35         kx 	zic_t		r_save;		/* offset from standard time */
    35         kx 	const char *	r_abbrvar;	/* variable part of abbreviation */
    35         kx 
    35         kx 	bool		r_todo;		/* a rule to do (used in outzone) */
    35         kx 	zic_t		r_temp;		/* used in outzone */
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** r_dycode	r_dayofmonth	r_wday
    35         kx */
    35         kx enum {
    35         kx   DC_DOM,	/* 1..31 */	/* unused */
    35         kx   DC_DOWGEQ,	/* 1..31 */	/* 0..6 (Sun..Sat) */
    35         kx   DC_DOWLEQ	/* 1..31 */	/* 0..6 (Sun..Sat) */
    35         kx };
    35         kx 
    35         kx struct zone {
    35         kx 	int		z_filenum;
    35         kx 	lineno		z_linenum;
    35         kx 
    35         kx 	const char *	z_name;
    35         kx 	zic_t		z_stdoff;
    35         kx 	char *		z_rule;
    35         kx 	const char *	z_format;
    35         kx 	char		z_format_specifier;
    35         kx 
    35         kx 	bool		z_isdst;
    35         kx 	zic_t		z_save;
    35         kx 
    35         kx 	struct rule *	z_rules;
    35         kx 	ptrdiff_t	z_nrules;
    35         kx 
    35         kx 	struct rule	z_untilrule;
    35         kx 	zic_t		z_untiltime;
    35         kx };
    35         kx 
    35         kx #if !HAVE_POSIX_DECLS
    35         kx extern int	getopt(int argc, char * const argv[],
    35         kx 			const char * options);
    35         kx extern int	link(const char * target, const char * linkname);
    35         kx extern char *	optarg;
    35         kx extern int	optind;
    35         kx #endif
    35         kx 
    35         kx #if ! HAVE_SYMLINK
    35         kx static ssize_t
    35         kx readlink(char const *restrict file, char *restrict buf, size_t size)
    35         kx {
    35         kx   errno = ENOTSUP;
    35         kx   return -1;
    35         kx }
    35         kx static int
    35         kx symlink(char const *target, char const *linkname)
    35         kx {
    35         kx   errno = ENOTSUP;
    35         kx   return -1;
    35         kx }
    35         kx #endif
    35         kx #ifndef AT_SYMLINK_FOLLOW
    35         kx # if HAVE_LINK
    35         kx #  define linkat(targetdir, target, linknamedir, linkname, flag) \
    35         kx      (itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname))
    35         kx # else
    35         kx #  define linkat(targetdir, target, linknamedir, linkname, flag) \
    35         kx      (errno = ENOTSUP, -1)
    35         kx # endif
    35         kx #endif
    35         kx 
    35         kx static void	addtt(zic_t starttime, int type);
    35         kx static int	addtype(zic_t, char const *, bool, bool, bool);
    35         kx static void	leapadd(zic_t, int, int);
    35         kx static void	adjleap(void);
    35         kx static void	associate(void);
    35         kx static void	dolink(const char *, const char *, bool);
    35         kx static int	getfields(char *, char **, int);
    35         kx static zic_t	gethms(const char * string, const char * errstring);
    35         kx static zic_t	getsave(char *, bool *);
    35         kx static void	inexpires(char **, int);
    35         kx static void	infile(int, char const *);
    35         kx static void	inleap(char ** fields, int nfields);
    35         kx static void	inlink(char ** fields, int nfields);
    35         kx static void	inrule(char ** fields, int nfields);
    35         kx static bool	inzcont(char ** fields, int nfields);
    35         kx static bool	inzone(char ** fields, int nfields);
    35         kx static bool	inzsub(char **, int, bool);
    35         kx static bool	itssymlink(char const *);
    35         kx static bool	is_alpha(char a);
    35         kx static char	lowerit(char);
    35         kx static void	mkdirs(char const *, bool);
    35         kx static void	newabbr(const char * abbr);
    35         kx static zic_t	oadd(zic_t t1, zic_t t2);
    35         kx static void	outzone(const struct zone * zp, ptrdiff_t ntzones);
    35         kx static zic_t	rpytime(const struct rule * rp, zic_t wantedy);
    35         kx static bool	rulesub(struct rule * rp,
    35         kx 			const char * loyearp, const char * hiyearp,
    35         kx 			const char * typep, const char * monthp,
    35         kx 			const char * dayp, const char * timep);
    35         kx static zic_t	tadd(zic_t t1, zic_t t2);
    35         kx 
    35         kx /* Bound on length of what %z can expand to.  */
    35         kx enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
    35         kx 
    35         kx static int		charcnt;
    35         kx static bool		errors;
    35         kx static bool		warnings;
    35         kx static int		filenum;
    35         kx static int		leapcnt;
    35         kx static bool		leapseen;
    35         kx static zic_t		leapminyear;
    35         kx static zic_t		leapmaxyear;
    35         kx static lineno		linenum;
    35         kx static int		max_abbrvar_len = PERCENT_Z_LEN_BOUND;
    35         kx static int		max_format_len;
    35         kx static zic_t		max_year;
    35         kx static zic_t		min_year;
    35         kx static bool		noise;
    35         kx static int		rfilenum;
    35         kx static lineno		rlinenum;
    35         kx static const char *	progname;
    35         kx static char const *	leapsec;
    35         kx static char *const *	main_argv;
    35         kx static ptrdiff_t	timecnt;
    35         kx static ptrdiff_t	timecnt_alloc;
    35         kx static int		typecnt;
    35         kx static int		unspecifiedtype;
    35         kx 
    35         kx /*
    35         kx ** Line codes.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   LC_RULE,
    35         kx   LC_ZONE,
    35         kx   LC_LINK,
    35         kx   LC_LEAP,
    35         kx   LC_EXPIRES
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** Which fields are which on a Zone line.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   ZF_NAME = 1,
    35         kx   ZF_STDOFF,
    35         kx   ZF_RULE,
    35         kx   ZF_FORMAT,
    35         kx   ZF_TILYEAR,
    35         kx   ZF_TILMONTH,
    35         kx   ZF_TILDAY,
    35         kx   ZF_TILTIME,
    35         kx   ZONE_MAXFIELDS,
    35         kx   ZONE_MINFIELDS = ZF_TILYEAR
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** Which fields are which on a Zone continuation line.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   ZFC_STDOFF,
    35         kx   ZFC_RULE,
    35         kx   ZFC_FORMAT,
    35         kx   ZFC_TILYEAR,
    35         kx   ZFC_TILMONTH,
    35         kx   ZFC_TILDAY,
    35         kx   ZFC_TILTIME,
    35         kx   ZONEC_MAXFIELDS,
    35         kx   ZONEC_MINFIELDS = ZFC_TILYEAR
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** Which files are which on a Rule line.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   RF_NAME = 1,
    35         kx   RF_LOYEAR,
    35         kx   RF_HIYEAR,
    35         kx   RF_COMMAND,
    35         kx   RF_MONTH,
    35         kx   RF_DAY,
    35         kx   RF_TOD,
    35         kx   RF_SAVE,
    35         kx   RF_ABBRVAR,
    35         kx   RULE_FIELDS
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** Which fields are which on a Link line.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   LF_TARGET = 1,
    35         kx   LF_LINKNAME,
    35         kx   LINK_FIELDS
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** Which fields are which on a Leap line.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   LP_YEAR = 1,
    35         kx   LP_MONTH,
    35         kx   LP_DAY,
    35         kx   LP_TIME,
    35         kx   LP_CORR,
    35         kx   LP_ROLL,
    35         kx   LEAP_FIELDS,
    35         kx 
    35         kx   /* Expires lines are like Leap lines, except without CORR and ROLL fields.  */
    35         kx   EXPIRES_FIELDS = LP_TIME + 1
    35         kx };
    35         kx 
    35         kx /* The maximum number of fields on any of the above lines.
    35         kx    (The "+"s pacify gcc -Wenum-compare.)  */
    35         kx enum {
    35         kx   MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
    35         kx 		   max(+LEAP_FIELDS, +EXPIRES_FIELDS))
    35         kx };
    35         kx 
    35         kx /*
    35         kx ** Year synonyms.
    35         kx */
    35         kx 
    35         kx enum {
    35         kx   YR_MINIMUM,
    35         kx   YR_MAXIMUM,
    35         kx   YR_ONLY
    35         kx };
    35         kx 
    35         kx static struct rule *	rules;
    35         kx static ptrdiff_t	nrules;	/* number of rules */
    35         kx static ptrdiff_t	nrules_alloc;
    35         kx 
    35         kx static struct zone *	zones;
    35         kx static ptrdiff_t	nzones;	/* number of zones */
    35         kx static ptrdiff_t	nzones_alloc;
    35         kx 
    35         kx struct link {
    35         kx 	int		l_filenum;
    35         kx 	lineno		l_linenum;
    35         kx 	const char *	l_target;
    35         kx 	const char *	l_linkname;
    35         kx };
    35         kx 
    35         kx static struct link *	links;
    35         kx static ptrdiff_t	nlinks;
    35         kx static ptrdiff_t	nlinks_alloc;
    35         kx 
    35         kx struct lookup {
    35         kx 	const char *	l_word;
    35         kx 	const int	l_value;
    35         kx };
    35         kx 
    35         kx static struct lookup const *	byword(const char * string,
    35         kx 					const struct lookup * lp);
    35         kx 
    35         kx static struct lookup const zi_line_codes[] = {
    35         kx 	{ "Rule",	LC_RULE },
    35         kx 	{ "Zone",	LC_ZONE },
    35         kx 	{ "Link",	LC_LINK },
    35         kx 	{ NULL,		0 }
    35         kx };
    35         kx static struct lookup const leap_line_codes[] = {
    35         kx 	{ "Leap",	LC_LEAP },
    35         kx 	{ "Expires",	LC_EXPIRES },
    35         kx 	{ NULL,		0}
    35         kx };
    35         kx 
    35         kx static struct lookup const	mon_names[] = {
    35         kx 	{ "January",	TM_JANUARY },
    35         kx 	{ "February",	TM_FEBRUARY },
    35         kx 	{ "March",	TM_MARCH },
    35         kx 	{ "April",	TM_APRIL },
    35         kx 	{ "May",	TM_MAY },
    35         kx 	{ "June",	TM_JUNE },
    35         kx 	{ "July",	TM_JULY },
    35         kx 	{ "August",	TM_AUGUST },
    35         kx 	{ "September",	TM_SEPTEMBER },
    35         kx 	{ "October",	TM_OCTOBER },
    35         kx 	{ "November",	TM_NOVEMBER },
    35         kx 	{ "December",	TM_DECEMBER },
    35         kx 	{ NULL,		0 }
    35         kx };
    35         kx 
    35         kx static struct lookup const	wday_names[] = {
    35         kx 	{ "Sunday",	TM_SUNDAY },
    35         kx 	{ "Monday",	TM_MONDAY },
    35         kx 	{ "Tuesday",	TM_TUESDAY },
    35         kx 	{ "Wednesday",	TM_WEDNESDAY },
    35         kx 	{ "Thursday",	TM_THURSDAY },
    35         kx 	{ "Friday",	TM_FRIDAY },
    35         kx 	{ "Saturday",	TM_SATURDAY },
    35         kx 	{ NULL,		0 }
    35         kx };
    35         kx 
    35         kx static struct lookup const	lasts[] = {
    35         kx 	{ "last-Sunday",	TM_SUNDAY },
    35         kx 	{ "last-Monday",	TM_MONDAY },
    35         kx 	{ "last-Tuesday",	TM_TUESDAY },
    35         kx 	{ "last-Wednesday",	TM_WEDNESDAY },
    35         kx 	{ "last-Thursday",	TM_THURSDAY },
    35         kx 	{ "last-Friday",	TM_FRIDAY },
    35         kx 	{ "last-Saturday",	TM_SATURDAY },
    35         kx 	{ NULL,			0 }
    35         kx };
    35         kx 
    35         kx static struct lookup const	begin_years[] = {
    35         kx 	{ "minimum",	YR_MINIMUM },
    35         kx 	{ "maximum",	YR_MAXIMUM },
    35         kx 	{ NULL,		0 }
    35         kx };
    35         kx 
    35         kx static struct lookup const	end_years[] = {
    35         kx 	{ "minimum",	YR_MINIMUM },
    35         kx 	{ "maximum",	YR_MAXIMUM },
    35         kx 	{ "only",	YR_ONLY },
    35         kx 	{ NULL,		0 }
    35         kx };
    35         kx 
    35         kx static struct lookup const	leap_types[] = {
    35         kx 	{ "Rolling",	true },
    35         kx 	{ "Stationary",	false },
    35         kx 	{ NULL,		0 }
    35         kx };
    35         kx 
    35         kx static const int	len_months[2][MONSPERYEAR] = {
    35         kx 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
    35         kx 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    35         kx };
    35         kx 
    35         kx static const int	len_years[2] = {
    35         kx 	DAYSPERNYEAR, DAYSPERLYEAR
    35         kx };
    35         kx 
    35         kx static struct attype {
    35         kx 	zic_t		at;
    35         kx 	bool		dontmerge;
    35         kx 	unsigned char	type;
    35         kx } *			attypes;
    35         kx static zic_t		utoffs[TZ_MAX_TYPES];
    35         kx static char		isdsts[TZ_MAX_TYPES];
    35         kx static unsigned char	desigidx[TZ_MAX_TYPES];
    35         kx static bool		ttisstds[TZ_MAX_TYPES];
    35         kx static bool		ttisuts[TZ_MAX_TYPES];
    35         kx static char		chars[TZ_MAX_CHARS];
    35         kx static zic_t		trans[TZ_MAX_LEAPS];
    35         kx static zic_t		corr[TZ_MAX_LEAPS];
    35         kx static char		roll[TZ_MAX_LEAPS];
    35         kx 
    35         kx /*
    35         kx ** Memory allocation.
    35         kx */
    35         kx 
    35         kx ATTRIBUTE_NORETURN static void
    35         kx memory_exhausted(const char *msg)
    35         kx {
    35         kx 	fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
    35         kx 	exit(EXIT_FAILURE);
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_NORETURN static void
    35         kx size_overflow(void)
    35         kx {
    35         kx   memory_exhausted(_("size overflow"));
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
    35         kx size_sum(size_t a, size_t b)
    35         kx {
    35         kx #ifdef ckd_add
    35         kx   ptrdiff_t sum;
    35         kx   if (!ckd_add(&sum, a, b) && sum <= INDEX_MAX)
    35         kx     return sum;
    35         kx #else
    35         kx   if (a <= INDEX_MAX && b <= INDEX_MAX - a)
    35         kx     return a + b;
    35         kx #endif
    35         kx   size_overflow();
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
    35         kx size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
    35         kx {
    35         kx #ifdef ckd_mul
    35         kx   ptrdiff_t product;
    35         kx   if (!ckd_mul(&product, nitems, itemsize) && product <= INDEX_MAX)
    35         kx     return product;
    35         kx #else
    35         kx   ptrdiff_t nitems_max = INDEX_MAX / itemsize;
    35         kx   if (nitems <= nitems_max)
    35         kx     return nitems * itemsize;
    35         kx #endif
    35         kx   size_overflow();
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
    35         kx align_to(ptrdiff_t size, ptrdiff_t alignment)
    35         kx {
    35         kx   ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits);
    35         kx   return sum & ~lo_bits;
    35         kx }
    35         kx 
    35         kx #if !HAVE_STRDUP
    35         kx static char *
    35         kx strdup(char const *str)
    35         kx {
    35         kx   char *result = malloc(strlen(str) + 1);
    35         kx   return result ? strcpy(result, str) : result;
    35         kx }
    35         kx #endif
    35         kx 
    35         kx static void *
    35         kx memcheck(void *ptr)
    35         kx {
    35         kx 	if (ptr == NULL)
    35         kx 	  memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM));
    35         kx 	return ptr;
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_MALLOC static void *
    35         kx emalloc(size_t size)
    35         kx {
    35         kx   return memcheck(malloc(size));
    35         kx }
    35         kx 
    35         kx static void *
    35         kx erealloc(void *ptr, size_t size)
    35         kx {
    35         kx   return memcheck(realloc(ptr, size));
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_MALLOC static char *
    35         kx estrdup(char const *str)
    35         kx {
    35         kx   return memcheck(strdup(str));
    35         kx }
    35         kx 
    35         kx static ptrdiff_t
    35         kx grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize)
    35         kx {
    35         kx   ptrdiff_t addend = (*nitems_alloc >> 1) + 1;
    35         kx #if defined ckd_add && defined ckd_mul
    35         kx   ptrdiff_t product;
    35         kx   if (!ckd_add(nitems_alloc, *nitems_alloc, addend)
    35         kx       && !ckd_mul(&product, *nitems_alloc, itemsize) && product <= INDEX_MAX)
    35         kx     return product;
    35         kx #else
    35         kx   if (*nitems_alloc <= ((INDEX_MAX - 1) / 3 * 2) / itemsize) {
    35         kx     *nitems_alloc += addend;
    35         kx     return *nitems_alloc * itemsize;
    35         kx   }
    35         kx #endif
    35         kx   memory_exhausted(_("integer overflow"));
    35         kx }
    35         kx 
    35         kx static void *
    35         kx growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
    35         kx 	  ptrdiff_t *nitems_alloc)
    35         kx {
    35         kx   return (nitems < *nitems_alloc
    35         kx 	  ? ptr
    35         kx 	  : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
    35         kx }
    35         kx 
    35         kx /*
    35         kx ** Error handling.
    35         kx */
    35         kx 
    35         kx /* In most of the code, an input file name is represented by its index
    35         kx    into the main argument vector, except that LEAPSEC_FILENUM stands
    35         kx    for leapsec and COMMAND_LINE_FILENUM stands for the command line.  */
    35         kx enum { LEAPSEC_FILENUM = -2, COMMAND_LINE_FILENUM = -1 };
    35         kx 
    35         kx /* Return the name of the Ith input file, for diagnostics.  */
    35         kx static char const *
    35         kx filename(int i)
    35         kx {
    35         kx   if (i == COMMAND_LINE_FILENUM)
    35         kx     return _("command line");
    35         kx   else {
    35         kx     char const *fname = i == LEAPSEC_FILENUM ? leapsec : main_argv[i];
    35         kx     return strcmp(fname, "-") == 0 ? _("standard input") : fname;
    35         kx   }
    35         kx }
    35         kx 
    35         kx static void
    35         kx eats(int fnum, lineno num, int rfnum, lineno rnum)
    35         kx {
    35         kx 	filenum = fnum;
    35         kx 	linenum = num;
    35         kx 	rfilenum = rfnum;
    35         kx 	rlinenum = rnum;
    35         kx }
    35         kx 
    35         kx static void
    35         kx eat(int fnum, lineno num)
    35         kx {
    35         kx 	eats(fnum, num, 0, -1);
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_FORMAT((printf, 1, 0)) static void
    35         kx verror(const char *const string, va_list args)
    35         kx {
    35         kx 	/*
    35         kx 	** Match the format of "cc" to allow sh users to
    35         kx 	**	zic ... 2>&1 | error -t "*" -v
    35         kx 	** on BSD systems.
    35         kx 	*/
    35         kx 	if (filenum)
    35         kx 	  fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
    35         kx 		  filename(filenum), linenum);
    35         kx 	vfprintf(stderr, string, args);
    35         kx 	if (rfilenum)
    35         kx 		fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
    35         kx 			filename(rfilenum), rlinenum);
    35         kx 	fprintf(stderr, "\n");
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_FORMAT((printf, 1, 2)) static void
    35         kx error(const char *const string, ...)
    35         kx {
    35         kx 	va_list args;
    35         kx 	va_start(args, string);
    35         kx 	verror(string, args);
    35         kx 	va_end(args);
    35         kx 	errors = true;
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_FORMAT((printf, 1, 2)) static void
    35         kx warning(const char *const string, ...)
    35         kx {
    35         kx 	va_list args;
    35         kx 	fprintf(stderr, _("warning: "));
    35         kx 	va_start(args, string);
    35         kx 	verror(string, args);
    35         kx 	va_end(args);
    35         kx 	warnings = true;
    35         kx }
    35         kx 
    35         kx /* Close STREAM.  If it had an I/O error, report it against DIR/NAME,
    35         kx    remove TEMPNAME if nonnull, and then exit.  */
    35         kx static void
    35         kx close_file(FILE *stream, char const *dir, char const *name,
    35         kx 	   char const *tempname)
    35         kx {
    35         kx   char const *e = (ferror(stream) ? _("I/O error")
    35         kx 		   : fclose(stream) != 0 ? strerror(errno) : NULL);
    35         kx   if (e) {
    35         kx     fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
    35         kx 	    dir ? dir : "", dir ? "/" : "",
    35         kx 	    name ? name : "", name ? ": " : "",
    35         kx 	    e);
    35         kx     if (tempname)
    35         kx       remove(tempname);
    35         kx     exit(EXIT_FAILURE);
    35         kx   }
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_NORETURN static void
    35         kx usage(FILE *stream, int status)
    35         kx {
    35         kx   fprintf(stream,
    35         kx 	  _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
    35         kx 	    "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
    35         kx 	    " [ -L leapseconds ] \\\n"
    35         kx 	    "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
    35         kx 	    "\t[ -t localtime-link ] \\\n"
    35         kx 	    "\t[ filename ... ]\n\n"
    35         kx 	    "Report bugs to %s.\n"),
    35         kx 	  progname, progname, REPORT_BUGS_TO);
    35         kx   if (status == EXIT_SUCCESS)
    35         kx     close_file(stream, NULL, NULL, NULL);
    35         kx   exit(status);
    35         kx }
    35         kx 
    35         kx /* Change the working directory to DIR, possibly creating DIR and its
    35         kx    ancestors.  After this is done, all files are accessed with names
    35         kx    relative to DIR.  */
    35         kx static void
    35         kx change_directory(char const *dir)
    35         kx {
    35         kx   if (chdir(dir) != 0) {
    35         kx     int chdir_errno = errno;
    35         kx     if (chdir_errno == ENOENT) {
    35         kx       mkdirs(dir, false);
    35         kx       chdir_errno = chdir(dir) == 0 ? 0 : errno;
    35         kx     }
    35         kx     if (chdir_errno != 0) {
    35         kx       fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
    35         kx 	      progname, dir, strerror(chdir_errno));
    35         kx       exit(EXIT_FAILURE);
    35         kx     }
    35         kx   }
    35         kx }
    35         kx 
    35         kx /* Compare the two links A and B, for a stable sort by link name.  */
    35         kx static int
    35         kx qsort_linkcmp(void const *a, void const *b)
    35         kx {
    35         kx   struct link const *l = a;
    35         kx   struct link const *m = b;
    35         kx   int cmp = strcmp(l->l_linkname, m->l_linkname);
    35         kx   if (cmp)
    35         kx     return cmp;
    35         kx 
    35         kx   /* The link names are the same.  Make the sort stable by comparing
    35         kx      file numbers (where subtraction cannot overflow) and possibly
    35         kx      line numbers (where it can).  */
    35         kx   cmp = l->l_filenum - m->l_filenum;
    35         kx   if (cmp)
    35         kx     return cmp;
    35         kx   return (l->l_linenum > m->l_linenum) - (l->l_linenum < m->l_linenum);
    35         kx }
    35         kx 
    35         kx /* Compare the string KEY to the link B, for bsearch.  */
    35         kx static int
    35         kx bsearch_linkcmp(void const *key, void const *b)
    35         kx {
    35         kx   struct link const *m = b;
    35         kx   return strcmp(key, m->l_linkname);
    35         kx }
    35         kx 
    35         kx /* Make the links specified by the Link lines.  */
    35         kx static void
    35         kx make_links(void)
    35         kx {
    35         kx   ptrdiff_t i, j, nalinks, pass_size;
    35         kx   if (1 < nlinks)
    35         kx     qsort(links, nlinks, sizeof *links, qsort_linkcmp);
    35         kx 
    35         kx   /* Ignore each link superseded by a later link with the same name.  */
    35         kx   j = 0;
    35         kx   for (i = 0; i < nlinks; i++) {
    35         kx     while (i + 1 < nlinks
    35         kx 	   && strcmp(links[i].l_linkname, links[i + 1].l_linkname) == 0)
    35         kx       i++;
    35         kx     links[j++] = links[i];
    35         kx   }
    35         kx   nlinks = pass_size = j;
    35         kx 
    35         kx   /* Walk through the link array making links.  However,
    35         kx      if a link's target has not been made yet, append a copy to the
    35         kx      end of the array.  The end of the array will gradually fill
    35         kx      up with a small sorted subsequence of not-yet-made links.
    35         kx      nalinks counts all the links in the array, including copies.
    35         kx      When we reach the copied subsequence, it may still contain
    35         kx      a link to a not-yet-made link, so the process repeats.
    35         kx      At any given point in time, the link array consists of the
    35         kx      following subregions, where 0 <= i <= j <= nalinks and
    35         kx      0 <= nlinks <= nalinks:
    35         kx 
    35         kx        0 .. (i - 1):
    35         kx 	 links that either have been made, or have been copied to a
    35         kx 	 later point point in the array (this later point can be in
    35         kx 	 any of the three subregions)
    35         kx        i .. (j - 1):
    35         kx 	 not-yet-made links for this pass
    35         kx        j .. (nalinks - 1):
    35         kx 	 not-yet-made links that this pass has skipped because
    35         kx 	 they were links to not-yet-made links
    35         kx 
    35         kx      The first subregion might not be sorted if nlinks < i;
    35         kx      the other two subregions are sorted.  This algorithm does
    35         kx      not alter entries 0 .. (nlinks - 1), which remain sorted.
    35         kx 
    35         kx      If there are L links, this algorithm is O(C*L*log(L)) where
    35         kx      C is the length of the longest link chain.  Usually C is
    35         kx      short (e.g., 3) though its worst-case value is L.  */
    35         kx 
    35         kx   j = nalinks = nlinks;
    35         kx 
    35         kx   for (i = 0; i < nalinks; i++) {
    35         kx     struct link *l;
    35         kx 
    35         kx     eat(links[i].l_filenum, links[i].l_linenum);
    35         kx 
    35         kx     /* If this pass examined all its links, start the next pass.  */
    35         kx     if (i == j) {
    35         kx       if (nalinks - i == pass_size) {
    35         kx 	error(_("\"Link %s %s\" is part of a link cycle"),
    35         kx 	      links[i].l_target, links[i].l_linkname);
    35         kx 	break;
    35         kx       }
    35         kx       j = nalinks;
    35         kx       pass_size = nalinks - i;
    35         kx     }
    35         kx 
    35         kx     /* Diagnose self links, which the cycle detection algorithm would not
    35         kx        otherwise catch.  */
    35         kx     if (strcmp(links[i].l_target, links[i].l_linkname) == 0) {
    35         kx       error(_("link %s targets itself"), links[i].l_target);
    35         kx       continue;
    35         kx     }
    35         kx 
    35         kx     /* Make this link unless its target has not been made yet.  */
    35         kx     l = bsearch(links[i].l_target, &links[i + 1], j - (i + 1),
    35         kx 		sizeof *links, bsearch_linkcmp);
    35         kx     if (!l)
    35         kx       l = bsearch(links[i].l_target, &links[j], nalinks - j,
    35         kx 		  sizeof *links, bsearch_linkcmp);
    35         kx     if (!l)
    35         kx       dolink(links[i].l_target, links[i].l_linkname, false);
    35         kx     else {
    35         kx       /* The link target has not been made yet; copy the link to the end.  */
    35         kx       links = growalloc(links, sizeof *links, nalinks, &nlinks_alloc);
    35         kx       links[nalinks++] = links[i];
    35         kx     }
    35         kx 
    35         kx     if (noise && i < nlinks) {
    35         kx       if (l)
    35         kx 	warning(_("link %s targeting link %s mishandled by pre-2023 zic"),
    35         kx 		links[i].l_linkname, links[i].l_target);
    35         kx       else if (bsearch(links[i].l_target, links, nlinks, sizeof *links,
    35         kx 		       bsearch_linkcmp))
    35         kx 	warning(_("link %s targeting link %s"),
    35         kx 		links[i].l_linkname, links[i].l_target);
    35         kx     }
    35         kx   }
    35         kx }
    35         kx 
    35         kx /* Simple signal handling: just set a flag that is checked
    35         kx    periodically outside critical sections.  To set up the handler,
    35         kx    prefer sigaction if available to close a signal race.  */
    35         kx 
    35         kx static sig_atomic_t got_signal;
    35         kx 
    35         kx static void
    35         kx signal_handler(int sig)
    35         kx {
    35         kx #ifndef SA_SIGINFO
    35         kx   signal(sig, signal_handler);
    35         kx #endif
    35         kx   got_signal = sig;
    35         kx }
    35         kx 
    35         kx /* Arrange for SIGINT etc. to be caught by the handler.  */
    35         kx static void
    35         kx catch_signals(void)
    35         kx {
    35         kx   static int const signals[] = {
    35         kx #ifdef SIGHUP
    35         kx     SIGHUP,
    35         kx #endif
    35         kx     SIGINT,
    35         kx #ifdef SIGPIPE
    35         kx     SIGPIPE,
    35         kx #endif
    35         kx     SIGTERM
    35         kx   };
    35         kx   int i;
    35         kx   for (i = 0; i < sizeof signals / sizeof signals[0]; i++) {
    35         kx #ifdef SA_SIGINFO
    35         kx     struct sigaction act0, act;
    35         kx     act.sa_handler = signal_handler;
    35         kx     sigemptyset(&act.sa_mask);
    35         kx     act.sa_flags = 0;
    35         kx     if (sigaction(signals[i], &act, &act0) == 0
    35         kx 	&& ! (act0.sa_flags & SA_SIGINFO) && act0.sa_handler == SIG_IGN) {
    35         kx       sigaction(signals[i], &act0, NULL);
    35         kx       got_signal = 0;
    35         kx     }
    35         kx #else
    35         kx     if (signal(signals[i], signal_handler) == SIG_IGN) {
    35         kx       signal(signals[i], SIG_IGN);
    35         kx       got_signal = 0;
    35         kx     }
    35         kx #endif
    35         kx   }
    35         kx }
    35         kx 
    35         kx /* If a signal has arrived, terminate zic with appropriate status.  */
    35         kx static void
    35         kx check_for_signal(void)
    35         kx {
    35         kx   int sig = got_signal;
    35         kx   if (sig) {
    35         kx     signal(sig, SIG_DFL);
    35         kx     raise(sig);
    35         kx     abort(); /* A bug in 'raise'.  */
    35         kx   }
    35         kx }
    35         kx 
    35         kx enum { TIME_T_BITS_IN_FILE = 64 };
    35         kx 
    35         kx /* The minimum and maximum values representable in a TZif file.  */
    35         kx static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
    35         kx static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
    35         kx 
    35         kx /* The minimum, and one less than the maximum, values specified by
    35         kx    the -r option.  These default to MIN_TIME and MAX_TIME.  */
    35         kx static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
    35         kx static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
    35         kx 
    35         kx /* The time specified by the -R option, defaulting to MIN_TIME.  */
    35         kx static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
    35         kx 
    35         kx /* The time specified by an Expires line, or negative if no such line.  */
    35         kx static zic_t leapexpires = -1;
    35         kx 
    35         kx /* Set the time range of the output to TIMERANGE.
    35         kx    Return true if successful.  */
    35         kx static bool
    35         kx timerange_option(char *timerange)
    35         kx {
    35         kx   intmax_t lo = min_time, hi = max_time;
    35         kx   char *lo_end = timerange, *hi_end;
    35         kx   if (*timerange == '@') {
    35         kx     errno = 0;
    35         kx     lo = strtoimax(timerange + 1, &lo_end, 10);
    35         kx     if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
    35         kx       return false;
    35         kx   }
    35         kx   hi_end = lo_end;
    35         kx   if (lo_end[0] == '/' && lo_end[1] == '@') {
    35         kx     errno = 0;
    35         kx     hi = strtoimax(lo_end + 2, &hi_end, 10);
    35         kx     if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
    35         kx       return false;
    35         kx     hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
    35         kx   }
    35         kx   if (*hi_end || hi < lo || max_time < lo || hi < min_time)
    35         kx     return false;
    35         kx   lo_time = max(lo, min_time);
    35         kx   hi_time = min(hi, max_time);
    35         kx   return true;
    35         kx }
    35         kx 
    35         kx /* Generate redundant time stamps up to OPT.  Return true if successful.  */
    35         kx static bool
    35         kx redundant_time_option(char *opt)
    35         kx {
    35         kx   if (*opt == '@') {
    35         kx     intmax_t redundant;
    35         kx     char *opt_end;
    35         kx     redundant = strtoimax(opt + 1, &opt_end, 10);
    35         kx     if (opt_end != opt + 1 && !*opt_end) {
    35         kx       redundant_time = max(redundant_time, redundant);
    35         kx       return true;
    35         kx     }
    35         kx   }
    35         kx   return false;
    35         kx }
    35         kx 
    35         kx static const char *	psxrules;
    35         kx static const char *	lcltime;
    35         kx static const char *	directory;
    35         kx static const char *	tzdefault;
    35         kx 
    35         kx /* -1 if the TZif output file should be slim, 0 if default, 1 if the
    35         kx    output should be fat for backward compatibility.  ZIC_BLOAT_DEFAULT
    35         kx    determines the default.  */
    35         kx static int bloat;
    35         kx 
    35         kx static bool
    35         kx want_bloat(void)
    35         kx {
    35         kx   return 0 <= bloat;
    35         kx }
    35         kx 
    35         kx #ifndef ZIC_BLOAT_DEFAULT
    35         kx # define ZIC_BLOAT_DEFAULT "slim"
    35         kx #endif
    35         kx 
    35         kx int
    35         kx main(int argc, char **argv)
    35         kx {
    35         kx 	register int c, k;
    35         kx 	register ptrdiff_t i, j;
    35         kx 	bool timerange_given = false;
    35         kx 
    35         kx #ifdef S_IWGRP
    35         kx 	umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
    35         kx #endif
    35         kx #if HAVE_GETTEXT
    35         kx 	setlocale(LC_ALL, "");
    35         kx # ifdef TZ_DOMAINDIR
    35         kx 	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
    35         kx # endif /* defined TEXTDOMAINDIR */
    35         kx 	textdomain(TZ_DOMAIN);
    35         kx #endif /* HAVE_GETTEXT */
    35         kx 	main_argv = argv;
    35         kx 	progname = argv[0] ? argv[0] : "zic";
    35         kx 	if (TYPE_BIT(zic_t) < 64) {
    35         kx 		fprintf(stderr, "%s: %s\n", progname,
    35         kx 			_("wild compilation-time specification of zic_t"));
    35         kx 		return EXIT_FAILURE;
    35         kx 	}
    35         kx 	for (k = 1; k < argc; k++)
    35         kx 		if (strcmp(argv[k], "--version") == 0) {
    35         kx 			printf("zic %s%s\n", PKGVERSION, TZVERSION);
    35         kx 			close_file(stdout, NULL, NULL, NULL);
    35         kx 			return EXIT_SUCCESS;
    35         kx 		} else if (strcmp(argv[k], "--help") == 0) {
    35         kx 			usage(stdout, EXIT_SUCCESS);
    35         kx 		}
    35         kx 	while ((c = getopt(argc, argv, "b:d:l:L:p:r:R:st:vy:")) != EOF
    35         kx 	       && c != -1)
    35         kx 		switch (c) {
    35         kx 			default:
    35         kx 				usage(stderr, EXIT_FAILURE);
    35         kx 			case 'b':
    35         kx 				if (strcmp(optarg, "slim") == 0) {
    35         kx 				  if (0 < bloat)
    35         kx 				    error(_("incompatible -b options"));
    35         kx 				  bloat = -1;
    35         kx 				} else if (strcmp(optarg, "fat") == 0) {
    35         kx 				  if (bloat < 0)
    35         kx 				    error(_("incompatible -b options"));
    35         kx 				  bloat = 1;
    35         kx 				} else
    35         kx 				  error(_("invalid option: -b '%s'"), optarg);
    35         kx 				break;
    35         kx 			case 'd':
    35         kx 				if (directory == NULL)
    35         kx 					directory = optarg;
    35         kx 				else {
    35         kx 					fprintf(stderr,
    35         kx _("%s: More than one -d option specified\n"),
    35         kx 						progname);
    35         kx 					return EXIT_FAILURE;
    35         kx 				}
    35         kx 				break;
    35         kx 			case 'l':
    35         kx 				if (lcltime == NULL)
    35         kx 					lcltime = optarg;
    35         kx 				else {
    35         kx 					fprintf(stderr,
    35         kx _("%s: More than one -l option specified\n"),
    35         kx 						progname);
    35         kx 					return EXIT_FAILURE;
    35         kx 				}
    35         kx 				break;
    35         kx 			case 'p':
    35         kx 				if (psxrules == NULL)
    35         kx 					psxrules = optarg;
    35         kx 				else {
    35         kx 					fprintf(stderr,
    35         kx _("%s: More than one -p option specified\n"),
    35         kx 						progname);
    35         kx 					return EXIT_FAILURE;
    35         kx 				}
    35         kx 				break;
    35         kx 			case 't':
    35         kx 				if (tzdefault != NULL) {
    35         kx 				  fprintf(stderr,
    35         kx 					  _("%s: More than one -t option"
    35         kx 					    " specified\n"),
    35         kx 					  progname);
    35         kx 				  return EXIT_FAILURE;
    35         kx 				}
    35         kx 				tzdefault = optarg;
    35         kx 				break;
    35         kx 			case 'y':
    35         kx 				warning(_("-y ignored"));
    35         kx 				break;
    35         kx 			case 'L':
    35         kx 				if (leapsec == NULL)
    35         kx 					leapsec = optarg;
    35         kx 				else {
    35         kx 					fprintf(stderr,
    35         kx _("%s: More than one -L option specified\n"),
    35         kx 						progname);
    35         kx 					return EXIT_FAILURE;
    35         kx 				}
    35         kx 				break;
    35         kx 			case 'v':
    35         kx 				noise = true;
    35         kx 				break;
    35         kx 			case 'r':
    35         kx 				if (timerange_given) {
    35         kx 				  fprintf(stderr,
    35         kx _("%s: More than one -r option specified\n"),
    35         kx 					  progname);
    35         kx 				  return EXIT_FAILURE;
    35         kx 				}
    35         kx 				if (! timerange_option(optarg)) {
    35         kx 				  fprintf(stderr,
    35         kx _("%s: invalid time range: %s\n"),
    35         kx 					  progname, optarg);
    35         kx 				  return EXIT_FAILURE;
    35         kx 				}
    35         kx 				timerange_given = true;
    35         kx 				break;
    35         kx 			case 'R':
    35         kx 				if (! redundant_time_option(optarg)) {
    35         kx 				  fprintf(stderr, _("%s: invalid time: %s\n"),
    35         kx 					  progname, optarg);
    35         kx 				  return EXIT_FAILURE;
    35         kx 				}
    35         kx 				break;
    35         kx 			case 's':
    35         kx 				warning(_("-s ignored"));
    35         kx 				break;
    35         kx 		}
    35         kx 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
    35         kx 		usage(stderr, EXIT_FAILURE);	/* usage message by request */
    35         kx 	if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
    35         kx 	  fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
    35         kx 	  return EXIT_FAILURE;
    35         kx 	}
    35         kx 	if (bloat == 0) {
    35         kx 	  static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
    35         kx 	  if (strcmp(bloat_default, "slim") == 0)
    35         kx 	    bloat = -1;
    35         kx 	  else if (strcmp(bloat_default, "fat") == 0)
    35         kx 	    bloat = 1;
    35         kx 	  else
    35         kx 	    abort(); /* Configuration error.  */
    35         kx 	}
    35         kx 	if (directory == NULL)
    35         kx 		directory = TZDIR;
    35         kx 	if (tzdefault == NULL)
    35         kx 		tzdefault = TZDEFAULT;
    35         kx 
    35         kx 	if (optind < argc && leapsec != NULL) {
    35         kx 		infile(LEAPSEC_FILENUM, leapsec);
    35         kx 		adjleap();
    35         kx 	}
    35         kx 
    35         kx 	for (k = optind; k < argc; k++)
    35         kx 	  infile(k, argv[k]);
    35         kx 	if (errors)
    35         kx 		return EXIT_FAILURE;
    35         kx 	associate();
    35         kx 	change_directory(directory);
    35         kx 	catch_signals();
    35         kx 	for (i = 0; i < nzones; i = j) {
    35         kx 		/*
    35         kx 		** Find the next non-continuation zone entry.
    35         kx 		*/
    35         kx 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
    35         kx 			continue;
    35         kx 		outzone(&zones[i], j - i);
    35         kx 	}
    35         kx 	make_links();
    35         kx 	if (lcltime != NULL) {
    35         kx 		eat(COMMAND_LINE_FILENUM, 1);
    35         kx 		dolink(lcltime, tzdefault, true);
    35         kx 	}
    35         kx 	if (psxrules != NULL) {
    35         kx 		eat(COMMAND_LINE_FILENUM, 1);
    35         kx 		dolink(psxrules, TZDEFRULES, true);
    35         kx 	}
    35         kx 	if (warnings && (ferror(stderr) || fclose(stderr) != 0))
    35         kx 	  return EXIT_FAILURE;
    35         kx 	return errors ? EXIT_FAILURE : EXIT_SUCCESS;
    35         kx }
    35         kx 
    35         kx static bool
    35         kx componentcheck(char const *name, char const *component,
    35         kx 	       char const *component_end)
    35         kx {
    35         kx 	enum { component_len_max = 14 };
    35         kx 	ptrdiff_t component_len = component_end - component;
    35         kx 	if (component_len == 0) {
    35         kx 	  if (!*name)
    35         kx 	    error(_("empty file name"));
    35         kx 	  else
    35         kx 	    error(_(component == name
    35         kx 		     ? "file name '%s' begins with '/'"
    35         kx 		     : *component_end
    35         kx 		     ? "file name '%s' contains '//'"
    35         kx 		     : "file name '%s' ends with '/'"),
    35         kx 		   name);
    35         kx 	  return false;
    35         kx 	}
    35         kx 	if (0 < component_len && component_len <= 2
    35         kx 	    && component[0] == '.' && component_end[-1] == '.') {
    35         kx 	  int len = component_len;
    35         kx 	  error(_("file name '%s' contains '%.*s' component"),
    35         kx 		name, len, component);
    35         kx 	  return false;
    35         kx 	}
    35         kx 	if (noise) {
    35         kx 	  if (0 < component_len && component[0] == '-')
    35         kx 	    warning(_("file name '%s' component contains leading '-'"),
    35         kx 		    name);
    35         kx 	  if (component_len_max < component_len)
    35         kx 	    warning(_("file name '%s' contains overlength component"
    35         kx 		      " '%.*s...'"),
    35         kx 		    name, component_len_max, component);
    35         kx 	}
    35         kx 	return true;
    35         kx }
    35         kx 
    35         kx static bool
    35         kx namecheck(const char *name)
    35         kx {
    35         kx 	register char const *cp;
    35         kx 
    35         kx 	/* Benign characters in a portable file name.  */
    35         kx 	static char const benign[] =
    35         kx 	  "-/_"
    35         kx 	  "abcdefghijklmnopqrstuvwxyz"
    35         kx 	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    35         kx 
    35         kx 	/* Non-control chars in the POSIX portable character set,
    35         kx 	   excluding the benign characters.  */
    35         kx 	static char const printable_and_not_benign[] =
    35         kx 	  " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
    35         kx 
    35         kx 	register char const *component = name;
    35         kx 	for (cp = name; *cp; cp++) {
    35         kx 		unsigned char c = *cp;
    35         kx 		if (noise && !strchr(benign, c)) {
    35         kx 			warning((strchr(printable_and_not_benign, c)
    35         kx 				 ? _("file name '%s' contains byte '%c'")
    35         kx 				 : _("file name '%s' contains byte '\\%o'")),
    35         kx 				name, c);
    35         kx 		}
    35         kx 		if (c == '/') {
    35         kx 			if (!componentcheck(name, component, cp))
    35         kx 			  return false;
    35         kx 			component = cp + 1;
    35         kx 		}
    35         kx 	}
    35         kx 	return componentcheck(name, component, cp);
    35         kx }
    35         kx 
    35         kx /* Return a random uint_fast64_t.  */
    35         kx static uint_fast64_t
    35         kx get_rand_u64(void)
    35         kx {
    35         kx #if HAVE_GETRANDOM
    35         kx   static uint_fast64_t entropy_buffer[max(1, 256 / sizeof(uint_fast64_t))];
    35         kx   static int nwords;
    35         kx   if (!nwords) {
    35         kx     ssize_t s;
    35         kx     do
    35         kx       s = getrandom(entropy_buffer, sizeof entropy_buffer, 0);
    35         kx     while (s < 0 && errno == EINTR);
    35         kx 
    35         kx     nwords = s < 0 ? -1 : s / sizeof *entropy_buffer;
    35         kx   }
    35         kx   if (0 < nwords)
    35         kx     return entropy_buffer[--nwords];
    35         kx #endif
    35         kx 
    35         kx   /* getrandom didn't work, so fall back on portable code that is
    35         kx      not the best because the seed isn't cryptographically random and
    35         kx      'rand' might not be cryptographically secure.  */
    35         kx   {
    35         kx     static bool initialized;
    35         kx     if (!initialized) {
    35         kx       srand(time(NULL));
    35         kx       initialized = true;
    35         kx     }
    35         kx   }
    35         kx 
    35         kx   /* Return a random number if rand() yields a random number and in
    35         kx      the typical case where RAND_MAX is one less than a power of two.
    35         kx      In other cases this code yields a sort-of-random number.  */
    35         kx   {
    35         kx     uint_fast64_t rand_max = RAND_MAX,
    35         kx       nrand = rand_max < UINT_FAST64_MAX ? rand_max + 1 : 0,
    35         kx       rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1,
    35         kx       r = 0, rmax = 0;
    35         kx 
    35         kx     do {
    35         kx       uint_fast64_t rmax1 = rmax;
    35         kx       if (rmod) {
    35         kx 	/* Avoid signed integer overflow on theoretical platforms
    35         kx 	   where uint_fast64_t promotes to int.  */
    35         kx 	rmax1 %= rmod;
    35         kx 	r %= rmod;
    35         kx       }
    35         kx       rmax1 = nrand * rmax1 + rand_max;
    35         kx       r = nrand * r + rand();
    35         kx       rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX;
    35         kx     } while (rmax < UINT_FAST64_MAX);
    35         kx 
    35         kx     return r;
    35         kx   }
    35         kx }
    35         kx 
    35         kx /* Generate a randomish name in the same directory as *NAME.  If
    35         kx    *NAMEALLOC, put the name into *NAMEALLOC which is assumed to be
    35         kx    that returned by a previous call and is thus already almost set up
    35         kx    and equal to *NAME; otherwise, allocate a new name and put its
    35         kx    address into both *NAMEALLOC and *NAME.  */
    35         kx static void
    35         kx random_dirent(char const **name, char **namealloc)
    35         kx {
    35         kx   char const *src = *name;
    35         kx   char *dst = *namealloc;
    35         kx   static char const prefix[] = ".zic";
    35         kx   static char const alphabet[] =
    35         kx     "abcdefghijklmnopqrstuvwxyz"
    35         kx     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    35         kx     "0123456789";
    35         kx   enum { prefixlen = sizeof prefix - 1, alphabetlen = sizeof alphabet - 1 };
    35         kx   int suffixlen = 6;
    35         kx   char const *lastslash = strrchr(src, '/');
    35         kx   ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0;
    35         kx   int i;
    35         kx   uint_fast64_t r;
    35         kx   uint_fast64_t base = alphabetlen;
    35         kx 
    35         kx   /* BASE**6 */
    35         kx   uint_fast64_t base__6 = base * base * base * base * base * base;
    35         kx 
    35         kx   /* The largest uintmax_t that is a multiple of BASE**6.  Any random
    35         kx      uintmax_t value that is this value or greater, yields a biased
    35         kx      remainder when divided by BASE**6.  UNFAIR_MIN equals the
    35         kx      mathematical value of ((UINTMAX_MAX + 1) - (UINTMAX_MAX + 1) % BASE**6)
    35         kx      computed without overflow.  */
    35         kx   uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
    35         kx 
    35         kx   if (!dst) {
    35         kx     dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
    35         kx     memcpy(dst, src, dirlen);
    35         kx     memcpy(dst + dirlen, prefix, prefixlen);
    35         kx     dst[dirlen + prefixlen + suffixlen] = '\0';
    35         kx     *name = *namealloc = dst;
    35         kx   }
    35         kx 
    35         kx   do
    35         kx     r = get_rand_u64();
    35         kx   while (unfair_min <= r);
    35         kx 
    35         kx   for (i = 0; i < suffixlen; i++) {
    35         kx     dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
    35         kx     r /= alphabetlen;
    35         kx   }
    35         kx }
    35         kx 
    35         kx /* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
    35         kx    name of the temporary file that will eventually be renamed to
    35         kx    *OUTNAME.  Assign the temporary file's name to both *OUTNAME and
    35         kx    *TEMPNAME.  If *TEMPNAME is null, allocate the name of any such
    35         kx    temporary file; otherwise, reuse *TEMPNAME's storage, which is
    35         kx    already set up and only needs its trailing suffix updated.  */
    35         kx static FILE *
    35         kx open_outfile(char const **outname, char **tempname)
    35         kx {
    35         kx #if __STDC_VERSION__ < 201112
    35         kx   static char const fopen_mode[] = "wb";
    35         kx #else
    35         kx   static char const fopen_mode[] = "wbx";
    35         kx #endif
    35         kx 
    35         kx   FILE *fp;
    35         kx   bool dirs_made = false;
    35         kx   if (!*tempname)
    35         kx     random_dirent(outname, tempname);
    35         kx 
    35         kx   while (! (fp = fopen(*outname, fopen_mode))) {
    35         kx     int fopen_errno = errno;
    35         kx     if (fopen_errno == ENOENT && !dirs_made) {
    35         kx       mkdirs(*outname, true);
    35         kx       dirs_made = true;
    35         kx     } else if (fopen_errno == EEXIST)
    35         kx       random_dirent(outname, tempname);
    35         kx     else {
    35         kx       fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
    35         kx 	      progname, directory, *outname, strerror(fopen_errno));
    35         kx       exit(EXIT_FAILURE);
    35         kx     }
    35         kx   }
    35         kx 
    35         kx   return fp;
    35         kx }
    35         kx 
    35         kx /* If TEMPNAME, the result is in the temporary file TEMPNAME even
    35         kx    though the user wanted it in NAME, so rename TEMPNAME to NAME.
    35         kx    Report an error and exit if there is trouble.  Also, free TEMPNAME.  */
    35         kx static void
    35         kx rename_dest(char *tempname, char const *name)
    35         kx {
    35         kx   if (tempname) {
    35         kx     if (rename(tempname, name) != 0) {
    35         kx       int rename_errno = errno;
    35         kx       remove(tempname);
    35         kx       fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
    35         kx 	      progname, directory, name, strerror(rename_errno));
    35         kx       exit(EXIT_FAILURE);
    35         kx     }
    35         kx     free(tempname);
    35         kx   }
    35         kx }
    35         kx 
    35         kx /* Create symlink contents suitable for symlinking FROM to TO, as a
    35         kx    freshly allocated string.  FROM should be a relative file name, and
    35         kx    is relative to the global variable DIRECTORY.  TO can be either
    35         kx    relative or absolute.  */
    35         kx static char *
    35         kx relname(char const *target, char const *linkname)
    35         kx {
    35         kx   size_t i, taillen, dir_len = 0, dotdots = 0;
    35         kx   ptrdiff_t dotdotetcsize, linksize = INDEX_MAX;
    35         kx   char const *f = target;
    35         kx   char *result = NULL;
    35         kx   if (*linkname == '/') {
    35         kx     /* Make F absolute too.  */
    35         kx     size_t len = strlen(directory);
    35         kx     size_t lenslash = len + (len && directory[len - 1] != '/');
    35         kx     size_t targetsize = strlen(target) + 1;
    35         kx     linksize = size_sum(lenslash, targetsize);
    35         kx     f = result = emalloc(linksize);
    35         kx     memcpy(result, directory, len);
    35         kx     result[len] = '/';
    35         kx     memcpy(result + lenslash, target, targetsize);
    35         kx   }
    35         kx   for (i = 0; f[i] && f[i] == linkname[i]; i++)
    35         kx     if (f[i] == '/')
    35         kx       dir_len = i + 1;
    35         kx   for (; linkname[i]; i++)
    35         kx     dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
    35         kx   taillen = strlen(f + dir_len);
    35         kx   dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
    35         kx   if (dotdotetcsize <= linksize) {
    35         kx     if (!result)
    35         kx       result = emalloc(dotdotetcsize);
    35         kx     for (i = 0; i < dotdots; i++)
    35         kx       memcpy(result + 3 * i, "../", 3);
    35         kx     memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
    35         kx   }
    35         kx   return result;
    35         kx }
    35         kx 
    35         kx static void
    35         kx dolink(char const *target, char const *linkname, bool staysymlink)
    35         kx {
    35         kx 	bool linkdirs_made = false;
    35         kx 	int link_errno;
    35         kx 	char *tempname = NULL;
    35         kx 	char const *outname = linkname;
    35         kx 
    35         kx 	check_for_signal();
    35         kx 
    35         kx 	if (strcmp(target, "-") == 0) {
    35         kx 	  if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR)
    35         kx 	    return;
    35         kx 	  else {
    35         kx 	    char const *e = strerror(errno);
    35         kx 	    fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
    35         kx 		    progname, directory, linkname, e);
    35         kx 	    exit(EXIT_FAILURE);
    35         kx 	  }
    35         kx 	}
    35         kx 
    35         kx 	while (true) {
    35         kx 	  if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW)
    35         kx 	      == 0) {
    35         kx 	    link_errno = 0;
    35         kx 	    break;
    35         kx 	  }
    35         kx 	  link_errno = errno;
    35         kx 	  if (link_errno == EXDEV || link_errno == ENOTSUP)
    35         kx 	    break;
    35         kx 
    35         kx 	  if (link_errno == EEXIST) {
    35         kx 	    staysymlink &= !tempname;
    35         kx 	    random_dirent(&outname, &tempname);
    35         kx 	    if (staysymlink && itssymlink(linkname))
    35         kx 	      break;
    35         kx 	  } else if (link_errno == ENOENT && !linkdirs_made) {
    35         kx 	    mkdirs(linkname, true);
    35         kx 	    linkdirs_made = true;
    35         kx 	  } else {
    35         kx 	    fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
    35         kx 		    progname, directory, target, directory, outname,
    35         kx 		    strerror(link_errno));
    35         kx 	    exit(EXIT_FAILURE);
    35         kx 	  }
    35         kx 	}
    35         kx 	if (link_errno != 0) {
    35         kx 	  bool absolute = *target == '/';
    35         kx 	  char *linkalloc = absolute ? NULL : relname(target, linkname);
    35         kx 	  char const *contents = absolute ? target : linkalloc;
    35         kx 	  int symlink_errno;
    35         kx 
    35         kx 	  while (true) {
    35         kx 	    if (symlink(contents, outname) == 0) {
    35         kx 	      symlink_errno = 0;
    35         kx 	      break;
    35         kx 	    }
    35         kx 	    symlink_errno = errno;
    35         kx 	    if (symlink_errno == EEXIST)
    35         kx 	      random_dirent(&outname, &tempname);
    35         kx 	    else if (symlink_errno == ENOENT && !linkdirs_made) {
    35         kx 	      mkdirs(linkname, true);
    35         kx 	      linkdirs_made = true;
    35         kx 	    } else
    35         kx 	      break;
    35         kx 	  }
    35         kx 	  free(linkalloc);
    35         kx 	  if (symlink_errno == 0) {
    35         kx 	    if (link_errno != ENOTSUP && link_errno != EEXIST)
    35         kx 	      warning(_("symbolic link used because hard link failed: %s"),
    35         kx 		      strerror(link_errno));
    35         kx 	  } else {
    35         kx 	    FILE *fp, *tp;
    35         kx 	    int c;
    35         kx 	    fp = fopen(target, "rb");
    35         kx 	    if (!fp) {
    35         kx 	      char const *e = strerror(errno);
    35         kx 	      fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
    35         kx 		      progname, directory, target, e);
    35         kx 	      exit(EXIT_FAILURE);
    35         kx 	    }
    35         kx 	    tp = open_outfile(&outname, &tempname);
    35         kx 	    while ((c = getc(fp)) != EOF)
    35         kx 	      putc(c, tp);
    35         kx 	    close_file(tp, directory, linkname, tempname);
    35         kx 	    close_file(fp, directory, target, NULL);
    35         kx 	    if (link_errno != ENOTSUP)
    35         kx 	      warning(_("copy used because hard link failed: %s"),
    35         kx 		      strerror(link_errno));
    35         kx 	    else if (symlink_errno != ENOTSUP)
    35         kx 	      warning(_("copy used because symbolic link failed: %s"),
    35         kx 		      strerror(symlink_errno));
    35         kx 	  }
    35         kx 	}
    35         kx 	rename_dest(tempname, linkname);
    35         kx }
    35         kx 
    35         kx /* Return true if NAME is a symbolic link.  */
    35         kx static bool
    35         kx itssymlink(char const *name)
    35         kx {
    35         kx   char c;
    35         kx   return 0 <= readlink(name, &c, 1);
    35         kx }
    35         kx 
    35         kx /*
    35         kx ** Associate sets of rules with zones.
    35         kx */
    35         kx 
    35         kx /*
    35         kx ** Sort by rule name.
    35         kx */
    35         kx 
    35         kx static int
    35         kx rcomp(const void *cp1, const void *cp2)
    35         kx {
    35         kx   struct rule const *r1 = cp1, *r2 = cp2;
    35         kx   return strcmp(r1->r_name, r2->r_name);
    35         kx }
    35         kx 
    35         kx static void
    35         kx associate(void)
    35         kx {
    35         kx 	register struct zone *	zp;
    35         kx 	register struct rule *	rp;
    35         kx 	register ptrdiff_t i, j, base, out;
    35         kx 
    35         kx 	if (1 < nrules) {
    35         kx 		qsort(rules, nrules, sizeof *rules, rcomp);
    35         kx 		for (i = 0; i < nrules - 1; ++i) {
    35         kx 			if (strcmp(rules[i].r_name,
    35         kx 				rules[i + 1].r_name) != 0)
    35         kx 					continue;
    35         kx 			if (rules[i].r_filenum == rules[i + 1].r_filenum)
    35         kx 					continue;
    35         kx 			eat(rules[i].r_filenum, rules[i].r_linenum);
    35         kx 			warning(_("same rule name in multiple files"));
    35         kx 			eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum);
    35         kx 			warning(_("same rule name in multiple files"));
    35         kx 			for (j = i + 2; j < nrules; ++j) {
    35         kx 				if (strcmp(rules[i].r_name,
    35         kx 					rules[j].r_name) != 0)
    35         kx 						break;
    35         kx 				if (rules[i].r_filenum == rules[j].r_filenum)
    35         kx 						continue;
    35         kx 				if (rules[i + 1].r_filenum
    35         kx 				    == rules[j].r_filenum)
    35         kx 						continue;
    35         kx 				break;
    35         kx 			}
    35         kx 			i = j - 1;
    35         kx 		}
    35         kx 	}
    35         kx 	for (i = 0; i < nzones; ++i) {
    35         kx 		zp = &zones[i];
    35         kx 		zp->z_rules = NULL;
    35         kx 		zp->z_nrules = 0;
    35         kx 	}
    35         kx 	for (base = 0; base < nrules; base = out) {
    35         kx 		rp = &rules[base];
    35         kx 		for (out = base + 1; out < nrules; ++out)
    35         kx 			if (strcmp(rp->r_name, rules[out].r_name) != 0)
    35         kx 				break;
    35         kx 		for (i = 0; i < nzones; ++i) {
    35         kx 			zp = &zones[i];
    35         kx 			if (strcmp(zp->z_rule, rp->r_name) != 0)
    35         kx 				continue;
    35         kx 			zp->z_rules = rp;
    35         kx 			zp->z_nrules = out - base;
    35         kx 		}
    35         kx 	}
    35         kx 	for (i = 0; i < nzones; ++i) {
    35         kx 		zp = &zones[i];
    35         kx 		if (zp->z_nrules == 0) {
    35         kx 			/*
    35         kx 			** Maybe we have a local standard time offset.
    35         kx 			*/
    35         kx 			eat(zp->z_filenum, zp->z_linenum);
    35         kx 			zp->z_save = getsave(zp->z_rule, &zp->z_isdst);
    35         kx 			/*
    35         kx 			** Note, though, that if there's no rule,
    35         kx 			** a '%s' in the format is a bad thing.
    35         kx 			*/
    35         kx 			if (zp->z_format_specifier == 's')
    35         kx 				error("%s", _("%s in ruleless zone"));
    35         kx 		}
    35         kx 	}
    35         kx 	if (errors)
    35         kx 		exit(EXIT_FAILURE);
    35         kx }
    35         kx 
    35         kx /* Read a text line from FP into BUF, which is of size BUFSIZE.
    35         kx    Terminate it with a NUL byte instead of a newline.
    35         kx    Return true if successful, false if EOF.
    35         kx    On error, report the error and exit.  */
    35         kx static bool
    35         kx inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
    35         kx {
    35         kx   ptrdiff_t linelen = 0, ch;
    35         kx   while ((ch = getc(fp)) != '\n') {
    35         kx     if (ch < 0) {
    35         kx       if (ferror(fp)) {
    35         kx 	error(_("input error"));
    35         kx 	exit(EXIT_FAILURE);
    35         kx       }
    35         kx       if (linelen == 0)
    35         kx 	return false;
    35         kx       error(_("unterminated line"));
    35         kx       exit(EXIT_FAILURE);
    35         kx     }
    35         kx     if (!ch) {
    35         kx       error(_("NUL input byte"));
    35         kx       exit(EXIT_FAILURE);
    35         kx     }
    35         kx     buf[linelen++] = ch;
    35         kx     if (linelen == bufsize) {
    35         kx       error(_("line too long"));
    35         kx       exit(EXIT_FAILURE);
    35         kx     }
    35         kx   }
    35         kx   buf[linelen] = '\0';
    35         kx   return true;
    35         kx }
    35         kx 
    35         kx static void
    35         kx infile(int fnum, char const *name)
    35         kx {
    35         kx 	register FILE *			fp;
    35         kx 	register const struct lookup *	lp;
    35         kx 	register bool			wantcont;
    35         kx 	register lineno			num;
    35         kx 
    35         kx 	if (strcmp(name, "-") == 0) {
    35         kx 		fp = stdin;
    35         kx 	} else if ((fp = fopen(name, "r")) == NULL) {
    35         kx 		const char *e = strerror(errno);
    35         kx 
    35         kx 		fprintf(stderr, _("%s: Can't open %s: %s\n"),
    35         kx 			progname, name, e);
    35         kx 		exit(EXIT_FAILURE);
    35         kx 	}
    35         kx 	wantcont = false;
    35         kx 	for (num = 1; ; ++num) {
    35         kx 		enum { bufsize_bound
    35         kx 		  = (min(INT_MAX, INDEX_MAX) / FORMAT_LEN_GROWTH_BOUND) };
    35         kx 		char buf[min(_POSIX2_LINE_MAX, bufsize_bound)];
    35         kx 		int nfields;
    35         kx 		char *fields[MAX_FIELDS];
    35         kx 		eat(fnum, num);
    35         kx 		if (!inputline(fp, buf, sizeof buf))
    35         kx 		  break;
    35         kx 		nfields = getfields(buf, fields,
    35         kx 				    sizeof fields / sizeof *fields);
    35         kx 		if (nfields == 0) {
    35         kx 			/* nothing to do */
    35         kx 		} else if (wantcont) {
    35         kx 			wantcont = inzcont(fields, nfields);
    35         kx 		} else {
    35         kx 			struct lookup const *line_codes
    35         kx 			  = fnum < 0 ? leap_line_codes : zi_line_codes;
    35         kx 			lp = byword(fields[0], line_codes);
    35         kx 			if (lp == NULL)
    35         kx 				error(_("input line of unknown type"));
    35         kx 			else switch (lp->l_value) {
    35         kx 				case LC_RULE:
    35         kx 					inrule(fields, nfields);
    35         kx 					wantcont = false;
    35         kx 					break;
    35         kx 				case LC_ZONE:
    35         kx 					wantcont = inzone(fields, nfields);
    35         kx 					break;
    35         kx 				case LC_LINK:
    35         kx 					inlink(fields, nfields);
    35         kx 					wantcont = false;
    35         kx 					break;
    35         kx 				case LC_LEAP:
    35         kx 					inleap(fields, nfields);
    35         kx 					wantcont = false;
    35         kx 					break;
    35         kx 				case LC_EXPIRES:
    35         kx 					inexpires(fields, nfields);
    35         kx 					wantcont = false;
    35         kx 					break;
    35         kx 				default: unreachable();
    35         kx 			}
    35         kx 		}
    35         kx 	}
    35         kx 	close_file(fp, NULL, filename(fnum), NULL);
    35         kx 	if (wantcont)
    35         kx 		error(_("expected continuation line not found"));
    35         kx }
    35         kx 
    35         kx /*
    35         kx ** Convert a string of one of the forms
    35         kx **	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss
    35         kx ** into a number of seconds.
    35         kx ** A null string maps to zero.
    35         kx ** Call error with errstring and return zero on errors.
    35         kx */
    35         kx 
    35         kx static zic_t
    35         kx gethms(char const *string, char const *errstring)
    35         kx {
    35         kx 	zic_t	hh;
    35         kx 	int sign, mm = 0, ss = 0;
    35         kx 	char hhx, mmx, ssx, xr = '0', xs;
    35         kx 	int tenths = 0;
    35         kx 	bool ok = true;
    35         kx 
    35         kx 	if (string == NULL || *string == '\0')
    35         kx 		return 0;
    35         kx 	if (*string == '-') {
    35         kx 		sign = -1;
    35         kx 		++string;
    35         kx 	} else	sign = 1;
    35         kx 	switch (sscanf(string,
    35         kx 		       "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
    35         kx 		       &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) {
    35         kx 	  default: ok = false; break;
    35         kx 	  case 8:
    35         kx 	    ok = '0' <= xr && xr <= '9';
    35         kx 	    ATTRIBUTE_FALLTHROUGH;
    35         kx 	  case 7:
    35         kx 	    ok &= ssx == '.';
    35         kx 	    if (ok && noise)
    35         kx 	      warning(_("fractional seconds rejected by"
    35         kx 			" pre-2018 versions of zic"));
    35         kx 	    ATTRIBUTE_FALLTHROUGH;
    35         kx 	  case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH;
    35         kx 	  case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH;
    35         kx 	  case 1: break;
    35         kx 	}
    35         kx 	if (!ok) {
    35         kx 			error("%s", errstring);
    35         kx 			return 0;
    35         kx 	}
    35         kx 	if (hh < 0 ||
    35         kx 		mm < 0 || mm >= MINSPERHOUR ||
    35         kx 		ss < 0 || ss > SECSPERMIN) {
    35         kx 			error("%s", errstring);
    35         kx 			return 0;
    35         kx 	}
    35         kx 	if (ZIC_MAX / SECSPERHOUR < hh) {
    35         kx 		error(_("time overflow"));
    35         kx 		return 0;
    35         kx 	}
    35         kx 	ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even.  */
    35         kx 	if (noise && (hh > HOURSPERDAY ||
    35         kx 		(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
    35         kx warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
    35         kx 	return oadd(sign * hh * SECSPERHOUR,
    35         kx 		    sign * (mm * SECSPERMIN + ss));
    35         kx }
    35         kx 
    35         kx static zic_t
    35         kx getsave(char *field, bool *isdst)
    35         kx {
    35         kx   int dst = -1;
    35         kx   zic_t save;
    35         kx   ptrdiff_t fieldlen = strlen(field);
    35         kx   if (fieldlen != 0) {
    35         kx     char *ep = field + fieldlen - 1;
    35         kx     switch (*ep) {
    35         kx       case 'd': dst = 1; *ep = '\0'; break;
    35         kx       case 's': dst = 0; *ep = '\0'; break;
    35         kx     }
    35         kx   }
    35         kx   save = gethms(field, _("invalid saved time"));
    35         kx   *isdst = dst < 0 ? save != 0 : dst;
    35         kx   return save;
    35         kx }
    35         kx 
    35         kx static void
    35         kx inrule(char **fields, int nfields)
    35         kx {
    35         kx 	struct rule r;
    35         kx 
    35         kx 	if (nfields != RULE_FIELDS) {
    35         kx 		error(_("wrong number of fields on Rule line"));
    35         kx 		return;
    35         kx 	}
    35         kx 	switch (*fields[RF_NAME]) {
    35         kx 	  case '\0':
    35         kx 	  case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
    35         kx 	  case '+': case '-':
    35         kx 	  case '0': case '1': case '2': case '3': case '4':
    35         kx 	  case '5': case '6': case '7': case '8': case '9':
    35         kx 		error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
    35         kx 		return;
    35         kx 	}
    35         kx 	r.r_filenum = filenum;
    35         kx 	r.r_linenum = linenum;
    35         kx 	r.r_save = getsave(fields[RF_SAVE], &r.r_isdst);
    35         kx 	if (!rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR],
    35         kx 		     fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
    35         kx 		     fields[RF_TOD]))
    35         kx 	  return;
    35         kx 	r.r_name = estrdup(fields[RF_NAME]);
    35         kx 	r.r_abbrvar = estrdup(fields[RF_ABBRVAR]);
    35         kx 	if (max_abbrvar_len < strlen(r.r_abbrvar))
    35         kx 		max_abbrvar_len = strlen(r.r_abbrvar);
    35         kx 	rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
    35         kx 	rules[nrules++] = r;
    35         kx }
    35         kx 
    35         kx static bool
    35         kx inzone(char **fields, int nfields)
    35         kx {
    35         kx 	register ptrdiff_t i;
    35         kx 
    35         kx 	if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
    35         kx 		error(_("wrong number of fields on Zone line"));
    35         kx 		return false;
    35         kx 	}
    35         kx 	if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
    35         kx 		error(
    35         kx _("\"Zone %s\" line and -l option are mutually exclusive"),
    35         kx 			tzdefault);
    35         kx 		return false;
    35         kx 	}
    35         kx 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
    35         kx 		error(
    35         kx _("\"Zone %s\" line and -p option are mutually exclusive"),
    35         kx 			TZDEFRULES);
    35         kx 		return false;
    35         kx 	}
    35         kx 	for (i = 0; i < nzones; ++i)
    35         kx 		if (zones[i].z_name != NULL &&
    35         kx 			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
    35         kx 				error(_("duplicate zone name %s"
    35         kx 					" (file \"%s\", line %"PRIdMAX")"),
    35         kx 				      fields[ZF_NAME],
    35         kx 				      filename(zones[i].z_filenum),
    35         kx 				      zones[i].z_linenum);
    35         kx 				return false;
    35         kx 		}
    35         kx 	return inzsub(fields, nfields, false);
    35         kx }
    35         kx 
    35         kx static bool
    35         kx inzcont(char **fields, int nfields)
    35         kx {
    35         kx 	if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
    35         kx 		error(_("wrong number of fields on Zone continuation line"));
    35         kx 		return false;
    35         kx 	}
    35         kx 	return inzsub(fields, nfields, true);
    35         kx }
    35         kx 
    35         kx static bool
    35         kx inzsub(char **fields, int nfields, bool iscont)
    35         kx {
    35         kx 	register char *		cp;
    35         kx 	char *			cp1;
    35         kx 	struct zone z;
    35         kx 	int format_len;
    35         kx 	register int		i_stdoff, i_rule, i_format;
    35         kx 	register int		i_untilyear, i_untilmonth;
    35         kx 	register int		i_untilday, i_untiltime;
    35         kx 	register bool		hasuntil;
    35         kx 
    35         kx 	if (iscont) {
    35         kx 		i_stdoff = ZFC_STDOFF;
    35         kx 		i_rule = ZFC_RULE;
    35         kx 		i_format = ZFC_FORMAT;
    35         kx 		i_untilyear = ZFC_TILYEAR;
    35         kx 		i_untilmonth = ZFC_TILMONTH;
    35         kx 		i_untilday = ZFC_TILDAY;
    35         kx 		i_untiltime = ZFC_TILTIME;
    35         kx 	} else if (!namecheck(fields[ZF_NAME]))
    35         kx 		return false;
    35         kx 	else {
    35         kx 		i_stdoff = ZF_STDOFF;
    35         kx 		i_rule = ZF_RULE;
    35         kx 		i_format = ZF_FORMAT;
    35         kx 		i_untilyear = ZF_TILYEAR;
    35         kx 		i_untilmonth = ZF_TILMONTH;
    35         kx 		i_untilday = ZF_TILDAY;
    35         kx 		i_untiltime = ZF_TILTIME;
    35         kx 	}
    35         kx 	z.z_filenum = filenum;
    35         kx 	z.z_linenum = linenum;
    35         kx 	z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
    35         kx 	if ((cp = strchr(fields[i_format], '%')) != 0) {
    35         kx 		if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
    35         kx 		    || strchr(fields[i_format], '/')) {
    35         kx 			error(_("invalid abbreviation format"));
    35         kx 			return false;
    35         kx 		}
    35         kx 	}
    35         kx 	z.z_format_specifier = cp ? *cp : '\0';
    35         kx 	format_len = strlen(fields[i_format]);
    35         kx 	if (max_format_len < format_len)
    35         kx 	  max_format_len = format_len;
    35         kx 	hasuntil = nfields > i_untilyear;
    35         kx 	if (hasuntil) {
    35         kx 		z.z_untilrule.r_filenum = filenum;
    35         kx 		z.z_untilrule.r_linenum = linenum;
    35         kx 		if (!rulesub(
    35         kx 			&z.z_untilrule,
    35         kx 			fields[i_untilyear],
    35         kx 			"only",
    35         kx 			"",
    35         kx 			(nfields > i_untilmonth) ?
    35         kx 			fields[i_untilmonth] : "Jan",
    35         kx 			(nfields > i_untilday) ? fields[i_untilday] : "1",
    35         kx 			(nfields > i_untiltime) ? fields[i_untiltime] : "0"))
    35         kx 		  return false;
    35         kx 		z.z_untiltime = rpytime(&z.z_untilrule,
    35         kx 			z.z_untilrule.r_loyear);
    35         kx 		if (iscont && nzones > 0 &&
    35         kx 			z.z_untiltime > min_time &&
    35         kx 			z.z_untiltime < max_time &&
    35         kx 			zones[nzones - 1].z_untiltime > min_time &&
    35         kx 			zones[nzones - 1].z_untiltime < max_time &&
    35         kx 			zones[nzones - 1].z_untiltime >= z.z_untiltime) {
    35         kx 				error(_(
    35         kx "Zone continuation line end time is not after end time of previous line"
    35         kx 					));
    35         kx 				return false;
    35         kx 		}
    35         kx 	}
    35         kx 	z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]);
    35         kx 	z.z_rule = estrdup(fields[i_rule]);
    35         kx 	z.z_format = cp1 = estrdup(fields[i_format]);
    35         kx 	if (z.z_format_specifier == 'z') {
    35         kx 	  cp1[cp - fields[i_format]] = 's';
    35         kx 	  if (noise)
    35         kx 	    warning(_("format '%s' not handled by pre-2015 versions of zic"),
    35         kx 		    fields[i_format]);
    35         kx 	}
    35         kx 	zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
    35         kx 	zones[nzones++] = z;
    35         kx 	/*
    35         kx 	** If there was an UNTIL field on this line,
    35         kx 	** there's more information about the zone on the next line.
    35         kx 	*/
    35         kx 	return hasuntil;
    35         kx }
    35         kx 
    35         kx static zic_t
    35         kx getleapdatetime(char **fields, bool expire_line)
    35         kx {
    35         kx 	register const char *		cp;
    35         kx 	register const struct lookup *	lp;
    35         kx 	register zic_t			i, j;
    35         kx 	zic_t				year;
    35         kx 	int				month, day;
    35         kx 	zic_t				dayoff, tod;
    35         kx 	zic_t				t;
    35         kx 	char xs;
    35         kx 
    35         kx 	dayoff = 0;
    35         kx 	cp = fields[LP_YEAR];
    35         kx 	if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) {
    35         kx 		/*
    35         kx 		** Leapin' Lizards!
    35         kx 		*/
    35         kx 		error(_("invalid leaping year"));
    35         kx 		return -1;
    35         kx 	}
    35         kx 	if (!expire_line) {
    35         kx 	    if (!leapseen || leapmaxyear < year)
    35         kx 		leapmaxyear = year;
    35         kx 	    if (!leapseen || leapminyear > year)
    35         kx 		leapminyear = year;
    35         kx 	    leapseen = true;
    35         kx 	}
    35         kx 	j = EPOCH_YEAR;
    35         kx 	while (j != year) {
    35         kx 		if (year > j) {
    35         kx 			i = len_years[isleap(j)];
    35         kx 			++j;
    35         kx 		} else {
    35         kx 			--j;
    35         kx 			i = -len_years[isleap(j)];
    35         kx 		}
    35         kx 		dayoff = oadd(dayoff, i);
    35         kx 	}
    35         kx 	if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
    35         kx 		error(_("invalid month name"));
    35         kx 		return -1;
    35         kx 	}
    35         kx 	month = lp->l_value;
    35         kx 	j = TM_JANUARY;
    35         kx 	while (j != month) {
    35         kx 		i = len_months[isleap(year)][j];
    35         kx 		dayoff = oadd(dayoff, i);
    35         kx 		++j;
    35         kx 	}
    35         kx 	cp = fields[LP_DAY];
    35         kx 	if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
    35         kx 		day <= 0 || day > len_months[isleap(year)][month]) {
    35         kx 			error(_("invalid day of month"));
    35         kx 			return -1;
    35         kx 	}
    35         kx 	dayoff = oadd(dayoff, day - 1);
    35         kx 	if (dayoff < min_time / SECSPERDAY) {
    35         kx 		error(_("time too small"));
    35         kx 		return -1;
    35         kx 	}
    35         kx 	if (dayoff > max_time / SECSPERDAY) {
    35         kx 		error(_("time too large"));
    35         kx 		return -1;
    35         kx 	}
    35         kx 	t = dayoff * SECSPERDAY;
    35         kx 	tod = gethms(fields[LP_TIME], _("invalid time of day"));
    35         kx 	t = tadd(t, tod);
    35         kx 	if (t < 0)
    35         kx 	  error(_("leap second precedes Epoch"));
    35         kx 	return t;
    35         kx }
    35         kx 
    35         kx static void
    35         kx inleap(char **fields, int nfields)
    35         kx {
    35         kx   if (nfields != LEAP_FIELDS)
    35         kx     error(_("wrong number of fields on Leap line"));
    35         kx   else {
    35         kx     zic_t t = getleapdatetime(fields, false);
    35         kx     if (0 <= t) {
    35         kx       struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
    35         kx       if (!lp)
    35         kx 	error(_("invalid Rolling/Stationary field on Leap line"));
    35         kx       else {
    35         kx 	int correction = 0;
    35         kx 	if (!fields[LP_CORR][0]) /* infile() turns "-" into "".  */
    35         kx 	  correction = -1;
    35         kx 	else if (strcmp(fields[LP_CORR], "+") == 0)
    35         kx 	  correction = 1;
    35         kx 	else
    35         kx 	  error(_("invalid CORRECTION field on Leap line"));
    35         kx 	if (correction)
    35         kx 	  leapadd(t, correction, lp->l_value);
    35         kx       }
    35         kx     }
    35         kx   }
    35         kx }
    35         kx 
    35         kx static void
    35         kx inexpires(char **fields, int nfields)
    35         kx {
    35         kx   if (nfields != EXPIRES_FIELDS)
    35         kx     error(_("wrong number of fields on Expires line"));
    35         kx   else if (0 <= leapexpires)
    35         kx     error(_("multiple Expires lines"));
    35         kx   else
    35         kx     leapexpires = getleapdatetime(fields, true);
    35         kx }
    35         kx 
    35         kx static void
    35         kx inlink(char **fields, int nfields)
    35         kx {
    35         kx 	struct link	l;
    35         kx 
    35         kx 	if (nfields != LINK_FIELDS) {
    35         kx 		error(_("wrong number of fields on Link line"));
    35         kx 		return;
    35         kx 	}
    35         kx 	if (*fields[LF_TARGET] == '\0') {
    35         kx 		error(_("blank TARGET field on Link line"));
    35         kx 		return;
    35         kx 	}
    35         kx 	if (! namecheck(fields[LF_LINKNAME]))
    35         kx 	  return;
    35         kx 	l.l_filenum = filenum;
    35         kx 	l.l_linenum = linenum;
    35         kx 	l.l_target = estrdup(fields[LF_TARGET]);
    35         kx 	l.l_linkname = estrdup(fields[LF_LINKNAME]);
    35         kx 	links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
    35         kx 	links[nlinks++] = l;
    35         kx }
    35         kx 
    35         kx static bool
    35         kx rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
    35         kx 	const char *typep, const char *monthp, const char *dayp,
    35         kx 	const char *timep)
    35         kx {
    35         kx 	register const struct lookup *	lp;
    35         kx 	register const char *		cp;
    35         kx 	register char *			dp;
    35         kx 	register char *			ep;
    35         kx 	char xs;
    35         kx 
    35         kx 	if ((lp = byword(monthp, mon_names)) == NULL) {
    35         kx 		error(_("invalid month name"));
    35         kx 		return false;
    35         kx 	}
    35         kx 	rp->r_month = lp->l_value;
    35         kx 	rp->r_todisstd = false;
    35         kx 	rp->r_todisut = false;
    35         kx 	dp = estrdup(timep);
    35         kx 	if (*dp != '\0') {
    35         kx 		ep = dp + strlen(dp) - 1;
    35         kx 		switch (lowerit(*ep)) {
    35         kx 			case 's':	/* Standard */
    35         kx 				rp->r_todisstd = true;
    35         kx 				rp->r_todisut = false;
    35         kx 				*ep = '\0';
    35         kx 				break;
    35         kx 			case 'w':	/* Wall */
    35         kx 				rp->r_todisstd = false;
    35         kx 				rp->r_todisut = false;
    35         kx 				*ep = '\0';
    35         kx 				break;
    35         kx 			case 'g':	/* Greenwich */
    35         kx 			case 'u':	/* Universal */
    35         kx 			case 'z':	/* Zulu */
    35         kx 				rp->r_todisstd = true;
    35         kx 				rp->r_todisut = true;
    35         kx 				*ep = '\0';
    35         kx 				break;
    35         kx 		}
    35         kx 	}
    35         kx 	rp->r_tod = gethms(dp, _("invalid time of day"));
    35         kx 	free(dp);
    35         kx 	/*
    35         kx 	** Year work.
    35         kx 	*/
    35         kx 	cp = loyearp;
    35         kx 	lp = byword(cp, begin_years);
    35         kx 	rp->r_lowasnum = lp == NULL;
    35         kx 	if (!rp->r_lowasnum) switch (lp->l_value) {
    35         kx 		case YR_MINIMUM:
    35         kx 			rp->r_loyear = ZIC_MIN;
    35         kx 			break;
    35         kx 		case YR_MAXIMUM:
    35         kx 			rp->r_loyear = ZIC_MAX;
    35         kx 			break;
    35         kx 		default: unreachable();
    35         kx 	} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
    35         kx 		error(_("invalid starting year"));
    35         kx 		return false;
    35         kx 	}
    35         kx 	cp = hiyearp;
    35         kx 	lp = byword(cp, end_years);
    35         kx 	rp->r_hiwasnum = lp == NULL;
    35         kx 	if (!rp->r_hiwasnum) switch (lp->l_value) {
    35         kx 		case YR_MINIMUM:
    35         kx 			rp->r_hiyear = ZIC_MIN;
    35         kx 			break;
    35         kx 		case YR_MAXIMUM:
    35         kx 			rp->r_hiyear = ZIC_MAX;
    35         kx 			break;
    35         kx 		case YR_ONLY:
    35         kx 			rp->r_hiyear = rp->r_loyear;
    35         kx 			break;
    35         kx 		default: unreachable();
    35         kx 	} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
    35         kx 		error(_("invalid ending year"));
    35         kx 		return false;
    35         kx 	}
    35         kx 	if (rp->r_loyear > rp->r_hiyear) {
    35         kx 		error(_("starting year greater than ending year"));
    35         kx 		return false;
    35         kx 	}
    35         kx 	if (*typep != '\0') {
    35         kx 		error(_("year type \"%s\" is unsupported; use \"-\" instead"),
    35         kx 			typep);
    35         kx 		return false;
    35         kx 	}
    35         kx 	/*
    35         kx 	** Day work.
    35         kx 	** Accept things such as:
    35         kx 	**	1
    35         kx 	**	lastSunday
    35         kx 	**	last-Sunday (undocumented; warn about this)
    35         kx 	**	Sun<=20
    35         kx 	**	Sun>=7
    35         kx 	*/
    35         kx 	dp = estrdup(dayp);
    35         kx 	if ((lp = byword(dp, lasts)) != NULL) {
    35         kx 		rp->r_dycode = DC_DOWLEQ;
    35         kx 		rp->r_wday = lp->l_value;
    35         kx 		rp->r_dayofmonth = len_months[1][rp->r_month];
    35         kx 	} else {
    35         kx 		if ((ep = strchr(dp, '<')) != 0)
    35         kx 			rp->r_dycode = DC_DOWLEQ;
    35         kx 		else if ((ep = strchr(dp, '>')) != 0)
    35         kx 			rp->r_dycode = DC_DOWGEQ;
    35         kx 		else {
    35         kx 			ep = dp;
    35         kx 			rp->r_dycode = DC_DOM;
    35         kx 		}
    35         kx 		if (rp->r_dycode != DC_DOM) {
    35         kx 			*ep++ = 0;
    35         kx 			if (*ep++ != '=') {
    35         kx 				error(_("invalid day of month"));
    35         kx 				free(dp);
    35         kx 				return false;
    35         kx 			}
    35         kx 			if ((lp = byword(dp, wday_names)) == NULL) {
    35         kx 				error(_("invalid weekday name"));
    35         kx 				free(dp);
    35         kx 				return false;
    35         kx 			}
    35         kx 			rp->r_wday = lp->l_value;
    35         kx 		}
    35         kx 		if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
    35         kx 			rp->r_dayofmonth <= 0 ||
    35         kx 			(rp->r_dayofmonth > len_months[1][rp->r_month])) {
    35         kx 				error(_("invalid day of month"));
    35         kx 				free(dp);
    35         kx 				return false;
    35         kx 		}
    35         kx 	}
    35         kx 	free(dp);
    35         kx 	return true;
    35         kx }
    35         kx 
    35         kx static void
    35         kx convert(uint_fast32_t val, char *buf)
    35         kx {
    35         kx 	register int	i;
    35         kx 	register int	shift;
    35         kx 	unsigned char *const b = (unsigned char *) buf;
    35         kx 
    35         kx 	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
    35         kx 	  b[i] = (val >> shift) & 0xff;
    35         kx }
    35         kx 
    35         kx static void
    35         kx convert64(uint_fast64_t val, char *buf)
    35         kx {
    35         kx 	register int	i;
    35         kx 	register int	shift;
    35         kx 	unsigned char *const b = (unsigned char *) buf;
    35         kx 
    35         kx 	for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
    35         kx 	  b[i] = (val >> shift) & 0xff;
    35         kx }
    35         kx 
    35         kx static void
    35         kx puttzcode(zic_t val, FILE *fp)
    35         kx {
    35         kx 	char	buf[4];
    35         kx 
    35         kx 	convert(val, buf);
    35         kx 	fwrite(buf, sizeof buf, 1, fp);
    35         kx }
    35         kx 
    35         kx static void
    35         kx puttzcodepass(zic_t val, FILE *fp, int pass)
    35         kx {
    35         kx   if (pass == 1)
    35         kx     puttzcode(val, fp);
    35         kx   else {
    35         kx 	char	buf[8];
    35         kx 
    35         kx 	convert64(val, buf);
    35         kx 	fwrite(buf, sizeof buf, 1, fp);
    35         kx   }
    35         kx }
    35         kx 
    35         kx static int
    35         kx atcomp(const void *avp, const void *bvp)
    35         kx {
    35         kx   struct attype const *ap = avp, *bp = bvp;
    35         kx   zic_t a = ap->at, b = bp->at;
    35         kx   return a < b ? -1 : a > b;
    35         kx }
    35         kx 
    35         kx struct timerange {
    35         kx   int defaulttype;
    35         kx   ptrdiff_t base, count;
    35         kx   int leapbase, leapcount;
    35         kx   bool leapexpiry;
    35         kx };
    35         kx 
    35         kx static struct timerange
    35         kx limitrange(struct timerange r, zic_t lo, zic_t hi,
    35         kx 	   zic_t const *ats, unsigned char const *types)
    35         kx {
    35         kx   /* Omit ordinary transitions < LO.  */
    35         kx   while (0 < r.count && ats[r.base] < lo) {
    35         kx     r.defaulttype = types[r.base];
    35         kx     r.count--;
    35         kx     r.base++;
    35         kx   }
    35         kx 
    35         kx   /* Omit as many initial leap seconds as possible, such that the
    35         kx      first leap second in the truncated list is <= LO, and is a
    35         kx      positive leap second if and only if it has a positive correction.
    35         kx      This supports common TZif readers that assume that the first leap
    35         kx      second is positive if and only if its correction is positive.  */
    35         kx   while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) {
    35         kx     r.leapcount--;
    35         kx     r.leapbase++;
    35         kx   }
    35         kx   while (0 < r.leapbase
    35         kx 	 && ((corr[r.leapbase - 1] < corr[r.leapbase])
    35         kx 	     != (0 < corr[r.leapbase]))) {
    35         kx     r.leapcount++;
    35         kx     r.leapbase--;
    35         kx   }
    35         kx 
    35         kx 
    35         kx   /* Omit ordinary and leap second transitions greater than HI + 1.  */
    35         kx   if (hi < max_time) {
    35         kx     while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
    35         kx       r.count--;
    35         kx     while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
    35         kx       r.leapcount--;
    35         kx   }
    35         kx 
    35         kx   /* Determine whether to append an expiration to the leap second table.  */
    35         kx   r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
    35         kx 
    35         kx   return r;
    35         kx }
    35         kx 
    35         kx static void
    35         kx writezone(const char *const name, const char *const string, char version,
    35         kx 	  int defaulttype)
    35         kx {
    35         kx 	register FILE *			fp;
    35         kx 	register ptrdiff_t		i, j;
    35         kx 	register int			pass;
    35         kx 	char *tempname = NULL;
    35         kx 	char const *outname = name;
    35         kx 
    35         kx 	/* Allocate the ATS and TYPES arrays via a single malloc,
    35         kx 	   as this is a bit faster.  Do not malloc(0) if !timecnt,
    35         kx 	   as that might return NULL even on success.  */
    35         kx 	zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt,
    35         kx 						   sizeof *ats + 1),
    35         kx 				      alignof(zic_t)));
    35         kx 	void *typesptr = ats + timecnt;
    35         kx 	unsigned char *types = typesptr;
    35         kx 	struct timerange rangeall = {0}, range32, range64;
    35         kx 
    35         kx 	/*
    35         kx 	** Sort.
    35         kx 	*/
    35         kx 	if (timecnt > 1)
    35         kx 		qsort(attypes, timecnt, sizeof *attypes, atcomp);
    35         kx 	/*
    35         kx 	** Optimize.
    35         kx 	*/
    35         kx 	{
    35         kx 		ptrdiff_t fromi, toi;
    35         kx 
    35         kx 		toi = 0;
    35         kx 		fromi = 0;
    35         kx 		for ( ; fromi < timecnt; ++fromi) {
    35         kx 			if (toi != 0
    35         kx 			    && ((attypes[fromi].at
    35         kx 				 + utoffs[attypes[toi - 1].type])
    35         kx 				<= (attypes[toi - 1].at
    35         kx 				    + utoffs[toi == 1 ? 0
    35         kx 					     : attypes[toi - 2].type]))) {
    35         kx 					attypes[toi - 1].type =
    35         kx 						attypes[fromi].type;
    35         kx 					continue;
    35         kx 			}
    35         kx 			if (toi == 0
    35         kx 			    || attypes[fromi].dontmerge
    35         kx 			    || (utoffs[attypes[toi - 1].type]
    35         kx 				!= utoffs[attypes[fromi].type])
    35         kx 			    || (isdsts[attypes[toi - 1].type]
    35         kx 				!= isdsts[attypes[fromi].type])
    35         kx 			    || (desigidx[attypes[toi - 1].type]
    35         kx 				!= desigidx[attypes[fromi].type]))
    35         kx 					attypes[toi++] = attypes[fromi];
    35         kx 		}
    35         kx 		timecnt = toi;
    35         kx 	}
    35         kx 
    35         kx 	if (noise && timecnt > 1200) {
    35         kx 	  if (timecnt > TZ_MAX_TIMES)
    35         kx 		warning(_("reference clients mishandle"
    35         kx 			  " more than %d transition times"),
    35         kx 			TZ_MAX_TIMES);
    35         kx 	  else
    35         kx 		warning(_("pre-2014 clients may mishandle"
    35         kx 			  " more than 1200 transition times"));
    35         kx 	}
    35         kx 	/*
    35         kx 	** Transfer.
    35         kx 	*/
    35         kx 	for (i = 0; i < timecnt; ++i) {
    35         kx 		ats[i] = attypes[i].at;
    35         kx 		types[i] = attypes[i].type;
    35         kx 	}
    35         kx 
    35         kx 	/*
    35         kx 	** Correct for leap seconds.
    35         kx 	*/
    35         kx 	for (i = 0; i < timecnt; ++i) {
    35         kx 		j = leapcnt;
    35         kx 		while (--j >= 0)
    35         kx 			if (ats[i] > trans[j] - corr[j]) {
    35         kx 				ats[i] = tadd(ats[i], corr[j]);
    35         kx 				break;
    35         kx 			}
    35         kx 	}
    35         kx 
    35         kx 	rangeall.defaulttype = defaulttype;
    35         kx 	rangeall.count = timecnt;
    35         kx 	rangeall.leapcount = leapcnt;
    35         kx 	range64 = limitrange(rangeall, lo_time,
    35         kx 			     max(hi_time,
    35         kx 				 redundant_time - (ZIC_MIN < redundant_time)),
    35         kx 			     ats, types);
    35         kx 	range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
    35         kx 
    35         kx 	/* TZif version 4 is needed if a no-op transition is appended to
    35         kx 	   indicate the expiration of the leap second table, or if the first
    35         kx 	   leap second transition is not to a +1 or -1 correction.  */
    35         kx 	for (pass = 1; pass <= 2; pass++) {
    35         kx 	  struct timerange const *r = pass == 1 ? &range32 : &range64;
    35         kx 	  if (pass == 1 && !want_bloat())
    35         kx 	    continue;
    35         kx 	  if (r->leapexpiry) {
    35         kx 	    if (noise)
    35         kx 	      warning(_("%s: pre-2021b clients may mishandle"
    35         kx 			" leap second expiry"),
    35         kx 		      name);
    35         kx 	    version = '4';
    35         kx 	  }
    35         kx 	  if (0 < r->leapcount
    35         kx 	      && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) {
    35         kx 	    if (noise)
    35         kx 	      warning(_("%s: pre-2021b clients may mishandle"
    35         kx 			" leap second table truncation"),
    35         kx 		      name);
    35         kx 	    version = '4';
    35         kx 	  }
    35         kx 	  if (version == '4')
    35         kx 	    break;
    35         kx 	}
    35         kx 
    35         kx 	fp = open_outfile(&outname, &tempname);
    35         kx 
    35         kx 	for (pass = 1; pass <= 2; ++pass) {
    35         kx 		register ptrdiff_t thistimei, thistimecnt, thistimelim;
    35         kx 		register int	thisleapi, thisleapcnt, thisleaplim;
    35         kx 		struct tzhead tzh;
    35         kx 		int pretranstype = -1, thisdefaulttype;
    35         kx 		bool locut, hicut, thisleapexpiry;
    35         kx 		zic_t lo, thismin, thismax;
    35         kx 		int old0;
    35         kx 		char		omittype[TZ_MAX_TYPES];
    35         kx 		int		typemap[TZ_MAX_TYPES];
    35         kx 		int		thistypecnt, stdcnt, utcnt;
    35         kx 		char		thischars[TZ_MAX_CHARS];
    35         kx 		int		thischarcnt;
    35         kx 		bool		toomanytimes;
    35         kx 		int		indmap[TZ_MAX_CHARS];
    35         kx 
    35         kx 		if (pass == 1) {
    35         kx 			thisdefaulttype = range32.defaulttype;
    35         kx 			thistimei = range32.base;
    35         kx 			thistimecnt = range32.count;
    35         kx 			toomanytimes = thistimecnt >> 31 >> 1 != 0;
    35         kx 			thisleapi = range32.leapbase;
    35         kx 			thisleapcnt = range32.leapcount;
    35         kx 			thisleapexpiry = range32.leapexpiry;
    35         kx 			thismin = ZIC32_MIN;
    35         kx 			thismax = ZIC32_MAX;
    35         kx 		} else {
    35         kx 			thisdefaulttype = range64.defaulttype;
    35         kx 			thistimei = range64.base;
    35         kx 			thistimecnt = range64.count;
    35         kx 			toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
    35         kx 			thisleapi = range64.leapbase;
    35         kx 			thisleapcnt = range64.leapcount;
    35         kx 			thisleapexpiry = range64.leapexpiry;
    35         kx 			thismin = min_time;
    35         kx 			thismax = max_time;
    35         kx 		}
    35         kx 		if (toomanytimes)
    35         kx 		  error(_("too many transition times"));
    35         kx 
    35         kx 		locut = thismin < lo_time && lo_time <= thismax;
    35         kx 		hicut = thismin <= hi_time && hi_time < thismax;
    35         kx 		thistimelim = thistimei + thistimecnt;
    35         kx 		memset(omittype, true, typecnt);
    35         kx 
    35         kx 		/* Determine whether to output a transition before the first
    35         kx 		   transition in range.  This is needed when the output is
    35         kx 		   truncated at the start, and is also useful when catering to
    35         kx 		   buggy 32-bit clients that do not use time type 0 for
    35         kx 		   timestamps before the first transition.  */
    35         kx 		if ((locut || (pass == 1 && thistimei))
    35         kx 		    && ! (thistimecnt && ats[thistimei] == lo_time)) {
    35         kx 		  pretranstype = thisdefaulttype;
    35         kx 		  omittype[pretranstype] = false;
    35         kx 		}
    35         kx 
    35         kx 		/* Arguably the default time type in the 32-bit data
    35         kx 		   should be range32.defaulttype, which is suited for
    35         kx 		   timestamps just before ZIC32_MIN.  However, zic
    35         kx 		   traditionally used the time type of the indefinite
    35         kx 		   past instead.  Internet RFC 8532 says readers should
    35         kx 		   ignore 32-bit data, so this discrepancy matters only
    35         kx 		   to obsolete readers where the traditional type might
    35         kx 		   be more appropriate even if it's "wrong".  So, use
    35         kx 		   the historical zic value, unless -r specifies a low
    35         kx 		   cutoff that excludes some 32-bit timestamps.  */
    35         kx 		if (pass == 1 && lo_time <= thismin)
    35         kx 		  thisdefaulttype = range64.defaulttype;
    35         kx 
    35         kx 		if (locut)
    35         kx 		  thisdefaulttype = unspecifiedtype;
    35         kx 		omittype[thisdefaulttype] = false;
    35         kx 		for (i = thistimei; i < thistimelim; i++)
    35         kx 		  omittype[types[i]] = false;
    35         kx 		if (hicut)
    35         kx 		  omittype[unspecifiedtype] = false;
    35         kx 
    35         kx 		/* Reorder types to make THISDEFAULTTYPE type 0.
    35         kx 		   Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that
    35         kx 		   THISDEFAULTTYPE appears as type 0 in the output instead
    35         kx 		   of OLD0.  TYPEMAP also omits unused types.  */
    35         kx 		old0 = strlen(omittype);
    35         kx 
    35         kx #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
    35         kx 		/*
    35         kx 		** For some pre-2011 systems: if the last-to-be-written
    35         kx 		** standard (or daylight) type has an offset different from the
    35         kx 		** most recently used offset,
    35         kx 		** append an (unused) copy of the most recently used type
    35         kx 		** (to help get global "altzone" and "timezone" variables
    35         kx 		** set correctly).
    35         kx 		*/
    35         kx 		if (want_bloat()) {
    35         kx 			register int	mrudst, mrustd, hidst, histd, type;
    35         kx 
    35         kx 			hidst = histd = mrudst = mrustd = -1;
    35         kx 			if (0 <= pretranstype) {
    35         kx 			  if (isdsts[pretranstype])
    35         kx 			    mrudst = pretranstype;
    35         kx 			  else
    35         kx 			    mrustd = pretranstype;
    35         kx 			}
    35         kx 			for (i = thistimei; i < thistimelim; i++)
    35         kx 				if (isdsts[types[i]])
    35         kx 					mrudst = types[i];
    35         kx 				else	mrustd = types[i];
    35         kx 			for (i = old0; i < typecnt; i++) {
    35         kx 			  int h = (i == old0 ? thisdefaulttype
    35         kx 				   : i == thisdefaulttype ? old0 : i);
    35         kx 			  if (!omittype[h]) {
    35         kx 			    if (isdsts[h])
    35         kx 			      hidst = i;
    35         kx 			    else
    35         kx 			      histd = i;
    35         kx 			  }
    35         kx 			}
    35         kx 			if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
    35         kx 				utoffs[hidst] != utoffs[mrudst]) {
    35         kx 					isdsts[mrudst] = -1;
    35         kx 					type = addtype(utoffs[mrudst],
    35         kx 						&chars[desigidx[mrudst]],
    35         kx 						true,
    35         kx 						ttisstds[mrudst],
    35         kx 						ttisuts[mrudst]);
    35         kx 					isdsts[mrudst] = 1;
    35         kx 					omittype[type] = false;
    35         kx 			}
    35         kx 			if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
    35         kx 				utoffs[histd] != utoffs[mrustd]) {
    35         kx 					isdsts[mrustd] = -1;
    35         kx 					type = addtype(utoffs[mrustd],
    35         kx 						&chars[desigidx[mrustd]],
    35         kx 						false,
    35         kx 						ttisstds[mrustd],
    35         kx 						ttisuts[mrustd]);
    35         kx 					isdsts[mrustd] = 0;
    35         kx 					omittype[type] = false;
    35         kx 			}
    35         kx 		}
    35         kx #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
    35         kx 		thistypecnt = 0;
    35         kx 		for (i = old0; i < typecnt; i++)
    35         kx 		  if (!omittype[i])
    35         kx 		    typemap[i == old0 ? thisdefaulttype
    35         kx 			    : i == thisdefaulttype ? old0 : i]
    35         kx 		      = thistypecnt++;
    35         kx 
    35         kx 		for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
    35         kx 			indmap[i] = -1;
    35         kx 		thischarcnt = stdcnt = utcnt = 0;
    35         kx 		for (i = old0; i < typecnt; i++) {
    35         kx 			register char *	thisabbr;
    35         kx 
    35         kx 			if (omittype[i])
    35         kx 				continue;
    35         kx 			if (ttisstds[i])
    35         kx 			  stdcnt = thistypecnt;
    35         kx 			if (ttisuts[i])
    35         kx 			  utcnt = thistypecnt;
    35         kx 			if (indmap[desigidx[i]] >= 0)
    35         kx 				continue;
    35         kx 			thisabbr = &chars[desigidx[i]];
    35         kx 			for (j = 0; j < thischarcnt; ++j)
    35         kx 				if (strcmp(&thischars[j], thisabbr) == 0)
    35         kx 					break;
    35         kx 			if (j == thischarcnt) {
    35         kx 				strcpy(&thischars[thischarcnt], thisabbr);
    35         kx 				thischarcnt += strlen(thisabbr) + 1;
    35         kx 			}
    35         kx 			indmap[desigidx[i]] = j;
    35         kx 		}
    35         kx 		if (pass == 1 && !want_bloat()) {
    35         kx 		  hicut = thisleapexpiry = false;
    35         kx 		  pretranstype = -1;
    35         kx 		  thistimecnt = thisleapcnt = 0;
    35         kx 		  thistypecnt = thischarcnt = 1;
    35         kx 		}
    35         kx #define DO(field)	fwrite(tzh.field, sizeof tzh.field, 1, fp)
    35         kx 		memset(&tzh, 0, sizeof tzh);
    35         kx 		memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
    35         kx 		tzh.tzh_version[0] = version;
    35         kx 		convert(utcnt, tzh.tzh_ttisutcnt);
    35         kx 		convert(stdcnt, tzh.tzh_ttisstdcnt);
    35         kx 		convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
    35         kx 		convert((0 <= pretranstype) + thistimecnt + hicut,
    35         kx 			tzh.tzh_timecnt);
    35         kx 		convert(thistypecnt, tzh.tzh_typecnt);
    35         kx 		convert(thischarcnt, tzh.tzh_charcnt);
    35         kx 		DO(tzh_magic);
    35         kx 		DO(tzh_version);
    35         kx 		DO(tzh_reserved);
    35         kx 		DO(tzh_ttisutcnt);
    35         kx 		DO(tzh_ttisstdcnt);
    35         kx 		DO(tzh_leapcnt);
    35         kx 		DO(tzh_timecnt);
    35         kx 		DO(tzh_typecnt);
    35         kx 		DO(tzh_charcnt);
    35         kx #undef DO
    35         kx 		if (pass == 1 && !want_bloat()) {
    35         kx 		  /* Output a minimal data block with just one time type.  */
    35         kx 		  puttzcode(0, fp);	/* utoff */
    35         kx 		  putc(0, fp);		/* dst */
    35         kx 		  putc(0, fp);		/* index of abbreviation */
    35         kx 		  putc(0, fp);		/* empty-string abbreviation */
    35         kx 		  continue;
    35         kx 		}
    35         kx 
    35         kx 		/* Output a LO_TIME transition if needed; see limitrange.
    35         kx 		   But do not go below the minimum representable value
    35         kx 		   for this pass.  */
    35         kx 		lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
    35         kx 
    35         kx 		if (0 <= pretranstype)
    35         kx 		  puttzcodepass(lo, fp, pass);
    35         kx 		for (i = thistimei; i < thistimelim; ++i) {
    35         kx 		  puttzcodepass(ats[i], fp, pass);
    35         kx 		}
    35         kx 		if (hicut)
    35         kx 		  puttzcodepass(hi_time + 1, fp, pass);
    35         kx 		if (0 <= pretranstype)
    35         kx 		  putc(typemap[pretranstype], fp);
    35         kx 		for (i = thistimei; i < thistimelim; i++)
    35         kx 		  putc(typemap[types[i]], fp);
    35         kx 		if (hicut)
    35         kx 		  putc(typemap[unspecifiedtype], fp);
    35         kx 
    35         kx 		for (i = old0; i < typecnt; i++) {
    35         kx 		  int h = (i == old0 ? thisdefaulttype
    35         kx 			   : i == thisdefaulttype ? old0 : i);
    35         kx 		  if (!omittype[h]) {
    35         kx 		    puttzcode(utoffs[h], fp);
    35         kx 		    putc(isdsts[h], fp);
    35         kx 		    putc(indmap[desigidx[h]], fp);
    35         kx 		  }
    35         kx 		}
    35         kx 		if (thischarcnt != 0)
    35         kx 			fwrite(thischars, sizeof thischars[0],
    35         kx 				      thischarcnt, fp);
    35         kx 		thisleaplim = thisleapi + thisleapcnt;
    35         kx 		for (i = thisleapi; i < thisleaplim; ++i) {
    35         kx 			register zic_t	todo;
    35         kx 
    35         kx 			if (roll[i]) {
    35         kx 				if (timecnt == 0 || trans[i] < ats[0]) {
    35         kx 					j = 0;
    35         kx 					while (isdsts[j])
    35         kx 						if (++j >= typecnt) {
    35         kx 							j = 0;
    35         kx 							break;
    35         kx 						}
    35         kx 				} else {
    35         kx 					j = 1;
    35         kx 					while (j < timecnt &&
    35         kx 						trans[i] >= ats[j])
    35         kx 							++j;
    35         kx 					j = types[j - 1];
    35         kx 				}
    35         kx 				todo = tadd(trans[i], -utoffs[j]);
    35         kx 			} else	todo = trans[i];
    35         kx 			puttzcodepass(todo, fp, pass);
    35         kx 			puttzcode(corr[i], fp);
    35         kx 		}
    35         kx 		if (thisleapexpiry) {
    35         kx 		  /* Append a no-op leap correction indicating when the leap
    35         kx 		     second table expires.  Although this does not conform to
    35         kx 		     Internet RFC 8536, most clients seem to accept this and
    35         kx 		     the plan is to amend the RFC to allow this in version 4
    35         kx 		     TZif files.  */
    35         kx 		  puttzcodepass(leapexpires, fp, pass);
    35         kx 		  puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp);
    35         kx 		}
    35         kx 		if (stdcnt != 0)
    35         kx 		  for (i = old0; i < typecnt; i++)
    35         kx 			if (!omittype[i])
    35         kx 				putc(ttisstds[i], fp);
    35         kx 		if (utcnt != 0)
    35         kx 		  for (i = old0; i < typecnt; i++)
    35         kx 			if (!omittype[i])
    35         kx 				putc(ttisuts[i], fp);
    35         kx 	}
    35         kx 	fprintf(fp, "\n%s\n", string);
    35         kx 	close_file(fp, directory, name, tempname);
    35         kx 	rename_dest(tempname, name);
    35         kx 	free(ats);
    35         kx }
    35         kx 
    35         kx static char const *
    35         kx abbroffset(char *buf, zic_t offset)
    35         kx {
    35         kx   char sign = '+';
    35         kx   int seconds, minutes;
    35         kx 
    35         kx   if (offset < 0) {
    35         kx     offset = -offset;
    35         kx     sign = '-';
    35         kx   }
    35         kx 
    35         kx   seconds = offset % SECSPERMIN;
    35         kx   offset /= SECSPERMIN;
    35         kx   minutes = offset % MINSPERHOUR;
    35         kx   offset /= MINSPERHOUR;
    35         kx   if (100 <= offset) {
    35         kx     error(_("%%z UT offset magnitude exceeds 99:59:59"));
    35         kx     return "%z";
    35         kx   } else {
    35         kx     char *p = buf;
    35         kx     *p++ = sign;
    35         kx     *p++ = '0' + offset / 10;
    35         kx     *p++ = '0' + offset % 10;
    35         kx     if (minutes | seconds) {
    35         kx       *p++ = '0' + minutes / 10;
    35         kx       *p++ = '0' + minutes % 10;
    35         kx       if (seconds) {
    35         kx 	*p++ = '0' + seconds / 10;
    35         kx 	*p++ = '0' + seconds % 10;
    35         kx       }
    35         kx     }
    35         kx     *p = '\0';
    35         kx     return buf;
    35         kx   }
    35         kx }
    35         kx 
    35         kx static char const disable_percent_s[] = "";
    35         kx 
    35         kx static ptrdiff_t
    35         kx doabbr(char *abbr, struct zone const *zp, char const *letters,
    35         kx        bool isdst, zic_t save, bool doquotes)
    35         kx {
    35         kx 	register char *	cp;
    35         kx 	register char *	slashp;
    35         kx 	ptrdiff_t len;
    35         kx 	char const *format = zp->z_format;
    35         kx 
    35         kx 	slashp = strchr(format, '/');
    35         kx 	if (slashp == NULL) {
    35         kx 	  char letterbuf[PERCENT_Z_LEN_BOUND + 1];
    35         kx 	  if (zp->z_format_specifier == 'z')
    35         kx 	    letters = abbroffset(letterbuf, zp->z_stdoff + save);
    35         kx 	  else if (!letters)
    35         kx 	    letters = "%s";
    35         kx 	  else if (letters == disable_percent_s)
    35         kx 	    return 0;
    35         kx 	  sprintf(abbr, format, letters);
    35         kx 	} else if (isdst) {
    35         kx 		strcpy(abbr, slashp + 1);
    35         kx 	} else {
    35         kx 		memcpy(abbr, format, slashp - format);
    35         kx 		abbr[slashp - format] = '\0';
    35         kx 	}
    35         kx 	len = strlen(abbr);
    35         kx 	if (!doquotes)
    35         kx 		return len;
    35         kx 	for (cp = abbr; is_alpha(*cp); cp++)
    35         kx 		continue;
    35         kx 	if (len > 0 && *cp == '\0')
    35         kx 		return len;
    35         kx 	abbr[len + 2] = '\0';
    35         kx 	abbr[len + 1] = '>';
    35         kx 	memmove(abbr + 1, abbr, len);
    35         kx 	abbr[0] = '<';
    35         kx 	return len + 2;
    35         kx }
    35         kx 
    35         kx static void
    35         kx updateminmax(const zic_t x)
    35         kx {
    35         kx 	if (min_year > x)
    35         kx 		min_year = x;
    35         kx 	if (max_year < x)
    35         kx 		max_year = x;
    35         kx }
    35         kx 
    35         kx static int
    35         kx stringoffset(char *result, zic_t offset)
    35         kx {
    35         kx 	register int	hours;
    35         kx 	register int	minutes;
    35         kx 	register int	seconds;
    35         kx 	bool negative = offset < 0;
    35         kx 	int len = negative;
    35         kx 
    35         kx 	if (negative) {
    35         kx 		offset = -offset;
    35         kx 		result[0] = '-';
    35         kx 	}
    35         kx 	seconds = offset % SECSPERMIN;
    35         kx 	offset /= SECSPERMIN;
    35         kx 	minutes = offset % MINSPERHOUR;
    35         kx 	offset /= MINSPERHOUR;
    35         kx 	hours = offset;
    35         kx 	if (hours >= HOURSPERDAY * DAYSPERWEEK) {
    35         kx 		result[0] = '\0';
    35         kx 		return 0;
    35         kx 	}
    35         kx 	len += sprintf(result + len, "%d", hours);
    35         kx 	if (minutes != 0 || seconds != 0) {
    35         kx 		len += sprintf(result + len, ":%02d", minutes);
    35         kx 		if (seconds != 0)
    35         kx 			len += sprintf(result + len, ":%02d", seconds);
    35         kx 	}
    35         kx 	return len;
    35         kx }
    35         kx 
    35         kx static int
    35         kx stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff)
    35         kx {
    35         kx 	register zic_t	tod = rp->r_tod;
    35         kx 	register int	compat = 0;
    35         kx 
    35         kx 	if (rp->r_dycode == DC_DOM) {
    35         kx 		register int	month, total;
    35         kx 
    35         kx 		if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
    35         kx 			return -1;
    35         kx 		total = 0;
    35         kx 		for (month = 0; month < rp->r_month; ++month)
    35         kx 			total += len_months[0][month];
    35         kx 		/* Omit the "J" in Jan and Feb, as that's shorter.  */
    35         kx 		if (rp->r_month <= 1)
    35         kx 		  result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
    35         kx 		else
    35         kx 		  result += sprintf(result, "J%d", total + rp->r_dayofmonth);
    35         kx 	} else {
    35         kx 		register int	week;
    35         kx 		register int	wday = rp->r_wday;
    35         kx 		register int	wdayoff;
    35         kx 
    35         kx 		if (rp->r_dycode == DC_DOWGEQ) {
    35         kx 			wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
    35         kx 			if (wdayoff)
    35         kx 				compat = 2013;
    35         kx 			wday -= wdayoff;
    35         kx 			tod += wdayoff * SECSPERDAY;
    35         kx 			week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
    35         kx 		} else if (rp->r_dycode == DC_DOWLEQ) {
    35         kx 			if (rp->r_dayofmonth == len_months[1][rp->r_month])
    35         kx 				week = 5;
    35         kx 			else {
    35         kx 				wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
    35         kx 				if (wdayoff)
    35         kx 					compat = 2013;
    35         kx 				wday -= wdayoff;
    35         kx 				tod += wdayoff * SECSPERDAY;
    35         kx 				week = rp->r_dayofmonth / DAYSPERWEEK;
    35         kx 			}
    35         kx 		} else	return -1;	/* "cannot happen" */
    35         kx 		if (wday < 0)
    35         kx 			wday += DAYSPERWEEK;
    35         kx 		result += sprintf(result, "M%d.%d.%d",
    35         kx 				  rp->r_month + 1, week, wday);
    35         kx 	}
    35         kx 	if (rp->r_todisut)
    35         kx 	  tod += stdoff;
    35         kx 	if (rp->r_todisstd && !rp->r_isdst)
    35         kx 	  tod += save;
    35         kx 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
    35         kx 		*result++ = '/';
    35         kx 		if (! stringoffset(result, tod))
    35         kx 			return -1;
    35         kx 		if (tod < 0) {
    35         kx 			if (compat < 2013)
    35         kx 				compat = 2013;
    35         kx 		} else if (SECSPERDAY <= tod) {
    35         kx 			if (compat < 1994)
    35         kx 				compat = 1994;
    35         kx 		}
    35         kx 	}
    35         kx 	return compat;
    35         kx }
    35         kx 
    35         kx static int
    35         kx rule_cmp(struct rule const *a, struct rule const *b)
    35         kx {
    35         kx 	if (!a)
    35         kx 		return -!!b;
    35         kx 	if (!b)
    35         kx 		return 1;
    35         kx 	if (a->r_hiyear != b->r_hiyear)
    35         kx 		return a->r_hiyear < b->r_hiyear ? -1 : 1;
    35         kx 	if (a->r_hiyear == ZIC_MAX)
    35         kx 		return 0;
    35         kx 	if (a->r_month - b->r_month != 0)
    35         kx 		return a->r_month - b->r_month;
    35         kx 	return a->r_dayofmonth - b->r_dayofmonth;
    35         kx }
    35         kx 
    35         kx static int
    35         kx stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
    35         kx {
    35         kx 	register const struct zone *	zp;
    35         kx 	register struct rule *		rp;
    35         kx 	register struct rule *		stdrp;
    35         kx 	register struct rule *		dstrp;
    35         kx 	register ptrdiff_t		i;
    35         kx 	register int			compat = 0;
    35         kx 	register int			c;
    35         kx 	int				offsetlen;
    35         kx 	struct rule			stdr, dstr;
    35         kx 	ptrdiff_t len;
    35         kx 	int dstcmp;
    35         kx 	struct rule *lastrp[2] = { NULL, NULL };
    35         kx 	struct zone zstr[2];
    35         kx 	struct zone const *stdzp;
    35         kx 	struct zone const *dstzp;
    35         kx 
    35         kx 	result[0] = '\0';
    35         kx 
    35         kx 	/* Internet RFC 8536 section 5.1 says to use an empty TZ string if
    35         kx 	   future timestamps are truncated.  */
    35         kx 	if (hi_time < max_time)
    35         kx 	  return -1;
    35         kx 
    35         kx 	zp = zpfirst + zonecount - 1;
    35         kx 	for (i = 0; i < zp->z_nrules; ++i) {
    35         kx 		struct rule **last;
    35         kx 		int cmp;
    35         kx 		rp = &zp->z_rules[i];
    35         kx 		last = &lastrp[rp->r_isdst];
    35         kx 		cmp = rule_cmp(*last, rp);
    35         kx 		if (cmp < 0)
    35         kx 		  *last = rp;
    35         kx 		else if (cmp == 0)
    35         kx 		  return -1;
    35         kx 	}
    35         kx 	stdrp = lastrp[false];
    35         kx 	dstrp = lastrp[true];
    35         kx 	dstcmp = zp->z_nrules ? rule_cmp(dstrp, stdrp) : zp->z_isdst ? 1 : -1;
    35         kx 	stdzp = dstzp = zp;
    35         kx 
    35         kx 	if (dstcmp < 0) {
    35         kx 	  /* Standard time all year.  */
    35         kx 	  dstrp = NULL;
    35         kx 	} else if (0 < dstcmp) {
    35         kx 	  /* DST all year.  Use an abbreviation like
    35         kx 	     "XXX3EDT4,0/0,J365/23" for EDT (-04) all year.  */
    35         kx 	  zic_t save = dstrp ? dstrp->r_save : zp->z_save;
    35         kx 	  if (0 <= save)
    35         kx 	    {
    35         kx 	      /* Positive DST, the typical case for all-year DST.
    35         kx 		 Fake a timezone with negative DST.  */
    35         kx 	      stdzp = &zstr[0];
    35         kx 	      dstzp = &zstr[1];
    35         kx 	      zstr[0].z_stdoff = zp->z_stdoff + 2 * save;
    35         kx 	      zstr[0].z_format = "XXX";  /* Any 3 letters will do.  */
    35         kx 	      zstr[0].z_format_specifier = 0;
    35         kx 	      zstr[1].z_stdoff = zstr[0].z_stdoff;
    35         kx 	      zstr[1].z_format = zp->z_format;
    35         kx 	      zstr[1].z_format_specifier = zp->z_format_specifier;
    35         kx 	    }
    35         kx 	  dstr.r_month = TM_JANUARY;
    35         kx 	  dstr.r_dycode = DC_DOM;
    35         kx 	  dstr.r_dayofmonth = 1;
    35         kx 	  dstr.r_tod = 0;
    35         kx 	  dstr.r_todisstd = dstr.r_todisut = false;
    35         kx 	  dstr.r_isdst = true;
    35         kx 	  dstr.r_save = save < 0 ? save : -save;
    35         kx 	  dstr.r_abbrvar = dstrp ? dstrp->r_abbrvar : NULL;
    35         kx 	  stdr.r_month = TM_DECEMBER;
    35         kx 	  stdr.r_dycode = DC_DOM;
    35         kx 	  stdr.r_dayofmonth = 31;
    35         kx 	  stdr.r_tod = SECSPERDAY + dstr.r_save;
    35         kx 	  stdr.r_todisstd = stdr.r_todisut = false;
    35         kx 	  stdr.r_isdst = false;
    35         kx 	  stdr.r_save = 0;
    35         kx 	  stdr.r_abbrvar = save < 0 && stdrp ? stdrp->r_abbrvar : NULL;
    35         kx 	  dstrp = &dstr;
    35         kx 	  stdrp = &stdr;
    35         kx 	}
    35         kx 	len = doabbr(result, stdzp, stdrp ? stdrp->r_abbrvar : NULL,
    35         kx 		     false, 0, true);
    35         kx 	offsetlen = stringoffset(result + len, - stdzp->z_stdoff);
    35         kx 	if (! offsetlen) {
    35         kx 		result[0] = '\0';
    35         kx 		return -1;
    35         kx 	}
    35         kx 	len += offsetlen;
    35         kx 	if (dstrp == NULL)
    35         kx 		return compat;
    35         kx 	len += doabbr(result + len, dstzp, dstrp->r_abbrvar,
    35         kx 		      dstrp->r_isdst, dstrp->r_save, true);
    35         kx 	if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) {
    35         kx 	  offsetlen = stringoffset(result + len,
    35         kx 				   - (dstzp->z_stdoff + dstrp->r_save));
    35         kx 	  if (! offsetlen) {
    35         kx 	    result[0] = '\0';
    35         kx 	    return -1;
    35         kx 	  }
    35         kx 	  len += offsetlen;
    35         kx 	}
    35         kx 	result[len++] = ',';
    35         kx 	c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff);
    35         kx 	if (c < 0) {
    35         kx 		result[0] = '\0';
    35         kx 		return -1;
    35         kx 	}
    35         kx 	if (compat < c)
    35         kx 		compat = c;
    35         kx 	len += strlen(result + len);
    35         kx 	result[len++] = ',';
    35         kx 	c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff);
    35         kx 	if (c < 0) {
    35         kx 		result[0] = '\0';
    35         kx 		return -1;
    35         kx 	}
    35         kx 	if (compat < c)
    35         kx 		compat = c;
    35         kx 	return compat;
    35         kx }
    35         kx 
    35         kx static void
    35         kx outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
    35         kx {
    35         kx 	register ptrdiff_t		i, j;
    35         kx 	register zic_t			starttime = 0, untiltime = 0;
    35         kx 	register bool			startttisstd;
    35         kx 	register bool			startttisut;
    35         kx 	register char *			startbuf;
    35         kx 	register char *			ab;
    35         kx 	register char *			envvar;
    35         kx 	register int			max_abbr_len;
    35         kx 	register int			max_envvar_len;
    35         kx 	register bool			prodstic; /* all rules are min to max */
    35         kx 	register int			compat;
    35         kx 	register bool			do_extend;
    35         kx 	register char			version;
    35         kx 	ptrdiff_t lastatmax = -1;
    35         kx 	zic_t max_year0;
    35         kx 	int defaulttype = -1;
    35         kx 
    35         kx 	check_for_signal();
    35         kx 
    35         kx 	/* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND.  */
    35         kx 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
    35         kx 	max_envvar_len = 2 * max_abbr_len + 5 * 9;
    35         kx 
    35         kx 	startbuf = emalloc(max_abbr_len + 1);
    35         kx 	ab = emalloc(max_abbr_len + 1);
    35         kx 	envvar = emalloc(max_envvar_len + 1);
    35         kx 	INITIALIZE(untiltime);
    35         kx 	INITIALIZE(starttime);
    35         kx 	/*
    35         kx 	** Now. . .finally. . .generate some useful data!
    35         kx 	*/
    35         kx 	timecnt = 0;
    35         kx 	typecnt = 0;
    35         kx 	charcnt = 0;
    35         kx 	prodstic = zonecount == 1;
    35         kx 	/*
    35         kx 	** Thanks to Earl Chew
    35         kx 	** for noting the need to unconditionally initialize startttisstd.
    35         kx 	*/
    35         kx 	startttisstd = false;
    35         kx 	startttisut = false;
    35         kx 	min_year = max_year = EPOCH_YEAR;
    35         kx 	if (leapseen) {
    35         kx 		updateminmax(leapminyear);
    35         kx 		updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
    35         kx 	}
    35         kx 	for (i = 0; i < zonecount; ++i) {
    35         kx 		struct zone const *zp = &zpfirst[i];
    35         kx 		if (i < zonecount - 1)
    35         kx 			updateminmax(zp->z_untilrule.r_loyear);
    35         kx 		for (j = 0; j < zp->z_nrules; ++j) {
    35         kx 			struct rule *rp = &zp->z_rules[j];
    35         kx 			if (rp->r_lowasnum)
    35         kx 				updateminmax(rp->r_loyear);
    35         kx 			if (rp->r_hiwasnum)
    35         kx 				updateminmax(rp->r_hiyear);
    35         kx 			if (rp->r_lowasnum || rp->r_hiwasnum)
    35         kx 				prodstic = false;
    35         kx 		}
    35         kx 	}
    35         kx 	/*
    35         kx 	** Generate lots of data if a rule can't cover all future times.
    35         kx 	*/
    35         kx 	compat = stringzone(envvar, zpfirst, zonecount);
    35         kx 	version = compat < 2013 ? '2' : '3';
    35         kx 	do_extend = compat < 0;
    35         kx 	if (noise) {
    35         kx 		if (!*envvar)
    35         kx 			warning("%s %s",
    35         kx 				_("no POSIX environment variable for zone"),
    35         kx 				zpfirst->z_name);
    35         kx 		else if (compat != 0) {
    35         kx 			/* Circa-COMPAT clients, and earlier clients, might
    35         kx 			   not work for this zone when given dates before
    35         kx 			   1970 or after 2038.  */
    35         kx 			warning(_("%s: pre-%d clients may mishandle"
    35         kx 				  " distant timestamps"),
    35         kx 				zpfirst->z_name, compat);
    35         kx 		}
    35         kx 	}
    35         kx 	if (do_extend) {
    35         kx 		/*
    35         kx 		** Search through a couple of extra years past the obvious
    35         kx 		** 400, to avoid edge cases.  For example, suppose a non-POSIX
    35         kx 		** rule applies from 2012 onwards and has transitions in March
    35         kx 		** and September, plus some one-off transitions in November
    35         kx 		** 2013.  If zic looked only at the last 400 years, it would
    35         kx 		** set max_year=2413, with the intent that the 400 years 2014
    35         kx 		** through 2413 will be repeated.  The last transition listed
    35         kx 		** in the tzfile would be in 2413-09, less than 400 years
    35         kx 		** after the last one-off transition in 2013-11.  Two years
    35         kx 		** might be overkill, but with the kind of edge cases
    35         kx 		** available we're not sure that one year would suffice.
    35         kx 		*/
    35         kx 		enum { years_of_observations = YEARSPERREPEAT + 2 };
    35         kx 
    35         kx 		if (min_year >= ZIC_MIN + years_of_observations)
    35         kx 			min_year -= years_of_observations;
    35         kx 		else	min_year = ZIC_MIN;
    35         kx 		if (max_year <= ZIC_MAX - years_of_observations)
    35         kx 			max_year += years_of_observations;
    35         kx 		else	max_year = ZIC_MAX;
    35         kx 		/*
    35         kx 		** Regardless of any of the above,
    35         kx 		** for a "proDSTic" zone which specifies that its rules
    35         kx 		** always have and always will be in effect,
    35         kx 		** we only need one cycle to define the zone.
    35         kx 		*/
    35         kx 		if (prodstic) {
    35         kx 			min_year = 1900;
    35         kx 			max_year = min_year + years_of_observations;
    35         kx 		}
    35         kx 	}
    35         kx 	max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
    35         kx 				  + EPOCH_YEAR + 1));
    35         kx 	max_year0 = max_year;
    35         kx 	if (want_bloat()) {
    35         kx 	  /* For the benefit of older systems,
    35         kx 	     generate data from 1900 through 2038.  */
    35         kx 	  if (min_year > 1900)
    35         kx 		min_year = 1900;
    35         kx 	  if (max_year < 2038)
    35         kx 		max_year = 2038;
    35         kx 	}
    35         kx 
    35         kx 	if (min_time < lo_time || hi_time < max_time)
    35         kx 	  unspecifiedtype = addtype(0, "-00", false, false, false);
    35         kx 
    35         kx 	for (i = 0; i < zonecount; ++i) {
    35         kx 		struct rule *prevrp = NULL;
    35         kx 		/*
    35         kx 		** A guess that may well be corrected later.
    35         kx 		*/
    35         kx 		zic_t save = 0;
    35         kx 		struct zone const *zp = &zpfirst[i];
    35         kx 		bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
    35         kx 		bool useuntil = i < (zonecount - 1);
    35         kx 		zic_t stdoff = zp->z_stdoff;
    35         kx 		zic_t startoff = stdoff;
    35         kx 		zic_t prevktime;
    35         kx 		INITIALIZE(prevktime);
    35         kx 		if (useuntil && zp->z_untiltime <= min_time)
    35         kx 			continue;
    35         kx 		eat(zp->z_filenum, zp->z_linenum);
    35         kx 		*startbuf = '\0';
    35         kx 		if (zp->z_nrules == 0) {
    35         kx 			int type;
    35         kx 			save = zp->z_save;
    35         kx 			doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
    35         kx 			type = addtype(oadd(zp->z_stdoff, save),
    35         kx 				startbuf, zp->z_isdst, startttisstd,
    35         kx 				startttisut);
    35         kx 			if (usestart) {
    35         kx 				addtt(starttime, type);
    35         kx 				usestart = false;
    35         kx 			} else
    35         kx 				defaulttype = type;
    35         kx 		} else {
    35         kx 		  zic_t year;
    35         kx 		  for (year = min_year; year <= max_year; ++year) {
    35         kx 			if (useuntil && year > zp->z_untilrule.r_hiyear)
    35         kx 				break;
    35         kx 			/*
    35         kx 			** Mark which rules to do in the current year.
    35         kx 			** For those to do, calculate rpytime(rp, year);
    35         kx 			** The former TYPE field was also considered here.
    35         kx 			*/
    35         kx 			for (j = 0; j < zp->z_nrules; ++j) {
    35         kx 				zic_t one = 1;
    35         kx 				zic_t y2038_boundary = one << 31;
    35         kx 				struct rule *rp = &zp->z_rules[j];
    35         kx 				eats(zp->z_filenum, zp->z_linenum,
    35         kx 				     rp->r_filenum, rp->r_linenum);
    35         kx 				rp->r_todo = year >= rp->r_loyear &&
    35         kx 						year <= rp->r_hiyear;
    35         kx 				if (rp->r_todo) {
    35         kx 					rp->r_temp = rpytime(rp, year);
    35         kx 					rp->r_todo
    35         kx 					  = (rp->r_temp < y2038_boundary
    35         kx 					     || year <= max_year0);
    35         kx 				}
    35         kx 			}
    35         kx 			for ( ; ; ) {
    35         kx 				register ptrdiff_t k;
    35         kx 				register zic_t	jtime = 0, ktime = 0;
    35         kx 				register zic_t	offset;
    35         kx 				struct rule *rp;
    35         kx 				int type;
    35         kx 
    35         kx 				INITIALIZE(ktime);
    35         kx 				if (useuntil) {
    35         kx 					/*
    35         kx 					** Turn untiltime into UT
    35         kx 					** assuming the current stdoff and
    35         kx 					** save values.
    35         kx 					*/
    35         kx 					untiltime = zp->z_untiltime;
    35         kx 					if (!zp->z_untilrule.r_todisut)
    35         kx 						untiltime = tadd(untiltime,
    35         kx 								 -stdoff);
    35         kx 					if (!zp->z_untilrule.r_todisstd)
    35         kx 						untiltime = tadd(untiltime,
    35         kx 								 -save);
    35         kx 				}
    35         kx 				/*
    35         kx 				** Find the rule (of those to do, if any)
    35         kx 				** that takes effect earliest in the year.
    35         kx 				*/
    35         kx 				k = -1;
    35         kx 				for (j = 0; j < zp->z_nrules; ++j) {
    35         kx 					struct rule *r = &zp->z_rules[j];
    35         kx 					if (!r->r_todo)
    35         kx 						continue;
    35         kx 					eats(zp->z_filenum, zp->z_linenum,
    35         kx 					     r->r_filenum, r->r_linenum);
    35         kx 					offset = r->r_todisut ? 0 : stdoff;
    35         kx 					if (!r->r_todisstd)
    35         kx 						offset = oadd(offset, save);
    35         kx 					jtime = r->r_temp;
    35         kx 					if (jtime == min_time ||
    35         kx 						jtime == max_time)
    35         kx 							continue;
    35         kx 					jtime = tadd(jtime, -offset);
    35         kx 					if (k < 0 || jtime < ktime) {
    35         kx 						k = j;
    35         kx 						ktime = jtime;
    35         kx 					} else if (jtime == ktime) {
    35         kx 					  char const *dup_rules_msg =
    35         kx 					    _("two rules for same instant");
    35         kx 					  eats(zp->z_filenum, zp->z_linenum,
    35         kx 					       r->r_filenum, r->r_linenum);
    35         kx 					  warning("%s", dup_rules_msg);
    35         kx 					  r = &zp->z_rules[k];
    35         kx 					  eats(zp->z_filenum, zp->z_linenum,
    35         kx 					       r->r_filenum, r->r_linenum);
    35         kx 					  error("%s", dup_rules_msg);
    35         kx 					}
    35         kx 				}
    35         kx 				if (k < 0)
    35         kx 					break;	/* go on to next year */
    35         kx 				rp = &zp->z_rules[k];
    35         kx 				rp->r_todo = false;
    35         kx 				if (useuntil && ktime >= untiltime) {
    35         kx 					if (!*startbuf
    35         kx 					    && (oadd(zp->z_stdoff, rp->r_save)
    35         kx 						== startoff))
    35         kx 					  doabbr(startbuf, zp, rp->r_abbrvar,
    35         kx 						 rp->r_isdst, rp->r_save,
    35         kx 						 false);
    35         kx 					break;
    35         kx 				}
    35         kx 				save = rp->r_save;
    35         kx 				if (usestart && ktime == starttime)
    35         kx 					usestart = false;
    35         kx 				if (usestart) {
    35         kx 					if (ktime < starttime) {
    35         kx 						startoff = oadd(zp->z_stdoff,
    35         kx 								save);
    35         kx 						doabbr(startbuf, zp,
    35         kx 							rp->r_abbrvar,
    35         kx 							rp->r_isdst,
    35         kx 							rp->r_save,
    35         kx 							false);
    35         kx 						continue;
    35         kx 					}
    35         kx 					if (*startbuf == '\0'
    35         kx 					    && startoff == oadd(zp->z_stdoff,
    35         kx 								save)) {
    35         kx 							doabbr(startbuf,
    35         kx 								zp,
    35         kx 								rp->r_abbrvar,
    35         kx 								rp->r_isdst,
    35         kx 								rp->r_save,
    35         kx 								false);
    35         kx 					}
    35         kx 				}
    35         kx 				eats(zp->z_filenum, zp->z_linenum,
    35         kx 				     rp->r_filenum, rp->r_linenum);
    35         kx 				doabbr(ab, zp, rp->r_abbrvar,
    35         kx 				       rp->r_isdst, rp->r_save, false);
    35         kx 				offset = oadd(zp->z_stdoff, rp->r_save);
    35         kx 				if (!want_bloat() && !useuntil && !do_extend
    35         kx 				    && prevrp && lo_time <= prevktime
    35         kx 				    && redundant_time <= ktime
    35         kx 				    && rp->r_hiyear == ZIC_MAX
    35         kx 				    && prevrp->r_hiyear == ZIC_MAX)
    35         kx 				  break;
    35         kx 				type = addtype(offset, ab, rp->r_isdst,
    35         kx 					rp->r_todisstd, rp->r_todisut);
    35         kx 				if (defaulttype < 0 && !rp->r_isdst)
    35         kx 				  defaulttype = type;
    35         kx 				if (rp->r_hiyear == ZIC_MAX
    35         kx 				    && ! (0 <= lastatmax
    35         kx 					  && ktime < attypes[lastatmax].at))
    35         kx 				  lastatmax = timecnt;
    35         kx 				addtt(ktime, type);
    35         kx 				prevrp = rp;
    35         kx 				prevktime = ktime;
    35         kx 			}
    35         kx 		  }
    35         kx 		}
    35         kx 		if (usestart) {
    35         kx 			bool isdst = startoff != zp->z_stdoff;
    35         kx 			if (*startbuf == '\0' && zp->z_format)
    35         kx 			  doabbr(startbuf, zp, disable_percent_s,
    35         kx 				 isdst, save, false);
    35         kx 			eat(zp->z_filenum, zp->z_linenum);
    35         kx 			if (*startbuf == '\0')
    35         kx error(_("can't determine time zone abbreviation to use just after until time"));
    35         kx 			else {
    35         kx 			  int type = addtype(startoff, startbuf, isdst,
    35         kx 					     startttisstd, startttisut);
    35         kx 			  if (defaulttype < 0 && !isdst)
    35         kx 			    defaulttype = type;
    35         kx 			  addtt(starttime, type);
    35         kx 			}
    35         kx 		}
    35         kx 		/*
    35         kx 		** Now we may get to set starttime for the next zone line.
    35         kx 		*/
    35         kx 		if (useuntil) {
    35         kx 			startttisstd = zp->z_untilrule.r_todisstd;
    35         kx 			startttisut = zp->z_untilrule.r_todisut;
    35         kx 			starttime = zp->z_untiltime;
    35         kx 			if (!startttisstd)
    35         kx 			  starttime = tadd(starttime, -save);
    35         kx 			if (!startttisut)
    35         kx 			  starttime = tadd(starttime, -stdoff);
    35         kx 		}
    35         kx 	}
    35         kx 	if (defaulttype < 0)
    35         kx 	  defaulttype = 0;
    35         kx 	if (0 <= lastatmax)
    35         kx 	  attypes[lastatmax].dontmerge = true;
    35         kx 	if (do_extend) {
    35         kx 		/*
    35         kx 		** If we're extending the explicitly listed observations
    35         kx 		** for 400 years because we can't fill the POSIX-TZ field,
    35         kx 		** check whether we actually ended up explicitly listing
    35         kx 		** observations through that period.  If there aren't any
    35         kx 		** near the end of the 400-year period, add a redundant
    35         kx 		** one at the end of the final year, to make it clear
    35         kx 		** that we are claiming to have definite knowledge of
    35         kx 		** the lack of transitions up to that point.
    35         kx 		*/
    35         kx 		struct rule xr;
    35         kx 		struct attype *lastat;
    35         kx 		xr.r_month = TM_JANUARY;
    35         kx 		xr.r_dycode = DC_DOM;
    35         kx 		xr.r_dayofmonth = 1;
    35         kx 		xr.r_tod = 0;
    35         kx 		for (lastat = attypes, i = 1; i < timecnt; i++)
    35         kx 			if (attypes[i].at > lastat->at)
    35         kx 				lastat = &attypes[i];
    35         kx 		if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) {
    35         kx 			addtt(rpytime(&xr, max_year + 1),
    35         kx 			      lastat ? lastat->type : defaulttype);
    35         kx 			attypes[timecnt - 1].dontmerge = true;
    35         kx 		}
    35         kx 	}
    35         kx 	writezone(zpfirst->z_name, envvar, version, defaulttype);
    35         kx 	free(startbuf);
    35         kx 	free(ab);
    35         kx 	free(envvar);
    35         kx }
    35         kx 
    35         kx static void
    35         kx addtt(zic_t starttime, int type)
    35         kx {
    35         kx 	attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
    35         kx 	attypes[timecnt].at = starttime;
    35         kx 	attypes[timecnt].dontmerge = false;
    35         kx 	attypes[timecnt].type = type;
    35         kx 	++timecnt;
    35         kx }
    35         kx 
    35         kx static int
    35         kx addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
    35         kx {
    35         kx 	register int	i, j;
    35         kx 
    35         kx 	if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) {
    35         kx 		error(_("UT offset out of range"));
    35         kx 		exit(EXIT_FAILURE);
    35         kx 	}
    35         kx 	if (!want_bloat())
    35         kx 	  ttisstd = ttisut = false;
    35         kx 
    35         kx 	for (j = 0; j < charcnt; ++j)
    35         kx 		if (strcmp(&chars[j], abbr) == 0)
    35         kx 			break;
    35         kx 	if (j == charcnt)
    35         kx 		newabbr(abbr);
    35         kx 	else {
    35         kx 	  /* If there's already an entry, return its index.  */
    35         kx 	  for (i = 0; i < typecnt; i++)
    35         kx 	    if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
    35         kx 		&& ttisstd == ttisstds[i] && ttisut == ttisuts[i])
    35         kx 	      return i;
    35         kx 	}
    35         kx 	/*
    35         kx 	** There isn't one; add a new one, unless there are already too
    35         kx 	** many.
    35         kx 	*/
    35         kx 	if (typecnt >= TZ_MAX_TYPES) {
    35         kx 		error(_("too many local time types"));
    35         kx 		exit(EXIT_FAILURE);
    35         kx 	}
    35         kx 	i = typecnt++;
    35         kx 	utoffs[i] = utoff;
    35         kx 	isdsts[i] = isdst;
    35         kx 	ttisstds[i] = ttisstd;
    35         kx 	ttisuts[i] = ttisut;
    35         kx 	desigidx[i] = j;
    35         kx 	return i;
    35         kx }
    35         kx 
    35         kx static void
    35         kx leapadd(zic_t t, int correction, int rolling)
    35         kx {
    35         kx 	register int i;
    35         kx 
    35         kx 	if (TZ_MAX_LEAPS <= leapcnt) {
    35         kx 		error(_("too many leap seconds"));
    35         kx 		exit(EXIT_FAILURE);
    35         kx 	}
    35         kx 	if (rolling && (lo_time != min_time || hi_time != max_time)) {
    35         kx 	  error(_("Rolling leap seconds not supported with -r"));
    35         kx 	  exit(EXIT_FAILURE);
    35         kx 	}
    35         kx 	for (i = 0; i < leapcnt; ++i)
    35         kx 		if (t <= trans[i])
    35         kx 			break;
    35         kx 	memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
    35         kx 	memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
    35         kx 	memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
    35         kx 	trans[i] = t;
    35         kx 	corr[i] = correction;
    35         kx 	roll[i] = rolling;
    35         kx 	++leapcnt;
    35         kx }
    35         kx 
    35         kx static void
    35         kx adjleap(void)
    35         kx {
    35         kx 	register int	i;
    35         kx 	register zic_t	last = 0;
    35         kx 	register zic_t	prevtrans = 0;
    35         kx 
    35         kx 	/*
    35         kx 	** propagate leap seconds forward
    35         kx 	*/
    35         kx 	for (i = 0; i < leapcnt; ++i) {
    35         kx 		if (trans[i] - prevtrans < 28 * SECSPERDAY) {
    35         kx 		  error(_("Leap seconds too close together"));
    35         kx 		  exit(EXIT_FAILURE);
    35         kx 		}
    35         kx 		prevtrans = trans[i];
    35         kx 		trans[i] = tadd(trans[i], last);
    35         kx 		last = corr[i] += last;
    35         kx 	}
    35         kx 
    35         kx 	if (0 <= leapexpires) {
    35         kx 	  leapexpires = oadd(leapexpires, last);
    35         kx 	  if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) {
    35         kx 	    error(_("last Leap time does not precede Expires time"));
    35         kx 	    exit(EXIT_FAILURE);
    35         kx 	  }
    35         kx 	}
    35         kx }
    35         kx 
    35         kx /* Is A a space character in the C locale?  */
    35         kx static bool
    35         kx is_space(char a)
    35         kx {
    35         kx 	switch (a) {
    35         kx 	  default:
    35         kx 		return false;
    35         kx 	  case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
    35         kx 		return true;
    35         kx 	}
    35         kx }
    35         kx 
    35         kx /* Is A an alphabetic character in the C locale?  */
    35         kx static bool
    35         kx is_alpha(char a)
    35         kx {
    35         kx 	switch (a) {
    35         kx 	  default:
    35         kx 		return false;
    35         kx 	  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
    35         kx 	  case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
    35         kx 	  case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
    35         kx 	  case 'V': case 'W': case 'X': case 'Y': case 'Z':
    35         kx 	  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
    35         kx 	  case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
    35         kx 	  case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
    35         kx 	  case 'v': case 'w': case 'x': case 'y': case 'z':
    35         kx 		return true;
    35         kx 	}
    35         kx }
    35         kx 
    35         kx /* If A is an uppercase character in the C locale, return its lowercase
    35         kx    counterpart.  Otherwise, return A.  */
    35         kx static char
    35         kx lowerit(char a)
    35         kx {
    35         kx 	switch (a) {
    35         kx 	  default: return a;
    35         kx 	  case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
    35         kx 	  case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
    35         kx 	  case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
    35         kx 	  case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
    35         kx 	  case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
    35         kx 	  case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
    35         kx 	  case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
    35         kx 	  case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
    35         kx 	  case 'Y': return 'y'; case 'Z': return 'z';
    35         kx 	}
    35         kx }
    35         kx 
    35         kx /* case-insensitive equality */
    35         kx ATTRIBUTE_REPRODUCIBLE static bool
    35         kx ciequal(register const char *ap, register const char *bp)
    35         kx {
    35         kx 	while (lowerit(*ap) == lowerit(*bp++))
    35         kx 		if (*ap++ == '\0')
    35         kx 			return true;
    35         kx 	return false;
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static bool
    35         kx itsabbr(register const char *abbr, register const char *word)
    35         kx {
    35         kx 	if (lowerit(*abbr) != lowerit(*word))
    35         kx 		return false;
    35         kx 	++word;
    35         kx 	while (*++abbr != '\0')
    35         kx 		do {
    35         kx 			if (*word == '\0')
    35         kx 				return false;
    35         kx 		} while (lowerit(*word++) != lowerit(*abbr));
    35         kx 	return true;
    35         kx }
    35         kx 
    35         kx /* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case.  */
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static bool
    35         kx ciprefix(char const *abbr, char const *word)
    35         kx {
    35         kx   do
    35         kx     if (!*abbr)
    35         kx       return true;
    35         kx   while (lowerit(*abbr++) == lowerit(*word++));
    35         kx 
    35         kx   return false;
    35         kx }
    35         kx 
    35         kx static const struct lookup *
    35         kx byword(const char *word, const struct lookup *table)
    35         kx {
    35         kx 	register const struct lookup *	foundlp;
    35         kx 	register const struct lookup *	lp;
    35         kx 
    35         kx 	if (word == NULL || table == NULL)
    35         kx 		return NULL;
    35         kx 
    35         kx 	/* If TABLE is LASTS and the word starts with "last" followed
    35         kx 	   by a non-'-', skip the "last" and look in WDAY_NAMES instead.
    35         kx 	   Warn about any usage of the undocumented prefix "last-".  */
    35         kx 	if (table == lasts && ciprefix("last", word) && word[4]) {
    35         kx 	  if (word[4] == '-')
    35         kx 	    warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
    35         kx 		    word, word + 5);
    35         kx 	  else {
    35         kx 	    word += 4;
    35         kx 	    table = wday_names;
    35         kx 	  }
    35         kx 	}
    35         kx 
    35         kx 	/*
    35         kx 	** Look for exact match.
    35         kx 	*/
    35         kx 	for (lp = table; lp->l_word != NULL; ++lp)
    35         kx 		if (ciequal(word, lp->l_word))
    35         kx 			return lp;
    35         kx 	/*
    35         kx 	** Look for inexact match.
    35         kx 	*/
    35         kx 	foundlp = NULL;
    35         kx 	for (lp = table; lp->l_word != NULL; ++lp)
    35         kx 		if (ciprefix(word, lp->l_word)) {
    35         kx 			if (foundlp == NULL)
    35         kx 				foundlp = lp;
    35         kx 			else	return NULL;	/* multiple inexact matches */
    35         kx 		}
    35         kx 
    35         kx 	if (foundlp && noise) {
    35         kx 	  /* Warn about any backward-compatibility issue with pre-2017c zic.  */
    35         kx 	  bool pre_2017c_match = false;
    35         kx 	  for (lp = table; lp->l_word; lp++)
    35         kx 	    if (itsabbr(word, lp->l_word)) {
    35         kx 	      if (pre_2017c_match) {
    35         kx 		warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
    35         kx 		break;
    35         kx 	      }
    35         kx 	      pre_2017c_match = true;
    35         kx 	    }
    35         kx 	}
    35         kx 
    35         kx 	return foundlp;
    35         kx }
    35         kx 
    35         kx static int
    35         kx getfields(char *cp, char **array, int arrayelts)
    35         kx {
    35         kx 	register char *		dp;
    35         kx 	register int		nsubs;
    35         kx 
    35         kx 	nsubs = 0;
    35         kx 	for ( ; ; ) {
    35         kx 		char *dstart;
    35         kx 		while (is_space(*cp))
    35         kx 				++cp;
    35         kx 		if (*cp == '\0' || *cp == '#')
    35         kx 			break;
    35         kx 		dstart = dp = cp;
    35         kx 		do {
    35         kx 			if ((*dp = *cp++) != '"')
    35         kx 				++dp;
    35         kx 			else while ((*dp = *cp++) != '"')
    35         kx 				if (*dp != '\0')
    35         kx 					++dp;
    35         kx 				else {
    35         kx 				  error(_("Odd number of quotation marks"));
    35         kx 				  exit(EXIT_FAILURE);
    35         kx 				}
    35         kx 		} while (*cp && *cp != '#' && !is_space(*cp));
    35         kx 		if (is_space(*cp))
    35         kx 			++cp;
    35         kx 		*dp = '\0';
    35         kx 		if (nsubs == arrayelts) {
    35         kx 		  error(_("Too many input fields"));
    35         kx 		  exit(EXIT_FAILURE);
    35         kx 		}
    35         kx 		array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
    35         kx 	}
    35         kx 	return nsubs;
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_NORETURN static void
    35         kx time_overflow(void)
    35         kx {
    35         kx   error(_("time overflow"));
    35         kx   exit(EXIT_FAILURE);
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static zic_t
    35         kx oadd(zic_t t1, zic_t t2)
    35         kx {
    35         kx #ifdef ckd_add
    35         kx   zic_t sum;
    35         kx   if (!ckd_add(&sum, t1, t2))
    35         kx     return sum;
    35         kx #else
    35         kx   if (t1 < 0 ? ZIC_MIN - t1 <= t2 : t2 <= ZIC_MAX - t1)
    35         kx     return t1 + t2;
    35         kx #endif
    35         kx   time_overflow();
    35         kx }
    35         kx 
    35         kx ATTRIBUTE_REPRODUCIBLE static zic_t
    35         kx tadd(zic_t t1, zic_t t2)
    35         kx {
    35         kx #ifdef ckd_add
    35         kx   zic_t sum;
    35         kx   if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time)
    35         kx     return sum;
    35         kx #else
    35         kx   if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1)
    35         kx     return t1 + t2;
    35         kx #endif
    35         kx   if (t1 == min_time || t1 == max_time)
    35         kx     return t1;
    35         kx   time_overflow();
    35         kx }
    35         kx 
    35         kx /*
    35         kx ** Given a rule, and a year, compute the date (in seconds since January 1,
    35         kx ** 1970, 00:00 LOCAL time) in that year that the rule refers to.
    35         kx */
    35         kx 
    35         kx static zic_t
    35         kx rpytime(const struct rule *rp, zic_t wantedy)
    35         kx {
    35         kx 	register int	m, i;
    35         kx 	register zic_t	dayoff;			/* with a nod to Margaret O. */
    35         kx 	register zic_t	t, y;
    35         kx 	int yrem;
    35         kx 
    35         kx 	if (wantedy == ZIC_MIN)
    35         kx 		return min_time;
    35         kx 	if (wantedy == ZIC_MAX)
    35         kx 		return max_time;
    35         kx 	m = TM_JANUARY;
    35         kx 	y = EPOCH_YEAR;
    35         kx 
    35         kx 	/* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
    35         kx 	   sans overflow.  */
    35         kx 	yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
    35         kx 	dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
    35         kx 		   + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
    35         kx 		  * DAYSPERREPEAT);
    35         kx 	/* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow.  */
    35         kx 	wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
    35         kx 
    35         kx 	while (wantedy != y) {
    35         kx 		i = len_years[isleap(y)];
    35         kx 		dayoff = oadd(dayoff, i);
    35         kx 		y++;
    35         kx 	}
    35         kx 	while (m != rp->r_month) {
    35         kx 		i = len_months[isleap(y)][m];
    35         kx 		dayoff = oadd(dayoff, i);
    35         kx 		++m;
    35         kx 	}
    35         kx 	i = rp->r_dayofmonth;
    35         kx 	if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
    35         kx 		if (rp->r_dycode == DC_DOWLEQ)
    35         kx 			--i;
    35         kx 		else {
    35         kx 			error(_("use of 2/29 in non leap-year"));
    35         kx 			exit(EXIT_FAILURE);
    35         kx 		}
    35         kx 	}
    35         kx 	--i;
    35         kx 	dayoff = oadd(dayoff, i);
    35         kx 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
    35         kx 		/*
    35         kx 		** Don't trust mod of negative numbers.
    35         kx 		*/
    35         kx 		zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK)
    35         kx 			      % DAYSPERWEEK);
    35         kx 		while (wday != rp->r_wday)
    35         kx 			if (rp->r_dycode == DC_DOWGEQ) {
    35         kx 				dayoff = oadd(dayoff, 1);
    35         kx 				if (++wday >= DAYSPERWEEK)
    35         kx 					wday = 0;
    35         kx 				++i;
    35         kx 			} else {
    35         kx 				dayoff = oadd(dayoff, -1);
    35         kx 				if (--wday < 0)
    35         kx 					wday = DAYSPERWEEK - 1;
    35         kx 				--i;
    35         kx 			}
    35         kx 		if (i < 0 || i >= len_months[isleap(y)][m]) {
    35         kx 			if (noise)
    35         kx 				warning(_("rule goes past start/end of month; \
    35         kx will not work with pre-2004 versions of zic"));
    35         kx 		}
    35         kx 	}
    35         kx 	if (dayoff < min_time / SECSPERDAY)
    35         kx 		return min_time;
    35         kx 	if (dayoff > max_time / SECSPERDAY)
    35         kx 		return max_time;
    35         kx 	t = (zic_t) dayoff * SECSPERDAY;
    35         kx 	return tadd(t, rp->r_tod);
    35         kx }
    35         kx 
    35         kx static void
    35         kx newabbr(const char *string)
    35         kx {
    35         kx 	register int	i;
    35         kx 
    35         kx 	if (strcmp(string, GRANDPARENTED) != 0) {
    35         kx 		register const char *	cp;
    35         kx 		const char *		mp;
    35         kx 
    35         kx 		cp = string;
    35         kx 		mp = NULL;
    35         kx 		while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
    35         kx 		       || *cp == '-' || *cp == '+')
    35         kx 				++cp;
    35         kx 		if (noise && cp - string < 3)
    35         kx 		  mp = _("time zone abbreviation has fewer than 3 characters");
    35         kx 		if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
    35         kx 		  mp = _("time zone abbreviation has too many characters");
    35         kx 		if (*cp != '\0')
    35         kx mp = _("time zone abbreviation differs from POSIX standard");
    35         kx 		if (mp != NULL)
    35         kx 			warning("%s (%s)", mp, string);
    35         kx 	}
    35         kx 	i = strlen(string) + 1;
    35         kx 	if (charcnt + i > TZ_MAX_CHARS) {
    35         kx 		error(_("too many, or too long, time zone abbreviations"));
    35         kx 		exit(EXIT_FAILURE);
    35         kx 	}
    35         kx 	strcpy(&chars[charcnt], string);
    35         kx 	charcnt += i;
    35         kx }
    35         kx 
    35         kx /* Ensure that the directories of ARGNAME exist, by making any missing
    35         kx    ones.  If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
    35         kx    do it for ARGNAME too.  Exit with failure if there is trouble.
    35         kx    Do not consider an existing file to be trouble.  */
    35         kx static void
    35         kx mkdirs(char const *argname, bool ancestors)
    35         kx {
    35         kx 	char *name = estrdup(argname);
    35         kx 	char *cp = name;
    35         kx 
    35         kx 	/* On MS-Windows systems, do not worry about drive letters or
    35         kx 	   backslashes, as this should suffice in practice.  Time zone
    35         kx 	   names do not use drive letters and backslashes.  If the -d
    35         kx 	   option of zic does not name an already-existing directory,
    35         kx 	   it can use slashes to separate the already-existing
    35         kx 	   ancestor prefix from the to-be-created subdirectories.  */
    35         kx 
    35         kx 	/* Do not mkdir a root directory, as it must exist.  */
    35         kx 	while (*cp == '/')
    35         kx 	  cp++;
    35         kx 
    35         kx 	while (cp && ((cp = strchr(cp, '/')) || !ancestors)) {
    35         kx 		if (cp)
    35         kx 		  *cp = '\0';
    35         kx 		/*
    35         kx 		** Try to create it.  It's OK if creation fails because
    35         kx 		** the directory already exists, perhaps because some
    35         kx 		** other process just created it.  For simplicity do
    35         kx 		** not check first whether it already exists, as that
    35         kx 		** is checked anyway if the mkdir fails.
    35         kx 		*/
    35         kx 		if (mkdir(name, MKDIR_UMASK) != 0) {
    35         kx 			/* Do not report an error if err == EEXIST, because
    35         kx 			   some other process might have made the directory
    35         kx 			   in the meantime.  Likewise for ENOSYS, because
    35         kx 			   Solaris 10 mkdir fails with ENOSYS if the
    35         kx 			   directory is an automounted mount point.
    35         kx 			   Likewise for EACCES, since mkdir can fail
    35         kx 			   with EACCES merely because the parent directory
    35         kx 			   is unwritable.  Likewise for most other error
    35         kx 			   numbers.  */
    35         kx 			int err = errno;
    35         kx 			if (err == ELOOP || err == ENAMETOOLONG
    35         kx 			    || err == ENOENT || err == ENOTDIR) {
    35         kx 				error(_("%s: Can't create directory %s: %s"),
    35         kx 				      progname, name, strerror(err));
    35         kx 				exit(EXIT_FAILURE);
    35         kx 			}
    35         kx 		}
    35         kx 		if (cp)
    35         kx 		  *cp++ = '/';
    35         kx 	}
    35         kx 	free(name);
    35         kx }