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