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