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 newt = t + SECSPERHOUR * 12; 318 if (newt >= cuthitime) 319 break; 320 if (newt <= t) 321 break; 322 newtmp = localtime(&newt); 323 if (newtmp != NULL) 324 newtm = *newtmp; 325 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 326 (delta(&newtm, &tm) != (newt - t) || 327 newtm.tm_isdst != tm.tm_isdst || 328 strcmp(abbr(&newtm), buf) != 0)) { 329 newt = hunt(argv[i], t, newt); 330 newtmp = localtime(&newt); 331 if (newtmp != NULL) { 332 newtm = *newtmp; 333 (void) strncpy(buf, 334 abbr(&newtm), 335 sizeof (buf) - 1); 336 } 337 } 338 t = newt; 339 tm = newtm; 340 tmp = newtmp; 341 } 342 t = absolute_max_time; 343 #if defined(sun) 344 show(argv[i], t, TRUE); 345 t -= SECSPERHOUR * HOURSPERDAY; 346 show(argv[i], t, TRUE); 347 #else /* !defined(sun) */ 348 t -= SECSPERHOUR * HOURSPERDAY; 349 show(argv[i], t, TRUE); 350 t += SECSPERHOUR * HOURSPERDAY; 351 show(argv[i], t, TRUE); 352 #endif /* !defined(sun) */ 353 } 354 if (fflush(stdout) || ferror(stdout)) { 355 (void) fprintf(stderr, "%s: ", progname); 356 (void) perror(gettext("Error writing standard output")); 357 exit(EXIT_FAILURE); 358 } 359 return (EXIT_SUCCESS); 360 } 361 362 static void 363 setabsolutes() 364 { 365 #if defined(sun) 366 absolute_min_time = LONG_MIN; 367 absolute_max_time = LONG_MAX; 368 #else 369 if (0.5 == (time_t)0.5) { 370 /* 371 * time_t is floating. 372 */ 373 if (sizeof (time_t) == sizeof (float)) { 374 absolute_min_time = (time_t)-FLT_MAX; 375 absolute_max_time = (time_t)FLT_MAX; 376 } else if (sizeof (time_t) == sizeof (double)) { 377 absolute_min_time = (time_t)-DBL_MAX; 378 absolute_max_time = (time_t)DBL_MAX; 379 } else { 380 (void) fprintf(stderr, gettext("%s: use of -v on " 381 "system with floating time_t other than float " 382 "or double\n"), progname); 383 exit(EXIT_FAILURE); 384 } 385 } else 386 /*CONSTANTCONDITION*/ 387 if (0 > (time_t)-1) { 388 /* 389 * time_t is signed. 390 */ 391 register time_t hibit; 392 393 for (hibit = 1; (hibit * 2) != 0; hibit *= 2) 394 continue; 395 absolute_min_time = hibit; 396 absolute_max_time = -(hibit + 1); 397 } else { 398 /* 399 * time_t is unsigned. 400 */ 401 absolute_min_time = 0; 402 absolute_max_time = absolute_min_time - 1; 403 } 404 #endif 405 } 406 407 static time_t 408 yeartot(y) 409 const long y; 410 { 411 register long myy; 412 register long seconds; 413 register time_t t; 414 415 myy = EPOCH_YEAR; 416 t = 0; 417 while (myy != y) { 418 if (myy < y) { 419 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 420 ++myy; 421 if (t > absolute_max_time - seconds) { 422 t = absolute_max_time; 423 break; 424 } 425 t += seconds; 426 } else { 427 --myy; 428 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 429 if (t < absolute_min_time + seconds) { 430 t = absolute_min_time; 431 break; 432 } 433 t -= seconds; 434 } 435 } 436 return (t); 437 } 438 439 static time_t 440 hunt(name, lot, hit) 441 char *name; 442 time_t lot; 443 time_t hit; 444 { 445 time_t t; 446 long diff; 447 struct tm lotm; 448 register struct tm *lotmp; 449 struct tm tm; 450 register struct tm *tmp; 451 char loab[MAX_STRING_LENGTH]; 452 453 lotmp = my_localtime(&lot); 454 if (lotmp != NULL) { 455 lotm = *lotmp; 456 (void) strncpy(loab, abbr(&lotm), sizeof (loab) - 1); 457 } 458 for (;;) { 459 diff = (long)(hit - lot); 460 if (diff < 2) 461 break; 462 t = lot; 463 t += diff / 2; 464 if (t <= lot) 465 ++t; 466 else if (t >= hit) 467 --t; 468 tmp = my_localtime(&t); 469 if (tmp != NULL) 470 tm = *tmp; 471 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 472 (delta(&tm, &lotm) == (t - lot) && 473 tm.tm_isdst == lotm.tm_isdst && 474 strcmp(abbr(&tm), loab) == 0)) { 475 lot = t; 476 lotm = tm; 477 lotmp = tmp; 478 } else hit = t; 479 } 480 show(name, lot, TRUE); 481 show(name, hit, TRUE); 482 return (hit); 483 } 484 485 /* 486 * Thanks to Paul Eggert for logic used in delta. 487 */ 488 489 static long 490 delta(newp, oldp) 491 struct tm *newp; 492 struct tm *oldp; 493 { 494 register long result; 495 register int tmy; 496 497 if (newp->tm_year < oldp->tm_year) 498 return (-delta(oldp, newp)); 499 result = 0; 500 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 501 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 502 result += newp->tm_yday - oldp->tm_yday; 503 result *= HOURSPERDAY; 504 result += newp->tm_hour - oldp->tm_hour; 505 result *= MINSPERHOUR; 506 result += newp->tm_min - oldp->tm_min; 507 result *= SECSPERMIN; 508 result += newp->tm_sec - oldp->tm_sec; 509 return (result); 510 } 511 512 static void 513 show(zone, t, v) 514 char *zone; 515 time_t t; 516 int v; 517 { 518 register struct tm *tmp; 519 520 (void) printf("%-*s ", (int)longest, zone); 521 if (v) { 522 tmp = gmtime(&t); 523 if (tmp == NULL) { 524 (void) printf(tformat(), t); 525 } else { 526 dumptime(tmp); 527 (void) printf(" UTC"); 528 } 529 (void) printf(" = "); 530 } 531 tmp = my_localtime(&t); 532 dumptime(tmp); 533 if (tmp != NULL) { 534 if (*abbr(tmp) != '\0') 535 (void) printf(" %s", abbr(tmp)); 536 if (v) { 537 (void) printf(" isdst=%d", tmp->tm_isdst); 538 #ifdef TM_GMTOFF 539 (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); 540 #endif /* defined TM_GMTOFF */ 541 } 542 } 543 (void) printf("\n"); 544 if (tmp != NULL && *abbr(tmp) != '\0') 545 abbrok(abbr(tmp), zone); 546 } 547 548 static char * 549 abbr(tmp) 550 struct tm *tmp; 551 { 552 register char *result; 553 static char nada; 554 555 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 556 return (&nada); 557 result = tzname[tmp->tm_isdst]; 558 return ((result == NULL) ? &nada : result); 559 } 560 561 /* 562 * The code below can fail on certain theoretical systems; 563 * it works on all known real-world systems as of 2004-12-30. 564 */ 565 566 static const char * 567 tformat() 568 { 569 #if defined(sun) 570 /* time_t is signed long */ 571 return ("%ld"); 572 #else 573 /*CONSTANTCONDITION*/ 574 if (0.5 == (time_t)0.5) { /* floating */ 575 /*CONSTANTCONDITION*/ 576 if (sizeof (time_t) > sizeof (double)) 577 return ("%Lg"); 578 return ("%g"); 579 } 580 /*CONSTANTCONDITION*/ 581 if (0 > (time_t)-1) { /* signed */ 582 /*CONSTANTCONDITION*/ 583 if (sizeof (time_t) > sizeof (long)) 584 return ("%lld"); 585 /*CONSTANTCONDITION*/ 586 if (sizeof (time_t) > sizeof (int)) 587 return ("%ld"); 588 return ("%d"); 589 } 590 /*CONSTANTCONDITION*/ 591 if (sizeof (time_t) > sizeof (unsigned long)) 592 return ("%llu"); 593 /*CONSTANTCONDITION*/ 594 if (sizeof (time_t) > sizeof (unsigned int)) 595 return ("%lu"); 596 return ("%u"); 597 #endif 598 } 599 600 static void 601 dumptime(timeptr) 602 register const struct tm *timeptr; 603 { 604 static const char wday_name[][3] = { 605 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 606 }; 607 static const char mon_name[][3] = { 608 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 609 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 610 }; 611 register const char *wn; 612 register const char *mn; 613 register int lead; 614 register int trail; 615 616 if (timeptr == NULL) { 617 (void) printf("NULL"); 618 return; 619 } 620 /* 621 * The packaged versions of localtime and gmtime never put out-of-range 622 * values in tm_wday or tm_mon, but since this code might be compiled 623 * with other (perhaps experimental) versions, paranoia is in order. 624 */ 625 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 626 (int)(sizeof (wday_name) / sizeof (wday_name[0]))) 627 wn = "???"; 628 else wn = wday_name[timeptr->tm_wday]; 629 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 630 (int)(sizeof (mon_name) / sizeof (mon_name[0]))) 631 mn = "???"; 632 else mn = mon_name[timeptr->tm_mon]; 633 (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 634 wn, mn, 635 timeptr->tm_mday, timeptr->tm_hour, 636 timeptr->tm_min, timeptr->tm_sec); 637 #define DIVISOR 10 638 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 639 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 640 trail / DIVISOR; 641 trail %= DIVISOR; 642 if (trail < 0 && lead > 0) { 643 trail += DIVISOR; 644 --lead; 645 } else if (lead < 0 && trail > 0) { 646 trail -= DIVISOR; 647 ++lead; 648 } 649 if (lead == 0) 650 (void) printf("%d", trail); 651 else 652 (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 653 } 654 655 static void 656 usage() 657 { 658 (void) fprintf(stderr, gettext( 659 "%s: [ --version ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n"), 660 progname); 661 exit(EXIT_FAILURE); 662 /* NOTREACHED */ 663 } 664