]>
Commit | Line | Data |
---|---|---|
b331163b A |
1 | /* |
2 | ** This file is in the public domain, so clarified as of | |
3 | ** 2009-05-17 by Arthur David Olson. | |
4 | */ | |
5 | ||
6 | #include "version.h" | |
4162bf98 A |
7 | |
8 | /* | |
9 | ** This code has been made independent of the rest of the time | |
10 | ** conversion package to increase confidence in the verification it provides. | |
11 | ** You can use this code to help in verifying other implementations. | |
b331163b A |
12 | ** |
13 | ** However, include private.h when debugging, so that it overrides | |
14 | ** time_t consistently with the rest of the package. | |
4162bf98 A |
15 | */ |
16 | ||
b331163b A |
17 | #ifdef time_tz |
18 | # include "private.h" | |
19 | #endif | |
4162bf98 A |
20 | |
21 | #include "stdio.h" /* for stdout, stderr, perror */ | |
22 | #include "string.h" /* for strcpy */ | |
23 | #include "sys/types.h" /* for time_t */ | |
24 | #include "time.h" /* for struct tm */ | |
25 | #include "stdlib.h" /* for exit, malloc, atoi */ | |
b331163b | 26 | #include "limits.h" /* for CHAR_BIT, LLONG_MAX */ |
4162bf98 A |
27 | #include "ctype.h" /* for isalpha et al. */ |
28 | ||
29 | /* Enable extensions and modifications for ICU. */ | |
30 | #define ICU | |
31 | ||
32 | #ifdef ICU | |
33 | #include "dirent.h" | |
34 | #endif | |
35 | ||
36 | #ifndef isascii | |
37 | #define isascii(x) 1 | |
38 | #endif /* !defined isascii */ | |
39 | ||
b331163b A |
40 | /* |
41 | ** Substitutes for pre-C99 compilers. | |
42 | ** Much of this section of code is stolen from private.h. | |
43 | */ | |
44 | ||
45 | #ifndef HAVE_STDINT_H | |
46 | # define HAVE_STDINT_H \ | |
47 | (199901 <= __STDC_VERSION__ || 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__))) | |
48 | #endif | |
49 | #if HAVE_STDINT_H | |
50 | # include "stdint.h" | |
51 | #endif | |
52 | #ifndef HAVE_INTTYPES_H | |
53 | # define HAVE_INTTYPES_H HAVE_STDINT_H | |
54 | #endif | |
55 | #if HAVE_INTTYPES_H | |
56 | # include <inttypes.h> | |
57 | #endif | |
58 | ||
59 | #ifndef INT_FAST32_MAX | |
60 | # if INT_MAX >> 31 == 0 | |
61 | typedef long int_fast32_t; | |
62 | # else | |
63 | typedef int int_fast32_t; | |
64 | # endif | |
65 | #endif | |
66 | ||
67 | #ifndef INTMAX_MAX | |
68 | # if defined LLONG_MAX || defined __LONG_LONG_MAX__ | |
69 | typedef long long intmax_t; | |
70 | # define strtoimax strtoll | |
71 | # define PRIdMAX "lld" | |
72 | # ifdef LLONG_MAX | |
73 | # define INTMAX_MAX LLONG_MAX | |
74 | # else | |
75 | # define INTMAX_MAX __LONG_LONG_MAX__ | |
76 | # endif | |
77 | # else | |
78 | typedef long intmax_t; | |
79 | # define strtoimax strtol | |
80 | # define PRIdMAX "ld" | |
81 | # define INTMAX_MAX LONG_MAX | |
82 | # endif | |
83 | #endif | |
84 | ||
85 | ||
4162bf98 A |
86 | #ifndef ZDUMP_LO_YEAR |
87 | #define ZDUMP_LO_YEAR (-500) | |
88 | #endif /* !defined ZDUMP_LO_YEAR */ | |
89 | ||
90 | #ifndef ZDUMP_HI_YEAR | |
91 | #define ZDUMP_HI_YEAR 2500 | |
92 | #endif /* !defined ZDUMP_HI_YEAR */ | |
93 | ||
94 | #ifndef MAX_STRING_LENGTH | |
95 | #define MAX_STRING_LENGTH 1024 | |
96 | #endif /* !defined MAX_STRING_LENGTH */ | |
97 | ||
98 | #ifndef TRUE | |
99 | #define TRUE 1 | |
100 | #endif /* !defined TRUE */ | |
101 | ||
102 | #ifndef FALSE | |
103 | #define FALSE 0 | |
104 | #endif /* !defined FALSE */ | |
105 | ||
106 | #ifndef EXIT_SUCCESS | |
107 | #define EXIT_SUCCESS 0 | |
108 | #endif /* !defined EXIT_SUCCESS */ | |
109 | ||
110 | #ifndef EXIT_FAILURE | |
111 | #define EXIT_FAILURE 1 | |
112 | #endif /* !defined EXIT_FAILURE */ | |
113 | ||
114 | #ifndef SECSPERMIN | |
115 | #define SECSPERMIN 60 | |
116 | #endif /* !defined SECSPERMIN */ | |
117 | ||
118 | #ifndef MINSPERHOUR | |
119 | #define MINSPERHOUR 60 | |
120 | #endif /* !defined MINSPERHOUR */ | |
121 | ||
122 | #ifndef SECSPERHOUR | |
123 | #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) | |
124 | #endif /* !defined SECSPERHOUR */ | |
125 | ||
126 | #ifndef HOURSPERDAY | |
127 | #define HOURSPERDAY 24 | |
128 | #endif /* !defined HOURSPERDAY */ | |
129 | ||
130 | #ifndef EPOCH_YEAR | |
131 | #define EPOCH_YEAR 1970 | |
132 | #endif /* !defined EPOCH_YEAR */ | |
133 | ||
134 | #ifndef TM_YEAR_BASE | |
135 | #define TM_YEAR_BASE 1900 | |
136 | #endif /* !defined TM_YEAR_BASE */ | |
137 | ||
138 | #ifndef DAYSPERNYEAR | |
139 | #define DAYSPERNYEAR 365 | |
140 | #endif /* !defined DAYSPERNYEAR */ | |
141 | ||
142 | #ifndef isleap | |
143 | #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) | |
144 | #endif /* !defined isleap */ | |
145 | ||
146 | #ifndef isleap_sum | |
147 | /* | |
148 | ** See tzfile.h for details on isleap_sum. | |
149 | */ | |
150 | #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) | |
151 | #endif /* !defined isleap_sum */ | |
152 | ||
b331163b | 153 | #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) |
4162bf98 A |
154 | #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) |
155 | #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) | |
b331163b A |
156 | #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ |
157 | + SECSPERLYEAR * (intmax_t) (100 - 3)) | |
158 | ||
159 | /* | |
160 | ** True if SECSPER400YEARS is known to be representable as an | |
161 | ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false | |
162 | ** even if SECSPER400YEARS is representable, because when that happens | |
163 | ** the code merely runs a bit more slowly, and this slowness doesn't | |
164 | ** occur on any practical platform. | |
165 | */ | |
166 | enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; | |
4162bf98 | 167 | |
fd0068a8 A |
168 | #ifndef HAVE_GETTEXT |
169 | #define HAVE_GETTEXT 0 | |
170 | #endif | |
4162bf98 A |
171 | #if HAVE_GETTEXT |
172 | #include "locale.h" /* for setlocale */ | |
173 | #include "libintl.h" | |
174 | #endif /* HAVE_GETTEXT */ | |
175 | ||
176 | #ifndef GNUC_or_lint | |
177 | #ifdef lint | |
178 | #define GNUC_or_lint | |
179 | #else /* !defined lint */ | |
180 | #ifdef __GNUC__ | |
181 | #define GNUC_or_lint | |
182 | #endif /* defined __GNUC__ */ | |
183 | #endif /* !defined lint */ | |
184 | #endif /* !defined GNUC_or_lint */ | |
185 | ||
b331163b A |
186 | #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) |
187 | # define ATTRIBUTE_PURE __attribute__ ((__pure__)) | |
188 | #else | |
189 | # define ATTRIBUTE_PURE /* empty */ | |
190 | #endif | |
4162bf98 A |
191 | |
192 | /* | |
193 | ** For the benefit of GNU folk... | |
194 | ** `_(MSGID)' uses the current locale's message library string for MSGID. | |
195 | ** The default is to use gettext if available, and use MSGID otherwise. | |
196 | */ | |
197 | ||
198 | #ifndef _ | |
199 | #if HAVE_GETTEXT | |
200 | #define _(msgid) gettext(msgid) | |
201 | #else /* !HAVE_GETTEXT */ | |
202 | #define _(msgid) msgid | |
203 | #endif /* !HAVE_GETTEXT */ | |
204 | #endif /* !defined _ */ | |
205 | ||
206 | #ifndef TZ_DOMAIN | |
207 | #define TZ_DOMAIN "tz" | |
208 | #endif /* !defined TZ_DOMAIN */ | |
209 | ||
4162bf98 | 210 | extern char ** environ; |
fd0068a8 A |
211 | extern int getopt(int argc, char * const argv[], |
212 | const char * options); | |
4162bf98 A |
213 | extern char * optarg; |
214 | extern int optind; | |
215 | extern char * tzname[2]; | |
216 | ||
b331163b A |
217 | /* The minimum and maximum finite time values. */ |
218 | static time_t const absolute_min_time = | |
219 | ((time_t) -1 < 0 | |
220 | ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) | |
221 | : 0); | |
222 | static time_t const absolute_max_time = | |
223 | ((time_t) -1 < 0 | |
224 | ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) | |
225 | : -1); | |
4162bf98 A |
226 | static size_t longest; |
227 | static char * progname; | |
228 | static int warned; | |
229 | ||
fd0068a8 A |
230 | static char * abbr(struct tm * tmp); |
231 | static void abbrok(const char * abbrp, const char * zone); | |
b331163b | 232 | static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE; |
fd0068a8 A |
233 | static void dumptime(const struct tm * tmp); |
234 | static time_t hunt(char * name, time_t lot, time_t hit); | |
fd0068a8 A |
235 | static void show(char * zone, time_t t, int v); |
236 | static const char * tformat(void); | |
b331163b | 237 | static time_t yeartot(intmax_t y) ATTRIBUTE_PURE; |
4162bf98 A |
238 | #ifdef ICU |
239 | typedef struct listentry { | |
240 | char * name; | |
241 | struct listentry * next; | |
242 | } listentry; | |
243 | ||
fd0068a8 A |
244 | static time_t huntICU(char * name, time_t lot, time_t hit, FILE *fp); |
245 | static void dumptimeICU(FILE * fp, time_t t); | |
246 | static void showICU(FILE * fp, char * zone, time_t t1, time_t t2); | |
247 | static int getall(struct listentry ** namelist); | |
248 | static void getzones(char * basedir, char * subdir, struct listentry ** last, int * count); | |
4162bf98 A |
249 | #endif |
250 | ||
251 | #ifndef TYPECHECK | |
252 | #define my_localtime localtime | |
253 | #else /* !defined TYPECHECK */ | |
254 | static struct tm * | |
b331163b | 255 | my_localtime(time_t *tp) |
4162bf98 A |
256 | { |
257 | register struct tm * tmp; | |
258 | ||
259 | tmp = localtime(tp); | |
260 | if (tp != NULL && tmp != NULL) { | |
261 | struct tm tm; | |
262 | register time_t t; | |
263 | ||
264 | tm = *tmp; | |
265 | t = mktime(&tm); | |
b331163b | 266 | if (t != *tp) { |
4162bf98 A |
267 | (void) fflush(stdout); |
268 | (void) fprintf(stderr, "\n%s: ", progname); | |
269 | (void) fprintf(stderr, tformat(), *tp); | |
270 | (void) fprintf(stderr, " ->"); | |
271 | (void) fprintf(stderr, " year=%d", tmp->tm_year); | |
272 | (void) fprintf(stderr, " mon=%d", tmp->tm_mon); | |
273 | (void) fprintf(stderr, " mday=%d", tmp->tm_mday); | |
274 | (void) fprintf(stderr, " hour=%d", tmp->tm_hour); | |
275 | (void) fprintf(stderr, " min=%d", tmp->tm_min); | |
276 | (void) fprintf(stderr, " sec=%d", tmp->tm_sec); | |
277 | (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); | |
278 | (void) fprintf(stderr, " -> "); | |
279 | (void) fprintf(stderr, tformat(), t); | |
280 | (void) fprintf(stderr, "\n"); | |
281 | } | |
282 | } | |
283 | return tmp; | |
284 | } | |
285 | #endif /* !defined TYPECHECK */ | |
286 | ||
287 | static void | |
b331163b | 288 | abbrok(const char *const abbrp, const char *const zone) |
4162bf98 A |
289 | { |
290 | register const char * cp; | |
b331163b | 291 | register const char * wp; |
4162bf98 A |
292 | |
293 | if (warned) | |
294 | return; | |
295 | cp = abbrp; | |
296 | wp = NULL; | |
297 | while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp)) | |
298 | ++cp; | |
299 | if (cp - abbrp == 0) | |
300 | wp = _("lacks alphabetic at start"); | |
301 | else if (cp - abbrp < 3) | |
302 | wp = _("has fewer than 3 alphabetics"); | |
303 | else if (cp - abbrp > 6) | |
304 | wp = _("has more than 6 alphabetics"); | |
305 | if (wp == NULL && (*cp == '+' || *cp == '-')) { | |
306 | ++cp; | |
307 | if (isascii((unsigned char) *cp) && | |
308 | isdigit((unsigned char) *cp)) | |
309 | if (*cp++ == '1' && *cp >= '0' && *cp <= '4') | |
310 | ++cp; | |
311 | if (*cp != '\0') | |
312 | wp = _("differs from POSIX standard"); | |
313 | } | |
314 | if (wp == NULL) | |
315 | return; | |
316 | (void) fflush(stdout); | |
317 | (void) fprintf(stderr, | |
318 | _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), | |
319 | progname, zone, abbrp, wp); | |
320 | warned = TRUE; | |
321 | } | |
322 | ||
fd0068a8 | 323 | static void |
b331163b | 324 | usage(FILE * const stream, const int status) |
fd0068a8 A |
325 | { |
326 | (void) fprintf(stream, | |
b331163b A |
327 | _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n" |
328 | "\n" | |
329 | "Report bugs to %s.\n"), | |
330 | progname, progname, REPORT_BUGS_TO); | |
fd0068a8 A |
331 | exit(status); |
332 | } | |
333 | ||
4162bf98 | 334 | int |
b331163b | 335 | main(int argc, char *argv[]) |
4162bf98 A |
336 | { |
337 | register int i; | |
4162bf98 | 338 | register int vflag; |
b331163b | 339 | register int Vflag; |
4162bf98 | 340 | register char * cutarg; |
b331163b | 341 | register char * cuttimes; |
4162bf98 A |
342 | register time_t cutlotime; |
343 | register time_t cuthitime; | |
344 | register char ** fakeenv; | |
345 | time_t now; | |
346 | time_t t; | |
347 | time_t newt; | |
348 | struct tm tm; | |
349 | struct tm newtm; | |
350 | register struct tm * tmp; | |
351 | register struct tm * newtmp; | |
352 | #ifdef ICU | |
353 | int nextopt; | |
354 | char * dirarg; | |
355 | int aflag; | |
356 | int iflag; | |
357 | listentry * namelist = NULL; | |
358 | FILE * fp = stdout; | |
359 | #endif | |
360 | ||
b331163b A |
361 | cutlotime = absolute_min_time; |
362 | cuthitime = absolute_max_time; | |
4162bf98 A |
363 | #if HAVE_GETTEXT |
364 | (void) setlocale(LC_ALL, ""); | |
365 | #ifdef TZ_DOMAINDIR | |
366 | (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); | |
367 | #endif /* defined TEXTDOMAINDIR */ | |
368 | (void) textdomain(TZ_DOMAIN); | |
369 | #endif /* HAVE_GETTEXT */ | |
370 | progname = argv[0]; | |
371 | for (i = 1; i < argc; ++i) | |
372 | if (strcmp(argv[i], "--version") == 0) { | |
b331163b | 373 | (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); |
4162bf98 | 374 | exit(EXIT_SUCCESS); |
fd0068a8 | 375 | } else if (strcmp(argv[i], "--help") == 0) { |
b331163b | 376 | usage(stdout, EXIT_SUCCESS); |
4162bf98 | 377 | } |
b331163b A |
378 | vflag = Vflag = 0; |
379 | cutarg = cuttimes = NULL; | |
4162bf98 A |
380 | #ifdef ICU |
381 | aflag = 0; | |
382 | iflag = 0; | |
383 | dirarg = NULL; | |
b331163b A |
384 | for (;;) |
385 | switch(getopt(argc, argv, "ac:d:it:vV")) { | |
386 | case 'a': aflag = 1; break; | |
387 | case 'c': cutarg = optarg; break; | |
388 | case 'd': dirarg = optarg; break; | |
389 | case 'i': iflag = 1; break; | |
390 | case 't': cuttimes = optarg; break; | |
391 | case 'v': vflag = 1; break; | |
392 | case 'V': Vflag = 1; break; | |
393 | case -1: | |
394 | if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) | |
395 | goto arg_processing_done; | |
396 | /* Fall through. */ | |
4162bf98 | 397 | default: |
4162bf98 | 398 | (void) fprintf(stderr, |
b331163b | 399 | _("%s: usage is %s [ --version ] [ -a ] [ -v ] [ -V ] [ -i ] [ -c [loyear,]hiyear ] [ -t [lotime,]hitime] ][ -d dir ] [ zonename ... ]\n"), |
4162bf98 A |
400 | progname, progname); |
401 | exit(EXIT_FAILURE); | |
b331163b A |
402 | } |
403 | #else | |
404 | for (;;) | |
405 | switch (getopt(argc, argv, "c:t:vV")) { | |
406 | case 'c': cutarg = optarg; break; | |
407 | case 't': cuttimes = optarg; break; | |
408 | case 'v': vflag = 1; break; | |
409 | case 'V': Vflag = 1; break; | |
410 | case -1: | |
411 | if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) | |
412 | goto arg_processing_done; | |
413 | /* Fall through. */ | |
414 | default: | |
415 | usage(stderr, EXIT_FAILURE); | |
416 | } | |
417 | #endif | |
418 | arg_processing_done:; | |
4162bf98 | 419 | |
b331163b | 420 | #ifdef ICU |
4162bf98 A |
421 | if (dirarg != NULL) { |
422 | DIR * dp; | |
423 | /* create the output directory */ | |
424 | mkdir(dirarg, 0777); | |
425 | if ((dp = opendir(dirarg)) == NULL) { | |
426 | fprintf(stderr, "cannot create the target directory"); | |
427 | exit(EXIT_FAILURE); | |
428 | } | |
429 | closedir(dp); | |
430 | } | |
b331163b A |
431 | #endif ICU |
432 | ||
433 | if (vflag | Vflag) { | |
434 | intmax_t lo; | |
435 | intmax_t hi; | |
436 | char *loend, *hiend; | |
437 | register intmax_t cutloyear = ZDUMP_LO_YEAR; | |
438 | register intmax_t cuthiyear = ZDUMP_HI_YEAR; | |
4162bf98 | 439 | if (cutarg != NULL) { |
b331163b A |
440 | lo = strtoimax(cutarg, &loend, 10); |
441 | if (cutarg != loend && !*loend) { | |
442 | hi = lo; | |
443 | cuthiyear = hi; | |
444 | } else if (cutarg != loend && *loend == ',' | |
445 | && (hi = strtoimax(loend + 1, &hiend, 10), | |
446 | loend + 1 != hiend && !*hiend)) { | |
447 | cutloyear = lo; | |
4162bf98 | 448 | cuthiyear = hi; |
4162bf98 A |
449 | } else { |
450 | (void) fprintf(stderr, _("%s: wild -c argument %s\n"), | |
451 | progname, cutarg); | |
452 | exit(EXIT_FAILURE); | |
453 | } | |
454 | } | |
b331163b A |
455 | if (cutarg != NULL || cuttimes == NULL) { |
456 | cutlotime = yeartot(cutloyear); | |
457 | cuthitime = yeartot(cuthiyear); | |
458 | } | |
459 | if (cuttimes != NULL) { | |
460 | lo = strtoimax(cuttimes, &loend, 10); | |
461 | if (cuttimes != loend && !*loend) { | |
462 | hi = lo; | |
463 | if (hi < cuthitime) { | |
464 | if (hi < absolute_min_time) | |
465 | hi = absolute_min_time; | |
466 | cuthitime = hi; | |
467 | } | |
468 | } else if (cuttimes != loend && *loend == ',' | |
469 | && (hi = strtoimax(loend + 1, &hiend, 10), | |
470 | loend + 1 != hiend && !*hiend)) { | |
471 | if (cutlotime < lo) { | |
472 | if (absolute_max_time < lo) | |
473 | lo = absolute_max_time; | |
474 | cutlotime = lo; | |
475 | } | |
476 | if (hi < cuthitime) { | |
477 | if (hi < absolute_min_time) | |
478 | hi = absolute_min_time; | |
479 | cuthitime = hi; | |
480 | } | |
481 | } else { | |
482 | (void) fprintf(stderr, | |
483 | _("%s: wild -t argument %s\n"), | |
484 | progname, cuttimes); | |
485 | exit(EXIT_FAILURE); | |
486 | } | |
487 | } | |
4162bf98 A |
488 | } |
489 | ||
490 | #ifdef ICU | |
491 | if (aflag) { | |
492 | /* get all available zones */ | |
493 | char ** fakeargv; | |
494 | int i; | |
495 | int count; | |
496 | ||
497 | count = getall(&namelist); | |
498 | ||
499 | fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv); | |
500 | /* | |
501 | if ((fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv)) == NULL) { | |
502 | exit(EXIT_FAILURE); | |
503 | } | |
504 | */ | |
505 | for (i = 0; i < argc; i++) { | |
506 | fakeargv[i] = argv[i]; | |
507 | } | |
508 | for (i = 0; i < count; i++) { | |
509 | fakeargv[i + argc] = namelist->name; | |
510 | namelist = namelist->next; | |
511 | } | |
512 | argv = fakeargv; | |
513 | argc += count; | |
514 | } | |
515 | #endif | |
516 | (void) time(&now); | |
517 | longest = 0; | |
518 | for (i = optind; i < argc; ++i) | |
519 | if (strlen(argv[i]) > longest) | |
520 | longest = strlen(argv[i]); | |
521 | { | |
522 | register int from; | |
523 | register int to; | |
524 | ||
525 | for (i = 0; environ[i] != NULL; ++i) | |
526 | continue; | |
b331163b A |
527 | fakeenv = malloc((i + 2) * sizeof *fakeenv); |
528 | if (fakeenv == NULL | |
529 | || (fakeenv[0] = malloc(longest + 4)) == NULL) { | |
4162bf98 A |
530 | (void) perror(progname); |
531 | exit(EXIT_FAILURE); | |
532 | } | |
533 | to = 0; | |
534 | (void) strcpy(fakeenv[to++], "TZ="); | |
535 | for (from = 0; environ[from] != NULL; ++from) | |
536 | if (strncmp(environ[from], "TZ=", 3) != 0) | |
537 | fakeenv[to++] = environ[from]; | |
538 | fakeenv[to] = NULL; | |
539 | environ = fakeenv; | |
540 | } | |
541 | for (i = optind; i < argc; ++i) { | |
542 | static char buf[MAX_STRING_LENGTH]; | |
543 | ||
544 | (void) strcpy(&fakeenv[0][3], argv[i]); | |
b331163b | 545 | if (! (vflag | Vflag)) { |
4162bf98 A |
546 | show(argv[i], now, FALSE); |
547 | continue; | |
548 | } | |
549 | #ifdef ICU | |
550 | fp = NULL; | |
551 | if (iflag) { | |
552 | if (dirarg == NULL) { | |
553 | /* we want to display a zone name here */ | |
554 | if (i != optind) { | |
555 | printf("\n"); | |
556 | } | |
557 | printf("ZONE: %s\n", argv[i]); | |
558 | } else { | |
559 | int zstart; | |
560 | char path[FILENAME_MAX + 1]; | |
561 | strcpy(path, dirarg); | |
562 | strcat(path, "/"); | |
563 | zstart = strlen(path); | |
564 | strcat(path, argv[i]); | |
565 | /* replace '/' with '-' */ | |
566 | while(path[++zstart] != 0) { | |
567 | if (path[zstart] == '/') { | |
568 | path[zstart] = '-'; | |
569 | } | |
570 | } | |
571 | if ((fp = fopen(path, "w")) == NULL) { | |
572 | fprintf(stderr, "cannot create output file %s\n", path); | |
573 | exit(EXIT_FAILURE); | |
574 | } | |
575 | } | |
576 | } | |
577 | #endif | |
578 | warned = FALSE; | |
579 | t = absolute_min_time; | |
580 | #ifdef ICU | |
581 | /* skip displaying info for the lowest time, which is actually not | |
582 | * a transition when -i option is set */ | |
583 | if (!iflag) { | |
584 | #endif | |
b331163b A |
585 | if (!Vflag) { |
586 | show(argv[i], t, TRUE); | |
587 | t += SECSPERDAY; | |
588 | show(argv[i], t, TRUE); | |
589 | } | |
4162bf98 A |
590 | #ifdef ICU |
591 | } | |
592 | #endif | |
593 | if (t < cutlotime) | |
594 | t = cutlotime; | |
595 | tmp = my_localtime(&t); | |
596 | if (tmp != NULL) { | |
597 | tm = *tmp; | |
598 | (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); | |
599 | } | |
600 | for ( ; ; ) { | |
b331163b A |
601 | newt = (t < absolute_max_time - SECSPERDAY / 2 |
602 | ? t + SECSPERDAY / 2 | |
603 | : absolute_max_time); | |
604 | if (cuthitime <= newt) | |
4162bf98 | 605 | break; |
4162bf98 A |
606 | newtmp = localtime(&newt); |
607 | if (newtmp != NULL) | |
608 | newtm = *newtmp; | |
609 | #ifdef ICU | |
610 | if (iflag) { | |
611 | /* We do not want to capture transitions just for | |
612 | * abbreviated zone name changes */ | |
613 | if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : | |
614 | (delta(&newtm, &tm) != (newt - t) || | |
615 | newtm.tm_isdst != tm.tm_isdst)) { | |
616 | newt = huntICU(argv[i], t, newt, fp); | |
617 | newtmp = localtime(&newt); | |
618 | if (newtmp != NULL) { | |
619 | newtm = *newtmp; | |
620 | (void) strncpy(buf, | |
621 | abbr(&newtm), | |
622 | (sizeof buf) - 1); | |
623 | } | |
624 | } | |
625 | } else { | |
626 | #endif | |
627 | if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : | |
628 | (delta(&newtm, &tm) != (newt - t) || | |
629 | newtm.tm_isdst != tm.tm_isdst || | |
630 | strcmp(abbr(&newtm), buf) != 0)) { | |
631 | newt = hunt(argv[i], t, newt); | |
632 | newtmp = localtime(&newt); | |
633 | if (newtmp != NULL) { | |
634 | newtm = *newtmp; | |
635 | (void) strncpy(buf, | |
636 | abbr(&newtm), | |
637 | (sizeof buf) - 1); | |
638 | } | |
639 | } | |
640 | #ifdef ICU | |
641 | } | |
642 | #endif | |
643 | t = newt; | |
644 | tm = newtm; | |
645 | tmp = newtmp; | |
646 | } | |
647 | #ifdef ICU | |
648 | if (!iflag) { | |
649 | /* skip displaying info for the highest time, which is actually not | |
650 | * a transition when -i option is used*/ | |
651 | #endif | |
b331163b A |
652 | if (!Vflag) { |
653 | t = absolute_max_time; | |
654 | t -= SECSPERDAY; | |
655 | show(argv[i], t, TRUE); | |
656 | t += SECSPERDAY; | |
657 | show(argv[i], t, TRUE); | |
658 | } | |
4162bf98 A |
659 | #ifdef ICU |
660 | } | |
661 | /* close file */ | |
662 | if (fp != NULL) { | |
663 | fclose(fp); | |
664 | } | |
665 | #endif | |
666 | } | |
667 | if (fflush(stdout) || ferror(stdout)) { | |
668 | (void) fprintf(stderr, "%s: ", progname); | |
669 | (void) perror(_("Error writing to standard output")); | |
670 | exit(EXIT_FAILURE); | |
671 | } | |
672 | #ifdef ICU | |
673 | if (aflag) { | |
674 | struct listentry * entry = namelist; | |
675 | struct listentry * next; | |
676 | while (entry != NULL) { | |
677 | free(entry->name); | |
678 | next = entry->next; | |
679 | free(entry); | |
680 | entry = next; | |
681 | } | |
682 | } | |
683 | #endif | |
684 | exit(EXIT_SUCCESS); | |
685 | /* If exit fails to exit... */ | |
686 | return EXIT_FAILURE; | |
687 | } | |
688 | ||
4162bf98 | 689 | static time_t |
b331163b | 690 | yeartot(const intmax_t y) |
4162bf98 | 691 | { |
b331163b A |
692 | register intmax_t myy, seconds, years; |
693 | register time_t t; | |
4162bf98 A |
694 | |
695 | myy = EPOCH_YEAR; | |
696 | t = 0; | |
b331163b A |
697 | while (myy < y) { |
698 | if (SECSPER400YEARS_FITS && 400 <= y - myy) { | |
699 | intmax_t diff400 = (y - myy) / 400; | |
700 | if (INTMAX_MAX / SECSPER400YEARS < diff400) | |
701 | return absolute_max_time; | |
702 | seconds = diff400 * SECSPER400YEARS; | |
703 | years = diff400 * 400; | |
704 | } else { | |
4162bf98 | 705 | seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; |
b331163b A |
706 | years = 1; |
707 | } | |
708 | myy += years; | |
709 | if (t > absolute_max_time - seconds) | |
710 | return absolute_max_time; | |
711 | t += seconds; | |
712 | } | |
713 | while (y < myy) { | |
714 | if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) { | |
715 | intmax_t diff400 = (myy - y) / 400; | |
716 | if (INTMAX_MAX / SECSPER400YEARS < diff400) | |
717 | return absolute_min_time; | |
718 | seconds = diff400 * SECSPER400YEARS; | |
719 | years = diff400 * 400; | |
4162bf98 | 720 | } else { |
b331163b A |
721 | seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR; |
722 | years = 1; | |
4162bf98 | 723 | } |
b331163b A |
724 | myy -= years; |
725 | if (t < absolute_min_time + seconds) | |
726 | return absolute_min_time; | |
727 | t -= seconds; | |
4162bf98 A |
728 | } |
729 | return t; | |
730 | } | |
731 | ||
732 | static time_t | |
733 | hunt(char *name, time_t lot, time_t hit) | |
734 | { | |
735 | time_t t; | |
4162bf98 A |
736 | struct tm lotm; |
737 | register struct tm * lotmp; | |
738 | struct tm tm; | |
739 | register struct tm * tmp; | |
740 | char loab[MAX_STRING_LENGTH]; | |
741 | ||
742 | lotmp = my_localtime(&lot); | |
743 | if (lotmp != NULL) { | |
744 | lotm = *lotmp; | |
745 | (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); | |
746 | } | |
747 | for ( ; ; ) { | |
b331163b | 748 | time_t diff = hit - lot; |
4162bf98 A |
749 | if (diff < 2) |
750 | break; | |
751 | t = lot; | |
752 | t += diff / 2; | |
753 | if (t <= lot) | |
754 | ++t; | |
755 | else if (t >= hit) | |
756 | --t; | |
757 | tmp = my_localtime(&t); | |
758 | if (tmp != NULL) | |
759 | tm = *tmp; | |
760 | if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : | |
761 | (delta(&tm, &lotm) == (t - lot) && | |
762 | tm.tm_isdst == lotm.tm_isdst && | |
763 | strcmp(abbr(&tm), loab) == 0)) { | |
764 | lot = t; | |
765 | lotm = tm; | |
766 | lotmp = tmp; | |
767 | } else hit = t; | |
768 | } | |
769 | show(name, lot, TRUE); | |
770 | show(name, hit, TRUE); | |
771 | return hit; | |
772 | } | |
773 | ||
774 | /* | |
775 | ** Thanks to Paul Eggert for logic used in delta. | |
776 | */ | |
777 | ||
b331163b A |
778 | static intmax_t |
779 | delta(struct tm * newp, struct tm *oldp) | |
4162bf98 | 780 | { |
b331163b A |
781 | register intmax_t result; |
782 | register int tmy; | |
4162bf98 A |
783 | |
784 | if (newp->tm_year < oldp->tm_year) | |
785 | return -delta(oldp, newp); | |
786 | result = 0; | |
787 | for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) | |
788 | result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); | |
789 | result += newp->tm_yday - oldp->tm_yday; | |
790 | result *= HOURSPERDAY; | |
791 | result += newp->tm_hour - oldp->tm_hour; | |
792 | result *= MINSPERHOUR; | |
793 | result += newp->tm_min - oldp->tm_min; | |
794 | result *= SECSPERMIN; | |
795 | result += newp->tm_sec - oldp->tm_sec; | |
796 | return result; | |
797 | } | |
798 | ||
799 | static void | |
800 | show(char *zone, time_t t, int v) | |
801 | { | |
802 | register struct tm * tmp; | |
803 | ||
804 | (void) printf("%-*s ", (int) longest, zone); | |
805 | if (v) { | |
806 | tmp = gmtime(&t); | |
807 | if (tmp == NULL) { | |
808 | (void) printf(tformat(), t); | |
809 | } else { | |
810 | dumptime(tmp); | |
b331163b | 811 | (void) printf(" UT"); |
4162bf98 A |
812 | } |
813 | (void) printf(" = "); | |
814 | } | |
815 | tmp = my_localtime(&t); | |
816 | dumptime(tmp); | |
817 | if (tmp != NULL) { | |
818 | if (*abbr(tmp) != '\0') | |
819 | (void) printf(" %s", abbr(tmp)); | |
820 | if (v) { | |
821 | (void) printf(" isdst=%d", tmp->tm_isdst); | |
822 | #ifdef TM_GMTOFF | |
823 | (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); | |
824 | #endif /* defined TM_GMTOFF */ | |
825 | } | |
826 | } | |
827 | (void) printf("\n"); | |
828 | if (tmp != NULL && *abbr(tmp) != '\0') | |
829 | abbrok(abbr(tmp), zone); | |
830 | } | |
831 | ||
832 | static char * | |
b331163b | 833 | abbr(struct tm *tmp) |
4162bf98 A |
834 | { |
835 | register char * result; | |
836 | static char nada; | |
837 | ||
838 | if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) | |
839 | return &nada; | |
840 | result = tzname[tmp->tm_isdst]; | |
841 | return (result == NULL) ? &nada : result; | |
842 | } | |
843 | ||
844 | /* | |
845 | ** The code below can fail on certain theoretical systems; | |
846 | ** it works on all known real-world systems as of 2004-12-30. | |
847 | */ | |
848 | ||
849 | static const char * | |
fd0068a8 | 850 | tformat(void) |
4162bf98 | 851 | { |
4162bf98 | 852 | if (0 > (time_t) -1) { /* signed */ |
b331163b A |
853 | if (sizeof (time_t) == sizeof (intmax_t)) |
854 | return "%"PRIdMAX; | |
4162bf98 A |
855 | if (sizeof (time_t) > sizeof (long)) |
856 | return "%lld"; | |
857 | if (sizeof (time_t) > sizeof (int)) | |
858 | return "%ld"; | |
859 | return "%d"; | |
860 | } | |
b331163b A |
861 | #ifdef PRIuMAX |
862 | if (sizeof (time_t) == sizeof (uintmax_t)) | |
863 | return "%"PRIuMAX; | |
864 | #endif | |
4162bf98 A |
865 | if (sizeof (time_t) > sizeof (unsigned long)) |
866 | return "%llu"; | |
867 | if (sizeof (time_t) > sizeof (unsigned int)) | |
868 | return "%lu"; | |
869 | return "%u"; | |
870 | } | |
871 | ||
872 | static void | |
b331163b | 873 | dumptime(register const struct tm *timeptr) |
4162bf98 A |
874 | { |
875 | static const char wday_name[][3] = { | |
876 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
877 | }; | |
878 | static const char mon_name[][3] = { | |
879 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
880 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
881 | }; | |
882 | register const char * wn; | |
883 | register const char * mn; | |
884 | register int lead; | |
885 | register int trail; | |
886 | ||
887 | if (timeptr == NULL) { | |
888 | (void) printf("NULL"); | |
889 | return; | |
890 | } | |
891 | /* | |
892 | ** The packaged versions of localtime and gmtime never put out-of-range | |
893 | ** values in tm_wday or tm_mon, but since this code might be compiled | |
894 | ** with other (perhaps experimental) versions, paranoia is in order. | |
895 | */ | |
896 | if (timeptr->tm_wday < 0 || timeptr->tm_wday >= | |
897 | (int) (sizeof wday_name / sizeof wday_name[0])) | |
898 | wn = "???"; | |
899 | else wn = wday_name[timeptr->tm_wday]; | |
900 | if (timeptr->tm_mon < 0 || timeptr->tm_mon >= | |
901 | (int) (sizeof mon_name / sizeof mon_name[0])) | |
902 | mn = "???"; | |
903 | else mn = mon_name[timeptr->tm_mon]; | |
904 | (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", | |
905 | wn, mn, | |
906 | timeptr->tm_mday, timeptr->tm_hour, | |
907 | timeptr->tm_min, timeptr->tm_sec); | |
908 | #define DIVISOR 10 | |
909 | trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; | |
910 | lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + | |
911 | trail / DIVISOR; | |
912 | trail %= DIVISOR; | |
913 | if (trail < 0 && lead > 0) { | |
914 | trail += DIVISOR; | |
915 | --lead; | |
916 | } else if (lead < 0 && trail > 0) { | |
917 | trail -= DIVISOR; | |
918 | ++lead; | |
919 | } | |
920 | if (lead == 0) | |
921 | (void) printf("%d", trail); | |
922 | else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); | |
923 | } | |
924 | ||
925 | #ifdef ICU | |
926 | static time_t | |
927 | huntICU(char *name, time_t lot, time_t hit, FILE * fp) | |
928 | { | |
929 | time_t t; | |
930 | long diff; | |
931 | struct tm lotm; | |
932 | register struct tm * lotmp; | |
933 | struct tm tm; | |
934 | register struct tm * tmp; | |
935 | char loab[MAX_STRING_LENGTH]; | |
936 | ||
937 | lotmp = my_localtime(&lot); | |
938 | if (lotmp != NULL) { | |
939 | lotm = *lotmp; | |
940 | (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); | |
941 | } | |
942 | for ( ; ; ) { | |
943 | diff = (long) (hit - lot); | |
944 | if (diff < 2) | |
945 | break; | |
946 | t = lot; | |
947 | t += diff / 2; | |
948 | if (t <= lot) | |
949 | ++t; | |
950 | else if (t >= hit) | |
951 | --t; | |
952 | tmp = my_localtime(&t); | |
953 | if (tmp != NULL) | |
954 | tm = *tmp; | |
955 | /* We do not want to capture transitions just for | |
956 | * abbreviated zone name changes */ | |
957 | if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : | |
958 | (delta(&tm, &lotm) == (t - lot) && | |
959 | tm.tm_isdst == lotm.tm_isdst)) { | |
960 | lot = t; | |
961 | lotm = tm; | |
962 | lotmp = tmp; | |
963 | } else hit = t; | |
964 | } | |
965 | showICU(fp, name, lot, hit); | |
966 | return hit; | |
967 | } | |
968 | ||
969 | static void showICU(FILE * fp, char *zone, time_t t1, time_t t2) | |
970 | { | |
971 | if (fp == NULL) { | |
972 | fp = stdout; | |
973 | } | |
974 | dumptimeICU(fp, t1); | |
975 | fprintf(fp, " > "); | |
976 | dumptimeICU(fp, t2); | |
977 | fprintf(fp, "\n"); | |
978 | } | |
979 | ||
980 | static void dumptimeICU(FILE * fp, time_t t) | |
981 | { | |
982 | static const char wday_name[][3] = { | |
983 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
984 | }; | |
985 | struct tm gmt; | |
986 | struct tm loc; | |
987 | register int lead; | |
988 | register int trail; | |
989 | long offset; | |
990 | long hour, min, sec; | |
991 | ||
992 | loc = *my_localtime(&t); | |
993 | ||
994 | trail = loc.tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; | |
995 | lead = loc.tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + trail / DIVISOR; | |
996 | trail %= DIVISOR; | |
997 | if (trail < 0 && lead > 0) { | |
998 | trail += DIVISOR; | |
999 | --lead; | |
1000 | } else if (lead < 0 && trail > 0) { | |
1001 | trail -= DIVISOR; | |
1002 | ++lead; | |
1003 | } | |
1004 | ||
1005 | fprintf(fp, "%04d-%02d-%02d", lead * DIVISOR + trail, loc.tm_mon + 1, loc.tm_mday); | |
1006 | fprintf(fp, " %.3s ", wday_name[loc.tm_wday]); | |
1007 | fprintf(fp, "%02d:%02d:%02d", loc.tm_hour, loc.tm_min, loc.tm_sec); | |
1008 | ||
1009 | gmt = *gmtime(&t); | |
1010 | offset = delta(&loc, &gmt); | |
1011 | if (offset < 0) { | |
1012 | offset = -offset; | |
1013 | fprintf(fp, "-"); | |
1014 | } else { | |
1015 | fprintf(fp, "+"); | |
1016 | } | |
1017 | ||
1018 | sec = offset % 60; | |
1019 | offset = (offset - sec) / 60; | |
1020 | min = offset % 60; | |
1021 | hour = offset / 60; | |
1022 | ||
1023 | fprintf(fp, "%02d", hour); | |
1024 | fprintf(fp, "%02d", min); | |
1025 | fprintf(fp, "%02d", sec); | |
1026 | fprintf(fp, "[DST=%d]", loc.tm_isdst); | |
1027 | } | |
1028 | ||
1029 | static int getall(struct listentry ** namelist) { | |
1030 | int count = 0; | |
1031 | struct listentry dummyentry; | |
1032 | struct listentry * last = &dummyentry; | |
1033 | ||
1034 | getzones(TZDIR, NULL, &last, &count); | |
1035 | if (count > 0) { | |
1036 | *namelist = dummyentry.next; | |
1037 | } | |
1038 | ||
1039 | return count; | |
1040 | } | |
1041 | ||
1042 | static void getzones(char * basedir, char * relpath, struct listentry ** last, int * count) { | |
1043 | char path[FILENAME_MAX + 1]; | |
1044 | struct dirent * dir; | |
1045 | DIR * dp; | |
1046 | ||
1047 | strcpy(path, basedir); | |
1048 | if (relpath != NULL) { | |
1049 | strcat(path, "/"); | |
1050 | strcat(path, relpath); | |
1051 | } | |
1052 | ||
1053 | if ((dp = opendir(path)) == NULL) { | |
1054 | /* file */ | |
4388f060 | 1055 | if (strstr(relpath, ".tab") == NULL && strcmp(relpath, "Etc/Unknown") != 0) { |
4162bf98 A |
1056 | char * pzonename; |
1057 | listentry * pentry; | |
1058 | ||
1059 | if ((pzonename = malloc(strlen(relpath) + 1)) == NULL) { | |
1060 | exit(EXIT_FAILURE); | |
1061 | } | |
1062 | strcpy(pzonename, relpath); | |
1063 | ||
1064 | if ((pentry = malloc(sizeof(listentry))) == NULL) { | |
1065 | exit(EXIT_FAILURE); | |
1066 | } | |
1067 | ||
1068 | pentry->name = pzonename; | |
1069 | pentry->next = NULL; | |
1070 | (*last)->next = pentry; | |
1071 | *last = pentry; | |
1072 | (*count)++; | |
1073 | } | |
1074 | } else { | |
1075 | /* directory */ | |
1076 | while ((dir = readdir(dp)) != NULL) { | |
1077 | char subpath[FILENAME_MAX + 1]; | |
1078 | ||
1079 | if (strcmp(dir->d_name, ".") == 0 | |
1080 | || strcmp(dir->d_name, "..") == 0) { | |
1081 | continue; | |
1082 | } | |
1083 | if (relpath != NULL) { | |
1084 | strcpy(subpath, relpath); | |
1085 | strcat(subpath, "/"); | |
1086 | strcat(subpath, dir->d_name); | |
1087 | } else { | |
1088 | strcpy(subpath, dir->d_name); | |
1089 | } | |
1090 | getzones(basedir, subpath, last, count); | |
1091 | } | |
1092 | closedir(dp); | |
1093 | } | |
1094 | } | |
1095 | #endif |