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