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