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