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