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 #endif 35 36 #ifndef isascii 37 #define isascii(x) 1 38 #endif /* !defined isascii */ 39 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 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 153 #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) 154 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 155 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 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 }; 167 168 #ifndef HAVE_GETTEXT 169 #define HAVE_GETTEXT 0 170 #endif 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 186 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) 187 # define ATTRIBUTE_PURE __attribute__ ((__pure__)) 188 #else 189 # define ATTRIBUTE_PURE /* empty */ 190 #endif 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 210 extern char ** environ; 211 extern int getopt(int argc, char * const argv[], 212 const char * options); 213 extern char * optarg; 214 extern int optind; 215 extern char * tzname[2]; 216 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); 226 static size_t longest; 227 static char * progname; 228 static int warned; 229 230 static char * abbr(struct tm * tmp); 231 static void abbrok(const char * abbrp, const char * zone); 232 static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE; 233 static void dumptime(const struct tm * tmp); 234 static time_t hunt(char * name, time_t lot, time_t hit); 235 static void show(char * zone, time_t t, int v); 236 static const char * tformat(void); 237 static time_t yeartot(intmax_t y) ATTRIBUTE_PURE; 238 #ifdef ICU 239 typedef struct listentry { 240 char * name; 241 struct listentry * next; 242 } listentry; 243 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); 249 #endif 250 251 #ifndef TYPECHECK 252 #define my_localtime localtime 253 #else /* !defined TYPECHECK */ 254 static struct tm * 255 my_localtime(time_t *tp) 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); 266 if (t != *tp) { 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 288 abbrok(const char *const abbrp, const char *const zone) 289 { 290 register const char * cp; 291 register const char * wp; 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 323 static void 324 usage(FILE * const stream, const int status) 325 { 326 (void) fprintf(stream, 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); 331 exit(status); 332 } 333 334 int 335 main(int argc, char *argv[]) 336 { 337 register int i; 338 register int vflag; 339 register int Vflag; 340 register char * cutarg; 341 register char * cuttimes; 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 361 cutlotime = absolute_min_time; 362 cuthitime = absolute_max_time; 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) { 373 (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); 374 exit(EXIT_SUCCESS); 375 } else if (strcmp(argv[i], "--help") == 0) { 376 usage(stdout, EXIT_SUCCESS); 377 } 378 vflag = Vflag = 0; 379 cutarg = cuttimes = NULL; 380 #ifdef ICU 381 aflag = 0; 382 iflag = 0; 383 dirarg = NULL; 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. */ 397 default: 398 (void) fprintf(stderr, 399 _("%s: usage is %s [ --version ] [ -a ] [ -v ] [ -V ] [ -i ] [ -c [loyear,]hiyear ] [ -t [lotime,]hitime] ][ -d dir ] [ zonename ... ]\n"), 400 progname, progname); 401 exit(EXIT_FAILURE); 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:; 419 420 #ifdef ICU 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 } 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; 439 if (cutarg != NULL) { 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; 448 cuthiyear = hi; 449 } else { 450 (void) fprintf(stderr, _("%s: wild -c argument %s\n"), 451 progname, cutarg); 452 exit(EXIT_FAILURE); 453 } 454 } 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 } 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; 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, "%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 */ 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