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 "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 */ 26 #include "limits.h" /* for CHAR_BIT, LLONG_MAX */ 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 #include "sys/stat.h" 35 #endif 36 37 #ifndef isascii 38 #define isascii(x) 1 39 #endif /* !defined isascii */ 40 41 /* 42 ** Substitutes for pre-C99 compilers. 43 ** Much of this section of code is stolen from private.h. 44 */ 45 46 #ifndef HAVE_STDINT_H 47 # define HAVE_STDINT_H \ 48 (199901 <= __STDC_VERSION__ || 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__))) 49 #endif 50 #if HAVE_STDINT_H 51 # include "stdint.h" 52 #endif 53 #ifndef HAVE_INTTYPES_H 54 # define HAVE_INTTYPES_H HAVE_STDINT_H 55 #endif 56 #if HAVE_INTTYPES_H 57 # include <inttypes.h> 58 #endif 59 60 #ifndef INT_FAST32_MAX 61 # if INT_MAX >> 31 == 0 62 typedef long int_fast32_t; 63 # else 64 typedef int int_fast32_t; 65 # endif 66 #endif 67 68 #ifndef INTMAX_MAX 69 # if defined LLONG_MAX || defined __LONG_LONG_MAX__ 70 typedef long long intmax_t; 71 # define strtoimax strtoll 72 # define PRIdMAX "lld" 73 # ifdef LLONG_MAX 74 # define INTMAX_MAX LLONG_MAX 75 # else 76 # define INTMAX_MAX __LONG_LONG_MAX__ 77 # endif 78 # else 79 typedef long intmax_t; 80 # define strtoimax strtol 81 # define PRIdMAX "ld" 82 # define INTMAX_MAX LONG_MAX 83 # endif 84 #endif 85 86 87 #ifndef ZDUMP_LO_YEAR 88 #define ZDUMP_LO_YEAR (-500) 89 #endif /* !defined ZDUMP_LO_YEAR */ 90 91 #ifndef ZDUMP_HI_YEAR 92 #define ZDUMP_HI_YEAR 2500 93 #endif /* !defined ZDUMP_HI_YEAR */ 94 95 #ifndef MAX_STRING_LENGTH 96 #define MAX_STRING_LENGTH 1024 97 #endif /* !defined MAX_STRING_LENGTH */ 98 99 #ifndef TRUE 100 #define TRUE 1 101 #endif /* !defined TRUE */ 102 103 #ifndef FALSE 104 #define FALSE 0 105 #endif /* !defined FALSE */ 106 107 #ifndef EXIT_SUCCESS 108 #define EXIT_SUCCESS 0 109 #endif /* !defined EXIT_SUCCESS */ 110 111 #ifndef EXIT_FAILURE 112 #define EXIT_FAILURE 1 113 #endif /* !defined EXIT_FAILURE */ 114 115 #ifndef SECSPERMIN 116 #define SECSPERMIN 60 117 #endif /* !defined SECSPERMIN */ 118 119 #ifndef MINSPERHOUR 120 #define MINSPERHOUR 60 121 #endif /* !defined MINSPERHOUR */ 122 123 #ifndef SECSPERHOUR 124 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 125 #endif /* !defined SECSPERHOUR */ 126 127 #ifndef HOURSPERDAY 128 #define HOURSPERDAY 24 129 #endif /* !defined HOURSPERDAY */ 130 131 #ifndef EPOCH_YEAR 132 #define EPOCH_YEAR 1970 133 #endif /* !defined EPOCH_YEAR */ 134 135 #ifndef TM_YEAR_BASE 136 #define TM_YEAR_BASE 1900 137 #endif /* !defined TM_YEAR_BASE */ 138 139 #ifndef DAYSPERNYEAR 140 #define DAYSPERNYEAR 365 141 #endif /* !defined DAYSPERNYEAR */ 142 143 #ifndef isleap 144 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 145 #endif /* !defined isleap */ 146 147 #ifndef isleap_sum 148 /* 149 ** See tzfile.h for details on isleap_sum. 150 */ 151 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) 152 #endif /* !defined isleap_sum */ 153 154 #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) 155 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 156 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 157 #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ 158 + SECSPERLYEAR * (intmax_t) (100 - 3)) 159 160 /* 161 ** True if SECSPER400YEARS is known to be representable as an 162 ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false 163 ** even if SECSPER400YEARS is representable, because when that happens 164 ** the code merely runs a bit more slowly, and this slowness doesn't 165 ** occur on any practical platform. 166 */ 167 enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; 168 169 #ifndef HAVE_GETTEXT 170 #define HAVE_GETTEXT 0 171 #endif 172 #if HAVE_GETTEXT 173 #include "locale.h" /* for setlocale */ 174 #include "libintl.h" 175 #endif /* HAVE_GETTEXT */ 176 177 #ifndef GNUC_or_lint 178 #ifdef lint 179 #define GNUC_or_lint 180 #else /* !defined lint */ 181 #ifdef __GNUC__ 182 #define GNUC_or_lint 183 #endif /* defined __GNUC__ */ 184 #endif /* !defined lint */ 185 #endif /* !defined GNUC_or_lint */ 186 187 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) 188 # define ATTRIBUTE_PURE __attribute__ ((__pure__)) 189 #else 190 # define ATTRIBUTE_PURE /* empty */ 191 #endif 192 193 /* 194 ** For the benefit of GNU folk... 195 ** `_(MSGID)' uses the current locale's message library string for MSGID. 196 ** The default is to use gettext if available, and use MSGID otherwise. 197 */ 198 199 #ifndef _ 200 #if HAVE_GETTEXT 201 #define _(msgid) gettext(msgid) 202 #else /* !HAVE_GETTEXT */ 203 #define _(msgid) msgid 204 #endif /* !HAVE_GETTEXT */ 205 #endif /* !defined _ */ 206 207 #ifndef TZ_DOMAIN 208 #define TZ_DOMAIN "tz" 209 #endif /* !defined TZ_DOMAIN */ 210 211 extern char ** environ; 212 extern int getopt(int argc, char * const argv[], 213 const char * options); 214 extern char * optarg; 215 extern int optind; 216 extern char * tzname[2]; 217 218 /* The minimum and maximum finite time values. */ 219 static time_t const absolute_min_time = 220 ((time_t) -1 < 0 221 ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) 222 : 0); 223 static time_t const absolute_max_time = 224 ((time_t) -1 < 0 225 ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) 226 : -1); 227 static size_t longest; 228 static char * progname; 229 static int warned; 230 231 static char * abbr(struct tm * tmp); 232 static void abbrok(const char * abbrp, const char * zone); 233 static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE; 234 static void dumptime(const struct tm * tmp); 235 static time_t hunt(char * name, time_t lot, time_t hit); 236 static void show(char * zone, time_t t, int v); 237 static const char * tformat(void); 238 static time_t yeartot(intmax_t y) ATTRIBUTE_PURE; 239 #ifdef ICU 240 typedef struct listentry { 241 char * name; 242 struct listentry * next; 243 } listentry; 244 245 static time_t huntICU(char * name, time_t lot, time_t hit, FILE *fp); 246 static void dumptimeICU(FILE * fp, time_t t); 247 static void showICU(FILE * fp, char * zone, time_t t1, time_t t2); 248 static int getall(struct listentry ** namelist); 249 static void getzones(char * basedir, char * subdir, struct listentry ** last, int * count); 250 #endif 251 252 #ifndef TYPECHECK 253 #define my_localtime localtime 254 #else /* !defined TYPECHECK */ 255 static struct tm * 256 my_localtime(time_t *tp) 257 { 258 register struct tm * tmp; 259 260 tmp = localtime(tp); 261 if (tp != NULL && tmp != NULL) { 262 struct tm tm; 263 register time_t t; 264 265 tm = *tmp; 266 t = mktime(&tm); 267 if (t != *tp) { 268 (void) fflush(stdout); 269 (void) fprintf(stderr, "\n%s: ", progname); 270 (void) fprintf(stderr, tformat(), *tp); 271 (void) fprintf(stderr, " ->"); 272 (void) fprintf(stderr, " year=%d", tmp->tm_year); 273 (void) fprintf(stderr, " mon=%d", tmp->tm_mon); 274 (void) fprintf(stderr, " mday=%d", tmp->tm_mday); 275 (void) fprintf(stderr, " hour=%d", tmp->tm_hour); 276 (void) fprintf(stderr, " min=%d", tmp->tm_min); 277 (void) fprintf(stderr, " sec=%d", tmp->tm_sec); 278 (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); 279 (void) fprintf(stderr, " -> "); 280 (void) fprintf(stderr, tformat(), t); 281 (void) fprintf(stderr, "\n"); 282 } 283 } 284 return tmp; 285 } 286 #endif /* !defined TYPECHECK */ 287 288 static void 289 abbrok(const char *const abbrp, const char *const zone) 290 { 291 register const char * cp; 292 register const char * wp; 293 294 if (warned) 295 return; 296 cp = abbrp; 297 wp = NULL; 298 while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp)) 299 ++cp; 300 if (cp - abbrp == 0) 301 wp = _("lacks alphabetic at start"); 302 else if (cp - abbrp < 3) 303 wp = _("has fewer than 3 alphabetics"); 304 else if (cp - abbrp > 6) 305 wp = _("has more than 6 alphabetics"); 306 if (wp == NULL && (*cp == '+' || *cp == '-')) { 307 ++cp; 308 if (isascii((unsigned char) *cp) && 309 isdigit((unsigned char) *cp)) 310 if (*cp++ == '1' && *cp >= '0' && *cp <= '4') 311 ++cp; 312 if (*cp != '\0') 313 wp = _("differs from POSIX standard"); 314 } 315 if (wp == NULL) 316 return; 317 (void) fflush(stdout); 318 (void) fprintf(stderr, 319 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), 320 progname, zone, abbrp, wp); 321 warned = TRUE; 322 } 323 324 static void 325 usage(FILE * const stream, const int status) 326 { 327 (void) fprintf(stream, 328 _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n" 329 "\n" 330 "Report bugs to %s.\n"), 331 progname, progname, REPORT_BUGS_TO); 332 exit(status); 333 } 334 335 int 336 main(int argc, char *argv[]) 337 { 338 register int i; 339 register int vflag; 340 register int Vflag; 341 register char * cutarg; 342 register char * cuttimes; 343 register time_t cutlotime; 344 register time_t cuthitime; 345 register char ** fakeenv; 346 time_t now; 347 time_t t; 348 time_t newt; 349 struct tm tm; 350 struct tm newtm; 351 register struct tm * tmp; 352 register struct tm * newtmp; 353 #ifdef ICU 354 int nextopt; 355 char * dirarg; 356 int aflag; 357 int iflag; 358 listentry * namelist = NULL; 359 FILE * fp = stdout; 360 #endif 361 362 cutlotime = absolute_min_time; 363 cuthitime = absolute_max_time; 364 #if HAVE_GETTEXT 365 (void) setlocale(LC_ALL, ""); 366 #ifdef TZ_DOMAINDIR 367 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 368 #endif /* defined TEXTDOMAINDIR */ 369 (void) textdomain(TZ_DOMAIN); 370 #endif /* HAVE_GETTEXT */ 371 progname = argv[0]; 372 for (i = 1; i < argc; ++i) 373 if (strcmp(argv[i], "--version") == 0) { 374 (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); 375 exit(EXIT_SUCCESS); 376 } else if (strcmp(argv[i], "--help") == 0) { 377 usage(stdout, EXIT_SUCCESS); 378 } 379 vflag = Vflag = 0; 380 cutarg = cuttimes = NULL; 381 #ifdef ICU 382 aflag = 0; 383 iflag = 0; 384 dirarg = NULL; 385 for (;;) 386 switch(getopt(argc, argv, "ac:d:it:vV")) { 387 case 'a': aflag = 1; break; 388 case 'c': cutarg = optarg; break; 389 case 'd': dirarg = optarg; break; 390 case 'i': iflag = 1; break; 391 case 't': cuttimes = optarg; break; 392 case 'v': vflag = 1; break; 393 case 'V': Vflag = 1; break; 394 case -1: 395 if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) 396 goto arg_processing_done; 397 /* Fall through. */ 398 default: 399 (void) fprintf(stderr, 400 _("%s: usage is %s [ --version ] [ -a ] [ -v ] [ -V ] [ -i ] [ -c [loyear,]hiyear ] [ -t [lotime,]hitime] ][ -d dir ] [ zonename ... ]\n"), 401 progname, progname); 402 exit(EXIT_FAILURE); 403 } 404 #else 405 for (;;) 406 switch (getopt(argc, argv, "c:t:vV")) { 407 case 'c': cutarg = optarg; break; 408 case 't': cuttimes = optarg; break; 409 case 'v': vflag = 1; break; 410 case 'V': Vflag = 1; break; 411 case -1: 412 if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) 413 goto arg_processing_done; 414 /* Fall through. */ 415 default: 416 usage(stderr, EXIT_FAILURE); 417 } 418 #endif 419 arg_processing_done:; 420 421 #ifdef ICU 422 if (dirarg != NULL) { 423 DIR * dp; 424 /* create the output directory */ 425 mkdir(dirarg, 0777); 426 if ((dp = opendir(dirarg)) == NULL) { 427 fprintf(stderr, "cannot create the target directory"); 428 exit(EXIT_FAILURE); 429 } 430 closedir(dp); 431 } 432 #endif 433 434 if (vflag | Vflag) { 435 intmax_t lo; 436 intmax_t hi; 437 char *loend, *hiend; 438 register intmax_t cutloyear = ZDUMP_LO_YEAR; 439 register intmax_t cuthiyear = ZDUMP_HI_YEAR; 440 if (cutarg != NULL) { 441 lo = strtoimax(cutarg, &loend, 10); 442 if (cutarg != loend && !*loend) { 443 hi = lo; 444 cuthiyear = hi; 445 } else if (cutarg != loend && *loend == ',' 446 && (hi = strtoimax(loend + 1, &hiend, 10), 447 loend + 1 != hiend && !*hiend)) { 448 cutloyear = lo; 449 cuthiyear = hi; 450 } else { 451 (void) fprintf(stderr, _("%s: wild -c argument %s\n"), 452 progname, cutarg); 453 exit(EXIT_FAILURE); 454 } 455 } 456 if (cutarg != NULL || cuttimes == NULL) { 457 cutlotime = yeartot(cutloyear); 458 cuthitime = yeartot(cuthiyear); 459 } 460 if (cuttimes != NULL) { 461 lo = strtoimax(cuttimes, &loend, 10); 462 if (cuttimes != loend && !*loend) { 463 hi = lo; 464 if (hi < cuthitime) { 465 if (hi < absolute_min_time) 466 hi = absolute_min_time; 467 cuthitime = hi; 468 } 469 } else if (cuttimes != loend && *loend == ',' 470 && (hi = strtoimax(loend + 1, &hiend, 10), 471 loend + 1 != hiend && !*hiend)) { 472 if (cutlotime < lo) { 473 if (absolute_max_time < lo) 474 lo = absolute_max_time; 475 cutlotime = lo; 476 } 477 if (hi < cuthitime) { 478 if (hi < absolute_min_time) 479 hi = absolute_min_time; 480 cuthitime = hi; 481 } 482 } else { 483 (void) fprintf(stderr, 484 _("%s: wild -t argument %s\n"), 485 progname, cuttimes); 486 exit(EXIT_FAILURE); 487 } 488 } 489 } 490 491 #ifdef ICU 492 if (aflag) { 493 /* get all available zones */ 494 char ** fakeargv; 495 int i; 496 int count; 497 498 count = getall(&namelist); 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; 527 fakeenv = malloc((i + 2) * sizeof *fakeenv); 528 if (fakeenv == NULL 529 || (fakeenv[0] = malloc(longest + 4)) == NULL) { 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]); 545 if (! (vflag | Vflag)) { 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 585 if (!Vflag) { 586 show(argv[i], t, TRUE); 587 t += SECSPERDAY; 588 show(argv[i], t, TRUE); 589 } 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 ( ; ; ) { 601 newt = (t < absolute_max_time - SECSPERDAY / 2 602 ? t + SECSPERDAY / 2 603 : absolute_max_time); 604 if (cuthitime <= newt) 605 break; 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 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 } 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 689 static time_t 690 yeartot(const intmax_t y) 691 { 692 register intmax_t myy, seconds, years; 693 register time_t t; 694 695 myy = EPOCH_YEAR; 696 t = 0; 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 { 705 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 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; 720 } else { 721 seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR; 722 years = 1; 723 } 724 myy -= years; 725 if (t < absolute_min_time + seconds) 726 return absolute_min_time; 727 t -= seconds; 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; 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 ( ; ; ) { 748 time_t diff = hit - lot; 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 778 static intmax_t 779 delta(struct tm * newp, struct tm *oldp) 780 { 781 register intmax_t result; 782 register int tmy; 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); 811 (void) printf(" UT"); 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 * 833 abbr(struct tm *tmp) 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 * 850 tformat(void) 851 { 852 if (0 > (time_t) -1) { /* signed */ 853 if (sizeof (time_t) == sizeof (intmax_t)) 854 return "%"PRIdMAX; 855 if (sizeof (time_t) > sizeof (long)) 856 return "%lld"; 857 if (sizeof (time_t) > sizeof (int)) 858 return "%ld"; 859 return "%d"; 860 } 861 #ifdef PRIuMAX 862 if (sizeof (time_t) == sizeof (uintmax_t)) 863 return "%"PRIuMAX; 864 #endif 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 873 dumptime(register const struct tm *timeptr) 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, "%02ld", hour); 1024 fprintf(fp, "%02ld", min); 1025 fprintf(fp, "%02ld", 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 */ 1055 if (strstr(relpath, ".tab") == NULL && strcmp(relpath, "Etc/Unknown") != 0) { 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 1096