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