1 /* $NetBSD: zdump.c,v 1.45 2016/10/20 17:41:34 christos Exp $ */ 2 /* 3 ** This file is in the public domain, so clarified as of 4 ** 2009-05-17 by Arthur David Olson. 5 */ 6 7 #include <sys/cdefs.h> 8 #ifndef lint 9 __RCSID("$NetBSD: zdump.c,v 1.45 2016/10/20 17:41:34 christos Exp $"); 10 #endif /* !defined lint */ 11 12 /* 13 ** This code has been made independent of the rest of the time 14 ** conversion package to increase confidence in the verification it provides. 15 ** You can use this code to help in verifying other implementations. 16 ** 17 ** To do this, compile with -DUSE_LTZ=0 and link without the tz library. 18 */ 19 20 #ifndef NETBSD_INSPIRED 21 # define NETBSD_INSPIRED 1 22 #endif 23 #ifndef USE_LTZ 24 # define USE_LTZ 1 25 #endif 26 27 #if USE_LTZ 28 #include "private.h" 29 #endif 30 31 /* Enable tm_gmtoff and tm_zone on GNUish systems. */ 32 #define _GNU_SOURCE 1 33 /* Enable strtoimax on Solaris 10. */ 34 #define __EXTENSIONS__ 1 35 36 #include "stdio.h" /* for stdout, stderr */ 37 #include "string.h" /* for strcpy */ 38 #include "sys/types.h" /* for time_t */ 39 #include "time.h" /* for struct tm */ 40 #include "stdlib.h" /* for exit, malloc, atoi */ 41 #include "limits.h" /* for CHAR_BIT, LLONG_MAX */ 42 #include <errno.h> 43 #include <err.h> 44 45 /* 46 ** Substitutes for pre-C99 compilers. 47 ** Much of this section of code is stolen from private.h. 48 */ 49 50 #ifndef HAVE_STDINT_H 51 # define HAVE_STDINT_H \ 52 (199901 <= __STDC_VERSION__ \ 53 || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \ 54 || __CYGWIN__ || INTMAX_MAX) 55 #endif 56 #if HAVE_STDINT_H 57 # include "stdint.h" 58 #endif 59 #ifndef HAVE_INTTYPES_H 60 # define HAVE_INTTYPES_H HAVE_STDINT_H 61 #endif 62 #if HAVE_INTTYPES_H 63 # include <inttypes.h> 64 #endif 65 66 #ifndef INT_FAST32_MAX 67 # if INT_MAX >> 31 == 0 68 typedef long int_fast32_t; 69 # else 70 typedef int int_fast32_t; 71 # endif 72 #endif 73 74 /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ 75 #if !defined LLONG_MAX && defined __LONG_LONG_MAX__ 76 # define LLONG_MAX __LONG_LONG_MAX__ 77 #endif 78 79 #ifndef INTMAX_MAX 80 # ifdef LLONG_MAX 81 typedef long long intmax_t; 82 # define strtoimax strtoll 83 # define INTMAX_MAX LLONG_MAX 84 # else 85 typedef long intmax_t; 86 # define strtoimax strtol 87 # define INTMAX_MAX LONG_MAX 88 # endif 89 #endif 90 91 #ifndef PRIdMAX 92 # if INTMAX_MAX == LLONG_MAX 93 # define PRIdMAX "lld" 94 # else 95 # define PRIdMAX "ld" 96 # endif 97 #endif 98 99 /* Infer TM_ZONE on systems where this information is known, but suppress 100 guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ 101 #if (defined __GLIBC__ \ 102 || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ 103 || (defined __APPLE__ && defined __MACH__)) 104 # if !defined TM_GMTOFF && !defined NO_TM_GMTOFF 105 # define TM_GMTOFF tm_gmtoff 106 # endif 107 # if !defined TM_ZONE && !defined NO_TM_ZONE 108 # define TM_ZONE tm_zone 109 # endif 110 #endif 111 112 #ifndef HAVE_LOCALTIME_R 113 # define HAVE_LOCALTIME_R 1 114 #endif 115 116 #ifndef HAVE_LOCALTIME_RZ 117 # ifdef TM_ZONE 118 # define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ) 119 # else 120 # define HAVE_LOCALTIME_RZ 0 121 # endif 122 #endif 123 124 #ifndef HAVE_TZSET 125 # define HAVE_TZSET 1 126 #endif 127 128 #ifndef ZDUMP_LO_YEAR 129 #define ZDUMP_LO_YEAR (-500) 130 #endif /* !defined ZDUMP_LO_YEAR */ 131 132 #ifndef ZDUMP_HI_YEAR 133 #define ZDUMP_HI_YEAR 2500 134 #endif /* !defined ZDUMP_HI_YEAR */ 135 136 #ifndef MAX_STRING_LENGTH 137 #define MAX_STRING_LENGTH 1024 138 #endif /* !defined MAX_STRING_LENGTH */ 139 140 #if __STDC_VERSION__ < 199901 141 # define true 1 142 # define false 0 143 # define bool int 144 #else 145 # include <stdbool.h> 146 #endif 147 148 #ifndef TYPE_BIT 149 #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) 150 #endif /* !defined TYPE_BIT */ 151 152 #ifndef TYPE_SIGNED 153 #define TYPE_SIGNED(type) (((type) -1) < 0) 154 #endif /* !defined TYPE_SIGNED */ 155 156 #ifndef INT_STRLEN_MAXIMUM 157 /* 158 ** 302 / 1000 is log10(2.0) rounded up. 159 ** Subtract one for the sign bit if the type is signed; 160 ** add one for integer division truncation; 161 ** add one more for a minus sign if the type is signed. 162 */ 163 #define INT_STRLEN_MAXIMUM(type) \ 164 ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ 165 1 + TYPE_SIGNED(type)) 166 #endif /* !defined INT_STRLEN_MAXIMUM */ 167 168 #ifndef EXIT_SUCCESS 169 #define EXIT_SUCCESS 0 170 #endif /* !defined EXIT_SUCCESS */ 171 172 #ifndef EXIT_FAILURE 173 #define EXIT_FAILURE 1 174 #endif /* !defined EXIT_FAILURE */ 175 176 #ifndef SECSPERMIN 177 #define SECSPERMIN 60 178 #endif /* !defined SECSPERMIN */ 179 180 #ifndef MINSPERHOUR 181 #define MINSPERHOUR 60 182 #endif /* !defined MINSPERHOUR */ 183 184 #ifndef SECSPERHOUR 185 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 186 #endif /* !defined SECSPERHOUR */ 187 188 #ifndef HOURSPERDAY 189 #define HOURSPERDAY 24 190 #endif /* !defined HOURSPERDAY */ 191 192 #ifndef EPOCH_YEAR 193 #define EPOCH_YEAR 1970 194 #endif /* !defined EPOCH_YEAR */ 195 196 #ifndef TM_YEAR_BASE 197 #define TM_YEAR_BASE 1900 198 #endif /* !defined TM_YEAR_BASE */ 199 200 #ifndef DAYSPERNYEAR 201 #define DAYSPERNYEAR 365 202 #endif /* !defined DAYSPERNYEAR */ 203 204 #ifndef isleap 205 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 206 #endif /* !defined isleap */ 207 208 #ifndef isleap_sum 209 /* 210 ** See tzfile.h for details on isleap_sum. 211 */ 212 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) 213 #endif /* !defined isleap_sum */ 214 215 #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) 216 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 217 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 218 #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ 219 + SECSPERLYEAR * (intmax_t) (100 - 3)) 220 221 /* 222 ** True if SECSPER400YEARS is known to be representable as an 223 ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false 224 ** even if SECSPER400YEARS is representable, because when that happens 225 ** the code merely runs a bit more slowly, and this slowness doesn't 226 ** occur on any practical platform. 227 */ 228 enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; 229 230 #ifndef HAVE_GETTEXT 231 #define HAVE_GETTEXT 0 232 #endif 233 #if HAVE_GETTEXT 234 #include "locale.h" /* for setlocale */ 235 #include "libintl.h" 236 #endif /* HAVE_GETTEXT */ 237 238 #ifndef ATTRIBUTE_PURE 239 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) 240 # define ATTRIBUTE_PURE __attribute__ ((ATTRIBUTE_PURE__)) 241 #else 242 # define ATTRIBUTE_PURE /* empty */ 243 #endif 244 #endif 245 246 #ifndef INITIALIZE 247 #if defined(__GNUC__) || defined(__lint__) 248 #define INITIALIZE(x) ((x) = 0) 249 #else /* !defined GNUC || lint */ 250 #define INITIALIZE(x) 251 #endif /* !defined GNUC || lint */ 252 #endif /* !defined INITIALIZE */ 253 254 /* 255 ** For the benefit of GNU folk... 256 ** '_(MSGID)' uses the current locale's message library string for MSGID. 257 ** The default is to use gettext if available, and use MSGID otherwise. 258 */ 259 260 #ifndef _ 261 #if HAVE_GETTEXT 262 #define _(msgid) gettext(msgid) 263 #else /* !HAVE_GETTEXT */ 264 #define _(msgid) msgid 265 #endif /* !HAVE_GETTEXT */ 266 #endif /* !defined _ */ 267 268 #if !defined TZ_DOMAIN && defined HAVE_GETTEXT 269 # define TZ_DOMAIN "tz" 270 #endif 271 272 #if ! HAVE_LOCALTIME_RZ 273 # undef timezone_t 274 # define timezone_t char ** 275 #endif 276 277 extern char ** environ; 278 279 #if !HAVE_POSIX_DECLS 280 extern int getopt(int argc, char * const argv[], 281 const char * options); 282 extern char * optarg; 283 extern int optind; 284 extern char * tzname[]; 285 #endif 286 287 /* The minimum and maximum finite time values. */ 288 enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 }; 289 static time_t absolute_min_time = 290 ((time_t) -1 < 0 291 ? (- ((time_t) ~ (time_t) 0 < 0) 292 - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))) 293 : 0); 294 static time_t absolute_max_time = 295 ((time_t) -1 < 0 296 ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) 297 : -1); 298 static size_t longest; 299 static char * progname; 300 static bool warned; 301 static bool errout; 302 303 static char const *abbr(struct tm const *); 304 static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE; 305 static void dumptime(struct tm const *); 306 static time_t hunt(timezone_t, char *, time_t, time_t); 307 static void show(timezone_t, char *, time_t, bool); 308 static void showtrans(char const *, struct tm const *, time_t, char const *, 309 char const *); 310 static const char *tformat(void); 311 static time_t yeartot(intmax_t) ATTRIBUTE_PURE; 312 313 /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ 314 #define is_digit(c) ((unsigned)(c) - '0' <= 9) 315 316 /* Is A an alphabetic character in the C locale? */ 317 static bool 318 is_alpha(char a) 319 { 320 switch (a) { 321 default: 322 return false; 323 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 324 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 325 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 326 case 'V': case 'W': case 'X': case 'Y': case 'Z': 327 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 328 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 329 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 330 case 'v': case 'w': case 'x': case 'y': case 'z': 331 return true; 332 } 333 } 334 335 /* Return A + B, exiting if the result would overflow. */ 336 static size_t 337 sumsize(size_t a, size_t b) 338 { 339 size_t sum = a + b; 340 if (sum < a) 341 errx(EXIT_FAILURE, "size overflow"); 342 return sum; 343 } 344 345 /* Return a pointer to a newly allocated buffer of size SIZE, exiting 346 on failure. SIZE should be nonzero. */ 347 static void * 348 xmalloc(size_t size) 349 { 350 void *p = malloc(size); 351 if (!p) { 352 perror(progname); 353 exit(EXIT_FAILURE); 354 } 355 return p; 356 } 357 358 #if ! HAVE_TZSET 359 # undef tzset 360 # define tzset zdump_tzset 361 static void tzset(void) { } 362 #endif 363 364 /* Assume gmtime_r works if localtime_r does. 365 A replacement localtime_r is defined below if needed. */ 366 #if ! HAVE_LOCALTIME_R 367 368 # undef gmtime_r 369 # define gmtime_r zdump_gmtime_r 370 371 static struct tm * 372 gmtime_r(time_t *tp, struct tm *tmp) 373 { 374 struct tm *r = gmtime(tp); 375 if (r) { 376 *tmp = *r; 377 r = tmp; 378 } 379 return r; 380 } 381 382 #endif 383 384 /* Platforms with TM_ZONE don't need tzname, so they can use the 385 faster localtime_rz or localtime_r if available. */ 386 387 #if defined TM_ZONE && HAVE_LOCALTIME_RZ 388 # define USE_LOCALTIME_RZ true 389 #else 390 # define USE_LOCALTIME_RZ false 391 #endif 392 393 #if ! USE_LOCALTIME_RZ 394 395 # if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET 396 # undef localtime_r 397 # define localtime_r zdump_localtime_r 398 static struct tm * 399 localtime_r(time_t *tp, struct tm *tmp) 400 { 401 struct tm *r = localtime(tp); 402 if (r) { 403 *tmp = *r; 404 r = tmp; 405 } 406 return r; 407 } 408 # endif 409 410 # undef localtime_rz 411 # define localtime_rz zdump_localtime_rz 412 static struct tm * 413 localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp) 414 { 415 return localtime_r(tp, tmp); 416 } 417 418 # ifdef TYPECHECK 419 # undef mktime_z 420 # define mktime_z zdump_mktime_z 421 static time_t 422 mktime_z(timezone_t tz, struct tm *tmp) 423 { 424 return mktime(tmp); 425 } 426 # endif 427 428 # undef tzalloc 429 # undef tzfree 430 # define tzalloc zdump_tzalloc 431 # define tzfree zdump_tzfree 432 433 static timezone_t 434 tzalloc(char const *val) 435 { 436 static char **fakeenv; 437 char **env = fakeenv; 438 char *env0; 439 if (! env) { 440 char **e = environ; 441 int to; 442 443 while (*e++) 444 continue; 445 env = xmalloc(sumsize(sizeof *environ, 446 (e - environ) * sizeof *environ)); 447 to = 1; 448 for (e = environ; (env[to] = *e); e++) 449 to += strncmp(*e, "TZ=", 3) != 0; 450 } 451 env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val))); 452 env[0] = strcat(strcpy(env0, "TZ="), val); 453 environ = fakeenv = env; 454 tzset(); 455 return env; 456 } 457 458 static void 459 tzfree(timezone_t env) 460 { 461 environ = env + 1; 462 free(env[0]); 463 } 464 #endif /* ! USE_LOCALTIME_RZ */ 465 466 /* A UTC time zone, and its initializer. */ 467 static timezone_t gmtz; 468 static void 469 gmtzinit(void) 470 { 471 if (USE_LOCALTIME_RZ) { 472 static char const utc[] = "UTC0"; 473 gmtz = tzalloc(utc); 474 if (!gmtz) { 475 err(EXIT_FAILURE, "Cannot create %s", utc); 476 } 477 } 478 } 479 480 /* Convert *TP to UTC, storing the broken-down time into *TMP. 481 Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP), 482 except typically faster if USE_LOCALTIME_RZ. */ 483 static struct tm * 484 my_gmtime_r(time_t *tp, struct tm *tmp) 485 { 486 return USE_LOCALTIME_RZ ? 487 localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp); 488 } 489 490 #ifndef TYPECHECK 491 #define my_localtime_rz localtime_rz 492 #else /* !defined TYPECHECK */ 493 static struct tm * 494 my_localtime_rz(timezone_t tz, const time_t *tp, struct tm *tmp) 495 { 496 tmp = localtime_rz(tz, tp, tmp); 497 if (tmp) { 498 struct tm tm; 499 time_t t; 500 501 tm = *tmp; 502 t = mktime_z(tz, &tm); 503 if (t != *tp) { 504 (void) fflush(stdout); 505 (void) fprintf(stderr, "\n%s: ", progname); 506 (void) fprintf(stderr, tformat(), *tp); 507 (void) fprintf(stderr, " ->"); 508 (void) fprintf(stderr, " year=%d", tmp->tm_year); 509 (void) fprintf(stderr, " mon=%d", tmp->tm_mon); 510 (void) fprintf(stderr, " mday=%d", tmp->tm_mday); 511 (void) fprintf(stderr, " hour=%d", tmp->tm_hour); 512 (void) fprintf(stderr, " min=%d", tmp->tm_min); 513 (void) fprintf(stderr, " sec=%d", tmp->tm_sec); 514 (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); 515 (void) fprintf(stderr, " -> "); 516 (void) fprintf(stderr, tformat(), t); 517 (void) fprintf(stderr, "\n"); 518 errout = true; 519 } 520 } 521 return tmp; 522 } 523 #endif /* !defined TYPECHECK */ 524 525 static void 526 abbrok(const char *const abbrp, const char *const zone) 527 { 528 const char *cp; 529 const char *wp; 530 531 if (warned) 532 return; 533 cp = abbrp; 534 while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+') 535 ++cp; 536 if (cp - abbrp < 3) 537 wp = _("has fewer than 3 characters"); 538 else if (cp - abbrp > 6) 539 wp = _("has more than 6 characters"); 540 else if (*cp) 541 wp = _("has characters other than ASCII alphanumerics, '-' or '+'"); 542 else 543 return; 544 (void) fflush(stdout); 545 (void) fprintf(stderr, 546 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), 547 progname, zone, abbrp, wp); 548 warned = errout = true; 549 } 550 551 /* Return a time zone abbreviation. If the abbreviation needs to be 552 saved, use *BUF (of size *BUFALLOC) to save it, and return the 553 abbreviation in the possibly-reallocated *BUF. Otherwise, just 554 return the abbreviation. Get the abbreviation from TMP. 555 Exit on memory allocation failure. */ 556 static char const * 557 saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp) 558 { 559 char const *ab = abbr(tmp); 560 if (HAVE_LOCALTIME_RZ) 561 return ab; 562 else { 563 size_t ablen = strlen(ab); 564 if (*bufalloc <= ablen) { 565 free(*buf); 566 567 /* Make the new buffer at least twice as long as the 568 old, to avoid O(N**2) behavior on repeated calls. */ 569 *bufalloc = sumsize(*bufalloc, ablen + 1); 570 *buf = xmalloc(*bufalloc); 571 } 572 return strcpy(*buf, ab); 573 } 574 } 575 576 static void 577 close_file(FILE *stream) 578 { 579 char const *e = (ferror(stream) ? _("I/O error") 580 : fclose(stream) != 0 ? strerror(errno) : NULL); 581 if (e) { 582 errx(EXIT_FAILURE, "%s", e); 583 } 584 } 585 586 __dead static void 587 usage(FILE *const stream, const int status) 588 { 589 (void) fprintf(stream, 590 _("%s: usage: %s OPTIONS ZONENAME ...\n" 591 "Options include:\n" 592 " -c [L,]U Start at year L (default -500), end before year U (default 2500)\n" 593 " -t [L,]U Start at time L, end before time U (in seconds since 1970)\n" 594 " -i List transitions briefly (format is experimental)\n" \ 595 " -v List transitions verbosely\n" 596 " -V List transitions a bit less verbosely\n" 597 " --help Output this help\n" 598 " --version Output version info\n" 599 "\n" 600 "Report bugs to %s.\n"), 601 progname, progname, REPORT_BUGS_TO); 602 if (status == EXIT_SUCCESS) 603 close_file(stream); 604 exit(status); 605 } 606 607 int 608 main(int argc, char *argv[]) 609 { 610 /* These are static so that they're initially zero. */ 611 static char * abbrev; 612 static size_t abbrevsize; 613 614 int i; 615 bool vflag; 616 bool Vflag; 617 char * cutarg; 618 char * cuttimes; 619 time_t cutlotime; 620 time_t cuthitime; 621 time_t now; 622 bool iflag = false; 623 624 cutlotime = absolute_min_time; 625 cuthitime = absolute_max_time; 626 #if HAVE_GETTEXT 627 (void) setlocale(LC_ALL, ""); 628 #ifdef TZ_DOMAINDIR 629 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 630 #endif /* defined TEXTDOMAINDIR */ 631 (void) textdomain(TZ_DOMAIN); 632 #endif /* HAVE_GETTEXT */ 633 progname = argv[0]; 634 for (i = 1; i < argc; ++i) 635 if (strcmp(argv[i], "--version") == 0) { 636 (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); 637 return EXIT_SUCCESS; 638 } else if (strcmp(argv[i], "--help") == 0) { 639 usage(stdout, EXIT_SUCCESS); 640 } 641 vflag = Vflag = false; 642 cutarg = cuttimes = NULL; 643 for (;;) 644 switch (getopt(argc, argv, "c:it:vV")) { 645 case 'c': cutarg = optarg; break; 646 case 't': cuttimes = optarg; break; 647 case 'i': iflag = true; break; 648 case 'v': vflag = true; break; 649 case 'V': Vflag = true; break; 650 case -1: 651 if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) 652 goto arg_processing_done; 653 /* Fall through. */ 654 default: 655 usage(stderr, EXIT_FAILURE); 656 } 657 arg_processing_done:; 658 659 if (iflag | vflag | Vflag) { 660 intmax_t lo; 661 intmax_t hi; 662 char *loend, *hiend; 663 intmax_t cutloyear = ZDUMP_LO_YEAR; 664 intmax_t cuthiyear = ZDUMP_HI_YEAR; 665 if (cutarg != NULL) { 666 lo = strtoimax(cutarg, &loend, 10); 667 if (cutarg != loend && !*loend) { 668 hi = lo; 669 cuthiyear = hi; 670 } else if (cutarg != loend && *loend == ',' 671 && (hi = strtoimax(loend + 1, &hiend, 10), 672 loend + 1 != hiend && !*hiend)) { 673 cutloyear = lo; 674 cuthiyear = hi; 675 } else { 676 fprintf(stderr, _("%s: wild -c argument %s\n"), 677 progname, cutarg); 678 return EXIT_FAILURE; 679 } 680 } 681 if (cutarg != NULL || cuttimes == NULL) { 682 cutlotime = yeartot(cutloyear); 683 cuthitime = yeartot(cuthiyear); 684 } 685 if (cuttimes != NULL) { 686 lo = strtoimax(cuttimes, &loend, 10); 687 if (cuttimes != loend && !*loend) { 688 hi = lo; 689 if (hi < cuthitime) { 690 if (hi < absolute_min_time) 691 hi = absolute_min_time; 692 cuthitime = hi; 693 } 694 } else if (cuttimes != loend && *loend == ',' 695 && (hi = strtoimax(loend + 1, &hiend, 10), 696 loend + 1 != hiend && !*hiend)) { 697 if (cutlotime < lo) { 698 if (absolute_max_time < lo) 699 lo = absolute_max_time; 700 cutlotime = lo; 701 } 702 if (hi < cuthitime) { 703 if (hi < absolute_min_time) 704 hi = absolute_min_time; 705 cuthitime = hi; 706 } 707 } else { 708 (void) fprintf(stderr, 709 _("%s: wild -t argument %s\n"), 710 progname, cuttimes); 711 return EXIT_FAILURE; 712 } 713 } 714 } 715 gmtzinit(); 716 INITIALIZE (now); 717 if (! (iflag | vflag | Vflag)) 718 now = time(NULL); 719 longest = 0; 720 for (i = optind; i < argc; i++) { 721 size_t arglen = strlen(argv[i]); 722 if (longest < arglen) 723 longest = arglen < INT_MAX ? arglen : INT_MAX; 724 } 725 726 for (i = optind; i < argc; ++i) { 727 timezone_t tz = tzalloc(argv[i]); 728 char const *ab; 729 time_t t; 730 struct tm tm, newtm; 731 bool tm_ok; 732 733 if (!tz) { 734 errx(EXIT_FAILURE, "%s", argv[i]); 735 } 736 if (! (iflag | vflag | Vflag)) { 737 show(tz, argv[i], now, false); 738 tzfree(tz); 739 continue; 740 } 741 warned = false; 742 t = absolute_min_time; 743 if (! (iflag | Vflag)) { 744 show(tz, argv[i], t, true); 745 t += SECSPERDAY; 746 show(tz, argv[i], t, true); 747 } 748 if (t < cutlotime) 749 t = cutlotime; 750 tm_ok = my_localtime_rz(tz, &t, &tm) != NULL; 751 if (tm_ok) { 752 ab = saveabbr(&abbrev, &abbrevsize, &tm); 753 if (iflag) { 754 showtrans("\nTZ=%f", &tm, t, ab, argv[i]); 755 showtrans("-\t-\t%Q", &tm, t, ab, argv[i]); 756 } 757 } else 758 ab = NULL; 759 while (t < cuthitime) { 760 time_t newt = ((t < absolute_max_time - SECSPERDAY / 2 761 && t + SECSPERDAY / 2 < cuthitime) 762 ? t + SECSPERDAY / 2 : cuthitime); 763 struct tm *newtmp = localtime_rz(tz, &newt, &newtm); 764 bool newtm_ok = newtmp != NULL; 765 if (! (tm_ok & newtm_ok 766 ? (delta(&newtm, &tm) == newt - t 767 && newtm.tm_isdst == tm.tm_isdst 768 && strcmp(abbr(&newtm), ab) == 0) 769 : tm_ok == newtm_ok)) { 770 newt = hunt(tz, argv[i], t, newt); 771 newtmp = localtime_rz(tz, &newt, &newtm); 772 newtm_ok = newtmp != NULL; 773 if (iflag) 774 showtrans("%Y-%m-%d\t%L\t%Q", 775 newtmp, newt, newtm_ok ? 776 abbr(&newtm) : NULL, argv[i]); 777 else { 778 show(tz, argv[i], newt - 1, true); 779 show(tz, argv[i], newt, true); 780 } 781 } 782 t = newt; 783 tm_ok = newtm_ok; 784 if (newtm_ok) { 785 ab = saveabbr(&abbrev, &abbrevsize, &newtm); 786 tm = newtm; 787 } 788 } 789 if (! (iflag | Vflag)) { 790 t = absolute_max_time; 791 t -= SECSPERDAY; 792 show(tz, argv[i], t, true); 793 t += SECSPERDAY; 794 show(tz, argv[i], t, true); 795 } 796 tzfree(tz); 797 } 798 close_file(stdout); 799 if (errout && (ferror(stderr) || fclose(stderr) != 0)) 800 return EXIT_FAILURE; 801 return EXIT_SUCCESS; 802 } 803 804 static time_t 805 yeartot(intmax_t y) 806 { 807 intmax_t myy, seconds, years; 808 time_t t; 809 810 myy = EPOCH_YEAR; 811 t = 0; 812 while (myy < y) { 813 if (SECSPER400YEARS_FITS && 400 <= y - myy) { 814 intmax_t diff400 = (y - myy) / 400; 815 if (INTMAX_MAX / SECSPER400YEARS < diff400) 816 return absolute_max_time; 817 seconds = diff400 * SECSPER400YEARS; 818 years = diff400 * 400; 819 } else { 820 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 821 years = 1; 822 } 823 myy += years; 824 if (t > absolute_max_time - seconds) 825 return absolute_max_time; 826 t += seconds; 827 } 828 while (y < myy) { 829 if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) { 830 intmax_t diff400 = (myy - y) / 400; 831 if (INTMAX_MAX / SECSPER400YEARS < diff400) 832 return absolute_min_time; 833 seconds = diff400 * SECSPER400YEARS; 834 years = diff400 * 400; 835 } else { 836 seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR; 837 years = 1; 838 } 839 myy -= years; 840 if (t < absolute_min_time + seconds) 841 return absolute_min_time; 842 t -= seconds; 843 } 844 return t; 845 } 846 847 static time_t 848 hunt(timezone_t tz, char *name, time_t lot, time_t hit) 849 { 850 static char * loab; 851 static size_t loabsize; 852 char const * ab; 853 time_t t; 854 struct tm lotm; 855 struct tm tm; 856 bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL; 857 bool tm_ok; 858 859 if (lotm_ok) 860 ab = saveabbr(&loab, &loabsize, &lotm); 861 else 862 ab = NULL; 863 for ( ; ; ) { 864 time_t diff = hit - lot; 865 if (diff < 2) 866 break; 867 t = lot; 868 t += diff / 2; 869 if (t <= lot) 870 ++t; 871 else if (t >= hit) 872 --t; 873 tm_ok = my_localtime_rz(tz, &t, &tm) != NULL; 874 if (lotm_ok & tm_ok 875 ? (delta(&tm, &lotm) == t - lot 876 && tm.tm_isdst == lotm.tm_isdst 877 && strcmp(abbr(&tm), ab) == 0) 878 : lotm_ok == tm_ok) { 879 lot = t; 880 if (tm_ok) 881 lotm = tm; 882 } else hit = t; 883 } 884 return hit; 885 } 886 887 /* 888 ** Thanks to Paul Eggert for logic used in delta. 889 */ 890 891 static intmax_t 892 delta(struct tm *newp, struct tm *oldp) 893 { 894 intmax_t result; 895 int tmy; 896 897 if (newp->tm_year < oldp->tm_year) 898 return -delta(oldp, newp); 899 result = 0; 900 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 901 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 902 result += newp->tm_yday - oldp->tm_yday; 903 result *= HOURSPERDAY; 904 result += newp->tm_hour - oldp->tm_hour; 905 result *= MINSPERHOUR; 906 result += newp->tm_min - oldp->tm_min; 907 result *= SECSPERMIN; 908 result += newp->tm_sec - oldp->tm_sec; 909 return result; 910 } 911 912 #ifndef TM_GMTOFF 913 /* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday. 914 Assume A and B differ by at most one year. */ 915 static int 916 adjusted_yday(struct tm const *a, struct tm const *b) 917 { 918 int yday = a->tm_yday; 919 if (b->tm_year < a->tm_year) 920 yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE); 921 return yday; 922 } 923 #endif 924 925 /* If A is the broken-down local time and B the broken-down UTC for 926 the same instant, return A's UTC offset in seconds, where positive 927 offsets are east of Greenwich. On failure, return LONG_MIN. 928 929 If T is nonnull, *T is the time stamp that corresponds to A; call 930 my_gmtime_r and use its result instead of B. Otherwise, B is the 931 possibly nonnull result of an earlier call to my_gmtime_r. */ 932 static long 933 gmtoff(struct tm const *a, time_t *t, struct tm const *b) 934 { 935 #ifdef TM_GMTOFF 936 return a->TM_GMTOFF; 937 #else 938 struct tm tm; 939 if (t) 940 b = my_gmtime_r(t, &tm); 941 if (! b) 942 return LONG_MIN; 943 else { 944 int ayday = adjusted_yday(a, b); 945 int byday = adjusted_yday(b, a); 946 int days = ayday - byday; 947 long hours = a->tm_hour - b->tm_hour + 24 * days; 948 long minutes = a->tm_min - b->tm_min + 60 * hours; 949 long seconds = a->tm_sec - b->tm_sec + 60 * minutes; 950 return seconds; 951 } 952 #endif 953 } 954 955 static void 956 show(timezone_t tz, char *zone, time_t t, bool v) 957 { 958 struct tm * tmp; 959 struct tm * gmtmp; 960 struct tm tm, gmtm; 961 962 (void) printf("%-*s ", (int) longest, zone); 963 if (v) { 964 gmtmp = my_gmtime_r(&t, &gmtm); 965 if (gmtmp == NULL) { 966 printf(tformat(), t); 967 } else { 968 dumptime(gmtmp); 969 (void) printf(" UT"); 970 } 971 (void) printf(" = "); 972 } 973 tmp = my_localtime_rz(tz, &t, &tm); 974 dumptime(tmp); 975 if (tmp != NULL) { 976 if (*abbr(tmp) != '\0') 977 (void) printf(" %s", abbr(tmp)); 978 if (v) { 979 long off = gmtoff(tmp, NULL, gmtmp); 980 (void) printf(" isdst=%d", tmp->tm_isdst); 981 if (off != LONG_MIN) 982 (void) printf(" gmtoff=%ld", off); 983 } 984 } 985 (void) printf("\n"); 986 if (tmp != NULL && *abbr(tmp) != '\0') 987 abbrok(abbr(tmp), zone); 988 } 989 990 /* Store into BUF, of size SIZE, a formatted local time taken from *TM. 991 Use ISO 8601 format +HH:MM:SS. Omit :SS if SS is zero, and omit 992 :MM too if MM is also zero. 993 994 Return the length of the resulting string. If the string does not 995 fit, return the length that the string would have been if it had 996 fit; do not overrun the output buffer. */ 997 static int 998 format_local_time(char *buf, size_t size, struct tm const *tm) 999 { 1000 int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour; 1001 return (ss 1002 ? snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss) 1003 : mm 1004 ? snprintf(buf, size, "%02d:%02d", hh, mm) 1005 : snprintf(buf, size, "%02d", hh)); 1006 } 1007 1008 /* Store into BUF, of size SIZE, a formatted UTC offset for the 1009 localtime *TM corresponding to time T. Use ISO 8601 format 1010 +HH:MM:SS, or -HH:MM:SS for time stamps west of Greenwich. Omit 1011 :SS if :SS is zero, and omit :MM too if :MM is also zero. If the 1012 time stamp represents an unknown UTC offset, use the format -00. 1013 1014 Return the length of the resulting string, or -1 if the result is 1015 not representable as a string. If the string does not fit, return 1016 the length that the string would have been if it had fit; do not 1017 overrun the output buffer. */ 1018 static int 1019 format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t) 1020 { 1021 long off = gmtoff(tm, &t, NULL); 1022 char sign = ((off < 0 1023 || (off == 0 1024 && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0))) 1025 ? '-' : '+'); 1026 long hh; 1027 int mm, ss; 1028 if (off < 0) 1029 { 1030 if (off == LONG_MIN) 1031 return -1; 1032 off = -off; 1033 } 1034 ss = off % 60; 1035 mm = off / 60 % 60; 1036 hh = off / 60 / 60; 1037 return (ss 1038 ? snprintf(buf, size, "%c%02ld:%02d:%02d", sign, hh, mm, ss) 1039 : mm 1040 ? snprintf(buf, size, "%c%02ld:%02d", sign, hh, mm) 1041 : snprintf(buf, size, "%c%02ld", sign, hh)); 1042 } 1043 1044 /* Store into BUF (of size SIZE) a quoted string representation of P. 1045 If the representation's length is less than SIZE, return the 1046 length; the representation is not null terminated. Otherwise 1047 return SIZE, to indicate that BUF is too small. */ 1048 static size_t 1049 format_quoted_string(char *buf, size_t size, char const *p) 1050 { 1051 char *b = buf; 1052 size_t s = size; 1053 if (!s) 1054 return size; 1055 *b++ = '"', s--; 1056 for (;;) { 1057 char c = *p++; 1058 if (s <= 1) 1059 return size; 1060 switch (c) { 1061 default: *b++ = c, s--; continue; 1062 case '\0': *b++ = '"', s--; return size - s; 1063 case '"': case '\\': break; 1064 case ' ': c = 's'; break; 1065 case '\f': c = 'f'; break; 1066 case '\n': c = 'n'; break; 1067 case '\r': c = 'r'; break; 1068 case '\t': c = 't'; break; 1069 case '\v': c = 'v'; break; 1070 } 1071 *b++ = '\\', *b++ = c, s -= 2; 1072 } 1073 } 1074 1075 /* Store into BUF (of size SIZE) a time stamp formatted by TIME_FMT. 1076 TM is the broken-down time, T the seconds count, AB the time zone 1077 abbreviation, and ZONE_NAME the zone name. Return true if 1078 successful, false if the output would require more than SIZE bytes. 1079 TIME_FMT uses the same format that strftime uses, with these 1080 additions: 1081 1082 %f zone name 1083 %L local time as per format_local_time 1084 %Q like "U\t%Z\tD" where U is the UTC offset as for format_utc_offset 1085 and D is the isdst flag; except omit D if it is zero, omit %Z if 1086 it equals U, quote and escape %Z if it contains nonalphabetics, 1087 and omit any trailing tabs. */ 1088 1089 static bool 1090 istrftime(char *buf, size_t size, char const *time_fmt, 1091 struct tm const *tm, time_t t, char const *ab, char const *zone_name) 1092 { 1093 char *b = buf; 1094 size_t s = size; 1095 char const *f = time_fmt, *p; 1096 1097 for (p = f; ; p++) 1098 if (*p == '%' && p[1] == '%') 1099 p++; 1100 else if (!*p 1101 || (*p == '%' 1102 && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) { 1103 size_t formatted_len; 1104 size_t f_prefix_len = p - f; 1105 size_t f_prefix_copy_size = p - f + 2; 1106 char fbuf[100]; 1107 bool oversized = sizeof fbuf <= f_prefix_copy_size; 1108 char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf; 1109 memcpy(f_prefix_copy, f, f_prefix_len); 1110 strcpy(f_prefix_copy + f_prefix_len, "X"); 1111 formatted_len = strftime(b, s, f_prefix_copy, tm); 1112 if (oversized) 1113 free(f_prefix_copy); 1114 if (formatted_len == 0) 1115 return false; 1116 formatted_len--; 1117 b += formatted_len, s -= formatted_len; 1118 if (!*p++) 1119 break; 1120 switch (*p) { 1121 case 'f': 1122 formatted_len = format_quoted_string(b, s, zone_name); 1123 break; 1124 case 'L': 1125 formatted_len = format_local_time(b, s, tm); 1126 break; 1127 case 'Q': 1128 { 1129 bool show_abbr; 1130 int offlen = format_utc_offset(b, s, tm, t); 1131 if (! (0 <= offlen && (size_t)offlen < s)) 1132 return false; 1133 show_abbr = strcmp(b, ab) != 0; 1134 b += offlen, s -= offlen; 1135 if (show_abbr) { 1136 char const *abp; 1137 size_t len; 1138 if (s <= 1) 1139 return false; 1140 *b++ = '\t', s--; 1141 for (abp = ab; is_alpha(*abp); abp++) 1142 continue; 1143 len = (!*abp && *ab 1144 ? (size_t)snprintf(b, s, "%s", ab) 1145 : format_quoted_string(b, s, ab)); 1146 if (s <= len) 1147 return false; 1148 b += len, s -= len; 1149 } 1150 formatted_len = (tm->tm_isdst 1151 ? snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst) 1152 : 0); 1153 } 1154 break; 1155 } 1156 if (! (formatted_len < s)) 1157 return false; 1158 b += formatted_len, s -= formatted_len; 1159 f = p + 1; 1160 } 1161 *b = '\0'; 1162 return true; 1163 } 1164 1165 /* Show a time transition. */ 1166 static void 1167 showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab, 1168 char const *zone_name) 1169 { 1170 if (!tm) { 1171 printf(tformat(), t); 1172 putchar('\n'); 1173 } else { 1174 char stackbuf[1000]; 1175 size_t size = sizeof stackbuf; 1176 char *buf = stackbuf; 1177 char *bufalloc = NULL; 1178 while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) { 1179 size = sumsize(size, size); 1180 free(bufalloc); 1181 buf = bufalloc = xmalloc(size); 1182 } 1183 puts(buf); 1184 free(bufalloc); 1185 } 1186 } 1187 1188 static const char * 1189 abbr(struct tm const *tmp) 1190 { 1191 #ifdef TM_ZONE 1192 return tmp->TM_ZONE; 1193 #else 1194 return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst] 1195 ? tzname[0 < tmp->tm_isdst] 1196 : ""); 1197 #endif 1198 } 1199 1200 /* 1201 ** The code below can fail on certain theoretical systems; 1202 ** it works on all known real-world systems as of 2004-12-30. 1203 */ 1204 1205 static const char * 1206 tformat(void) 1207 { 1208 if (0 > (time_t) -1) { /* signed */ 1209 if (sizeof (time_t) == sizeof (intmax_t)) 1210 return "%"PRIdMAX; 1211 if (sizeof (time_t) > sizeof (long)) 1212 return "%lld"; 1213 if (sizeof (time_t) > sizeof (int)) 1214 return "%ld"; 1215 return "%d"; 1216 } 1217 #ifdef PRIuMAX 1218 if (sizeof (time_t) == sizeof (uintmax_t)) 1219 return "%"PRIuMAX; 1220 #endif 1221 if (sizeof (time_t) > sizeof (unsigned long)) 1222 return "%llu"; 1223 if (sizeof (time_t) > sizeof (unsigned int)) 1224 return "%lu"; 1225 return "%u"; 1226 } 1227 1228 static void 1229 dumptime(const struct tm *timeptr) 1230 { 1231 static const char wday_name[][3] = { 1232 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 1233 }; 1234 static const char mon_name[][3] = { 1235 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 1236 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 1237 }; 1238 const char * wn; 1239 const char * mn; 1240 int lead; 1241 int trail; 1242 1243 if (timeptr == NULL) { 1244 printf("NULL"); 1245 return; 1246 } 1247 /* 1248 ** The packaged localtime_rz and gmtime_r never put out-of-range 1249 ** values in tm_wday or tm_mon, but since this code might be compiled 1250 ** with other (perhaps experimental) versions, paranoia is in order. 1251 */ 1252 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 1253 (int) (sizeof wday_name / sizeof wday_name[0])) 1254 wn = "???"; 1255 else wn = wday_name[timeptr->tm_wday]; 1256 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 1257 (int) (sizeof mon_name / sizeof mon_name[0])) 1258 mn = "???"; 1259 else mn = mon_name[timeptr->tm_mon]; 1260 printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 1261 wn, mn, 1262 timeptr->tm_mday, timeptr->tm_hour, 1263 timeptr->tm_min, timeptr->tm_sec); 1264 #define DIVISOR 10 1265 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 1266 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 1267 trail / DIVISOR; 1268 trail %= DIVISOR; 1269 if (trail < 0 && lead > 0) { 1270 trail += DIVISOR; 1271 --lead; 1272 } else if (lead < 0 && trail > 0) { 1273 trail -= DIVISOR; 1274 ++lead; 1275 } 1276 if (lead == 0) 1277 printf("%d", trail); 1278 else printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 1279 } 1280