| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | ** This file is in the public domain, so clarified as of | 
|  | 3 | ** 2006-07-17 by Arthur David Olson. | 
|  | 4 | */ | 
|  | 5 |  | 
|  | 6 | #include "version.h" | 
|  | 7 | #include "private.h" | 
|  | 8 | #include "locale.h" | 
|  | 9 | #include "tzfile.h" | 
|  | 10 |  | 
|  | 11 | #include <stdarg.h> | 
|  | 12 |  | 
|  | 13 | #define	ZIC_VERSION_PRE_2013 '2' | 
|  | 14 | #define	ZIC_VERSION	'3' | 
|  | 15 |  | 
|  | 16 | typedef int_fast64_t	zic_t; | 
|  | 17 | #define ZIC_MIN INT_FAST64_MIN | 
|  | 18 | #define ZIC_MAX INT_FAST64_MAX | 
|  | 19 | #define SCNdZIC SCNdFAST64 | 
|  | 20 |  | 
|  | 21 | #ifndef ZIC_MAX_ABBR_LEN_WO_WARN | 
|  | 22 | #define ZIC_MAX_ABBR_LEN_WO_WARN	6 | 
|  | 23 | #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ | 
|  | 24 |  | 
|  | 25 | #if HAVE_SYS_STAT_H | 
|  | 26 | #include <sys/stat.h> | 
|  | 27 | #endif | 
|  | 28 | #ifdef S_IRUSR | 
|  | 29 | #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) | 
|  | 30 | #else | 
|  | 31 | #define MKDIR_UMASK 0755 | 
|  | 32 | #endif | 
|  | 33 |  | 
|  | 34 | struct rule { | 
|  | 35 | const char *	r_filename; | 
|  | 36 | int		r_linenum; | 
|  | 37 | const char *	r_name; | 
|  | 38 |  | 
|  | 39 | zic_t		r_loyear;	/* for example, 1986 */ | 
|  | 40 | zic_t		r_hiyear;	/* for example, 1986 */ | 
|  | 41 | const char *	r_yrtype; | 
|  | 42 | bool		r_lowasnum; | 
|  | 43 | bool		r_hiwasnum; | 
|  | 44 |  | 
|  | 45 | int		r_month;	/* 0..11 */ | 
|  | 46 |  | 
|  | 47 | int		r_dycode;	/* see below */ | 
|  | 48 | int		r_dayofmonth; | 
|  | 49 | int		r_wday; | 
|  | 50 |  | 
|  | 51 | zic_t		r_tod;		/* time from midnight */ | 
|  | 52 | bool		r_todisstd;	/* above is standard time if 1 */ | 
|  | 53 | /* or wall clock time if 0 */ | 
|  | 54 | bool		r_todisgmt;	/* above is GMT if 1 */ | 
|  | 55 | /* or local time if 0 */ | 
|  | 56 | zic_t		r_stdoff;	/* offset from standard time */ | 
|  | 57 | const char *	r_abbrvar;	/* variable part of abbreviation */ | 
|  | 58 |  | 
|  | 59 | int		r_todo;		/* a rule to do (used in outzone) */ | 
|  | 60 | zic_t		r_temp;		/* used in outzone */ | 
|  | 61 | }; | 
|  | 62 |  | 
|  | 63 | /* | 
|  | 64 | **	r_dycode		r_dayofmonth	r_wday | 
|  | 65 | */ | 
|  | 66 |  | 
|  | 67 | #define DC_DOM		0	/* 1..31 */	/* unused */ | 
|  | 68 | #define DC_DOWGEQ	1	/* 1..31 */	/* 0..6 (Sun..Sat) */ | 
|  | 69 | #define DC_DOWLEQ	2	/* 1..31 */	/* 0..6 (Sun..Sat) */ | 
|  | 70 |  | 
|  | 71 | struct zone { | 
|  | 72 | const char *	z_filename; | 
|  | 73 | int		z_linenum; | 
|  | 74 |  | 
|  | 75 | const char *	z_name; | 
|  | 76 | zic_t		z_gmtoff; | 
|  | 77 | const char *	z_rule; | 
|  | 78 | const char *	z_format; | 
|  | 79 | char		z_format_specifier; | 
|  | 80 |  | 
|  | 81 | zic_t		z_stdoff; | 
|  | 82 |  | 
|  | 83 | struct rule *	z_rules; | 
|  | 84 | int		z_nrules; | 
|  | 85 |  | 
|  | 86 | struct rule	z_untilrule; | 
|  | 87 | zic_t		z_untiltime; | 
|  | 88 | }; | 
|  | 89 |  | 
|  | 90 | extern int	getopt(int argc, char * const argv[], | 
|  | 91 | const char * options); | 
|  | 92 | extern int	link(const char * fromname, const char * toname); | 
|  | 93 | extern char *	optarg; | 
|  | 94 | extern int	optind; | 
|  | 95 |  | 
|  | 96 | #if ! HAVE_LINK | 
|  | 97 | # define link(from, to) (-1) | 
|  | 98 | #endif | 
|  | 99 | #if ! HAVE_SYMLINK | 
|  | 100 | # define symlink(from, to) (-1) | 
|  | 101 | #endif | 
|  | 102 |  | 
|  | 103 | static void	addtt(zic_t starttime, int type); | 
|  | 104 | static int	addtype(zic_t, char const *, bool, bool, bool); | 
|  | 105 | static void	leapadd(zic_t, bool, int, int); | 
|  | 106 | static void	adjleap(void); | 
|  | 107 | static void	associate(void); | 
|  | 108 | static void	dolink(const char * fromfield, const char * tofield); | 
|  | 109 | static char **	getfields(char * buf); | 
|  | 110 | static zic_t	gethms(const char * string, const char * errstring, | 
|  | 111 | bool); | 
|  | 112 | static void	infile(const char * filename); | 
|  | 113 | static void	inleap(char ** fields, int nfields); | 
|  | 114 | static void	inlink(char ** fields, int nfields); | 
|  | 115 | static void	inrule(char ** fields, int nfields); | 
|  | 116 | static bool	inzcont(char ** fields, int nfields); | 
|  | 117 | static bool	inzone(char ** fields, int nfields); | 
|  | 118 | static bool	inzsub(char **, int, bool); | 
|  | 119 | static int	itsdir(const char * name); | 
|  | 120 | static bool	is_alpha(char a); | 
|  | 121 | static char	lowerit(char); | 
|  | 122 | static bool	mkdirs(char *); | 
|  | 123 | static void	newabbr(const char * abbr); | 
|  | 124 | static zic_t	oadd(zic_t t1, zic_t t2); | 
|  | 125 | static void	outzone(const struct zone * zp, int ntzones); | 
|  | 126 | static zic_t	rpytime(const struct rule * rp, zic_t wantedy); | 
|  | 127 | static void	rulesub(struct rule * rp, | 
|  | 128 | const char * loyearp, const char * hiyearp, | 
|  | 129 | const char * typep, const char * monthp, | 
|  | 130 | const char * dayp, const char * timep); | 
|  | 131 | static zic_t	tadd(zic_t t1, zic_t t2); | 
|  | 132 | static bool	yearistype(int year, const char * type); | 
|  | 133 |  | 
|  | 134 | /* Bound on length of what %z can expand to.  */ | 
|  | 135 | enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 }; | 
|  | 136 |  | 
|  | 137 | static int		charcnt; | 
|  | 138 | static bool		errors; | 
|  | 139 | static bool		warnings; | 
|  | 140 | static const char *	filename; | 
|  | 141 | static int		leapcnt; | 
|  | 142 | static bool		leapseen; | 
|  | 143 | static zic_t		leapminyear; | 
|  | 144 | static zic_t		leapmaxyear; | 
|  | 145 | static int		linenum; | 
|  | 146 | static int		max_abbrvar_len = PERCENT_Z_LEN_BOUND; | 
|  | 147 | static int		max_format_len; | 
|  | 148 | static zic_t		max_year; | 
|  | 149 | static zic_t		min_year; | 
|  | 150 | static bool		noise; | 
|  | 151 | static const char *	rfilename; | 
|  | 152 | static int		rlinenum; | 
|  | 153 | static const char *	progname; | 
|  | 154 | static int		timecnt; | 
|  | 155 | static int		timecnt_alloc; | 
|  | 156 | static int		typecnt; | 
|  | 157 |  | 
|  | 158 | /* | 
|  | 159 | ** Line codes. | 
|  | 160 | */ | 
|  | 161 |  | 
|  | 162 | #define LC_RULE		0 | 
|  | 163 | #define LC_ZONE		1 | 
|  | 164 | #define LC_LINK		2 | 
|  | 165 | #define LC_LEAP		3 | 
|  | 166 |  | 
|  | 167 | /* | 
|  | 168 | ** Which fields are which on a Zone line. | 
|  | 169 | */ | 
|  | 170 |  | 
|  | 171 | #define ZF_NAME		1 | 
|  | 172 | #define ZF_GMTOFF	2 | 
|  | 173 | #define ZF_RULE		3 | 
|  | 174 | #define ZF_FORMAT	4 | 
|  | 175 | #define ZF_TILYEAR	5 | 
|  | 176 | #define ZF_TILMONTH	6 | 
|  | 177 | #define ZF_TILDAY	7 | 
|  | 178 | #define ZF_TILTIME	8 | 
|  | 179 | #define ZONE_MINFIELDS	5 | 
|  | 180 | #define ZONE_MAXFIELDS	9 | 
|  | 181 |  | 
|  | 182 | /* | 
|  | 183 | ** Which fields are which on a Zone continuation line. | 
|  | 184 | */ | 
|  | 185 |  | 
|  | 186 | #define ZFC_GMTOFF	0 | 
|  | 187 | #define ZFC_RULE	1 | 
|  | 188 | #define ZFC_FORMAT	2 | 
|  | 189 | #define ZFC_TILYEAR	3 | 
|  | 190 | #define ZFC_TILMONTH	4 | 
|  | 191 | #define ZFC_TILDAY	5 | 
|  | 192 | #define ZFC_TILTIME	6 | 
|  | 193 | #define ZONEC_MINFIELDS	3 | 
|  | 194 | #define ZONEC_MAXFIELDS	7 | 
|  | 195 |  | 
|  | 196 | /* | 
|  | 197 | ** Which files are which on a Rule line. | 
|  | 198 | */ | 
|  | 199 |  | 
|  | 200 | #define RF_NAME		1 | 
|  | 201 | #define RF_LOYEAR	2 | 
|  | 202 | #define RF_HIYEAR	3 | 
|  | 203 | #define RF_COMMAND	4 | 
|  | 204 | #define RF_MONTH	5 | 
|  | 205 | #define RF_DAY		6 | 
|  | 206 | #define RF_TOD		7 | 
|  | 207 | #define RF_STDOFF	8 | 
|  | 208 | #define RF_ABBRVAR	9 | 
|  | 209 | #define RULE_FIELDS	10 | 
|  | 210 |  | 
|  | 211 | /* | 
|  | 212 | ** Which fields are which on a Link line. | 
|  | 213 | */ | 
|  | 214 |  | 
|  | 215 | #define LF_FROM		1 | 
|  | 216 | #define LF_TO		2 | 
|  | 217 | #define LINK_FIELDS	3 | 
|  | 218 |  | 
|  | 219 | /* | 
|  | 220 | ** Which fields are which on a Leap line. | 
|  | 221 | */ | 
|  | 222 |  | 
|  | 223 | #define LP_YEAR		1 | 
|  | 224 | #define LP_MONTH	2 | 
|  | 225 | #define LP_DAY		3 | 
|  | 226 | #define LP_TIME		4 | 
|  | 227 | #define LP_CORR		5 | 
|  | 228 | #define LP_ROLL		6 | 
|  | 229 | #define LEAP_FIELDS	7 | 
|  | 230 |  | 
|  | 231 | /* | 
|  | 232 | ** Year synonyms. | 
|  | 233 | */ | 
|  | 234 |  | 
|  | 235 | #define YR_MINIMUM	0 | 
|  | 236 | #define YR_MAXIMUM	1 | 
|  | 237 | #define YR_ONLY		2 | 
|  | 238 |  | 
|  | 239 | static struct rule *	rules; | 
|  | 240 | static int		nrules;	/* number of rules */ | 
|  | 241 | static int		nrules_alloc; | 
|  | 242 |  | 
|  | 243 | static struct zone *	zones; | 
|  | 244 | static int		nzones;	/* number of zones */ | 
|  | 245 | static int		nzones_alloc; | 
|  | 246 |  | 
|  | 247 | struct link { | 
|  | 248 | const char *	l_filename; | 
|  | 249 | int		l_linenum; | 
|  | 250 | const char *	l_from; | 
|  | 251 | const char *	l_to; | 
|  | 252 | }; | 
|  | 253 |  | 
|  | 254 | static struct link *	links; | 
|  | 255 | static int		nlinks; | 
|  | 256 | static int		nlinks_alloc; | 
|  | 257 |  | 
|  | 258 | struct lookup { | 
|  | 259 | const char *	l_word; | 
|  | 260 | const int	l_value; | 
|  | 261 | }; | 
|  | 262 |  | 
|  | 263 | static struct lookup const *	byword(const char * string, | 
|  | 264 | const struct lookup * lp); | 
|  | 265 |  | 
|  | 266 | static struct lookup const	line_codes[] = { | 
|  | 267 | { "Rule",	LC_RULE }, | 
|  | 268 | { "Zone",	LC_ZONE }, | 
|  | 269 | { "Link",	LC_LINK }, | 
|  | 270 | { "Leap",	LC_LEAP }, | 
|  | 271 | { NULL,		0} | 
|  | 272 | }; | 
|  | 273 |  | 
|  | 274 | static struct lookup const	mon_names[] = { | 
|  | 275 | { "January",	TM_JANUARY }, | 
|  | 276 | { "February",	TM_FEBRUARY }, | 
|  | 277 | { "March",	TM_MARCH }, | 
|  | 278 | { "April",	TM_APRIL }, | 
|  | 279 | { "May",	TM_MAY }, | 
|  | 280 | { "June",	TM_JUNE }, | 
|  | 281 | { "July",	TM_JULY }, | 
|  | 282 | { "August",	TM_AUGUST }, | 
|  | 283 | { "September",	TM_SEPTEMBER }, | 
|  | 284 | { "October",	TM_OCTOBER }, | 
|  | 285 | { "November",	TM_NOVEMBER }, | 
|  | 286 | { "December",	TM_DECEMBER }, | 
|  | 287 | { NULL,		0 } | 
|  | 288 | }; | 
|  | 289 |  | 
|  | 290 | static struct lookup const	wday_names[] = { | 
|  | 291 | { "Sunday",	TM_SUNDAY }, | 
|  | 292 | { "Monday",	TM_MONDAY }, | 
|  | 293 | { "Tuesday",	TM_TUESDAY }, | 
|  | 294 | { "Wednesday",	TM_WEDNESDAY }, | 
|  | 295 | { "Thursday",	TM_THURSDAY }, | 
|  | 296 | { "Friday",	TM_FRIDAY }, | 
|  | 297 | { "Saturday",	TM_SATURDAY }, | 
|  | 298 | { NULL,		0 } | 
|  | 299 | }; | 
|  | 300 |  | 
|  | 301 | static struct lookup const	lasts[] = { | 
|  | 302 | { "last-Sunday",	TM_SUNDAY }, | 
|  | 303 | { "last-Monday",	TM_MONDAY }, | 
|  | 304 | { "last-Tuesday",	TM_TUESDAY }, | 
|  | 305 | { "last-Wednesday",	TM_WEDNESDAY }, | 
|  | 306 | { "last-Thursday",	TM_THURSDAY }, | 
|  | 307 | { "last-Friday",	TM_FRIDAY }, | 
|  | 308 | { "last-Saturday",	TM_SATURDAY }, | 
|  | 309 | { NULL,			0 } | 
|  | 310 | }; | 
|  | 311 |  | 
|  | 312 | static struct lookup const	begin_years[] = { | 
|  | 313 | { "minimum",	YR_MINIMUM }, | 
|  | 314 | { "maximum",	YR_MAXIMUM }, | 
|  | 315 | { NULL,		0 } | 
|  | 316 | }; | 
|  | 317 |  | 
|  | 318 | static struct lookup const	end_years[] = { | 
|  | 319 | { "minimum",	YR_MINIMUM }, | 
|  | 320 | { "maximum",	YR_MAXIMUM }, | 
|  | 321 | { "only",	YR_ONLY }, | 
|  | 322 | { NULL,		0 } | 
|  | 323 | }; | 
|  | 324 |  | 
|  | 325 | static struct lookup const	leap_types[] = { | 
|  | 326 | { "Rolling",	true }, | 
|  | 327 | { "Stationary",	false }, | 
|  | 328 | { NULL,		0 } | 
|  | 329 | }; | 
|  | 330 |  | 
|  | 331 | static const int	len_months[2][MONSPERYEAR] = { | 
|  | 332 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, | 
|  | 333 | { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } | 
|  | 334 | }; | 
|  | 335 |  | 
|  | 336 | static const int	len_years[2] = { | 
|  | 337 | DAYSPERNYEAR, DAYSPERLYEAR | 
|  | 338 | }; | 
|  | 339 |  | 
|  | 340 | static struct attype { | 
|  | 341 | zic_t		at; | 
|  | 342 | unsigned char	type; | 
|  | 343 | } *			attypes; | 
|  | 344 | static zic_t		gmtoffs[TZ_MAX_TYPES]; | 
|  | 345 | static char		isdsts[TZ_MAX_TYPES]; | 
|  | 346 | static unsigned char	abbrinds[TZ_MAX_TYPES]; | 
|  | 347 | static bool		ttisstds[TZ_MAX_TYPES]; | 
|  | 348 | static bool		ttisgmts[TZ_MAX_TYPES]; | 
|  | 349 | static char		chars[TZ_MAX_CHARS]; | 
|  | 350 | static zic_t		trans[TZ_MAX_LEAPS]; | 
|  | 351 | static zic_t		corr[TZ_MAX_LEAPS]; | 
|  | 352 | static char		roll[TZ_MAX_LEAPS]; | 
|  | 353 |  | 
|  | 354 | /* | 
|  | 355 | ** Memory allocation. | 
|  | 356 | */ | 
|  | 357 |  | 
|  | 358 | static _Noreturn void | 
|  | 359 | memory_exhausted(const char *msg) | 
|  | 360 | { | 
|  | 361 | fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg); | 
|  | 362 | exit(EXIT_FAILURE); | 
|  | 363 | } | 
|  | 364 |  | 
|  | 365 | static ATTRIBUTE_PURE size_t | 
|  | 366 | size_product(size_t nitems, size_t itemsize) | 
|  | 367 | { | 
|  | 368 | if (SIZE_MAX / itemsize < nitems) | 
|  | 369 | memory_exhausted(_("size overflow")); | 
|  | 370 | return nitems * itemsize; | 
|  | 371 | } | 
|  | 372 |  | 
|  | 373 | #if !HAVE_STRDUP | 
|  | 374 | static char * | 
|  | 375 | strdup(char const *str) | 
|  | 376 | { | 
|  | 377 | char *result = malloc(strlen(str) + 1); | 
|  | 378 | return result ? strcpy(result, str) : result; | 
|  | 379 | } | 
|  | 380 | #endif | 
|  | 381 |  | 
|  | 382 | static ATTRIBUTE_PURE void * | 
|  | 383 | memcheck(void *ptr) | 
|  | 384 | { | 
|  | 385 | if (ptr == NULL) | 
|  | 386 | memory_exhausted(strerror(errno)); | 
|  | 387 | return ptr; | 
|  | 388 | } | 
|  | 389 |  | 
|  | 390 | static void * | 
|  | 391 | emalloc(size_t size) | 
|  | 392 | { | 
|  | 393 | return memcheck(malloc(size)); | 
|  | 394 | } | 
|  | 395 |  | 
|  | 396 | static void * | 
|  | 397 | erealloc(void *ptr, size_t size) | 
|  | 398 | { | 
|  | 399 | return memcheck(realloc(ptr, size)); | 
|  | 400 | } | 
|  | 401 |  | 
|  | 402 | static char * | 
|  | 403 | ecpyalloc (char const *str) | 
|  | 404 | { | 
|  | 405 | return memcheck(strdup(str)); | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | static void * | 
|  | 409 | growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc) | 
|  | 410 | { | 
|  | 411 | if (nitems < *nitems_alloc) | 
|  | 412 | return ptr; | 
|  | 413 | else { | 
|  | 414 | int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX; | 
|  | 415 | if ((amax - 1) / 3 * 2 < *nitems_alloc) | 
|  | 416 | memory_exhausted(_("int overflow")); | 
|  | 417 | *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1; | 
|  | 418 | return erealloc(ptr, size_product(*nitems_alloc, itemsize)); | 
|  | 419 | } | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | /* | 
|  | 423 | ** Error handling. | 
|  | 424 | */ | 
|  | 425 |  | 
|  | 426 | static void | 
|  | 427 | eats(const char *const name, const int num, const char *const rname, | 
|  | 428 | const int rnum) | 
|  | 429 | { | 
|  | 430 | filename = name; | 
|  | 431 | linenum = num; | 
|  | 432 | rfilename = rname; | 
|  | 433 | rlinenum = rnum; | 
|  | 434 | } | 
|  | 435 |  | 
|  | 436 | static void | 
|  | 437 | eat(const char *const name, const int num) | 
|  | 438 | { | 
|  | 439 | eats(name, num, NULL, -1); | 
|  | 440 | } | 
|  | 441 |  | 
|  | 442 | static void ATTRIBUTE_FORMAT((printf, 1, 0)) | 
|  | 443 | verror(const char *const string, va_list args) | 
|  | 444 | { | 
|  | 445 | /* | 
|  | 446 | ** Match the format of "cc" to allow sh users to | 
|  | 447 | **	zic ... 2>&1 | error -t "*" -v | 
|  | 448 | ** on BSD systems. | 
|  | 449 | */ | 
|  | 450 | if (filename) | 
|  | 451 | fprintf(stderr, _("\"%s\", line %d: "), filename, linenum); | 
|  | 452 | vfprintf(stderr, string, args); | 
|  | 453 | if (rfilename != NULL) | 
|  | 454 | fprintf(stderr, _(" (rule from \"%s\", line %d)"), | 
|  | 455 | rfilename, rlinenum); | 
|  | 456 | fprintf(stderr, "\n"); | 
|  | 457 | } | 
|  | 458 |  | 
|  | 459 | static void ATTRIBUTE_FORMAT((printf, 1, 2)) | 
|  | 460 | error(const char *const string, ...) | 
|  | 461 | { | 
|  | 462 | va_list args; | 
|  | 463 | va_start(args, string); | 
|  | 464 | verror(string, args); | 
|  | 465 | va_end(args); | 
|  | 466 | errors = true; | 
|  | 467 | } | 
|  | 468 |  | 
|  | 469 | static void ATTRIBUTE_FORMAT((printf, 1, 2)) | 
|  | 470 | warning(const char *const string, ...) | 
|  | 471 | { | 
|  | 472 | va_list args; | 
|  | 473 | fprintf(stderr, _("warning: ")); | 
|  | 474 | va_start(args, string); | 
|  | 475 | verror(string, args); | 
|  | 476 | va_end(args); | 
|  | 477 | warnings = true; | 
|  | 478 | } | 
|  | 479 |  | 
|  | 480 | static void | 
|  | 481 | close_file(FILE *stream, char const *name) | 
|  | 482 | { | 
|  | 483 | char const *e = (ferror(stream) ? _("I/O error") | 
|  | 484 | : fclose(stream) != 0 ? strerror(errno) : NULL); | 
|  | 485 | if (e) { | 
|  | 486 | fprintf(stderr, "%s: ", progname); | 
|  | 487 | if (name) | 
|  | 488 | fprintf(stderr, "%s: ", name); | 
|  | 489 | fprintf(stderr, "%s\n", e); | 
|  | 490 | exit(EXIT_FAILURE); | 
|  | 491 | } | 
|  | 492 | } | 
|  | 493 |  | 
|  | 494 | static _Noreturn void | 
|  | 495 | usage(FILE *stream, int status) | 
|  | 496 | { | 
|  | 497 | fprintf(stream, | 
|  | 498 | _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n" | 
|  | 499 | "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n" | 
|  | 500 | "\t[ -L leapseconds ] [ filename ... ]\n\n" | 
|  | 501 | "Report bugs to %s.\n"), | 
|  | 502 | progname, progname, REPORT_BUGS_TO); | 
|  | 503 | if (status == EXIT_SUCCESS) | 
|  | 504 | close_file(stream, NULL); | 
|  | 505 | exit(status); | 
|  | 506 | } | 
|  | 507 |  | 
|  | 508 | static const char *	psxrules; | 
|  | 509 | static const char *	lcltime; | 
|  | 510 | static const char *	directory; | 
|  | 511 | static const char *	leapsec; | 
|  | 512 | static const char *	yitcommand; | 
|  | 513 |  | 
|  | 514 | int | 
|  | 515 | main(int argc, char **argv) | 
|  | 516 | { | 
|  | 517 | register int	i; | 
|  | 518 | register int	j; | 
|  | 519 | register int	c; | 
|  | 520 |  | 
|  | 521 | #ifdef S_IWGRP | 
|  | 522 | umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); | 
|  | 523 | #endif | 
|  | 524 | #if HAVE_GETTEXT | 
|  | 525 | setlocale(LC_ALL, ""); | 
|  | 526 | #ifdef TZ_DOMAINDIR | 
|  | 527 | bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); | 
|  | 528 | #endif /* defined TEXTDOMAINDIR */ | 
|  | 529 | textdomain(TZ_DOMAIN); | 
|  | 530 | #endif /* HAVE_GETTEXT */ | 
|  | 531 | progname = argv[0]; | 
|  | 532 | if (TYPE_BIT(zic_t) < 64) { | 
|  | 533 | fprintf(stderr, "%s: %s\n", progname, | 
|  | 534 | _("wild compilation-time specification of zic_t")); | 
|  | 535 | return EXIT_FAILURE; | 
|  | 536 | } | 
|  | 537 | for (i = 1; i < argc; ++i) | 
|  | 538 | if (strcmp(argv[i], "--version") == 0) { | 
|  | 539 | printf("zic %s%s\n", PKGVERSION, TZVERSION); | 
|  | 540 | close_file(stdout, NULL); | 
|  | 541 | return EXIT_SUCCESS; | 
|  | 542 | } else if (strcmp(argv[i], "--help") == 0) { | 
|  | 543 | usage(stdout, EXIT_SUCCESS); | 
|  | 544 | } | 
|  | 545 | while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) | 
|  | 546 | switch (c) { | 
|  | 547 | default: | 
|  | 548 | usage(stderr, EXIT_FAILURE); | 
|  | 549 | case 'd': | 
|  | 550 | if (directory == NULL) | 
|  | 551 | directory = optarg; | 
|  | 552 | else { | 
|  | 553 | fprintf(stderr, | 
|  | 554 | _("%s: More than one -d option specified\n"), | 
|  | 555 | progname); | 
|  | 556 | return EXIT_FAILURE; | 
|  | 557 | } | 
|  | 558 | break; | 
|  | 559 | case 'l': | 
|  | 560 | if (lcltime == NULL) | 
|  | 561 | lcltime = optarg; | 
|  | 562 | else { | 
|  | 563 | fprintf(stderr, | 
|  | 564 | _("%s: More than one -l option specified\n"), | 
|  | 565 | progname); | 
|  | 566 | return EXIT_FAILURE; | 
|  | 567 | } | 
|  | 568 | break; | 
|  | 569 | case 'p': | 
|  | 570 | if (psxrules == NULL) | 
|  | 571 | psxrules = optarg; | 
|  | 572 | else { | 
|  | 573 | fprintf(stderr, | 
|  | 574 | _("%s: More than one -p option specified\n"), | 
|  | 575 | progname); | 
|  | 576 | return EXIT_FAILURE; | 
|  | 577 | } | 
|  | 578 | break; | 
|  | 579 | case 'y': | 
|  | 580 | if (yitcommand == NULL) | 
|  | 581 | yitcommand = optarg; | 
|  | 582 | else { | 
|  | 583 | fprintf(stderr, | 
|  | 584 | _("%s: More than one -y option specified\n"), | 
|  | 585 | progname); | 
|  | 586 | return EXIT_FAILURE; | 
|  | 587 | } | 
|  | 588 | break; | 
|  | 589 | case 'L': | 
|  | 590 | if (leapsec == NULL) | 
|  | 591 | leapsec = optarg; | 
|  | 592 | else { | 
|  | 593 | fprintf(stderr, | 
|  | 594 | _("%s: More than one -L option specified\n"), | 
|  | 595 | progname); | 
|  | 596 | return EXIT_FAILURE; | 
|  | 597 | } | 
|  | 598 | break; | 
|  | 599 | case 'v': | 
|  | 600 | noise = true; | 
|  | 601 | break; | 
|  | 602 | case 's': | 
|  | 603 | warning(_("-s ignored")); | 
|  | 604 | break; | 
|  | 605 | } | 
|  | 606 | if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) | 
|  | 607 | usage(stderr, EXIT_FAILURE);	/* usage message by request */ | 
|  | 608 | if (directory == NULL) | 
|  | 609 | directory = TZDIR; | 
|  | 610 | if (yitcommand == NULL) | 
|  | 611 | yitcommand = "yearistype"; | 
|  | 612 |  | 
|  | 613 | if (optind < argc && leapsec != NULL) { | 
|  | 614 | infile(leapsec); | 
|  | 615 | adjleap(); | 
|  | 616 | } | 
|  | 617 |  | 
|  | 618 | for (i = optind; i < argc; ++i) | 
|  | 619 | infile(argv[i]); | 
|  | 620 | if (errors) | 
|  | 621 | return EXIT_FAILURE; | 
|  | 622 | associate(); | 
|  | 623 | for (i = 0; i < nzones; i = j) { | 
|  | 624 | /* | 
|  | 625 | ** Find the next non-continuation zone entry. | 
|  | 626 | */ | 
|  | 627 | for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) | 
|  | 628 | continue; | 
|  | 629 | outzone(&zones[i], j - i); | 
|  | 630 | } | 
|  | 631 | /* | 
|  | 632 | ** Make links. | 
|  | 633 | */ | 
|  | 634 | for (i = 0; i < nlinks; ++i) { | 
|  | 635 | eat(links[i].l_filename, links[i].l_linenum); | 
|  | 636 | dolink(links[i].l_from, links[i].l_to); | 
|  | 637 | if (noise) | 
|  | 638 | for (j = 0; j < nlinks; ++j) | 
|  | 639 | if (strcmp(links[i].l_to, | 
|  | 640 | links[j].l_from) == 0) | 
|  | 641 | warning(_("link to link")); | 
|  | 642 | } | 
|  | 643 | if (lcltime != NULL) { | 
|  | 644 | eat(_("command line"), 1); | 
|  | 645 | dolink(lcltime, TZDEFAULT); | 
|  | 646 | } | 
|  | 647 | if (psxrules != NULL) { | 
|  | 648 | eat(_("command line"), 1); | 
|  | 649 | dolink(psxrules, TZDEFRULES); | 
|  | 650 | } | 
|  | 651 | if (warnings && (ferror(stderr) || fclose(stderr) != 0)) | 
|  | 652 | return EXIT_FAILURE; | 
|  | 653 | return errors ? EXIT_FAILURE : EXIT_SUCCESS; | 
|  | 654 | } | 
|  | 655 |  | 
|  | 656 | static bool | 
|  | 657 | componentcheck(char const *name, char const *component, | 
|  | 658 | char const *component_end) | 
|  | 659 | { | 
|  | 660 | enum { component_len_max = 14 }; | 
|  | 661 | size_t component_len = component_end - component; | 
|  | 662 | if (component_len == 0) { | 
|  | 663 | if (!*name) | 
|  | 664 | error (_("empty file name")); | 
|  | 665 | else | 
|  | 666 | error (_(component == name | 
|  | 667 | ? "file name '%s' begins with '/'" | 
|  | 668 | : *component_end | 
|  | 669 | ? "file name '%s' contains '//'" | 
|  | 670 | : "file name '%s' ends with '/'"), | 
|  | 671 | name); | 
|  | 672 | return false; | 
|  | 673 | } | 
|  | 674 | if (0 < component_len && component_len <= 2 | 
|  | 675 | && component[0] == '.' && component_end[-1] == '.') { | 
|  | 676 | error(_("file name '%s' contains '%.*s' component"), | 
|  | 677 | name, (int) component_len, component); | 
|  | 678 | return false; | 
|  | 679 | } | 
|  | 680 | if (noise) { | 
|  | 681 | if (0 < component_len && component[0] == '-') | 
|  | 682 | warning(_("file name '%s' component contains leading '-'"), | 
|  | 683 | name); | 
|  | 684 | if (component_len_max < component_len) | 
|  | 685 | warning(_("file name '%s' contains overlength component" | 
|  | 686 | " '%.*s...'"), | 
|  | 687 | name, component_len_max, component); | 
|  | 688 | } | 
|  | 689 | return true; | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | static bool | 
|  | 693 | namecheck(const char *name) | 
|  | 694 | { | 
|  | 695 | register char const *cp; | 
|  | 696 |  | 
|  | 697 | /* Benign characters in a portable file name.  */ | 
|  | 698 | static char const benign[] = | 
|  | 699 | "-/_" | 
|  | 700 | "abcdefghijklmnopqrstuvwxyz" | 
|  | 701 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 
|  | 702 |  | 
|  | 703 | /* Non-control chars in the POSIX portable character set, | 
|  | 704 | excluding the benign characters.  */ | 
|  | 705 | static char const printable_and_not_benign[] = | 
|  | 706 | " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~"; | 
|  | 707 |  | 
|  | 708 | register char const *component = name; | 
|  | 709 | for (cp = name; *cp; cp++) { | 
|  | 710 | unsigned char c = *cp; | 
|  | 711 | if (noise && !strchr(benign, c)) { | 
|  | 712 | warning((strchr(printable_and_not_benign, c) | 
|  | 713 | ? _("file name '%s' contains byte '%c'") | 
|  | 714 | : _("file name '%s' contains byte '\\%o'")), | 
|  | 715 | name, c); | 
|  | 716 | } | 
|  | 717 | if (c == '/') { | 
|  | 718 | if (!componentcheck(name, component, cp)) | 
|  | 719 | return false; | 
|  | 720 | component = cp + 1; | 
|  | 721 | } | 
|  | 722 | } | 
|  | 723 | return componentcheck(name, component, cp); | 
|  | 724 | } | 
|  | 725 |  | 
|  | 726 | static char * | 
|  | 727 | relname(char const *dir, char const *base) | 
|  | 728 | { | 
|  | 729 | if (*base == '/') | 
|  | 730 | return ecpyalloc(base); | 
|  | 731 | else { | 
|  | 732 | size_t dir_len = strlen(dir); | 
|  | 733 | bool needs_slash = dir_len && dir[dir_len - 1] != '/'; | 
|  | 734 | char *result = emalloc(dir_len + needs_slash + strlen(base) + 1); | 
|  | 735 | result[dir_len] = '/'; | 
|  | 736 | strcpy(result + dir_len + needs_slash, base); | 
|  | 737 | return memcpy(result, dir, dir_len); | 
|  | 738 | } | 
|  | 739 | } | 
|  | 740 |  | 
|  | 741 | static void | 
|  | 742 | dolink(char const *fromfield, char const *tofield) | 
|  | 743 | { | 
|  | 744 | register char *	fromname; | 
|  | 745 | register char *	toname; | 
|  | 746 | register int fromisdir; | 
|  | 747 |  | 
|  | 748 | fromname = relname(directory, fromfield); | 
|  | 749 | toname = relname(directory, tofield); | 
|  | 750 | /* | 
|  | 751 | ** We get to be careful here since | 
|  | 752 | ** there's a fair chance of root running us. | 
|  | 753 | */ | 
|  | 754 | fromisdir = itsdir(fromname); | 
|  | 755 | if (fromisdir) { | 
|  | 756 | char const *e = strerror(fromisdir < 0 ? errno : EPERM); | 
|  | 757 | fprintf(stderr, _("%s: link from %s failed: %s"), | 
|  | 758 | progname, fromname, e); | 
|  | 759 | exit(EXIT_FAILURE); | 
|  | 760 | } | 
|  | 761 | if (itsdir(toname) <= 0) | 
|  | 762 | remove(toname); | 
|  | 763 | if (link(fromname, toname) != 0) { | 
|  | 764 | int	result; | 
|  | 765 |  | 
|  | 766 | if (! mkdirs(toname)) | 
|  | 767 | exit(EXIT_FAILURE); | 
|  | 768 |  | 
|  | 769 | result = link(fromname, toname); | 
|  | 770 | if (result != 0) { | 
|  | 771 | const char *s = fromfield; | 
|  | 772 | const char *t; | 
|  | 773 | char *p; | 
|  | 774 | size_t dotdots = 0; | 
|  | 775 | register char * symlinkcontents = NULL; | 
|  | 776 |  | 
|  | 777 | do | 
|  | 778 | t = s; | 
|  | 779 | while ((s = strchr(s, '/')) | 
|  | 780 | && ! strncmp (fromfield, tofield, | 
|  | 781 | ++s - fromfield)); | 
|  | 782 |  | 
|  | 783 | for (s = tofield + (t - fromfield); *s; s++) | 
|  | 784 | dotdots += *s == '/'; | 
|  | 785 | symlinkcontents | 
|  | 786 | = emalloc(3 * dotdots + strlen(t) + 1); | 
|  | 787 | for (p = symlinkcontents; dotdots-- != 0; p += 3) | 
|  | 788 | memcpy(p, "../", 3); | 
|  | 789 | strcpy(p, t); | 
|  | 790 | result = symlink(symlinkcontents, toname); | 
|  | 791 | if (result == 0) | 
|  | 792 | warning(_("hard link failed, symbolic link used")); | 
|  | 793 | free(symlinkcontents); | 
|  | 794 | } | 
|  | 795 | if (result != 0) { | 
|  | 796 | FILE *fp, *tp; | 
|  | 797 | int c; | 
|  | 798 | fp = fopen(fromname, "rb"); | 
|  | 799 | if (!fp) { | 
|  | 800 | const char *e = strerror(errno); | 
|  | 801 | fprintf(stderr, | 
|  | 802 | _("%s: Can't read %s: %s\n"), | 
|  | 803 | progname, fromname, e); | 
|  | 804 | exit(EXIT_FAILURE); | 
|  | 805 | } | 
|  | 806 | tp = fopen(toname, "wb"); | 
|  | 807 | if (!tp) { | 
|  | 808 | const char *e = strerror(errno); | 
|  | 809 | fprintf(stderr, | 
|  | 810 | _("%s: Can't create %s: %s\n"), | 
|  | 811 | progname, toname, e); | 
|  | 812 | exit(EXIT_FAILURE); | 
|  | 813 | } | 
|  | 814 | while ((c = getc(fp)) != EOF) | 
|  | 815 | putc(c, tp); | 
|  | 816 | close_file(fp, fromname); | 
|  | 817 | close_file(tp, toname); | 
|  | 818 | warning(_("link failed, copy used")); | 
|  | 819 | } | 
|  | 820 | } | 
|  | 821 | free(fromname); | 
|  | 822 | free(toname); | 
|  | 823 | } | 
|  | 824 |  | 
|  | 825 | #define TIME_T_BITS_IN_FILE	64 | 
|  | 826 |  | 
|  | 827 | static zic_t const min_time = MINVAL (zic_t, TIME_T_BITS_IN_FILE); | 
|  | 828 | static zic_t const max_time = MAXVAL (zic_t, TIME_T_BITS_IN_FILE); | 
|  | 829 |  | 
|  | 830 | /* Estimated time of the Big Bang, in seconds since the POSIX epoch. | 
|  | 831 | rounded downward to the negation of a power of two that is | 
|  | 832 | comfortably outside the error bounds. | 
|  | 833 |  | 
|  | 834 | zic does not output time stamps before this, partly because they | 
|  | 835 | are physically suspect, and partly because GNOME mishandles them; see | 
|  | 836 | GNOME bug 730332 <https://bugzilla.gnome.org/show_bug.cgi?id=730332>. | 
|  | 837 |  | 
|  | 838 | For the time of the Big Bang, see: | 
|  | 839 |  | 
|  | 840 | Ade PAR, Aghanim N, Armitage-Caplan C et al.  Planck 2013 results. | 
|  | 841 | I. Overview of products and scientific results. | 
|  | 842 | arXiv:1303.5062 2013-03-20 20:10:01 UTC | 
|  | 843 | <http://arxiv.org/pdf/1303.5062v1> [PDF] | 
|  | 844 |  | 
|  | 845 | Page 36, Table 9, row Age/Gyr, column Planck+WP+highL+BAO 68% limits | 
|  | 846 | gives the value 13.798 plus-or-minus 0.037 billion years. | 
|  | 847 | Multiplying this by 1000000000 and then by 31557600 (the number of | 
|  | 848 | seconds in an astronomical year) gives a value that is comfortably | 
|  | 849 | less than 2**59, so BIG_BANG is - 2**59. | 
|  | 850 |  | 
|  | 851 | BIG_BANG is approximate, and may change in future versions. | 
|  | 852 | Please do not rely on its exact value.  */ | 
|  | 853 |  | 
|  | 854 | #ifndef BIG_BANG | 
|  | 855 | #define BIG_BANG (- (1LL << 59)) | 
|  | 856 | #endif | 
|  | 857 |  | 
|  | 858 | static const zic_t big_bang_time = BIG_BANG; | 
|  | 859 |  | 
|  | 860 | /* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble.  */ | 
|  | 861 | static int | 
|  | 862 | itsdir(char const *name) | 
|  | 863 | { | 
|  | 864 | struct stat st; | 
|  | 865 | int res = stat(name, &st); | 
|  | 866 | if (res != 0) | 
|  | 867 | return res; | 
|  | 868 | #ifdef S_ISDIR | 
|  | 869 | return S_ISDIR(st.st_mode) != 0; | 
|  | 870 | #else | 
|  | 871 | { | 
|  | 872 | char *nameslashdot = relname(name, "."); | 
|  | 873 | res = stat(nameslashdot, &st); | 
|  | 874 | free(nameslashdot); | 
|  | 875 | return res == 0; | 
|  | 876 | } | 
|  | 877 | #endif | 
|  | 878 | } | 
|  | 879 |  | 
|  | 880 | /* | 
|  | 881 | ** Associate sets of rules with zones. | 
|  | 882 | */ | 
|  | 883 |  | 
|  | 884 | /* | 
|  | 885 | ** Sort by rule name. | 
|  | 886 | */ | 
|  | 887 |  | 
|  | 888 | static int | 
|  | 889 | rcomp(const void *cp1, const void *cp2) | 
|  | 890 | { | 
|  | 891 | return strcmp(((const struct rule *) cp1)->r_name, | 
|  | 892 | ((const struct rule *) cp2)->r_name); | 
|  | 893 | } | 
|  | 894 |  | 
|  | 895 | static void | 
|  | 896 | associate(void) | 
|  | 897 | { | 
|  | 898 | register struct zone *	zp; | 
|  | 899 | register struct rule *	rp; | 
|  | 900 | register int		base, out; | 
|  | 901 | register int		i, j; | 
|  | 902 |  | 
|  | 903 | if (nrules != 0) { | 
|  | 904 | qsort(rules, nrules, sizeof *rules, rcomp); | 
|  | 905 | for (i = 0; i < nrules - 1; ++i) { | 
|  | 906 | if (strcmp(rules[i].r_name, | 
|  | 907 | rules[i + 1].r_name) != 0) | 
|  | 908 | continue; | 
|  | 909 | if (strcmp(rules[i].r_filename, | 
|  | 910 | rules[i + 1].r_filename) == 0) | 
|  | 911 | continue; | 
|  | 912 | eat(rules[i].r_filename, rules[i].r_linenum); | 
|  | 913 | warning(_("same rule name in multiple files")); | 
|  | 914 | eat(rules[i + 1].r_filename, rules[i + 1].r_linenum); | 
|  | 915 | warning(_("same rule name in multiple files")); | 
|  | 916 | for (j = i + 2; j < nrules; ++j) { | 
|  | 917 | if (strcmp(rules[i].r_name, | 
|  | 918 | rules[j].r_name) != 0) | 
|  | 919 | break; | 
|  | 920 | if (strcmp(rules[i].r_filename, | 
|  | 921 | rules[j].r_filename) == 0) | 
|  | 922 | continue; | 
|  | 923 | if (strcmp(rules[i + 1].r_filename, | 
|  | 924 | rules[j].r_filename) == 0) | 
|  | 925 | continue; | 
|  | 926 | break; | 
|  | 927 | } | 
|  | 928 | i = j - 1; | 
|  | 929 | } | 
|  | 930 | } | 
|  | 931 | for (i = 0; i < nzones; ++i) { | 
|  | 932 | zp = &zones[i]; | 
|  | 933 | zp->z_rules = NULL; | 
|  | 934 | zp->z_nrules = 0; | 
|  | 935 | } | 
|  | 936 | for (base = 0; base < nrules; base = out) { | 
|  | 937 | rp = &rules[base]; | 
|  | 938 | for (out = base + 1; out < nrules; ++out) | 
|  | 939 | if (strcmp(rp->r_name, rules[out].r_name) != 0) | 
|  | 940 | break; | 
|  | 941 | for (i = 0; i < nzones; ++i) { | 
|  | 942 | zp = &zones[i]; | 
|  | 943 | if (strcmp(zp->z_rule, rp->r_name) != 0) | 
|  | 944 | continue; | 
|  | 945 | zp->z_rules = rp; | 
|  | 946 | zp->z_nrules = out - base; | 
|  | 947 | } | 
|  | 948 | } | 
|  | 949 | for (i = 0; i < nzones; ++i) { | 
|  | 950 | zp = &zones[i]; | 
|  | 951 | if (zp->z_nrules == 0) { | 
|  | 952 | /* | 
|  | 953 | ** Maybe we have a local standard time offset. | 
|  | 954 | */ | 
|  | 955 | eat(zp->z_filename, zp->z_linenum); | 
|  | 956 | zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), | 
|  | 957 | true); | 
|  | 958 | /* | 
|  | 959 | ** Note, though, that if there's no rule, | 
|  | 960 | ** a '%s' in the format is a bad thing. | 
|  | 961 | */ | 
|  | 962 | if (zp->z_format_specifier == 's') | 
|  | 963 | error("%s", _("%s in ruleless zone")); | 
|  | 964 | } | 
|  | 965 | } | 
|  | 966 | if (errors) | 
|  | 967 | exit(EXIT_FAILURE); | 
|  | 968 | } | 
|  | 969 |  | 
|  | 970 | static void | 
|  | 971 | infile(const char *name) | 
|  | 972 | { | 
|  | 973 | register FILE *			fp; | 
|  | 974 | register char **		fields; | 
|  | 975 | register char *			cp; | 
|  | 976 | register const struct lookup *	lp; | 
|  | 977 | register int			nfields; | 
|  | 978 | register bool			wantcont; | 
|  | 979 | register int			num; | 
|  | 980 | char				buf[BUFSIZ]; | 
|  | 981 |  | 
|  | 982 | if (strcmp(name, "-") == 0) { | 
|  | 983 | name = _("standard input"); | 
|  | 984 | fp = stdin; | 
|  | 985 | } else if ((fp = fopen(name, "r")) == NULL) { | 
|  | 986 | const char *e = strerror(errno); | 
|  | 987 |  | 
|  | 988 | fprintf(stderr, _("%s: Can't open %s: %s\n"), | 
|  | 989 | progname, name, e); | 
|  | 990 | exit(EXIT_FAILURE); | 
|  | 991 | } | 
|  | 992 | wantcont = false; | 
|  | 993 | for (num = 1; ; ++num) { | 
|  | 994 | eat(name, num); | 
|  | 995 | if (fgets(buf, sizeof buf, fp) != buf) | 
|  | 996 | break; | 
|  | 997 | cp = strchr(buf, '\n'); | 
|  | 998 | if (cp == NULL) { | 
|  | 999 | error(_("line too long")); | 
|  | 1000 | exit(EXIT_FAILURE); | 
|  | 1001 | } | 
|  | 1002 | *cp = '\0'; | 
|  | 1003 | fields = getfields(buf); | 
|  | 1004 | nfields = 0; | 
|  | 1005 | while (fields[nfields] != NULL) { | 
|  | 1006 | static char	nada; | 
|  | 1007 |  | 
|  | 1008 | if (strcmp(fields[nfields], "-") == 0) | 
|  | 1009 | fields[nfields] = &nada; | 
|  | 1010 | ++nfields; | 
|  | 1011 | } | 
|  | 1012 | if (nfields == 0) { | 
|  | 1013 | /* nothing to do */ | 
|  | 1014 | } else if (wantcont) { | 
|  | 1015 | wantcont = inzcont(fields, nfields); | 
|  | 1016 | } else { | 
|  | 1017 | lp = byword(fields[0], line_codes); | 
|  | 1018 | if (lp == NULL) | 
|  | 1019 | error(_("input line of unknown type")); | 
|  | 1020 | else switch ((int) (lp->l_value)) { | 
|  | 1021 | case LC_RULE: | 
|  | 1022 | inrule(fields, nfields); | 
|  | 1023 | wantcont = false; | 
|  | 1024 | break; | 
|  | 1025 | case LC_ZONE: | 
|  | 1026 | wantcont = inzone(fields, nfields); | 
|  | 1027 | break; | 
|  | 1028 | case LC_LINK: | 
|  | 1029 | inlink(fields, nfields); | 
|  | 1030 | wantcont = false; | 
|  | 1031 | break; | 
|  | 1032 | case LC_LEAP: | 
|  | 1033 | if (name != leapsec) | 
|  | 1034 | warning(_("%s: Leap line in non leap" | 
|  | 1035 | " seconds file %s"), | 
|  | 1036 | progname, name); | 
|  | 1037 | else	inleap(fields, nfields); | 
|  | 1038 | wantcont = false; | 
|  | 1039 | break; | 
|  | 1040 | default:	/* "cannot happen" */ | 
|  | 1041 | fprintf(stderr, | 
|  | 1042 | _("%s: panic: Invalid l_value %d\n"), | 
|  | 1043 | progname, lp->l_value); | 
|  | 1044 | exit(EXIT_FAILURE); | 
|  | 1045 | } | 
|  | 1046 | } | 
|  | 1047 | free(fields); | 
|  | 1048 | } | 
|  | 1049 | close_file(fp, filename); | 
|  | 1050 | if (wantcont) | 
|  | 1051 | error(_("expected continuation line not found")); | 
|  | 1052 | } | 
|  | 1053 |  | 
|  | 1054 | /* | 
|  | 1055 | ** Convert a string of one of the forms | 
|  | 1056 | **	h	-h	hh:mm	-hh:mm	hh:mm:ss	-hh:mm:ss | 
|  | 1057 | ** into a number of seconds. | 
|  | 1058 | ** A null string maps to zero. | 
|  | 1059 | ** Call error with errstring and return zero on errors. | 
|  | 1060 | */ | 
|  | 1061 |  | 
|  | 1062 | static zic_t | 
|  | 1063 | gethms(char const *string, char const *errstring, bool signable) | 
|  | 1064 | { | 
|  | 1065 | zic_t	hh; | 
|  | 1066 | int	mm, ss, sign; | 
|  | 1067 | char xs; | 
|  | 1068 |  | 
|  | 1069 | if (string == NULL || *string == '\0') | 
|  | 1070 | return 0; | 
|  | 1071 | if (!signable) | 
|  | 1072 | sign = 1; | 
|  | 1073 | else if (*string == '-') { | 
|  | 1074 | sign = -1; | 
|  | 1075 | ++string; | 
|  | 1076 | } else	sign = 1; | 
|  | 1077 | if (sscanf(string, "%"SCNdZIC"%c", &hh, &xs) == 1) | 
|  | 1078 | mm = ss = 0; | 
|  | 1079 | else if (sscanf(string, "%"SCNdZIC":%d%c", &hh, &mm, &xs) == 2) | 
|  | 1080 | ss = 0; | 
|  | 1081 | else if (sscanf(string, "%"SCNdZIC":%d:%d%c", &hh, &mm, &ss, &xs) | 
|  | 1082 | != 3) { | 
|  | 1083 | error("%s", errstring); | 
|  | 1084 | return 0; | 
|  | 1085 | } | 
|  | 1086 | if (hh < 0 || | 
|  | 1087 | mm < 0 || mm >= MINSPERHOUR || | 
|  | 1088 | ss < 0 || ss > SECSPERMIN) { | 
|  | 1089 | error("%s", errstring); | 
|  | 1090 | return 0; | 
|  | 1091 | } | 
|  | 1092 | if (ZIC_MAX / SECSPERHOUR < hh) { | 
|  | 1093 | error(_("time overflow")); | 
|  | 1094 | return 0; | 
|  | 1095 | } | 
|  | 1096 | if (noise && (hh > HOURSPERDAY || | 
|  | 1097 | (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) | 
|  | 1098 | warning(_("values over 24 hours not handled by pre-2007 versions of zic")); | 
|  | 1099 | return oadd(sign * hh * SECSPERHOUR, | 
|  | 1100 | sign * (mm * SECSPERMIN + ss)); | 
|  | 1101 | } | 
|  | 1102 |  | 
|  | 1103 | static void | 
|  | 1104 | inrule(char **fields, int nfields) | 
|  | 1105 | { | 
|  | 1106 | static struct rule	r; | 
|  | 1107 |  | 
|  | 1108 | if (nfields != RULE_FIELDS) { | 
|  | 1109 | error(_("wrong number of fields on Rule line")); | 
|  | 1110 | return; | 
|  | 1111 | } | 
|  | 1112 | if (*fields[RF_NAME] == '\0') { | 
|  | 1113 | error(_("nameless rule")); | 
|  | 1114 | return; | 
|  | 1115 | } | 
|  | 1116 | r.r_filename = filename; | 
|  | 1117 | r.r_linenum = linenum; | 
|  | 1118 | r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true); | 
|  | 1119 | rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], | 
|  | 1120 | fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); | 
|  | 1121 | r.r_name = ecpyalloc(fields[RF_NAME]); | 
|  | 1122 | r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); | 
|  | 1123 | if (max_abbrvar_len < strlen(r.r_abbrvar)) | 
|  | 1124 | max_abbrvar_len = strlen(r.r_abbrvar); | 
|  | 1125 | rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc); | 
|  | 1126 | rules[nrules++] = r; | 
|  | 1127 | } | 
|  | 1128 |  | 
|  | 1129 | static bool | 
|  | 1130 | inzone(char **fields, int nfields) | 
|  | 1131 | { | 
|  | 1132 | register int	i; | 
|  | 1133 |  | 
|  | 1134 | if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { | 
|  | 1135 | error(_("wrong number of fields on Zone line")); | 
|  | 1136 | return false; | 
|  | 1137 | } | 
|  | 1138 | if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { | 
|  | 1139 | error( | 
|  | 1140 | _("\"Zone %s\" line and -l option are mutually exclusive"), | 
|  | 1141 | TZDEFAULT); | 
|  | 1142 | return false; | 
|  | 1143 | } | 
|  | 1144 | if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { | 
|  | 1145 | error( | 
|  | 1146 | _("\"Zone %s\" line and -p option are mutually exclusive"), | 
|  | 1147 | TZDEFRULES); | 
|  | 1148 | return false; | 
|  | 1149 | } | 
|  | 1150 | for (i = 0; i < nzones; ++i) | 
|  | 1151 | if (zones[i].z_name != NULL && | 
|  | 1152 | strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { | 
|  | 1153 | error( | 
|  | 1154 | _("duplicate zone name %s (file \"%s\", line %d)"), | 
|  | 1155 | fields[ZF_NAME], | 
|  | 1156 | zones[i].z_filename, | 
|  | 1157 | zones[i].z_linenum); | 
|  | 1158 | return false; | 
|  | 1159 | } | 
|  | 1160 | return inzsub(fields, nfields, false); | 
|  | 1161 | } | 
|  | 1162 |  | 
|  | 1163 | static bool | 
|  | 1164 | inzcont(char **fields, int nfields) | 
|  | 1165 | { | 
|  | 1166 | if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { | 
|  | 1167 | error(_("wrong number of fields on Zone continuation line")); | 
|  | 1168 | return false; | 
|  | 1169 | } | 
|  | 1170 | return inzsub(fields, nfields, true); | 
|  | 1171 | } | 
|  | 1172 |  | 
|  | 1173 | static bool | 
|  | 1174 | inzsub(char **fields, int nfields, bool iscont) | 
|  | 1175 | { | 
|  | 1176 | register char *		cp; | 
|  | 1177 | char *			cp1; | 
|  | 1178 | static struct zone	z; | 
|  | 1179 | register int		i_gmtoff, i_rule, i_format; | 
|  | 1180 | register int		i_untilyear, i_untilmonth; | 
|  | 1181 | register int		i_untilday, i_untiltime; | 
|  | 1182 | register bool		hasuntil; | 
|  | 1183 |  | 
|  | 1184 | if (iscont) { | 
|  | 1185 | i_gmtoff = ZFC_GMTOFF; | 
|  | 1186 | i_rule = ZFC_RULE; | 
|  | 1187 | i_format = ZFC_FORMAT; | 
|  | 1188 | i_untilyear = ZFC_TILYEAR; | 
|  | 1189 | i_untilmonth = ZFC_TILMONTH; | 
|  | 1190 | i_untilday = ZFC_TILDAY; | 
|  | 1191 | i_untiltime = ZFC_TILTIME; | 
|  | 1192 | z.z_name = NULL; | 
|  | 1193 | } else if (!namecheck(fields[ZF_NAME])) | 
|  | 1194 | return false; | 
|  | 1195 | else { | 
|  | 1196 | i_gmtoff = ZF_GMTOFF; | 
|  | 1197 | i_rule = ZF_RULE; | 
|  | 1198 | i_format = ZF_FORMAT; | 
|  | 1199 | i_untilyear = ZF_TILYEAR; | 
|  | 1200 | i_untilmonth = ZF_TILMONTH; | 
|  | 1201 | i_untilday = ZF_TILDAY; | 
|  | 1202 | i_untiltime = ZF_TILTIME; | 
|  | 1203 | z.z_name = ecpyalloc(fields[ZF_NAME]); | 
|  | 1204 | } | 
|  | 1205 | z.z_filename = filename; | 
|  | 1206 | z.z_linenum = linenum; | 
|  | 1207 | z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true); | 
|  | 1208 | if ((cp = strchr(fields[i_format], '%')) != 0) { | 
|  | 1209 | if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') | 
|  | 1210 | || strchr(fields[i_format], '/')) { | 
|  | 1211 | error(_("invalid abbreviation format")); | 
|  | 1212 | return false; | 
|  | 1213 | } | 
|  | 1214 | } | 
|  | 1215 | z.z_rule = ecpyalloc(fields[i_rule]); | 
|  | 1216 | z.z_format = cp1 = ecpyalloc(fields[i_format]); | 
|  | 1217 | z.z_format_specifier = cp ? *cp : '\0'; | 
|  | 1218 | if (z.z_format_specifier == 'z') { | 
|  | 1219 | if (noise) | 
|  | 1220 | warning(_("format '%s' not handled by pre-2015 versions of zic"), | 
|  | 1221 | z.z_format); | 
|  | 1222 | cp1[cp - fields[i_format]] = 's'; | 
|  | 1223 | } | 
|  | 1224 | if (max_format_len < strlen(z.z_format)) | 
|  | 1225 | max_format_len = strlen(z.z_format); | 
|  | 1226 | hasuntil = nfields > i_untilyear; | 
|  | 1227 | if (hasuntil) { | 
|  | 1228 | z.z_untilrule.r_filename = filename; | 
|  | 1229 | z.z_untilrule.r_linenum = linenum; | 
|  | 1230 | rulesub(&z.z_untilrule, | 
|  | 1231 | fields[i_untilyear], | 
|  | 1232 | "only", | 
|  | 1233 | "", | 
|  | 1234 | (nfields > i_untilmonth) ? | 
|  | 1235 | fields[i_untilmonth] : "Jan", | 
|  | 1236 | (nfields > i_untilday) ? fields[i_untilday] : "1", | 
|  | 1237 | (nfields > i_untiltime) ? fields[i_untiltime] : "0"); | 
|  | 1238 | z.z_untiltime = rpytime(&z.z_untilrule, | 
|  | 1239 | z.z_untilrule.r_loyear); | 
|  | 1240 | if (iscont && nzones > 0 && | 
|  | 1241 | z.z_untiltime > min_time && | 
|  | 1242 | z.z_untiltime < max_time && | 
|  | 1243 | zones[nzones - 1].z_untiltime > min_time && | 
|  | 1244 | zones[nzones - 1].z_untiltime < max_time && | 
|  | 1245 | zones[nzones - 1].z_untiltime >= z.z_untiltime) { | 
|  | 1246 | error(_( | 
|  | 1247 | "Zone continuation line end time is not after end time of previous line" | 
|  | 1248 | )); | 
|  | 1249 | return false; | 
|  | 1250 | } | 
|  | 1251 | } | 
|  | 1252 | zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc); | 
|  | 1253 | zones[nzones++] = z; | 
|  | 1254 | /* | 
|  | 1255 | ** If there was an UNTIL field on this line, | 
|  | 1256 | ** there's more information about the zone on the next line. | 
|  | 1257 | */ | 
|  | 1258 | return hasuntil; | 
|  | 1259 | } | 
|  | 1260 |  | 
|  | 1261 | static void | 
|  | 1262 | inleap(char **fields, int nfields) | 
|  | 1263 | { | 
|  | 1264 | register const char *		cp; | 
|  | 1265 | register const struct lookup *	lp; | 
|  | 1266 | register int			i, j; | 
|  | 1267 | zic_t				year; | 
|  | 1268 | int				month, day; | 
|  | 1269 | zic_t				dayoff, tod; | 
|  | 1270 | zic_t				t; | 
|  | 1271 | char xs; | 
|  | 1272 |  | 
|  | 1273 | if (nfields != LEAP_FIELDS) { | 
|  | 1274 | error(_("wrong number of fields on Leap line")); | 
|  | 1275 | return; | 
|  | 1276 | } | 
|  | 1277 | dayoff = 0; | 
|  | 1278 | cp = fields[LP_YEAR]; | 
|  | 1279 | if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) { | 
|  | 1280 | /* | 
|  | 1281 | ** Leapin' Lizards! | 
|  | 1282 | */ | 
|  | 1283 | error(_("invalid leaping year")); | 
|  | 1284 | return; | 
|  | 1285 | } | 
|  | 1286 | if (!leapseen || leapmaxyear < year) | 
|  | 1287 | leapmaxyear = year; | 
|  | 1288 | if (!leapseen || leapminyear > year) | 
|  | 1289 | leapminyear = year; | 
|  | 1290 | leapseen = true; | 
|  | 1291 | j = EPOCH_YEAR; | 
|  | 1292 | while (j != year) { | 
|  | 1293 | if (year > j) { | 
|  | 1294 | i = len_years[isleap(j)]; | 
|  | 1295 | ++j; | 
|  | 1296 | } else { | 
|  | 1297 | --j; | 
|  | 1298 | i = -len_years[isleap(j)]; | 
|  | 1299 | } | 
|  | 1300 | dayoff = oadd(dayoff, i); | 
|  | 1301 | } | 
|  | 1302 | if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { | 
|  | 1303 | error(_("invalid month name")); | 
|  | 1304 | return; | 
|  | 1305 | } | 
|  | 1306 | month = lp->l_value; | 
|  | 1307 | j = TM_JANUARY; | 
|  | 1308 | while (j != month) { | 
|  | 1309 | i = len_months[isleap(year)][j]; | 
|  | 1310 | dayoff = oadd(dayoff, i); | 
|  | 1311 | ++j; | 
|  | 1312 | } | 
|  | 1313 | cp = fields[LP_DAY]; | 
|  | 1314 | if (sscanf(cp, "%d%c", &day, &xs) != 1 || | 
|  | 1315 | day <= 0 || day > len_months[isleap(year)][month]) { | 
|  | 1316 | error(_("invalid day of month")); | 
|  | 1317 | return; | 
|  | 1318 | } | 
|  | 1319 | dayoff = oadd(dayoff, day - 1); | 
|  | 1320 | if (dayoff < min_time / SECSPERDAY) { | 
|  | 1321 | error(_("time too small")); | 
|  | 1322 | return; | 
|  | 1323 | } | 
|  | 1324 | if (dayoff > max_time / SECSPERDAY) { | 
|  | 1325 | error(_("time too large")); | 
|  | 1326 | return; | 
|  | 1327 | } | 
|  | 1328 | t = dayoff * SECSPERDAY; | 
|  | 1329 | tod = gethms(fields[LP_TIME], _("invalid time of day"), false); | 
|  | 1330 | cp = fields[LP_CORR]; | 
|  | 1331 | { | 
|  | 1332 | register bool	positive; | 
|  | 1333 | int		count; | 
|  | 1334 |  | 
|  | 1335 | if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ | 
|  | 1336 | positive = false; | 
|  | 1337 | count = 1; | 
|  | 1338 | } else if (strcmp(cp, "--") == 0) { | 
|  | 1339 | positive = false; | 
|  | 1340 | count = 2; | 
|  | 1341 | } else if (strcmp(cp, "+") == 0) { | 
|  | 1342 | positive = true; | 
|  | 1343 | count = 1; | 
|  | 1344 | } else if (strcmp(cp, "++") == 0) { | 
|  | 1345 | positive = true; | 
|  | 1346 | count = 2; | 
|  | 1347 | } else { | 
|  | 1348 | error(_("illegal CORRECTION field on Leap line")); | 
|  | 1349 | return; | 
|  | 1350 | } | 
|  | 1351 | if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { | 
|  | 1352 | error(_( | 
|  | 1353 | "illegal Rolling/Stationary field on Leap line" | 
|  | 1354 | )); | 
|  | 1355 | return; | 
|  | 1356 | } | 
|  | 1357 | t = tadd(t, tod); | 
|  | 1358 | if (t < big_bang_time) { | 
|  | 1359 | error(_("leap second precedes Big Bang")); | 
|  | 1360 | return; | 
|  | 1361 | } | 
|  | 1362 | leapadd(t, positive, lp->l_value, count); | 
|  | 1363 | } | 
|  | 1364 | } | 
|  | 1365 |  | 
|  | 1366 | static void | 
|  | 1367 | inlink(char **fields, int nfields) | 
|  | 1368 | { | 
|  | 1369 | struct link	l; | 
|  | 1370 |  | 
|  | 1371 | if (nfields != LINK_FIELDS) { | 
|  | 1372 | error(_("wrong number of fields on Link line")); | 
|  | 1373 | return; | 
|  | 1374 | } | 
|  | 1375 | if (*fields[LF_FROM] == '\0') { | 
|  | 1376 | error(_("blank FROM field on Link line")); | 
|  | 1377 | return; | 
|  | 1378 | } | 
|  | 1379 | if (! namecheck(fields[LF_TO])) | 
|  | 1380 | return; | 
|  | 1381 | l.l_filename = filename; | 
|  | 1382 | l.l_linenum = linenum; | 
|  | 1383 | l.l_from = ecpyalloc(fields[LF_FROM]); | 
|  | 1384 | l.l_to = ecpyalloc(fields[LF_TO]); | 
|  | 1385 | links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc); | 
|  | 1386 | links[nlinks++] = l; | 
|  | 1387 | } | 
|  | 1388 |  | 
|  | 1389 | static void | 
|  | 1390 | rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, | 
|  | 1391 | const char *typep, const char *monthp, const char *dayp, | 
|  | 1392 | const char *timep) | 
|  | 1393 | { | 
|  | 1394 | register const struct lookup *	lp; | 
|  | 1395 | register const char *		cp; | 
|  | 1396 | register char *			dp; | 
|  | 1397 | register char *			ep; | 
|  | 1398 | char xs; | 
|  | 1399 |  | 
|  | 1400 | if ((lp = byword(monthp, mon_names)) == NULL) { | 
|  | 1401 | error(_("invalid month name")); | 
|  | 1402 | return; | 
|  | 1403 | } | 
|  | 1404 | rp->r_month = lp->l_value; | 
|  | 1405 | rp->r_todisstd = false; | 
|  | 1406 | rp->r_todisgmt = false; | 
|  | 1407 | dp = ecpyalloc(timep); | 
|  | 1408 | if (*dp != '\0') { | 
|  | 1409 | ep = dp + strlen(dp) - 1; | 
|  | 1410 | switch (lowerit(*ep)) { | 
|  | 1411 | case 's':	/* Standard */ | 
|  | 1412 | rp->r_todisstd = true; | 
|  | 1413 | rp->r_todisgmt = false; | 
|  | 1414 | *ep = '\0'; | 
|  | 1415 | break; | 
|  | 1416 | case 'w':	/* Wall */ | 
|  | 1417 | rp->r_todisstd = false; | 
|  | 1418 | rp->r_todisgmt = false; | 
|  | 1419 | *ep = '\0'; | 
|  | 1420 | break; | 
|  | 1421 | case 'g':	/* Greenwich */ | 
|  | 1422 | case 'u':	/* Universal */ | 
|  | 1423 | case 'z':	/* Zulu */ | 
|  | 1424 | rp->r_todisstd = true; | 
|  | 1425 | rp->r_todisgmt = true; | 
|  | 1426 | *ep = '\0'; | 
|  | 1427 | break; | 
|  | 1428 | } | 
|  | 1429 | } | 
|  | 1430 | rp->r_tod = gethms(dp, _("invalid time of day"), false); | 
|  | 1431 | free(dp); | 
|  | 1432 | /* | 
|  | 1433 | ** Year work. | 
|  | 1434 | */ | 
|  | 1435 | cp = loyearp; | 
|  | 1436 | lp = byword(cp, begin_years); | 
|  | 1437 | rp->r_lowasnum = lp == NULL; | 
|  | 1438 | if (!rp->r_lowasnum) switch ((int) lp->l_value) { | 
|  | 1439 | case YR_MINIMUM: | 
|  | 1440 | rp->r_loyear = ZIC_MIN; | 
|  | 1441 | break; | 
|  | 1442 | case YR_MAXIMUM: | 
|  | 1443 | rp->r_loyear = ZIC_MAX; | 
|  | 1444 | break; | 
|  | 1445 | default:	/* "cannot happen" */ | 
|  | 1446 | fprintf(stderr, | 
|  | 1447 | _("%s: panic: Invalid l_value %d\n"), | 
|  | 1448 | progname, lp->l_value); | 
|  | 1449 | exit(EXIT_FAILURE); | 
|  | 1450 | } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) { | 
|  | 1451 | error(_("invalid starting year")); | 
|  | 1452 | return; | 
|  | 1453 | } | 
|  | 1454 | cp = hiyearp; | 
|  | 1455 | lp = byword(cp, end_years); | 
|  | 1456 | rp->r_hiwasnum = lp == NULL; | 
|  | 1457 | if (!rp->r_hiwasnum) switch ((int) lp->l_value) { | 
|  | 1458 | case YR_MINIMUM: | 
|  | 1459 | rp->r_hiyear = ZIC_MIN; | 
|  | 1460 | break; | 
|  | 1461 | case YR_MAXIMUM: | 
|  | 1462 | rp->r_hiyear = ZIC_MAX; | 
|  | 1463 | break; | 
|  | 1464 | case YR_ONLY: | 
|  | 1465 | rp->r_hiyear = rp->r_loyear; | 
|  | 1466 | break; | 
|  | 1467 | default:	/* "cannot happen" */ | 
|  | 1468 | fprintf(stderr, | 
|  | 1469 | _("%s: panic: Invalid l_value %d\n"), | 
|  | 1470 | progname, lp->l_value); | 
|  | 1471 | exit(EXIT_FAILURE); | 
|  | 1472 | } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) { | 
|  | 1473 | error(_("invalid ending year")); | 
|  | 1474 | return; | 
|  | 1475 | } | 
|  | 1476 | if (rp->r_loyear > rp->r_hiyear) { | 
|  | 1477 | error(_("starting year greater than ending year")); | 
|  | 1478 | return; | 
|  | 1479 | } | 
|  | 1480 | if (*typep == '\0') | 
|  | 1481 | rp->r_yrtype = NULL; | 
|  | 1482 | else { | 
|  | 1483 | if (rp->r_loyear == rp->r_hiyear) { | 
|  | 1484 | error(_("typed single year")); | 
|  | 1485 | return; | 
|  | 1486 | } | 
|  | 1487 | rp->r_yrtype = ecpyalloc(typep); | 
|  | 1488 | } | 
|  | 1489 | /* | 
|  | 1490 | ** Day work. | 
|  | 1491 | ** Accept things such as: | 
|  | 1492 | **	1 | 
|  | 1493 | **	last-Sunday | 
|  | 1494 | **	Sun<=20 | 
|  | 1495 | **	Sun>=7 | 
|  | 1496 | */ | 
|  | 1497 | dp = ecpyalloc(dayp); | 
|  | 1498 | if ((lp = byword(dp, lasts)) != NULL) { | 
|  | 1499 | rp->r_dycode = DC_DOWLEQ; | 
|  | 1500 | rp->r_wday = lp->l_value; | 
|  | 1501 | rp->r_dayofmonth = len_months[1][rp->r_month]; | 
|  | 1502 | } else { | 
|  | 1503 | if ((ep = strchr(dp, '<')) != 0) | 
|  | 1504 | rp->r_dycode = DC_DOWLEQ; | 
|  | 1505 | else if ((ep = strchr(dp, '>')) != 0) | 
|  | 1506 | rp->r_dycode = DC_DOWGEQ; | 
|  | 1507 | else { | 
|  | 1508 | ep = dp; | 
|  | 1509 | rp->r_dycode = DC_DOM; | 
|  | 1510 | } | 
|  | 1511 | if (rp->r_dycode != DC_DOM) { | 
|  | 1512 | *ep++ = 0; | 
|  | 1513 | if (*ep++ != '=') { | 
|  | 1514 | error(_("invalid day of month")); | 
|  | 1515 | free(dp); | 
|  | 1516 | return; | 
|  | 1517 | } | 
|  | 1518 | if ((lp = byword(dp, wday_names)) == NULL) { | 
|  | 1519 | error(_("invalid weekday name")); | 
|  | 1520 | free(dp); | 
|  | 1521 | return; | 
|  | 1522 | } | 
|  | 1523 | rp->r_wday = lp->l_value; | 
|  | 1524 | } | 
|  | 1525 | if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 || | 
|  | 1526 | rp->r_dayofmonth <= 0 || | 
|  | 1527 | (rp->r_dayofmonth > len_months[1][rp->r_month])) { | 
|  | 1528 | error(_("invalid day of month")); | 
|  | 1529 | free(dp); | 
|  | 1530 | return; | 
|  | 1531 | } | 
|  | 1532 | } | 
|  | 1533 | free(dp); | 
|  | 1534 | } | 
|  | 1535 |  | 
|  | 1536 | static void | 
|  | 1537 | convert(const int_fast32_t val, char *const buf) | 
|  | 1538 | { | 
|  | 1539 | register int	i; | 
|  | 1540 | register int	shift; | 
|  | 1541 | unsigned char *const b = (unsigned char *) buf; | 
|  | 1542 |  | 
|  | 1543 | for (i = 0, shift = 24; i < 4; ++i, shift -= 8) | 
|  | 1544 | b[i] = val >> shift; | 
|  | 1545 | } | 
|  | 1546 |  | 
|  | 1547 | static void | 
|  | 1548 | convert64(const zic_t val, char *const buf) | 
|  | 1549 | { | 
|  | 1550 | register int	i; | 
|  | 1551 | register int	shift; | 
|  | 1552 | unsigned char *const b = (unsigned char *) buf; | 
|  | 1553 |  | 
|  | 1554 | for (i = 0, shift = 56; i < 8; ++i, shift -= 8) | 
|  | 1555 | b[i] = val >> shift; | 
|  | 1556 | } | 
|  | 1557 |  | 
|  | 1558 | static void | 
|  | 1559 | puttzcode(const int_fast32_t val, FILE *const fp) | 
|  | 1560 | { | 
|  | 1561 | char	buf[4]; | 
|  | 1562 |  | 
|  | 1563 | convert(val, buf); | 
|  | 1564 | fwrite(buf, sizeof buf, 1, fp); | 
|  | 1565 | } | 
|  | 1566 |  | 
|  | 1567 | static void | 
|  | 1568 | puttzcode64(const zic_t val, FILE *const fp) | 
|  | 1569 | { | 
|  | 1570 | char	buf[8]; | 
|  | 1571 |  | 
|  | 1572 | convert64(val, buf); | 
|  | 1573 | fwrite(buf, sizeof buf, 1, fp); | 
|  | 1574 | } | 
|  | 1575 |  | 
|  | 1576 | static int | 
|  | 1577 | atcomp(const void *avp, const void *bvp) | 
|  | 1578 | { | 
|  | 1579 | const zic_t	a = ((const struct attype *) avp)->at; | 
|  | 1580 | const zic_t	b = ((const struct attype *) bvp)->at; | 
|  | 1581 |  | 
|  | 1582 | return (a < b) ? -1 : (a > b); | 
|  | 1583 | } | 
|  | 1584 |  | 
|  | 1585 | static bool | 
|  | 1586 | is32(const zic_t x) | 
|  | 1587 | { | 
|  | 1588 | return INT32_MIN <= x && x <= INT32_MAX; | 
|  | 1589 | } | 
|  | 1590 |  | 
|  | 1591 | static void | 
|  | 1592 | writezone(const char *const name, const char *const string, char version) | 
|  | 1593 | { | 
|  | 1594 | register FILE *			fp; | 
|  | 1595 | register int			i, j; | 
|  | 1596 | register int			leapcnt32, leapi32; | 
|  | 1597 | register int			timecnt32, timei32; | 
|  | 1598 | register int			pass; | 
|  | 1599 | char *				fullname; | 
|  | 1600 | static const struct tzhead	tzh0; | 
|  | 1601 | static struct tzhead		tzh; | 
|  | 1602 | zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1)); | 
|  | 1603 | void *typesptr = ats + timecnt; | 
|  | 1604 | unsigned char *types = typesptr; | 
|  | 1605 |  | 
|  | 1606 | /* | 
|  | 1607 | ** Sort. | 
|  | 1608 | */ | 
|  | 1609 | if (timecnt > 1) | 
|  | 1610 | qsort(attypes, timecnt, sizeof *attypes, atcomp); | 
|  | 1611 | /* | 
|  | 1612 | ** Optimize. | 
|  | 1613 | */ | 
|  | 1614 | { | 
|  | 1615 | int	fromi; | 
|  | 1616 | int	toi; | 
|  | 1617 |  | 
|  | 1618 | toi = 0; | 
|  | 1619 | fromi = 0; | 
|  | 1620 | while (fromi < timecnt && attypes[fromi].at < big_bang_time) | 
|  | 1621 | ++fromi; | 
|  | 1622 | for ( ; fromi < timecnt; ++fromi) { | 
|  | 1623 | if (toi > 1 && ((attypes[fromi].at + | 
|  | 1624 | gmtoffs[attypes[toi - 1].type]) <= | 
|  | 1625 | (attypes[toi - 1].at + | 
|  | 1626 | gmtoffs[attypes[toi - 2].type]))) { | 
|  | 1627 | attypes[toi - 1].type = | 
|  | 1628 | attypes[fromi].type; | 
|  | 1629 | continue; | 
|  | 1630 | } | 
|  | 1631 | if (toi == 0 || | 
|  | 1632 | attypes[toi - 1].type != attypes[fromi].type) | 
|  | 1633 | attypes[toi++] = attypes[fromi]; | 
|  | 1634 | } | 
|  | 1635 | timecnt = toi; | 
|  | 1636 | } | 
|  | 1637 | if (noise && timecnt > 1200) | 
|  | 1638 | warning(_("pre-2014 clients may mishandle" | 
|  | 1639 | " more than 1200 transition times")); | 
|  | 1640 | /* | 
|  | 1641 | ** Transfer. | 
|  | 1642 | */ | 
|  | 1643 | for (i = 0; i < timecnt; ++i) { | 
|  | 1644 | ats[i] = attypes[i].at; | 
|  | 1645 | types[i] = attypes[i].type; | 
|  | 1646 | } | 
|  | 1647 | /* | 
|  | 1648 | ** Correct for leap seconds. | 
|  | 1649 | */ | 
|  | 1650 | for (i = 0; i < timecnt; ++i) { | 
|  | 1651 | j = leapcnt; | 
|  | 1652 | while (--j >= 0) | 
|  | 1653 | if (ats[i] > trans[j] - corr[j]) { | 
|  | 1654 | ats[i] = tadd(ats[i], corr[j]); | 
|  | 1655 | break; | 
|  | 1656 | } | 
|  | 1657 | } | 
|  | 1658 | /* | 
|  | 1659 | ** Figure out 32-bit-limited starts and counts. | 
|  | 1660 | */ | 
|  | 1661 | timecnt32 = timecnt; | 
|  | 1662 | timei32 = 0; | 
|  | 1663 | leapcnt32 = leapcnt; | 
|  | 1664 | leapi32 = 0; | 
|  | 1665 | while (timecnt32 > 0 && !is32(ats[timecnt32 - 1])) | 
|  | 1666 | --timecnt32; | 
|  | 1667 | while (timecnt32 > 0 && !is32(ats[timei32])) { | 
|  | 1668 | --timecnt32; | 
|  | 1669 | ++timei32; | 
|  | 1670 | } | 
|  | 1671 | /* | 
|  | 1672 | ** Output an INT32_MIN "transition" if appropriate; see below. | 
|  | 1673 | */ | 
|  | 1674 | if (timei32 > 0 && ats[timei32] > INT32_MIN) { | 
|  | 1675 | --timei32; | 
|  | 1676 | ++timecnt32; | 
|  | 1677 | } | 
|  | 1678 | while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) | 
|  | 1679 | --leapcnt32; | 
|  | 1680 | while (leapcnt32 > 0 && !is32(trans[leapi32])) { | 
|  | 1681 | --leapcnt32; | 
|  | 1682 | ++leapi32; | 
|  | 1683 | } | 
|  | 1684 | fullname = relname(directory, name); | 
|  | 1685 | /* | 
|  | 1686 | ** Remove old file, if any, to snap links. | 
|  | 1687 | */ | 
|  | 1688 | if (itsdir(fullname) <= 0 && remove(fullname) != 0 && errno != ENOENT) { | 
|  | 1689 | const char *e = strerror(errno); | 
|  | 1690 |  | 
|  | 1691 | fprintf(stderr, _("%s: Can't remove %s: %s\n"), | 
|  | 1692 | progname, fullname, e); | 
|  | 1693 | exit(EXIT_FAILURE); | 
|  | 1694 | } | 
|  | 1695 | if ((fp = fopen(fullname, "wb")) == NULL) { | 
|  | 1696 | if (! mkdirs(fullname)) | 
|  | 1697 | exit(EXIT_FAILURE); | 
|  | 1698 | if ((fp = fopen(fullname, "wb")) == NULL) { | 
|  | 1699 | const char *e = strerror(errno); | 
|  | 1700 |  | 
|  | 1701 | fprintf(stderr, _("%s: Can't create %s: %s\n"), | 
|  | 1702 | progname, fullname, e); | 
|  | 1703 | exit(EXIT_FAILURE); | 
|  | 1704 | } | 
|  | 1705 | } | 
|  | 1706 | for (pass = 1; pass <= 2; ++pass) { | 
|  | 1707 | register int	thistimei, thistimecnt; | 
|  | 1708 | register int	thisleapi, thisleapcnt; | 
|  | 1709 | register int	thistimelim, thisleaplim; | 
|  | 1710 | int		writetype[TZ_MAX_TYPES]; | 
|  | 1711 | int		typemap[TZ_MAX_TYPES]; | 
|  | 1712 | register int	thistypecnt; | 
|  | 1713 | char		thischars[TZ_MAX_CHARS]; | 
|  | 1714 | char		thischarcnt; | 
|  | 1715 | int		indmap[TZ_MAX_CHARS]; | 
|  | 1716 |  | 
|  | 1717 | if (pass == 1) { | 
|  | 1718 | thistimei = timei32; | 
|  | 1719 | thistimecnt = timecnt32; | 
|  | 1720 | thisleapi = leapi32; | 
|  | 1721 | thisleapcnt = leapcnt32; | 
|  | 1722 | } else { | 
|  | 1723 | thistimei = 0; | 
|  | 1724 | thistimecnt = timecnt; | 
|  | 1725 | thisleapi = 0; | 
|  | 1726 | thisleapcnt = leapcnt; | 
|  | 1727 | } | 
|  | 1728 | thistimelim = thistimei + thistimecnt; | 
|  | 1729 | thisleaplim = thisleapi + thisleapcnt; | 
|  | 1730 | for (i = 0; i < typecnt; ++i) | 
|  | 1731 | writetype[i] = thistimecnt == timecnt; | 
|  | 1732 | if (thistimecnt == 0) { | 
|  | 1733 | /* | 
|  | 1734 | ** No transition times fall in the current | 
|  | 1735 | ** (32- or 64-bit) window. | 
|  | 1736 | */ | 
|  | 1737 | if (typecnt != 0) | 
|  | 1738 | writetype[typecnt - 1] = true; | 
|  | 1739 | } else { | 
|  | 1740 | for (i = thistimei - 1; i < thistimelim; ++i) | 
|  | 1741 | if (i >= 0) | 
|  | 1742 | writetype[types[i]] = true; | 
|  | 1743 | /* | 
|  | 1744 | ** For America/Godthab and Antarctica/Palmer | 
|  | 1745 | */ | 
|  | 1746 | if (thistimei == 0) | 
|  | 1747 | writetype[0] = true; | 
|  | 1748 | } | 
|  | 1749 | #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH | 
|  | 1750 | /* | 
|  | 1751 | ** For some pre-2011 systems: if the last-to-be-written | 
|  | 1752 | ** standard (or daylight) type has an offset different from the | 
|  | 1753 | ** most recently used offset, | 
|  | 1754 | ** append an (unused) copy of the most recently used type | 
|  | 1755 | ** (to help get global "altzone" and "timezone" variables | 
|  | 1756 | ** set correctly). | 
|  | 1757 | */ | 
|  | 1758 | { | 
|  | 1759 | register int	mrudst, mrustd, hidst, histd, type; | 
|  | 1760 |  | 
|  | 1761 | hidst = histd = mrudst = mrustd = -1; | 
|  | 1762 | for (i = thistimei; i < thistimelim; ++i) | 
|  | 1763 | if (isdsts[types[i]]) | 
|  | 1764 | mrudst = types[i]; | 
|  | 1765 | else	mrustd = types[i]; | 
|  | 1766 | for (i = 0; i < typecnt; ++i) | 
|  | 1767 | if (writetype[i]) { | 
|  | 1768 | if (isdsts[i]) | 
|  | 1769 | hidst = i; | 
|  | 1770 | else	histd = i; | 
|  | 1771 | } | 
|  | 1772 | if (hidst >= 0 && mrudst >= 0 && hidst != mrudst && | 
|  | 1773 | gmtoffs[hidst] != gmtoffs[mrudst]) { | 
|  | 1774 | isdsts[mrudst] = -1; | 
|  | 1775 | type = addtype(gmtoffs[mrudst], | 
|  | 1776 | &chars[abbrinds[mrudst]], | 
|  | 1777 | true, | 
|  | 1778 | ttisstds[mrudst], | 
|  | 1779 | ttisgmts[mrudst]); | 
|  | 1780 | isdsts[mrudst] = 1; | 
|  | 1781 | writetype[type] = true; | 
|  | 1782 | } | 
|  | 1783 | if (histd >= 0 && mrustd >= 0 && histd != mrustd && | 
|  | 1784 | gmtoffs[histd] != gmtoffs[mrustd]) { | 
|  | 1785 | isdsts[mrustd] = -1; | 
|  | 1786 | type = addtype(gmtoffs[mrustd], | 
|  | 1787 | &chars[abbrinds[mrustd]], | 
|  | 1788 | false, | 
|  | 1789 | ttisstds[mrustd], | 
|  | 1790 | ttisgmts[mrustd]); | 
|  | 1791 | isdsts[mrustd] = 0; | 
|  | 1792 | writetype[type] = true; | 
|  | 1793 | } | 
|  | 1794 | } | 
|  | 1795 | #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ | 
|  | 1796 | thistypecnt = 0; | 
|  | 1797 | for (i = 0; i < typecnt; ++i) | 
|  | 1798 | typemap[i] = writetype[i] ?  thistypecnt++ : -1; | 
|  | 1799 | for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i) | 
|  | 1800 | indmap[i] = -1; | 
|  | 1801 | thischarcnt = 0; | 
|  | 1802 | for (i = 0; i < typecnt; ++i) { | 
|  | 1803 | register char *	thisabbr; | 
|  | 1804 |  | 
|  | 1805 | if (!writetype[i]) | 
|  | 1806 | continue; | 
|  | 1807 | if (indmap[abbrinds[i]] >= 0) | 
|  | 1808 | continue; | 
|  | 1809 | thisabbr = &chars[abbrinds[i]]; | 
|  | 1810 | for (j = 0; j < thischarcnt; ++j) | 
|  | 1811 | if (strcmp(&thischars[j], thisabbr) == 0) | 
|  | 1812 | break; | 
|  | 1813 | if (j == thischarcnt) { | 
|  | 1814 | strcpy(&thischars[(int) thischarcnt], | 
|  | 1815 | thisabbr); | 
|  | 1816 | thischarcnt += strlen(thisabbr) + 1; | 
|  | 1817 | } | 
|  | 1818 | indmap[abbrinds[i]] = j; | 
|  | 1819 | } | 
|  | 1820 | #define DO(field)	fwrite(tzh.field, sizeof tzh.field, 1, fp) | 
|  | 1821 | tzh = tzh0; | 
|  | 1822 | strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); | 
|  | 1823 | tzh.tzh_version[0] = version; | 
|  | 1824 | convert(thistypecnt, tzh.tzh_ttisgmtcnt); | 
|  | 1825 | convert(thistypecnt, tzh.tzh_ttisstdcnt); | 
|  | 1826 | convert(thisleapcnt, tzh.tzh_leapcnt); | 
|  | 1827 | convert(thistimecnt, tzh.tzh_timecnt); | 
|  | 1828 | convert(thistypecnt, tzh.tzh_typecnt); | 
|  | 1829 | convert(thischarcnt, tzh.tzh_charcnt); | 
|  | 1830 | DO(tzh_magic); | 
|  | 1831 | DO(tzh_version); | 
|  | 1832 | DO(tzh_reserved); | 
|  | 1833 | DO(tzh_ttisgmtcnt); | 
|  | 1834 | DO(tzh_ttisstdcnt); | 
|  | 1835 | DO(tzh_leapcnt); | 
|  | 1836 | DO(tzh_timecnt); | 
|  | 1837 | DO(tzh_typecnt); | 
|  | 1838 | DO(tzh_charcnt); | 
|  | 1839 | #undef DO | 
|  | 1840 | for (i = thistimei; i < thistimelim; ++i) | 
|  | 1841 | if (pass == 1) | 
|  | 1842 | /* | 
|  | 1843 | ** Output an INT32_MIN "transition" | 
|  | 1844 | ** if appropriate; see above. | 
|  | 1845 | */ | 
|  | 1846 | puttzcode(((ats[i] < INT32_MIN) ? | 
|  | 1847 | INT32_MIN : ats[i]), fp); | 
|  | 1848 | else	puttzcode64(ats[i], fp); | 
|  | 1849 | for (i = thistimei; i < thistimelim; ++i) { | 
|  | 1850 | unsigned char	uc; | 
|  | 1851 |  | 
|  | 1852 | uc = typemap[types[i]]; | 
|  | 1853 | fwrite(&uc, sizeof uc, 1, fp); | 
|  | 1854 | } | 
|  | 1855 | for (i = 0; i < typecnt; ++i) | 
|  | 1856 | if (writetype[i]) { | 
|  | 1857 | puttzcode(gmtoffs[i], fp); | 
|  | 1858 | putc(isdsts[i], fp); | 
|  | 1859 | putc((unsigned char) indmap[abbrinds[i]], fp); | 
|  | 1860 | } | 
|  | 1861 | if (thischarcnt != 0) | 
|  | 1862 | fwrite(thischars, sizeof thischars[0], | 
|  | 1863 | thischarcnt, fp); | 
|  | 1864 | for (i = thisleapi; i < thisleaplim; ++i) { | 
|  | 1865 | register zic_t	todo; | 
|  | 1866 |  | 
|  | 1867 | if (roll[i]) { | 
|  | 1868 | if (timecnt == 0 || trans[i] < ats[0]) { | 
|  | 1869 | j = 0; | 
|  | 1870 | while (isdsts[j]) | 
|  | 1871 | if (++j >= typecnt) { | 
|  | 1872 | j = 0; | 
|  | 1873 | break; | 
|  | 1874 | } | 
|  | 1875 | } else { | 
|  | 1876 | j = 1; | 
|  | 1877 | while (j < timecnt && | 
|  | 1878 | trans[i] >= ats[j]) | 
|  | 1879 | ++j; | 
|  | 1880 | j = types[j - 1]; | 
|  | 1881 | } | 
|  | 1882 | todo = tadd(trans[i], -gmtoffs[j]); | 
|  | 1883 | } else	todo = trans[i]; | 
|  | 1884 | if (pass == 1) | 
|  | 1885 | puttzcode(todo, fp); | 
|  | 1886 | else	puttzcode64(todo, fp); | 
|  | 1887 | puttzcode(corr[i], fp); | 
|  | 1888 | } | 
|  | 1889 | for (i = 0; i < typecnt; ++i) | 
|  | 1890 | if (writetype[i]) | 
|  | 1891 | putc(ttisstds[i], fp); | 
|  | 1892 | for (i = 0; i < typecnt; ++i) | 
|  | 1893 | if (writetype[i]) | 
|  | 1894 | putc(ttisgmts[i], fp); | 
|  | 1895 | } | 
|  | 1896 | fprintf(fp, "\n%s\n", string); | 
|  | 1897 | close_file(fp, fullname); | 
|  | 1898 | free(ats); | 
|  | 1899 | free(fullname); | 
|  | 1900 | } | 
|  | 1901 |  | 
|  | 1902 | static char const * | 
|  | 1903 | abbroffset(char *buf, zic_t offset) | 
|  | 1904 | { | 
|  | 1905 | char sign = '+'; | 
|  | 1906 | int seconds, minutes; | 
|  | 1907 |  | 
|  | 1908 | if (offset < 0) { | 
|  | 1909 | offset = -offset; | 
|  | 1910 | sign = '-'; | 
|  | 1911 | } | 
|  | 1912 |  | 
|  | 1913 | seconds = offset % SECSPERMIN; | 
|  | 1914 | offset /= SECSPERMIN; | 
|  | 1915 | minutes = offset % MINSPERHOUR; | 
|  | 1916 | offset /= MINSPERHOUR; | 
|  | 1917 | if (100 <= offset) { | 
|  | 1918 | error(_("%%z UTC offset magnitude exceeds 99:59:59")); | 
|  | 1919 | return "%z"; | 
|  | 1920 | } else { | 
|  | 1921 | char *p = buf; | 
|  | 1922 | *p++ = sign; | 
|  | 1923 | *p++ = '0' + offset / 10; | 
|  | 1924 | *p++ = '0' + offset % 10; | 
|  | 1925 | if (minutes | seconds) { | 
|  | 1926 | *p++ = '0' + minutes / 10; | 
|  | 1927 | *p++ = '0' + minutes % 10; | 
|  | 1928 | if (seconds) { | 
|  | 1929 | *p++ = '0' + seconds / 10; | 
|  | 1930 | *p++ = '0' + seconds % 10; | 
|  | 1931 | } | 
|  | 1932 | } | 
|  | 1933 | *p = '\0'; | 
|  | 1934 | return buf; | 
|  | 1935 | } | 
|  | 1936 | } | 
|  | 1937 |  | 
|  | 1938 | static size_t | 
|  | 1939 | doabbr(char *abbr, struct zone const *zp, char const *letters, | 
|  | 1940 | zic_t stdoff, bool doquotes) | 
|  | 1941 | { | 
|  | 1942 | register char *	cp; | 
|  | 1943 | register char *	slashp; | 
|  | 1944 | register size_t	len; | 
|  | 1945 | char const *format = zp->z_format; | 
|  | 1946 |  | 
|  | 1947 | slashp = strchr(format, '/'); | 
|  | 1948 | if (slashp == NULL) { | 
|  | 1949 | char letterbuf[PERCENT_Z_LEN_BOUND + 1]; | 
|  | 1950 | if (zp->z_format_specifier == 'z') | 
|  | 1951 | letters = abbroffset(letterbuf, zp->z_gmtoff + stdoff); | 
|  | 1952 | else if (!letters) | 
|  | 1953 | letters = "%s"; | 
|  | 1954 | sprintf(abbr, format, letters); | 
|  | 1955 | } else if (stdoff != 0) { | 
|  | 1956 | strcpy(abbr, slashp + 1); | 
|  | 1957 | } else { | 
|  | 1958 | memcpy(abbr, format, slashp - format); | 
|  | 1959 | abbr[slashp - format] = '\0'; | 
|  | 1960 | } | 
|  | 1961 | len = strlen(abbr); | 
|  | 1962 | if (!doquotes) | 
|  | 1963 | return len; | 
|  | 1964 | for (cp = abbr; is_alpha(*cp); cp++) | 
|  | 1965 | continue; | 
|  | 1966 | if (len > 0 && *cp == '\0') | 
|  | 1967 | return len; | 
|  | 1968 | abbr[len + 2] = '\0'; | 
|  | 1969 | abbr[len + 1] = '>'; | 
|  | 1970 | memmove(abbr + 1, abbr, len); | 
|  | 1971 | abbr[0] = '<'; | 
|  | 1972 | return len + 2; | 
|  | 1973 | } | 
|  | 1974 |  | 
|  | 1975 | static void | 
|  | 1976 | updateminmax(const zic_t x) | 
|  | 1977 | { | 
|  | 1978 | if (min_year > x) | 
|  | 1979 | min_year = x; | 
|  | 1980 | if (max_year < x) | 
|  | 1981 | max_year = x; | 
|  | 1982 | } | 
|  | 1983 |  | 
|  | 1984 | static int | 
|  | 1985 | stringoffset(char *result, zic_t offset) | 
|  | 1986 | { | 
|  | 1987 | register int	hours; | 
|  | 1988 | register int	minutes; | 
|  | 1989 | register int	seconds; | 
|  | 1990 | bool negative = offset < 0; | 
|  | 1991 | int len = negative; | 
|  | 1992 |  | 
|  | 1993 | if (negative) { | 
|  | 1994 | offset = -offset; | 
|  | 1995 | result[0] = '-'; | 
|  | 1996 | } | 
|  | 1997 | seconds = offset % SECSPERMIN; | 
|  | 1998 | offset /= SECSPERMIN; | 
|  | 1999 | minutes = offset % MINSPERHOUR; | 
|  | 2000 | offset /= MINSPERHOUR; | 
|  | 2001 | hours = offset; | 
|  | 2002 | if (hours >= HOURSPERDAY * DAYSPERWEEK) { | 
|  | 2003 | result[0] = '\0'; | 
|  | 2004 | return 0; | 
|  | 2005 | } | 
|  | 2006 | len += sprintf(result + len, "%d", hours); | 
|  | 2007 | if (minutes != 0 || seconds != 0) { | 
|  | 2008 | len += sprintf(result + len, ":%02d", minutes); | 
|  | 2009 | if (seconds != 0) | 
|  | 2010 | len += sprintf(result + len, ":%02d", seconds); | 
|  | 2011 | } | 
|  | 2012 | return len; | 
|  | 2013 | } | 
|  | 2014 |  | 
|  | 2015 | static int | 
|  | 2016 | stringrule(char *result, const struct rule *const rp, const zic_t dstoff, | 
|  | 2017 | const zic_t gmtoff) | 
|  | 2018 | { | 
|  | 2019 | register zic_t	tod = rp->r_tod; | 
|  | 2020 | register int	compat = 0; | 
|  | 2021 |  | 
|  | 2022 | if (rp->r_dycode == DC_DOM) { | 
|  | 2023 | register int	month, total; | 
|  | 2024 |  | 
|  | 2025 | if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY) | 
|  | 2026 | return -1; | 
|  | 2027 | total = 0; | 
|  | 2028 | for (month = 0; month < rp->r_month; ++month) | 
|  | 2029 | total += len_months[0][month]; | 
|  | 2030 | /* Omit the "J" in Jan and Feb, as that's shorter.  */ | 
|  | 2031 | if (rp->r_month <= 1) | 
|  | 2032 | result += sprintf(result, "%d", total + rp->r_dayofmonth - 1); | 
|  | 2033 | else | 
|  | 2034 | result += sprintf(result, "J%d", total + rp->r_dayofmonth); | 
|  | 2035 | } else { | 
|  | 2036 | register int	week; | 
|  | 2037 | register int	wday = rp->r_wday; | 
|  | 2038 | register int	wdayoff; | 
|  | 2039 |  | 
|  | 2040 | if (rp->r_dycode == DC_DOWGEQ) { | 
|  | 2041 | wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK; | 
|  | 2042 | if (wdayoff) | 
|  | 2043 | compat = 2013; | 
|  | 2044 | wday -= wdayoff; | 
|  | 2045 | tod += wdayoff * SECSPERDAY; | 
|  | 2046 | week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK; | 
|  | 2047 | } else if (rp->r_dycode == DC_DOWLEQ) { | 
|  | 2048 | if (rp->r_dayofmonth == len_months[1][rp->r_month]) | 
|  | 2049 | week = 5; | 
|  | 2050 | else { | 
|  | 2051 | wdayoff = rp->r_dayofmonth % DAYSPERWEEK; | 
|  | 2052 | if (wdayoff) | 
|  | 2053 | compat = 2013; | 
|  | 2054 | wday -= wdayoff; | 
|  | 2055 | tod += wdayoff * SECSPERDAY; | 
|  | 2056 | week = rp->r_dayofmonth / DAYSPERWEEK; | 
|  | 2057 | } | 
|  | 2058 | } else	return -1;	/* "cannot happen" */ | 
|  | 2059 | if (wday < 0) | 
|  | 2060 | wday += DAYSPERWEEK; | 
|  | 2061 | result += sprintf(result, "M%d.%d.%d", | 
|  | 2062 | rp->r_month + 1, week, wday); | 
|  | 2063 | } | 
|  | 2064 | if (rp->r_todisgmt) | 
|  | 2065 | tod += gmtoff; | 
|  | 2066 | if (rp->r_todisstd && rp->r_stdoff == 0) | 
|  | 2067 | tod += dstoff; | 
|  | 2068 | if (tod != 2 * SECSPERMIN * MINSPERHOUR) { | 
|  | 2069 | *result++ = '/'; | 
|  | 2070 | if (! stringoffset(result, tod)) | 
|  | 2071 | return -1; | 
|  | 2072 | if (tod < 0) { | 
|  | 2073 | if (compat < 2013) | 
|  | 2074 | compat = 2013; | 
|  | 2075 | } else if (SECSPERDAY <= tod) { | 
|  | 2076 | if (compat < 1994) | 
|  | 2077 | compat = 1994; | 
|  | 2078 | } | 
|  | 2079 | } | 
|  | 2080 | return compat; | 
|  | 2081 | } | 
|  | 2082 |  | 
|  | 2083 | static int | 
|  | 2084 | rule_cmp(struct rule const *a, struct rule const *b) | 
|  | 2085 | { | 
|  | 2086 | if (!a) | 
|  | 2087 | return -!!b; | 
|  | 2088 | if (!b) | 
|  | 2089 | return 1; | 
|  | 2090 | if (a->r_hiyear != b->r_hiyear) | 
|  | 2091 | return a->r_hiyear < b->r_hiyear ? -1 : 1; | 
|  | 2092 | if (a->r_month - b->r_month != 0) | 
|  | 2093 | return a->r_month - b->r_month; | 
|  | 2094 | return a->r_dayofmonth - b->r_dayofmonth; | 
|  | 2095 | } | 
|  | 2096 |  | 
|  | 2097 | enum { YEAR_BY_YEAR_ZONE = 1 }; | 
|  | 2098 |  | 
|  | 2099 | static int | 
|  | 2100 | stringzone(char *result, const struct zone *const zpfirst, const int zonecount) | 
|  | 2101 | { | 
|  | 2102 | register const struct zone *	zp; | 
|  | 2103 | register struct rule *		rp; | 
|  | 2104 | register struct rule *		stdrp; | 
|  | 2105 | register struct rule *		dstrp; | 
|  | 2106 | register int			i; | 
|  | 2107 | register const char *		abbrvar; | 
|  | 2108 | register int			compat = 0; | 
|  | 2109 | register int			c; | 
|  | 2110 | size_t				len; | 
|  | 2111 | int				offsetlen; | 
|  | 2112 | struct rule			stdr, dstr; | 
|  | 2113 |  | 
|  | 2114 | result[0] = '\0'; | 
|  | 2115 | zp = zpfirst + zonecount - 1; | 
|  | 2116 | stdrp = dstrp = NULL; | 
|  | 2117 | for (i = 0; i < zp->z_nrules; ++i) { | 
|  | 2118 | rp = &zp->z_rules[i]; | 
|  | 2119 | if (rp->r_hiwasnum || rp->r_hiyear != ZIC_MAX) | 
|  | 2120 | continue; | 
|  | 2121 | if (rp->r_yrtype != NULL) | 
|  | 2122 | continue; | 
|  | 2123 | if (rp->r_stdoff == 0) { | 
|  | 2124 | if (stdrp == NULL) | 
|  | 2125 | stdrp = rp; | 
|  | 2126 | else	return -1; | 
|  | 2127 | } else { | 
|  | 2128 | if (dstrp == NULL) | 
|  | 2129 | dstrp = rp; | 
|  | 2130 | else	return -1; | 
|  | 2131 | } | 
|  | 2132 | } | 
|  | 2133 | if (stdrp == NULL && dstrp == NULL) { | 
|  | 2134 | /* | 
|  | 2135 | ** There are no rules running through "max". | 
|  | 2136 | ** Find the latest std rule in stdabbrrp | 
|  | 2137 | ** and latest rule of any type in stdrp. | 
|  | 2138 | */ | 
|  | 2139 | register struct rule *stdabbrrp = NULL; | 
|  | 2140 | for (i = 0; i < zp->z_nrules; ++i) { | 
|  | 2141 | rp = &zp->z_rules[i]; | 
|  | 2142 | if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0) | 
|  | 2143 | stdabbrrp = rp; | 
|  | 2144 | if (rule_cmp(stdrp, rp) < 0) | 
|  | 2145 | stdrp = rp; | 
|  | 2146 | } | 
|  | 2147 | /* | 
|  | 2148 | ** Horrid special case: if year is 2037, | 
|  | 2149 | ** presume this is a zone handled on a year-by-year basis; | 
|  | 2150 | ** do not try to apply a rule to the zone. | 
|  | 2151 | */ | 
|  | 2152 | if (stdrp != NULL && stdrp->r_hiyear == 2037) | 
|  | 2153 | return YEAR_BY_YEAR_ZONE; | 
|  | 2154 |  | 
|  | 2155 | if (stdrp != NULL && stdrp->r_stdoff != 0) { | 
|  | 2156 | /* Perpetual DST.  */ | 
|  | 2157 | dstr.r_month = TM_JANUARY; | 
|  | 2158 | dstr.r_dycode = DC_DOM; | 
|  | 2159 | dstr.r_dayofmonth = 1; | 
|  | 2160 | dstr.r_tod = 0; | 
|  | 2161 | dstr.r_todisstd = dstr.r_todisgmt = false; | 
|  | 2162 | dstr.r_stdoff = stdrp->r_stdoff; | 
|  | 2163 | dstr.r_abbrvar = stdrp->r_abbrvar; | 
|  | 2164 | stdr.r_month = TM_DECEMBER; | 
|  | 2165 | stdr.r_dycode = DC_DOM; | 
|  | 2166 | stdr.r_dayofmonth = 31; | 
|  | 2167 | stdr.r_tod = SECSPERDAY + stdrp->r_stdoff; | 
|  | 2168 | stdr.r_todisstd = stdr.r_todisgmt = false; | 
|  | 2169 | stdr.r_stdoff = 0; | 
|  | 2170 | stdr.r_abbrvar | 
|  | 2171 | = (stdabbrrp ? stdabbrrp->r_abbrvar : ""); | 
|  | 2172 | dstrp = &dstr; | 
|  | 2173 | stdrp = &stdr; | 
|  | 2174 | } | 
|  | 2175 | } | 
|  | 2176 | if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) | 
|  | 2177 | return -1; | 
|  | 2178 | abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; | 
|  | 2179 | len = doabbr(result, zp, abbrvar, 0, true); | 
|  | 2180 | offsetlen = stringoffset(result + len, -zp->z_gmtoff); | 
|  | 2181 | if (! offsetlen) { | 
|  | 2182 | result[0] = '\0'; | 
|  | 2183 | return -1; | 
|  | 2184 | } | 
|  | 2185 | len += offsetlen; | 
|  | 2186 | if (dstrp == NULL) | 
|  | 2187 | return compat; | 
|  | 2188 | len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true); | 
|  | 2189 | if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) { | 
|  | 2190 | offsetlen = stringoffset(result + len, | 
|  | 2191 | -(zp->z_gmtoff + dstrp->r_stdoff)); | 
|  | 2192 | if (! offsetlen) { | 
|  | 2193 | result[0] = '\0'; | 
|  | 2194 | return -1; | 
|  | 2195 | } | 
|  | 2196 | len += offsetlen; | 
|  | 2197 | } | 
|  | 2198 | result[len++] = ','; | 
|  | 2199 | c = stringrule(result + len, dstrp, dstrp->r_stdoff, zp->z_gmtoff); | 
|  | 2200 | if (c < 0) { | 
|  | 2201 | result[0] = '\0'; | 
|  | 2202 | return -1; | 
|  | 2203 | } | 
|  | 2204 | if (compat < c) | 
|  | 2205 | compat = c; | 
|  | 2206 | len += strlen(result + len); | 
|  | 2207 | result[len++] = ','; | 
|  | 2208 | c = stringrule(result + len, stdrp, dstrp->r_stdoff, zp->z_gmtoff); | 
|  | 2209 | if (c < 0) { | 
|  | 2210 | result[0] = '\0'; | 
|  | 2211 | return -1; | 
|  | 2212 | } | 
|  | 2213 | if (compat < c) | 
|  | 2214 | compat = c; | 
|  | 2215 | return compat; | 
|  | 2216 | } | 
|  | 2217 |  | 
|  | 2218 | static void | 
|  | 2219 | outzone(const struct zone *zpfirst, int zonecount) | 
|  | 2220 | { | 
|  | 2221 | register const struct zone *	zp; | 
|  | 2222 | register struct rule *		rp; | 
|  | 2223 | register int			i, j; | 
|  | 2224 | register bool			usestart, useuntil; | 
|  | 2225 | register zic_t			starttime, untiltime; | 
|  | 2226 | register zic_t			gmtoff; | 
|  | 2227 | register zic_t			stdoff; | 
|  | 2228 | register zic_t			year; | 
|  | 2229 | register zic_t			startoff; | 
|  | 2230 | register bool			startttisstd; | 
|  | 2231 | register bool			startttisgmt; | 
|  | 2232 | register int			type; | 
|  | 2233 | register char *			startbuf; | 
|  | 2234 | register char *			ab; | 
|  | 2235 | register char *			envvar; | 
|  | 2236 | register int			max_abbr_len; | 
|  | 2237 | register int			max_envvar_len; | 
|  | 2238 | register bool			prodstic; /* all rules are min to max */ | 
|  | 2239 | register int			compat; | 
|  | 2240 | register bool			do_extend; | 
|  | 2241 | register char			version; | 
|  | 2242 |  | 
|  | 2243 | max_abbr_len = 2 + max_format_len + max_abbrvar_len; | 
|  | 2244 | max_envvar_len = 2 * max_abbr_len + 5 * 9; | 
|  | 2245 | startbuf = emalloc(max_abbr_len + 1); | 
|  | 2246 | ab = emalloc(max_abbr_len + 1); | 
|  | 2247 | envvar = emalloc(max_envvar_len + 1); | 
|  | 2248 | INITIALIZE(untiltime); | 
|  | 2249 | INITIALIZE(starttime); | 
|  | 2250 | /* | 
|  | 2251 | ** Now. . .finally. . .generate some useful data! | 
|  | 2252 | */ | 
|  | 2253 | timecnt = 0; | 
|  | 2254 | typecnt = 0; | 
|  | 2255 | charcnt = 0; | 
|  | 2256 | prodstic = zonecount == 1; | 
|  | 2257 | /* | 
|  | 2258 | ** Thanks to Earl Chew | 
|  | 2259 | ** for noting the need to unconditionally initialize startttisstd. | 
|  | 2260 | */ | 
|  | 2261 | startttisstd = false; | 
|  | 2262 | startttisgmt = false; | 
|  | 2263 | min_year = max_year = EPOCH_YEAR; | 
|  | 2264 | if (leapseen) { | 
|  | 2265 | updateminmax(leapminyear); | 
|  | 2266 | updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX)); | 
|  | 2267 | } | 
|  | 2268 | for (i = 0; i < zonecount; ++i) { | 
|  | 2269 | zp = &zpfirst[i]; | 
|  | 2270 | if (i < zonecount - 1) | 
|  | 2271 | updateminmax(zp->z_untilrule.r_loyear); | 
|  | 2272 | for (j = 0; j < zp->z_nrules; ++j) { | 
|  | 2273 | rp = &zp->z_rules[j]; | 
|  | 2274 | if (rp->r_lowasnum) | 
|  | 2275 | updateminmax(rp->r_loyear); | 
|  | 2276 | if (rp->r_hiwasnum) | 
|  | 2277 | updateminmax(rp->r_hiyear); | 
|  | 2278 | if (rp->r_lowasnum || rp->r_hiwasnum) | 
|  | 2279 | prodstic = false; | 
|  | 2280 | } | 
|  | 2281 | } | 
|  | 2282 | /* | 
|  | 2283 | ** Generate lots of data if a rule can't cover all future times. | 
|  | 2284 | */ | 
|  | 2285 | compat = stringzone(envvar, zpfirst, zonecount); | 
|  | 2286 | version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION; | 
|  | 2287 | do_extend = compat < 0 || compat == YEAR_BY_YEAR_ZONE; | 
|  | 2288 | if (noise) { | 
|  | 2289 | if (!*envvar) | 
|  | 2290 | warning("%s %s", | 
|  | 2291 | _("no POSIX environment variable for zone"), | 
|  | 2292 | zpfirst->z_name); | 
|  | 2293 | else if (compat != 0 && compat != YEAR_BY_YEAR_ZONE) { | 
|  | 2294 | /* Circa-COMPAT clients, and earlier clients, might | 
|  | 2295 | not work for this zone when given dates before | 
|  | 2296 | 1970 or after 2038.  */ | 
|  | 2297 | warning(_("%s: pre-%d clients may mishandle" | 
|  | 2298 | " distant timestamps"), | 
|  | 2299 | zpfirst->z_name, compat); | 
|  | 2300 | } | 
|  | 2301 | } | 
|  | 2302 | if (do_extend) { | 
|  | 2303 | /* | 
|  | 2304 | ** Search through a couple of extra years past the obvious | 
|  | 2305 | ** 400, to avoid edge cases.  For example, suppose a non-POSIX | 
|  | 2306 | ** rule applies from 2012 onwards and has transitions in March | 
|  | 2307 | ** and September, plus some one-off transitions in November | 
|  | 2308 | ** 2013.  If zic looked only at the last 400 years, it would | 
|  | 2309 | ** set max_year=2413, with the intent that the 400 years 2014 | 
|  | 2310 | ** through 2413 will be repeated.  The last transition listed | 
|  | 2311 | ** in the tzfile would be in 2413-09, less than 400 years | 
|  | 2312 | ** after the last one-off transition in 2013-11.  Two years | 
|  | 2313 | ** might be overkill, but with the kind of edge cases | 
|  | 2314 | ** available we're not sure that one year would suffice. | 
|  | 2315 | */ | 
|  | 2316 | enum { years_of_observations = YEARSPERREPEAT + 2 }; | 
|  | 2317 |  | 
|  | 2318 | if (min_year >= ZIC_MIN + years_of_observations) | 
|  | 2319 | min_year -= years_of_observations; | 
|  | 2320 | else	min_year = ZIC_MIN; | 
|  | 2321 | if (max_year <= ZIC_MAX - years_of_observations) | 
|  | 2322 | max_year += years_of_observations; | 
|  | 2323 | else	max_year = ZIC_MAX; | 
|  | 2324 | /* | 
|  | 2325 | ** Regardless of any of the above, | 
|  | 2326 | ** for a "proDSTic" zone which specifies that its rules | 
|  | 2327 | ** always have and always will be in effect, | 
|  | 2328 | ** we only need one cycle to define the zone. | 
|  | 2329 | */ | 
|  | 2330 | if (prodstic) { | 
|  | 2331 | min_year = 1900; | 
|  | 2332 | max_year = min_year + years_of_observations; | 
|  | 2333 | } | 
|  | 2334 | } | 
|  | 2335 | /* | 
|  | 2336 | ** For the benefit of older systems, | 
|  | 2337 | ** generate data from 1900 through 2037. | 
|  | 2338 | */ | 
|  | 2339 | if (min_year > 1900) | 
|  | 2340 | min_year = 1900; | 
|  | 2341 | if (max_year < 2037) | 
|  | 2342 | max_year = 2037; | 
|  | 2343 | for (i = 0; i < zonecount; ++i) { | 
|  | 2344 | /* | 
|  | 2345 | ** A guess that may well be corrected later. | 
|  | 2346 | */ | 
|  | 2347 | stdoff = 0; | 
|  | 2348 | zp = &zpfirst[i]; | 
|  | 2349 | usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time; | 
|  | 2350 | useuntil = i < (zonecount - 1); | 
|  | 2351 | if (useuntil && zp->z_untiltime <= big_bang_time) | 
|  | 2352 | continue; | 
|  | 2353 | gmtoff = zp->z_gmtoff; | 
|  | 2354 | eat(zp->z_filename, zp->z_linenum); | 
|  | 2355 | *startbuf = '\0'; | 
|  | 2356 | startoff = zp->z_gmtoff; | 
|  | 2357 | if (zp->z_nrules == 0) { | 
|  | 2358 | stdoff = zp->z_stdoff; | 
|  | 2359 | doabbr(startbuf, zp, NULL, stdoff, false); | 
|  | 2360 | type = addtype(oadd(zp->z_gmtoff, stdoff), | 
|  | 2361 | startbuf, stdoff != 0, startttisstd, | 
|  | 2362 | startttisgmt); | 
|  | 2363 | if (usestart) { | 
|  | 2364 | addtt(starttime, type); | 
|  | 2365 | usestart = false; | 
|  | 2366 | } else	addtt(big_bang_time, type); | 
|  | 2367 | } else for (year = min_year; year <= max_year; ++year) { | 
|  | 2368 | if (useuntil && year > zp->z_untilrule.r_hiyear) | 
|  | 2369 | break; | 
|  | 2370 | /* | 
|  | 2371 | ** Mark which rules to do in the current year. | 
|  | 2372 | ** For those to do, calculate rpytime(rp, year); | 
|  | 2373 | */ | 
|  | 2374 | for (j = 0; j < zp->z_nrules; ++j) { | 
|  | 2375 | rp = &zp->z_rules[j]; | 
|  | 2376 | eats(zp->z_filename, zp->z_linenum, | 
|  | 2377 | rp->r_filename, rp->r_linenum); | 
|  | 2378 | rp->r_todo = year >= rp->r_loyear && | 
|  | 2379 | year <= rp->r_hiyear && | 
|  | 2380 | yearistype(year, rp->r_yrtype); | 
|  | 2381 | if (rp->r_todo) | 
|  | 2382 | rp->r_temp = rpytime(rp, year); | 
|  | 2383 | } | 
|  | 2384 | for ( ; ; ) { | 
|  | 2385 | register int	k; | 
|  | 2386 | register zic_t	jtime, ktime; | 
|  | 2387 | register zic_t	offset; | 
|  | 2388 |  | 
|  | 2389 | INITIALIZE(ktime); | 
|  | 2390 | if (useuntil) { | 
|  | 2391 | /* | 
|  | 2392 | ** Turn untiltime into UT | 
|  | 2393 | ** assuming the current gmtoff and | 
|  | 2394 | ** stdoff values. | 
|  | 2395 | */ | 
|  | 2396 | untiltime = zp->z_untiltime; | 
|  | 2397 | if (!zp->z_untilrule.r_todisgmt) | 
|  | 2398 | untiltime = tadd(untiltime, | 
|  | 2399 | -gmtoff); | 
|  | 2400 | if (!zp->z_untilrule.r_todisstd) | 
|  | 2401 | untiltime = tadd(untiltime, | 
|  | 2402 | -stdoff); | 
|  | 2403 | } | 
|  | 2404 | /* | 
|  | 2405 | ** Find the rule (of those to do, if any) | 
|  | 2406 | ** that takes effect earliest in the year. | 
|  | 2407 | */ | 
|  | 2408 | k = -1; | 
|  | 2409 | for (j = 0; j < zp->z_nrules; ++j) { | 
|  | 2410 | rp = &zp->z_rules[j]; | 
|  | 2411 | if (!rp->r_todo) | 
|  | 2412 | continue; | 
|  | 2413 | eats(zp->z_filename, zp->z_linenum, | 
|  | 2414 | rp->r_filename, rp->r_linenum); | 
|  | 2415 | offset = rp->r_todisgmt ? 0 : gmtoff; | 
|  | 2416 | if (!rp->r_todisstd) | 
|  | 2417 | offset = oadd(offset, stdoff); | 
|  | 2418 | jtime = rp->r_temp; | 
|  | 2419 | if (jtime == min_time || | 
|  | 2420 | jtime == max_time) | 
|  | 2421 | continue; | 
|  | 2422 | jtime = tadd(jtime, -offset); | 
|  | 2423 | if (k < 0 || jtime < ktime) { | 
|  | 2424 | k = j; | 
|  | 2425 | ktime = jtime; | 
|  | 2426 | } else if (jtime == ktime) { | 
|  | 2427 | char const *dup_rules_msg = | 
|  | 2428 | _("two rules for same instant"); | 
|  | 2429 | eats(zp->z_filename, zp->z_linenum, | 
|  | 2430 | rp->r_filename, rp->r_linenum); | 
|  | 2431 | warning("%s", dup_rules_msg); | 
|  | 2432 | rp = &zp->z_rules[k]; | 
|  | 2433 | eats(zp->z_filename, zp->z_linenum, | 
|  | 2434 | rp->r_filename, rp->r_linenum); | 
|  | 2435 | error("%s", dup_rules_msg); | 
|  | 2436 | } | 
|  | 2437 | } | 
|  | 2438 | if (k < 0) | 
|  | 2439 | break;	/* go on to next year */ | 
|  | 2440 | rp = &zp->z_rules[k]; | 
|  | 2441 | rp->r_todo = false; | 
|  | 2442 | if (useuntil && ktime >= untiltime) | 
|  | 2443 | break; | 
|  | 2444 | stdoff = rp->r_stdoff; | 
|  | 2445 | if (usestart && ktime == starttime) | 
|  | 2446 | usestart = false; | 
|  | 2447 | if (usestart) { | 
|  | 2448 | if (ktime < starttime) { | 
|  | 2449 | startoff = oadd(zp->z_gmtoff, | 
|  | 2450 | stdoff); | 
|  | 2451 | doabbr(startbuf, zp, | 
|  | 2452 | rp->r_abbrvar, | 
|  | 2453 | rp->r_stdoff, | 
|  | 2454 | false); | 
|  | 2455 | continue; | 
|  | 2456 | } | 
|  | 2457 | if (*startbuf == '\0' && | 
|  | 2458 | startoff == oadd(zp->z_gmtoff, | 
|  | 2459 | stdoff)) { | 
|  | 2460 | doabbr(startbuf, | 
|  | 2461 | zp, | 
|  | 2462 | rp->r_abbrvar, | 
|  | 2463 | rp->r_stdoff, | 
|  | 2464 | false); | 
|  | 2465 | } | 
|  | 2466 | } | 
|  | 2467 | eats(zp->z_filename, zp->z_linenum, | 
|  | 2468 | rp->r_filename, rp->r_linenum); | 
|  | 2469 | doabbr(ab, zp, rp->r_abbrvar, | 
|  | 2470 | rp->r_stdoff, false); | 
|  | 2471 | offset = oadd(zp->z_gmtoff, rp->r_stdoff); | 
|  | 2472 | type = addtype(offset, ab, rp->r_stdoff != 0, | 
|  | 2473 | rp->r_todisstd, rp->r_todisgmt); | 
|  | 2474 | addtt(ktime, type); | 
|  | 2475 | } | 
|  | 2476 | } | 
|  | 2477 | if (usestart) { | 
|  | 2478 | if (*startbuf == '\0' && | 
|  | 2479 | zp->z_format != NULL && | 
|  | 2480 | strchr(zp->z_format, '%') == NULL && | 
|  | 2481 | strchr(zp->z_format, '/') == NULL) | 
|  | 2482 | strcpy(startbuf, zp->z_format); | 
|  | 2483 | eat(zp->z_filename, zp->z_linenum); | 
|  | 2484 | if (*startbuf == '\0') | 
|  | 2485 | error(_("can't determine time zone abbreviation to use just after until time")); | 
|  | 2486 | else	addtt(starttime, | 
|  | 2487 | addtype(startoff, startbuf, | 
|  | 2488 | startoff != zp->z_gmtoff, | 
|  | 2489 | startttisstd, | 
|  | 2490 | startttisgmt)); | 
|  | 2491 | } | 
|  | 2492 | /* | 
|  | 2493 | ** Now we may get to set starttime for the next zone line. | 
|  | 2494 | */ | 
|  | 2495 | if (useuntil) { | 
|  | 2496 | startttisstd = zp->z_untilrule.r_todisstd; | 
|  | 2497 | startttisgmt = zp->z_untilrule.r_todisgmt; | 
|  | 2498 | starttime = zp->z_untiltime; | 
|  | 2499 | if (!startttisstd) | 
|  | 2500 | starttime = tadd(starttime, -stdoff); | 
|  | 2501 | if (!startttisgmt) | 
|  | 2502 | starttime = tadd(starttime, -gmtoff); | 
|  | 2503 | } | 
|  | 2504 | } | 
|  | 2505 | if (do_extend) { | 
|  | 2506 | /* | 
|  | 2507 | ** If we're extending the explicitly listed observations | 
|  | 2508 | ** for 400 years because we can't fill the POSIX-TZ field, | 
|  | 2509 | ** check whether we actually ended up explicitly listing | 
|  | 2510 | ** observations through that period.  If there aren't any | 
|  | 2511 | ** near the end of the 400-year period, add a redundant | 
|  | 2512 | ** one at the end of the final year, to make it clear | 
|  | 2513 | ** that we are claiming to have definite knowledge of | 
|  | 2514 | ** the lack of transitions up to that point. | 
|  | 2515 | */ | 
|  | 2516 | struct rule xr; | 
|  | 2517 | struct attype *lastat; | 
|  | 2518 | xr.r_month = TM_JANUARY; | 
|  | 2519 | xr.r_dycode = DC_DOM; | 
|  | 2520 | xr.r_dayofmonth = 1; | 
|  | 2521 | xr.r_tod = 0; | 
|  | 2522 | for (lastat = &attypes[0], i = 1; i < timecnt; i++) | 
|  | 2523 | if (attypes[i].at > lastat->at) | 
|  | 2524 | lastat = &attypes[i]; | 
|  | 2525 | if (lastat->at < rpytime(&xr, max_year - 1)) { | 
|  | 2526 | /* | 
|  | 2527 | ** Create new type code for the redundant entry, | 
|  | 2528 | ** to prevent it being optimized away. | 
|  | 2529 | */ | 
|  | 2530 | if (typecnt >= TZ_MAX_TYPES) { | 
|  | 2531 | error(_("too many local time types")); | 
|  | 2532 | exit(EXIT_FAILURE); | 
|  | 2533 | } | 
|  | 2534 | gmtoffs[typecnt] = gmtoffs[lastat->type]; | 
|  | 2535 | isdsts[typecnt] = isdsts[lastat->type]; | 
|  | 2536 | ttisstds[typecnt] = ttisstds[lastat->type]; | 
|  | 2537 | ttisgmts[typecnt] = ttisgmts[lastat->type]; | 
|  | 2538 | abbrinds[typecnt] = abbrinds[lastat->type]; | 
|  | 2539 | ++typecnt; | 
|  | 2540 | addtt(rpytime(&xr, max_year + 1), typecnt-1); | 
|  | 2541 | } | 
|  | 2542 | } | 
|  | 2543 | writezone(zpfirst->z_name, envvar, version); | 
|  | 2544 | free(startbuf); | 
|  | 2545 | free(ab); | 
|  | 2546 | free(envvar); | 
|  | 2547 | } | 
|  | 2548 |  | 
|  | 2549 | static void | 
|  | 2550 | addtt(zic_t starttime, int type) | 
|  | 2551 | { | 
|  | 2552 | if (starttime <= big_bang_time || | 
|  | 2553 | (timecnt == 1 && attypes[0].at < big_bang_time)) { | 
|  | 2554 | gmtoffs[0] = gmtoffs[type]; | 
|  | 2555 | isdsts[0] = isdsts[type]; | 
|  | 2556 | ttisstds[0] = ttisstds[type]; | 
|  | 2557 | ttisgmts[0] = ttisgmts[type]; | 
|  | 2558 | if (abbrinds[type] != 0) | 
|  | 2559 | strcpy(chars, &chars[abbrinds[type]]); | 
|  | 2560 | abbrinds[0] = 0; | 
|  | 2561 | charcnt = strlen(chars) + 1; | 
|  | 2562 | typecnt = 1; | 
|  | 2563 | timecnt = 0; | 
|  | 2564 | type = 0; | 
|  | 2565 | } | 
|  | 2566 | attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc); | 
|  | 2567 | attypes[timecnt].at = starttime; | 
|  | 2568 | attypes[timecnt].type = type; | 
|  | 2569 | ++timecnt; | 
|  | 2570 | } | 
|  | 2571 |  | 
|  | 2572 | static int | 
|  | 2573 | addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt) | 
|  | 2574 | { | 
|  | 2575 | register int	i, j; | 
|  | 2576 |  | 
|  | 2577 | /* | 
|  | 2578 | ** See if there's already an entry for this zone type. | 
|  | 2579 | ** If so, just return its index. | 
|  | 2580 | */ | 
|  | 2581 | for (i = 0; i < typecnt; ++i) { | 
|  | 2582 | if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && | 
|  | 2583 | strcmp(abbr, &chars[abbrinds[i]]) == 0 && | 
|  | 2584 | ttisstd == ttisstds[i] && | 
|  | 2585 | ttisgmt == ttisgmts[i]) | 
|  | 2586 | return i; | 
|  | 2587 | } | 
|  | 2588 | /* | 
|  | 2589 | ** There isn't one; add a new one, unless there are already too | 
|  | 2590 | ** many. | 
|  | 2591 | */ | 
|  | 2592 | if (typecnt >= TZ_MAX_TYPES) { | 
|  | 2593 | error(_("too many local time types")); | 
|  | 2594 | exit(EXIT_FAILURE); | 
|  | 2595 | } | 
|  | 2596 | if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { | 
|  | 2597 | error(_("UT offset out of range")); | 
|  | 2598 | exit(EXIT_FAILURE); | 
|  | 2599 | } | 
|  | 2600 | gmtoffs[i] = gmtoff; | 
|  | 2601 | isdsts[i] = isdst; | 
|  | 2602 | ttisstds[i] = ttisstd; | 
|  | 2603 | ttisgmts[i] = ttisgmt; | 
|  | 2604 |  | 
|  | 2605 | for (j = 0; j < charcnt; ++j) | 
|  | 2606 | if (strcmp(&chars[j], abbr) == 0) | 
|  | 2607 | break; | 
|  | 2608 | if (j == charcnt) | 
|  | 2609 | newabbr(abbr); | 
|  | 2610 | abbrinds[i] = j; | 
|  | 2611 | ++typecnt; | 
|  | 2612 | return i; | 
|  | 2613 | } | 
|  | 2614 |  | 
|  | 2615 | static void | 
|  | 2616 | leapadd(zic_t t, bool positive, int rolling, int count) | 
|  | 2617 | { | 
|  | 2618 | register int	i, j; | 
|  | 2619 |  | 
|  | 2620 | if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { | 
|  | 2621 | error(_("too many leap seconds")); | 
|  | 2622 | exit(EXIT_FAILURE); | 
|  | 2623 | } | 
|  | 2624 | for (i = 0; i < leapcnt; ++i) | 
|  | 2625 | if (t <= trans[i]) { | 
|  | 2626 | if (t == trans[i]) { | 
|  | 2627 | error(_("repeated leap second moment")); | 
|  | 2628 | exit(EXIT_FAILURE); | 
|  | 2629 | } | 
|  | 2630 | break; | 
|  | 2631 | } | 
|  | 2632 | do { | 
|  | 2633 | for (j = leapcnt; j > i; --j) { | 
|  | 2634 | trans[j] = trans[j - 1]; | 
|  | 2635 | corr[j] = corr[j - 1]; | 
|  | 2636 | roll[j] = roll[j - 1]; | 
|  | 2637 | } | 
|  | 2638 | trans[i] = t; | 
|  | 2639 | corr[i] = positive ? 1 : -count; | 
|  | 2640 | roll[i] = rolling; | 
|  | 2641 | ++leapcnt; | 
|  | 2642 | } while (positive && --count != 0); | 
|  | 2643 | } | 
|  | 2644 |  | 
|  | 2645 | static void | 
|  | 2646 | adjleap(void) | 
|  | 2647 | { | 
|  | 2648 | register int	i; | 
|  | 2649 | register zic_t	last = 0; | 
|  | 2650 |  | 
|  | 2651 | /* | 
|  | 2652 | ** propagate leap seconds forward | 
|  | 2653 | */ | 
|  | 2654 | for (i = 0; i < leapcnt; ++i) { | 
|  | 2655 | trans[i] = tadd(trans[i], last); | 
|  | 2656 | last = corr[i] += last; | 
|  | 2657 | } | 
|  | 2658 | } | 
|  | 2659 |  | 
|  | 2660 | static bool | 
|  | 2661 | yearistype(int year, const char *type) | 
|  | 2662 | { | 
|  | 2663 | static char *	buf; | 
|  | 2664 | int		result; | 
|  | 2665 |  | 
|  | 2666 | if (type == NULL || *type == '\0') | 
|  | 2667 | return true; | 
|  | 2668 | buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type)); | 
|  | 2669 | sprintf(buf, "%s %d %s", yitcommand, year, type); | 
|  | 2670 | result = system(buf); | 
|  | 2671 | if (WIFEXITED(result)) switch (WEXITSTATUS(result)) { | 
|  | 2672 | case 0: | 
|  | 2673 | return true; | 
|  | 2674 | case 1: | 
|  | 2675 | return false; | 
|  | 2676 | } | 
|  | 2677 | error(_("Wild result from command execution")); | 
|  | 2678 | fprintf(stderr, _("%s: command was '%s', result was %d\n"), | 
|  | 2679 | progname, buf, result); | 
|  | 2680 | for ( ; ; ) | 
|  | 2681 | exit(EXIT_FAILURE); | 
|  | 2682 | } | 
|  | 2683 |  | 
|  | 2684 | /* Is A a space character in the C locale?  */ | 
|  | 2685 | static bool | 
|  | 2686 | is_space(char a) | 
|  | 2687 | { | 
|  | 2688 | switch (a) { | 
|  | 2689 | default: | 
|  | 2690 | return false; | 
|  | 2691 | case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': | 
|  | 2692 | return true; | 
|  | 2693 | } | 
|  | 2694 | } | 
|  | 2695 |  | 
|  | 2696 | /* Is A an alphabetic character in the C locale?  */ | 
|  | 2697 | static bool | 
|  | 2698 | is_alpha(char a) | 
|  | 2699 | { | 
|  | 2700 | switch (a) { | 
|  | 2701 | default: | 
|  | 2702 | return false; | 
|  | 2703 | case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': | 
|  | 2704 | case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': | 
|  | 2705 | case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': | 
|  | 2706 | case 'V': case 'W': case 'X': case 'Y': case 'Z': | 
|  | 2707 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': | 
|  | 2708 | case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': | 
|  | 2709 | case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': | 
|  | 2710 | case 'v': case 'w': case 'x': case 'y': case 'z': | 
|  | 2711 | return true; | 
|  | 2712 | } | 
|  | 2713 | } | 
|  | 2714 |  | 
|  | 2715 | /* If A is an uppercase character in the C locale, return its lowercase | 
|  | 2716 | counterpart.  Otherwise, return A.  */ | 
|  | 2717 | static char | 
|  | 2718 | lowerit(char a) | 
|  | 2719 | { | 
|  | 2720 | switch (a) { | 
|  | 2721 | default: return a; | 
|  | 2722 | case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c'; | 
|  | 2723 | case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f'; | 
|  | 2724 | case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i'; | 
|  | 2725 | case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l'; | 
|  | 2726 | case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o'; | 
|  | 2727 | case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r'; | 
|  | 2728 | case 'S': return 's'; case 'T': return 't'; case 'U': return 'u'; | 
|  | 2729 | case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x'; | 
|  | 2730 | case 'Y': return 'y'; case 'Z': return 'z'; | 
|  | 2731 | } | 
|  | 2732 | } | 
|  | 2733 |  | 
|  | 2734 | /* case-insensitive equality */ | 
|  | 2735 | static ATTRIBUTE_PURE bool | 
|  | 2736 | ciequal(register const char *ap, register const char *bp) | 
|  | 2737 | { | 
|  | 2738 | while (lowerit(*ap) == lowerit(*bp++)) | 
|  | 2739 | if (*ap++ == '\0') | 
|  | 2740 | return true; | 
|  | 2741 | return false; | 
|  | 2742 | } | 
|  | 2743 |  | 
|  | 2744 | static ATTRIBUTE_PURE bool | 
|  | 2745 | itsabbr(register const char *abbr, register const char *word) | 
|  | 2746 | { | 
|  | 2747 | if (lowerit(*abbr) != lowerit(*word)) | 
|  | 2748 | return false; | 
|  | 2749 | ++word; | 
|  | 2750 | while (*++abbr != '\0') | 
|  | 2751 | do { | 
|  | 2752 | if (*word == '\0') | 
|  | 2753 | return false; | 
|  | 2754 | } while (lowerit(*word++) != lowerit(*abbr)); | 
|  | 2755 | return true; | 
|  | 2756 | } | 
|  | 2757 |  | 
|  | 2758 | static ATTRIBUTE_PURE const struct lookup * | 
|  | 2759 | byword(const char *word, const struct lookup *table) | 
|  | 2760 | { | 
|  | 2761 | register const struct lookup *	foundlp; | 
|  | 2762 | register const struct lookup *	lp; | 
|  | 2763 |  | 
|  | 2764 | if (word == NULL || table == NULL) | 
|  | 2765 | return NULL; | 
|  | 2766 | /* | 
|  | 2767 | ** Look for exact match. | 
|  | 2768 | */ | 
|  | 2769 | for (lp = table; lp->l_word != NULL; ++lp) | 
|  | 2770 | if (ciequal(word, lp->l_word)) | 
|  | 2771 | return lp; | 
|  | 2772 | /* | 
|  | 2773 | ** Look for inexact match. | 
|  | 2774 | */ | 
|  | 2775 | foundlp = NULL; | 
|  | 2776 | for (lp = table; lp->l_word != NULL; ++lp) | 
|  | 2777 | if (itsabbr(word, lp->l_word)) { | 
|  | 2778 | if (foundlp == NULL) | 
|  | 2779 | foundlp = lp; | 
|  | 2780 | else	return NULL;	/* multiple inexact matches */ | 
|  | 2781 | } | 
|  | 2782 | return foundlp; | 
|  | 2783 | } | 
|  | 2784 |  | 
|  | 2785 | static char ** | 
|  | 2786 | getfields(register char *cp) | 
|  | 2787 | { | 
|  | 2788 | register char *		dp; | 
|  | 2789 | register char **	array; | 
|  | 2790 | register int		nsubs; | 
|  | 2791 |  | 
|  | 2792 | if (cp == NULL) | 
|  | 2793 | return NULL; | 
|  | 2794 | array = emalloc(size_product(strlen(cp) + 1, sizeof *array)); | 
|  | 2795 | nsubs = 0; | 
|  | 2796 | for ( ; ; ) { | 
|  | 2797 | while (is_space(*cp)) | 
|  | 2798 | ++cp; | 
|  | 2799 | if (*cp == '\0' || *cp == '#') | 
|  | 2800 | break; | 
|  | 2801 | array[nsubs++] = dp = cp; | 
|  | 2802 | do { | 
|  | 2803 | if ((*dp = *cp++) != '"') | 
|  | 2804 | ++dp; | 
|  | 2805 | else while ((*dp = *cp++) != '"') | 
|  | 2806 | if (*dp != '\0') | 
|  | 2807 | ++dp; | 
|  | 2808 | else { | 
|  | 2809 | error(_( | 
|  | 2810 | "Odd number of quotation marks" | 
|  | 2811 | )); | 
|  | 2812 | exit(1); | 
|  | 2813 | } | 
|  | 2814 | } while (*cp && *cp != '#' && !is_space(*cp)); | 
|  | 2815 | if (is_space(*cp)) | 
|  | 2816 | ++cp; | 
|  | 2817 | *dp = '\0'; | 
|  | 2818 | } | 
|  | 2819 | array[nsubs] = NULL; | 
|  | 2820 | return array; | 
|  | 2821 | } | 
|  | 2822 |  | 
|  | 2823 | static _Noreturn void | 
|  | 2824 | time_overflow(void) | 
|  | 2825 | { | 
|  | 2826 | error(_("time overflow")); | 
|  | 2827 | exit(EXIT_FAILURE); | 
|  | 2828 | } | 
|  | 2829 |  | 
|  | 2830 | static ATTRIBUTE_PURE zic_t | 
|  | 2831 | oadd(zic_t t1, zic_t t2) | 
|  | 2832 | { | 
|  | 2833 | if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) | 
|  | 2834 | time_overflow(); | 
|  | 2835 | return t1 + t2; | 
|  | 2836 | } | 
|  | 2837 |  | 
|  | 2838 | static ATTRIBUTE_PURE zic_t | 
|  | 2839 | tadd(zic_t t1, zic_t t2) | 
|  | 2840 | { | 
|  | 2841 | if (t1 < 0) { | 
|  | 2842 | if (t2 < min_time - t1) { | 
|  | 2843 | if (t1 != min_time) | 
|  | 2844 | time_overflow(); | 
|  | 2845 | return min_time; | 
|  | 2846 | } | 
|  | 2847 | } else { | 
|  | 2848 | if (max_time - t1 < t2) { | 
|  | 2849 | if (t1 != max_time) | 
|  | 2850 | time_overflow(); | 
|  | 2851 | return max_time; | 
|  | 2852 | } | 
|  | 2853 | } | 
|  | 2854 | return t1 + t2; | 
|  | 2855 | } | 
|  | 2856 |  | 
|  | 2857 | /* | 
|  | 2858 | ** Given a rule, and a year, compute the date (in seconds since January 1, | 
|  | 2859 | ** 1970, 00:00 LOCAL time) in that year that the rule refers to. | 
|  | 2860 | */ | 
|  | 2861 |  | 
|  | 2862 | static zic_t | 
|  | 2863 | rpytime(const struct rule *rp, zic_t wantedy) | 
|  | 2864 | { | 
|  | 2865 | register int	m, i; | 
|  | 2866 | register zic_t	dayoff;			/* with a nod to Margaret O. */ | 
|  | 2867 | register zic_t	t, y; | 
|  | 2868 |  | 
|  | 2869 | if (wantedy == ZIC_MIN) | 
|  | 2870 | return min_time; | 
|  | 2871 | if (wantedy == ZIC_MAX) | 
|  | 2872 | return max_time; | 
|  | 2873 | dayoff = 0; | 
|  | 2874 | m = TM_JANUARY; | 
|  | 2875 | y = EPOCH_YEAR; | 
|  | 2876 | while (wantedy != y) { | 
|  | 2877 | if (wantedy > y) { | 
|  | 2878 | i = len_years[isleap(y)]; | 
|  | 2879 | ++y; | 
|  | 2880 | } else { | 
|  | 2881 | --y; | 
|  | 2882 | i = -len_years[isleap(y)]; | 
|  | 2883 | } | 
|  | 2884 | dayoff = oadd(dayoff, i); | 
|  | 2885 | } | 
|  | 2886 | while (m != rp->r_month) { | 
|  | 2887 | i = len_months[isleap(y)][m]; | 
|  | 2888 | dayoff = oadd(dayoff, i); | 
|  | 2889 | ++m; | 
|  | 2890 | } | 
|  | 2891 | i = rp->r_dayofmonth; | 
|  | 2892 | if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { | 
|  | 2893 | if (rp->r_dycode == DC_DOWLEQ) | 
|  | 2894 | --i; | 
|  | 2895 | else { | 
|  | 2896 | error(_("use of 2/29 in non leap-year")); | 
|  | 2897 | exit(EXIT_FAILURE); | 
|  | 2898 | } | 
|  | 2899 | } | 
|  | 2900 | --i; | 
|  | 2901 | dayoff = oadd(dayoff, i); | 
|  | 2902 | if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { | 
|  | 2903 | register zic_t	wday; | 
|  | 2904 |  | 
|  | 2905 | #define LDAYSPERWEEK	((zic_t) DAYSPERWEEK) | 
|  | 2906 | wday = EPOCH_WDAY; | 
|  | 2907 | /* | 
|  | 2908 | ** Don't trust mod of negative numbers. | 
|  | 2909 | */ | 
|  | 2910 | if (dayoff >= 0) | 
|  | 2911 | wday = (wday + dayoff) % LDAYSPERWEEK; | 
|  | 2912 | else { | 
|  | 2913 | wday -= ((-dayoff) % LDAYSPERWEEK); | 
|  | 2914 | if (wday < 0) | 
|  | 2915 | wday += LDAYSPERWEEK; | 
|  | 2916 | } | 
|  | 2917 | while (wday != rp->r_wday) | 
|  | 2918 | if (rp->r_dycode == DC_DOWGEQ) { | 
|  | 2919 | dayoff = oadd(dayoff, 1); | 
|  | 2920 | if (++wday >= LDAYSPERWEEK) | 
|  | 2921 | wday = 0; | 
|  | 2922 | ++i; | 
|  | 2923 | } else { | 
|  | 2924 | dayoff = oadd(dayoff, -1); | 
|  | 2925 | if (--wday < 0) | 
|  | 2926 | wday = LDAYSPERWEEK - 1; | 
|  | 2927 | --i; | 
|  | 2928 | } | 
|  | 2929 | if (i < 0 || i >= len_months[isleap(y)][m]) { | 
|  | 2930 | if (noise) | 
|  | 2931 | warning(_("rule goes past start/end of month; \ | 
|  | 2932 | will not work with pre-2004 versions of zic")); | 
|  | 2933 | } | 
|  | 2934 | } | 
|  | 2935 | if (dayoff < min_time / SECSPERDAY) | 
|  | 2936 | return min_time; | 
|  | 2937 | if (dayoff > max_time / SECSPERDAY) | 
|  | 2938 | return max_time; | 
|  | 2939 | t = (zic_t) dayoff * SECSPERDAY; | 
|  | 2940 | return tadd(t, rp->r_tod); | 
|  | 2941 | } | 
|  | 2942 |  | 
|  | 2943 | static void | 
|  | 2944 | newabbr(const char *string) | 
|  | 2945 | { | 
|  | 2946 | register int	i; | 
|  | 2947 |  | 
|  | 2948 | if (strcmp(string, GRANDPARENTED) != 0) { | 
|  | 2949 | register const char *	cp; | 
|  | 2950 | const char *		mp; | 
|  | 2951 |  | 
|  | 2952 | cp = string; | 
|  | 2953 | mp = NULL; | 
|  | 2954 | while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9') | 
|  | 2955 | || *cp == '-' || *cp == '+') | 
|  | 2956 | ++cp; | 
|  | 2957 | if (noise && cp - string < 3) | 
|  | 2958 | mp = _("time zone abbreviation has fewer than 3 characters"); | 
|  | 2959 | if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) | 
|  | 2960 | mp = _("time zone abbreviation has too many characters"); | 
|  | 2961 | if (*cp != '\0') | 
|  | 2962 | mp = _("time zone abbreviation differs from POSIX standard"); | 
|  | 2963 | if (mp != NULL) | 
|  | 2964 | warning("%s (%s)", mp, string); | 
|  | 2965 | } | 
|  | 2966 | i = strlen(string) + 1; | 
|  | 2967 | if (charcnt + i > TZ_MAX_CHARS) { | 
|  | 2968 | error(_("too many, or too long, time zone abbreviations")); | 
|  | 2969 | exit(EXIT_FAILURE); | 
|  | 2970 | } | 
|  | 2971 | strcpy(&chars[charcnt], string); | 
|  | 2972 | charcnt += i; | 
|  | 2973 | } | 
|  | 2974 |  | 
|  | 2975 | static bool | 
|  | 2976 | mkdirs(char *argname) | 
|  | 2977 | { | 
|  | 2978 | register char *	name; | 
|  | 2979 | register char *	cp; | 
|  | 2980 |  | 
|  | 2981 | if (argname == NULL || *argname == '\0') | 
|  | 2982 | return true; | 
|  | 2983 | cp = name = ecpyalloc(argname); | 
|  | 2984 | while ((cp = strchr(cp + 1, '/')) != 0) { | 
|  | 2985 | *cp = '\0'; | 
|  | 2986 | #ifdef HAVE_DOS_FILE_NAMES | 
|  | 2987 | /* | 
|  | 2988 | ** DOS drive specifier? | 
|  | 2989 | */ | 
|  | 2990 | if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0') { | 
|  | 2991 | *cp = '/'; | 
|  | 2992 | continue; | 
|  | 2993 | } | 
|  | 2994 | #endif | 
|  | 2995 | /* | 
|  | 2996 | ** Try to create it.  It's OK if creation fails because | 
|  | 2997 | ** the directory already exists, perhaps because some | 
|  | 2998 | ** other process just created it. | 
|  | 2999 | */ | 
|  | 3000 | if (mkdir(name, MKDIR_UMASK) != 0) { | 
|  | 3001 | int err = errno; | 
|  | 3002 | if (itsdir(name) <= 0) { | 
|  | 3003 | char const *e = strerror(err); | 
|  | 3004 | warning(_("%s: Can't create directory" | 
|  | 3005 | " %s: %s"), | 
|  | 3006 | progname, name, e); | 
|  | 3007 | free(name); | 
|  | 3008 | return false; | 
|  | 3009 | } | 
|  | 3010 | } | 
|  | 3011 | *cp = '/'; | 
|  | 3012 | } | 
|  | 3013 | free(name); | 
|  | 3014 | return true; | 
|  | 3015 | } |