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