1 /* $NetBSD: zdump.c,v 1.35 2014/08/16 16:22:21 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.35 2014/08/16 16:22:21 christos Exp $"); 10 #endif /* !defined lint */ 11 12 #include "version.h" 13 /* 14 ** This code has been made independent of the rest of the time 15 ** conversion package to increase confidence in the verification it provides. 16 ** You can use this code to help in verifying other implementations. 17 ** 18 ** However, include private.h when debugging, so that it overrides 19 ** time_t consistently with the rest of the package. 20 */ 21 22 #include "private.h" 23 24 #include "stdio.h" /* for stdout, stderr */ 25 #include "string.h" /* for strcpy */ 26 #include "sys/types.h" /* for time_t */ 27 #include "time.h" /* for struct tm */ 28 #include "stdlib.h" /* for exit, malloc, atoi */ 29 #include <err.h> 30 31 /* 32 ** Substitutes for pre-C99 compilers. 33 ** Much of this section of code is stolen from private.h. 34 */ 35 36 #ifndef HAVE_STDINT_H 37 # define HAVE_STDINT_H \ 38 (199901 <= __STDC_VERSION__ \ 39 || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \ 40 || __CYGWIN__) 41 #endif 42 #if HAVE_STDINT_H 43 # include "stdint.h" 44 #endif 45 #ifndef HAVE_INTTYPES_H 46 # define HAVE_INTTYPES_H HAVE_STDINT_H 47 #endif 48 #if HAVE_INTTYPES_H 49 # include <inttypes.h> 50 #endif 51 52 #ifndef INT_FAST32_MAX 53 # if INT_MAX >> 31 == 0 54 typedef long int_fast32_t; 55 # else 56 typedef int int_fast32_t; 57 # endif 58 #endif 59 60 #ifndef INTMAX_MAX 61 # if defined LLONG_MAX || defined __LONG_LONG_MAX__ 62 typedef long long intmax_t; 63 # define strtoimax strtoll 64 # define PRIdMAX "lld" 65 # ifdef LLONG_MAX 66 # define INTMAX_MAX LLONG_MAX 67 # else 68 # define INTMAX_MAX __LONG_LONG_MAX__ 69 # endif 70 # else 71 typedef long intmax_t; 72 # define strtoimax strtol 73 # define PRIdMAX "ld" 74 # define INTMAX_MAX LONG_MAX 75 # endif 76 #endif 77 78 79 #ifndef ZDUMP_LO_YEAR 80 #define ZDUMP_LO_YEAR (-500) 81 #endif /* !defined ZDUMP_LO_YEAR */ 82 83 #ifndef ZDUMP_HI_YEAR 84 #define ZDUMP_HI_YEAR 2500 85 #endif /* !defined ZDUMP_HI_YEAR */ 86 87 #ifndef MAX_STRING_LENGTH 88 #define MAX_STRING_LENGTH 1024 89 #endif /* !defined MAX_STRING_LENGTH */ 90 91 #ifndef TRUE 92 #define TRUE 1 93 #endif /* !defined TRUE */ 94 95 #ifndef FALSE 96 #define FALSE 0 97 #endif /* !defined FALSE */ 98 99 #ifndef EXIT_SUCCESS 100 #define EXIT_SUCCESS 0 101 #endif /* !defined EXIT_SUCCESS */ 102 103 #ifndef EXIT_FAILURE 104 #define EXIT_FAILURE 1 105 #endif /* !defined EXIT_FAILURE */ 106 107 #ifndef SECSPERMIN 108 #define SECSPERMIN 60 109 #endif /* !defined SECSPERMIN */ 110 111 #ifndef MINSPERHOUR 112 #define MINSPERHOUR 60 113 #endif /* !defined MINSPERHOUR */ 114 115 #ifndef SECSPERHOUR 116 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 117 #endif /* !defined SECSPERHOUR */ 118 119 #ifndef HOURSPERDAY 120 #define HOURSPERDAY 24 121 #endif /* !defined HOURSPERDAY */ 122 123 #ifndef EPOCH_YEAR 124 #define EPOCH_YEAR 1970 125 #endif /* !defined EPOCH_YEAR */ 126 127 #ifndef TM_YEAR_BASE 128 #define TM_YEAR_BASE 1900 129 #endif /* !defined TM_YEAR_BASE */ 130 131 #ifndef DAYSPERNYEAR 132 #define DAYSPERNYEAR 365 133 #endif /* !defined DAYSPERNYEAR */ 134 135 #ifndef isleap 136 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 137 #endif /* !defined isleap */ 138 139 #ifndef isleap_sum 140 /* 141 ** See tzfile.h for details on isleap_sum. 142 */ 143 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) 144 #endif /* !defined isleap_sum */ 145 146 #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) 147 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 148 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 149 #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ 150 + SECSPERLYEAR * (intmax_t) (100 - 3)) 151 152 /* 153 ** True if SECSPER400YEARS is known to be representable as an 154 ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false 155 ** even if SECSPER400YEARS is representable, because when that happens 156 ** the code merely runs a bit more slowly, and this slowness doesn't 157 ** occur on any practical platform. 158 */ 159 enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; 160 161 #ifndef HAVE_GETTEXT 162 #define HAVE_GETTEXT 0 163 #endif 164 #if HAVE_GETTEXT 165 #include "locale.h" /* for setlocale */ 166 #include "libintl.h" 167 #endif /* HAVE_GETTEXT */ 168 169 #ifndef ATTRIBUTE_PURE 170 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) 171 # define ATTRIBUTE_PURE __attribute__ ((ATTRIBUTE_PURE__)) 172 #else 173 # define ATTRIBUTE_PURE /* empty */ 174 #endif 175 #endif 176 177 #ifndef INITIALIZE 178 #if defined(__GNUC__) || defined(__lint__) 179 #define INITIALIZE(x) ((x) = 0) 180 #else /* !defined GNUC || lint */ 181 #define INITIALIZE(x) 182 #endif /* !defined GNUC || lint */ 183 #endif /* !defined INITIALIZE */ 184 185 /* 186 ** For the benefit of GNU folk... 187 ** '_(MSGID)' uses the current locale's message library string for MSGID. 188 ** The default is to use gettext if available, and use MSGID otherwise. 189 */ 190 191 #ifndef _ 192 #if HAVE_GETTEXT 193 #define _(msgid) gettext(msgid) 194 #else /* !HAVE_GETTEXT */ 195 #define _(msgid) msgid 196 #endif /* !HAVE_GETTEXT */ 197 #endif /* !defined _ */ 198 199 #if !defined TZ_DOMAIN && defined HAVE_GETTEXT 200 # define TZ_DOMAIN "tz" 201 #endif 202 203 extern char ** environ; 204 extern int getopt(int argc, char * const argv[], 205 const char * options); 206 extern char * optarg; 207 extern int optind; 208 209 /* The minimum and maximum finite time values. */ 210 static time_t absolute_min_time = 211 ((time_t) -1 < 0 212 ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) 213 : 0); 214 static time_t absolute_max_time = 215 ((time_t) -1 < 0 216 ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) 217 : -1); 218 static size_t longest; 219 static char * progname; 220 static int warned; 221 222 static const char * abbr(struct tm * tmp); 223 static void abbrok(const char * abbrp, const char * zone); 224 static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE; 225 static void dumptime(const struct tm * tmp); 226 static time_t hunt(char * name, time_t lot, time_t hit); 227 static void show(char * zone, time_t t, int v); 228 static const char * tformat(void); 229 static time_t yeartot(long y) ATTRIBUTE_PURE; 230 231 /* Is A an alphabetic character in the C locale? */ 232 static int 233 is_alpha(char a) 234 { 235 switch (a) { 236 default: 237 return 0; 238 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 239 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 240 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 241 case 'V': case 'W': case 'X': case 'Y': case 'Z': 242 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 243 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 244 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 245 case 'v': case 'w': case 'x': case 'y': case 'z': 246 return 1; 247 } 248 } 249 250 #ifndef TYPECHECK 251 #define my_localtime localtime 252 #else /* !defined TYPECHECK */ 253 static struct tm * 254 my_localtime(time_t *tp) 255 { 256 struct tm *tmp; 257 258 tmp = localtime(tp); 259 if (tp != NULL && tmp != NULL) { 260 struct tm tm; 261 time_t t; 262 263 tm = *tmp; 264 t = mktime(&tm); 265 if (t != *tp) { 266 (void) fflush(stdout); 267 (void) fprintf(stderr, "\n%s: ", progname); 268 (void) fprintf(stderr, tformat(), *tp); 269 (void) fprintf(stderr, " ->"); 270 (void) fprintf(stderr, " year=%d", tmp->tm_year); 271 (void) fprintf(stderr, " mon=%d", tmp->tm_mon); 272 (void) fprintf(stderr, " mday=%d", tmp->tm_mday); 273 (void) fprintf(stderr, " hour=%d", tmp->tm_hour); 274 (void) fprintf(stderr, " min=%d", tmp->tm_min); 275 (void) fprintf(stderr, " sec=%d", tmp->tm_sec); 276 (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); 277 (void) fprintf(stderr, " -> "); 278 (void) fprintf(stderr, tformat(), t); 279 (void) fprintf(stderr, "\n"); 280 } 281 } 282 return tmp; 283 } 284 #endif /* !defined TYPECHECK */ 285 286 static void 287 abbrok(const char *const abbrp, const char *const zone) 288 { 289 const char *cp; 290 const char *wp; 291 292 if (warned) 293 return; 294 cp = abbrp; 295 wp = NULL; 296 while (is_alpha(*cp)) 297 ++cp; 298 if (cp - abbrp == 0) 299 wp = _("lacks alphabetic at start"); 300 else if (cp - abbrp < 3) 301 wp = _("has fewer than 3 alphabetics"); 302 else if (cp - abbrp > 6) 303 wp = _("has more than 6 alphabetics"); 304 if (wp == NULL && (*cp == '+' || *cp == '-')) { 305 ++cp; 306 if ('0' <= *cp && *cp <= '9') 307 if (*cp++ == '1' && '0' <= *cp && *cp <= '4') 308 cp++; 309 if (*cp != '\0') 310 wp = _("differs from POSIX standard"); 311 } 312 if (wp == NULL) 313 return; 314 (void) fflush(stdout); 315 (void) fprintf(stderr, 316 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), 317 progname, zone, abbrp, wp); 318 warned = TRUE; 319 } 320 321 __dead static void 322 usage(FILE *const stream, const int status) 323 { 324 (void) fprintf(stream, 325 _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n" 326 "\n" 327 "Report bugs to %s.\n"), 328 progname, progname, REPORT_BUGS_TO); 329 exit(status); 330 } 331 332 int 333 main(int argc, char *argv[]) 334 { 335 int i; 336 int vflag; 337 int Vflag; 338 char * cutarg; 339 char * cuttimes; 340 time_t cutlotime; 341 time_t cuthitime; 342 char ** fakeenv; 343 time_t now; 344 time_t t; 345 time_t newt; 346 struct tm tm; 347 struct tm newtm; 348 struct tm * tmp; 349 struct tm * newtmp; 350 351 cutlotime = absolute_min_time; 352 cuthitime = absolute_max_time; 353 #if HAVE_GETTEXT 354 (void) setlocale(LC_ALL, ""); 355 #ifdef TZ_DOMAINDIR 356 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 357 #endif /* defined TEXTDOMAINDIR */ 358 (void) textdomain(TZ_DOMAIN); 359 #endif /* HAVE_GETTEXT */ 360 progname = argv[0]; 361 for (i = 1; i < argc; ++i) 362 if (strcmp(argv[i], "--version") == 0) { 363 (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); 364 exit(EXIT_SUCCESS); 365 } else if (strcmp(argv[i], "--help") == 0) { 366 usage(stdout, EXIT_SUCCESS); 367 } 368 vflag = Vflag = 0; 369 cutarg = cuttimes = NULL; 370 for (;;) 371 switch (getopt(argc, argv, "c:t:vV")) { 372 case 'c': cutarg = optarg; break; 373 case 't': cuttimes = optarg; break; 374 case 'v': vflag = 1; break; 375 case 'V': Vflag = 1; break; 376 case -1: 377 if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) 378 goto arg_processing_done; 379 /* Fall through. */ 380 default: 381 usage(stderr, EXIT_FAILURE); 382 } 383 arg_processing_done:; 384 385 if (vflag | Vflag) { 386 intmax_t lo; 387 intmax_t hi; 388 char *loend, *hiend; 389 intmax_t cutloyear = ZDUMP_LO_YEAR; 390 intmax_t cuthiyear = ZDUMP_HI_YEAR; 391 if (cutarg != NULL) { 392 lo = strtoimax(cutarg, &loend, 10); 393 if (cutarg != loend && !*loend) { 394 hi = lo; 395 cuthiyear = hi; 396 } else if (cutarg != loend && *loend == ',' 397 && (hi = strtoimax(loend + 1, &hiend, 10), 398 loend + 1 != hiend && !*hiend)) { 399 cutloyear = lo; 400 cuthiyear = hi; 401 } else { 402 (void) fprintf(stderr, _("%s: wild -c argument %s\n"), 403 progname, cutarg); 404 exit(EXIT_FAILURE); 405 } 406 } 407 if (cutarg != NULL || cuttimes == NULL) { 408 cutlotime = yeartot(cutloyear); 409 cuthitime = yeartot(cuthiyear); 410 } 411 if (cuttimes != NULL) { 412 lo = strtoimax(cuttimes, &loend, 10); 413 if (cuttimes != loend && !*loend) { 414 hi = lo; 415 if (hi < cuthitime) { 416 if (hi < absolute_min_time) 417 hi = absolute_min_time; 418 cuthitime = hi; 419 } 420 } else if (cuttimes != loend && *loend == ',' 421 && (hi = strtoimax(loend + 1, &hiend, 10), 422 loend + 1 != hiend && !*hiend)) { 423 if (cutlotime < lo) { 424 if (absolute_max_time < lo) 425 lo = absolute_max_time; 426 cutlotime = lo; 427 } 428 if (hi < cuthitime) { 429 if (hi < absolute_min_time) 430 hi = absolute_min_time; 431 cuthitime = hi; 432 } 433 } else { 434 (void) fprintf(stderr, 435 _("%s: wild -t argument %s\n"), 436 progname, cuttimes); 437 exit(EXIT_FAILURE); 438 } 439 } 440 } 441 (void) time(&now); 442 longest = 0; 443 for (i = optind; i < argc; ++i) 444 if (strlen(argv[i]) > longest) 445 longest = strlen(argv[i]); 446 { 447 int from; 448 int to; 449 450 for (i = 0; environ[i] != NULL; ++i) 451 continue; 452 fakeenv = malloc((i + 2) * sizeof *fakeenv); 453 if (fakeenv == NULL || 454 (fakeenv[0] = malloc(longest + 4)) == NULL) { 455 err(EXIT_FAILURE, "Can't allocated %zu bytes", 456 longest + 4); 457 } 458 to = 0; 459 (void)strcpy(fakeenv[to++], "TZ="); /* XXX strcpy is safe */ 460 for (from = 0; environ[from] != NULL; ++from) 461 if (strncmp(environ[from], "TZ=", 3) != 0) 462 fakeenv[to++] = environ[from]; 463 fakeenv[to] = NULL; 464 environ = fakeenv; 465 } 466 for (i = optind; i < argc; ++i) { 467 static char buf[MAX_STRING_LENGTH]; 468 469 (void) strcpy(&fakeenv[0][3], argv[i]); /* XXX strcpy is safe */ 470 if (! (vflag | Vflag)) { 471 show(argv[i], now, FALSE); 472 continue; 473 } 474 warned = FALSE; 475 t = absolute_min_time; 476 if (!Vflag) { 477 show(argv[i], t, TRUE); 478 t += SECSPERDAY; 479 show(argv[i], t, TRUE); 480 } 481 if (t < cutlotime) 482 t = cutlotime; 483 tmp = my_localtime(&t); 484 if (tmp != NULL) { 485 tm = *tmp; 486 (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); 487 } 488 for ( ; ; ) { 489 newt = (t < absolute_max_time - SECSPERDAY / 2 490 ? t + SECSPERDAY / 2 491 : absolute_max_time); 492 if (cuthitime <= newt) 493 break; 494 newtmp = localtime(&newt); 495 if (newtmp != NULL) 496 newtm = *newtmp; 497 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 498 (delta(&newtm, &tm) != (newt - t) || 499 newtm.tm_isdst != tm.tm_isdst || 500 strcmp(abbr(&newtm), buf) != 0)) { 501 newt = hunt(argv[i], t, newt); 502 newtmp = localtime(&newt); 503 if (newtmp != NULL) { 504 newtm = *newtmp; 505 (void) strncpy(buf, 506 abbr(&newtm), 507 (sizeof buf) - 1); 508 } 509 } 510 t = newt; 511 tm = newtm; 512 tmp = newtmp; 513 } 514 if (!Vflag) { 515 t = absolute_max_time; 516 t -= SECSPERDAY; 517 show(argv[i], t, TRUE); 518 t += SECSPERDAY; 519 show(argv[i], t, TRUE); 520 } 521 } 522 if (fflush(stdout) || ferror(stdout)) { 523 err(EXIT_FAILURE, _("Error writing standard output")); 524 } 525 exit(EXIT_SUCCESS); 526 /* If exit fails to exit... */ 527 return EXIT_FAILURE; 528 } 529 530 static time_t 531 yeartot(const long y) 532 { 533 intmax_t myy, seconds, years; 534 time_t t; 535 536 myy = EPOCH_YEAR; 537 t = 0; 538 while (myy < y) { 539 if (SECSPER400YEARS_FITS && 400 <= y - myy) { 540 intmax_t diff400 = (y - myy) / 400; 541 if (INTMAX_MAX / SECSPER400YEARS < diff400) 542 return absolute_max_time; 543 seconds = diff400 * SECSPER400YEARS; 544 years = diff400 * 400; 545 } else { 546 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 547 years = 1; 548 } 549 myy += years; 550 if (t > absolute_max_time - seconds) 551 return absolute_max_time; 552 t += seconds; 553 } 554 while (y < myy) { 555 if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) { 556 intmax_t diff400 = (myy - y) / 400; 557 if (INTMAX_MAX / SECSPER400YEARS < diff400) 558 return absolute_min_time; 559 seconds = diff400 * SECSPER400YEARS; 560 years = diff400 * 400; 561 } else { 562 seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR; 563 years = 1; 564 } 565 myy -= years; 566 if (t < absolute_min_time + seconds) 567 return absolute_min_time; 568 t -= seconds; 569 } 570 return t; 571 } 572 573 static time_t 574 hunt(char *name, time_t lot, time_t hit) 575 { 576 time_t t; 577 struct tm lotm; 578 struct tm * lotmp; 579 struct tm tm; 580 struct tm * tmp; 581 char loab[MAX_STRING_LENGTH]; 582 583 lotmp = my_localtime(&lot); 584 if (lotmp != NULL) { 585 lotm = *lotmp; 586 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); 587 } 588 for ( ; ; ) { 589 time_t diff = hit - lot; 590 if (diff < 2) 591 break; 592 t = lot; 593 t += diff / 2; 594 if (t <= lot) 595 ++t; 596 else if (t >= hit) 597 --t; 598 tmp = my_localtime(&t); 599 if (tmp != NULL) 600 tm = *tmp; 601 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 602 (delta(&tm, &lotm) == (t - lot) && 603 tm.tm_isdst == lotm.tm_isdst && 604 strcmp(abbr(&tm), loab) == 0)) { 605 lot = t; 606 lotm = tm; 607 lotmp = tmp; 608 } else hit = t; 609 } 610 show(name, lot, TRUE); 611 show(name, hit, TRUE); 612 return hit; 613 } 614 615 /* 616 ** Thanks to Paul Eggert for logic used in delta. 617 */ 618 619 static intmax_t 620 delta(struct tm *newp, struct tm *oldp) 621 { 622 intmax_t result; 623 int tmy; 624 625 if (newp->tm_year < oldp->tm_year) 626 return -delta(oldp, newp); 627 result = 0; 628 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 629 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 630 result += newp->tm_yday - oldp->tm_yday; 631 result *= HOURSPERDAY; 632 result += newp->tm_hour - oldp->tm_hour; 633 result *= MINSPERHOUR; 634 result += newp->tm_min - oldp->tm_min; 635 result *= SECSPERMIN; 636 result += newp->tm_sec - oldp->tm_sec; 637 return result; 638 } 639 640 static void 641 show(char *zone, time_t t, int v) 642 { 643 struct tm * tmp; 644 645 (void) printf("%-*s ", (int) longest, zone); 646 if (v) { 647 tmp = gmtime(&t); 648 if (tmp == NULL) { 649 (void) printf(tformat(), t); 650 } else { 651 dumptime(tmp); 652 (void) printf(" UT"); 653 } 654 (void) printf(" = "); 655 } 656 tmp = my_localtime(&t); 657 dumptime(tmp); 658 if (tmp != NULL) { 659 if (*abbr(tmp) != '\0') 660 (void) printf(" %s", abbr(tmp)); 661 if (v) { 662 (void) printf(" isdst=%d", tmp->tm_isdst); 663 #ifdef TM_GMTOFF 664 (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); 665 #endif /* defined TM_GMTOFF */ 666 } 667 } 668 (void) printf("\n"); 669 if (tmp != NULL && *abbr(tmp) != '\0') 670 abbrok(abbr(tmp), zone); 671 } 672 673 static const char * 674 abbr(struct tm *tmp) 675 { 676 const char * result; 677 static const char nada; 678 679 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 680 return &nada; 681 result = tzname[tmp->tm_isdst]; 682 return (result == NULL) ? &nada : result; 683 } 684 685 /* 686 ** The code below can fail on certain theoretical systems; 687 ** it works on all known real-world systems as of 2004-12-30. 688 */ 689 690 static const char * 691 tformat(void) 692 { 693 if (0 > (time_t) -1) { /* signed */ 694 if (sizeof (time_t) == sizeof (intmax_t)) 695 return "%"PRIdMAX; 696 if (sizeof (time_t) > sizeof (long)) 697 return "%lld"; 698 if (sizeof (time_t) > sizeof (int)) 699 return "%ld"; 700 return "%d"; 701 } 702 #ifdef PRIuMAX 703 if (sizeof (time_t) == sizeof (uintmax_t)) 704 return "%"PRIuMAX; 705 #endif 706 if (sizeof (time_t) > sizeof (unsigned long)) 707 return "%llu"; 708 if (sizeof (time_t) > sizeof (unsigned int)) 709 return "%lu"; 710 return "%u"; 711 } 712 713 static void 714 dumptime(const struct tm *timeptr) 715 { 716 static const char wday_name[][3] = { 717 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 718 }; 719 static const char mon_name[][3] = { 720 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 721 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 722 }; 723 const char * wn; 724 const char * mn; 725 int lead; 726 int trail; 727 728 if (timeptr == NULL) { 729 (void) printf("NULL"); 730 return; 731 } 732 /* 733 ** The packaged versions of localtime and gmtime never put out-of-range 734 ** values in tm_wday or tm_mon, but since this code might be compiled 735 ** with other (perhaps experimental) versions, paranoia is in order. 736 */ 737 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 738 (int) (sizeof wday_name / sizeof wday_name[0])) 739 wn = "???"; 740 else wn = wday_name[timeptr->tm_wday]; 741 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 742 (int) (sizeof mon_name / sizeof mon_name[0])) 743 mn = "???"; 744 else mn = mon_name[timeptr->tm_mon]; 745 (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 746 wn, mn, 747 timeptr->tm_mday, timeptr->tm_hour, 748 timeptr->tm_min, timeptr->tm_sec); 749 #define DIVISOR 10 750 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 751 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 752 trail / DIVISOR; 753 trail %= DIVISOR; 754 if (trail < 0 && lead > 0) { 755 trail += DIVISOR; 756 --lead; 757 } else if (lead < 0 && trail > 0) { 758 trail -= DIVISOR; 759 ++lead; 760 } 761 if (lead == 0) 762 (void) printf("%d", trail); 763 else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 764 } 765