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