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