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(const char * const abbrp, const char * const zone)
 146 {
 147         register const char *cp;
 148         int error = 0;
 149 
 150         if (warned)
 151                 return;
 152         cp = abbrp;
 153         while (isalpha(*cp) || isdigit(*cp) || *cp == '-' || *cp == '+')
 154                 ++cp;
 155         (void) fflush(stdout);
 156         if (cp - abbrp < 3) {
 157                 (void) fprintf(stderr, gettext("%s: warning: zone \"%s\" "
 158                     "abbreviation \"%s\" has fewer than 3 alphabetics\n"),
 159                     progname, zone, abbrp);
 160                 error = 1;
 161         } else if (cp - abbrp > 6) {
 162                 (void) fprintf(stderr, gettext("%s: warning: zone \"%s\" "
 163                     "abbreviation \"%s\" has more than 6 characters\n"),
 164                     progname, zone, abbrp);
 165                 error = 1;
 166         } else if (*cp != '\0') {
 167                 (void) fprintf(stderr, gettext("%s: warning: zone \"%s\" "
 168                     "abbreviation \"%s\" has characters other than "
 169                     "alphanumerics\n"), progname, zone, abbrp);
 170                 error = 1;
 171         }
 172         if (error)
 173                 warned = TRUE;
 174 }
 175 
 176 int
 177 main(argc, argv)
 178 int     argc;
 179 char    *argv[];
 180 {
 181         register int            i;
 182         register int            c;
 183         register int            vflag;
 184         register char           *cutarg;
 185         register long           cutloyear = ZDUMP_LO_YEAR;
 186         register long           cuthiyear = ZDUMP_HI_YEAR;
 187         register time_t         cutlotime;
 188         register time_t         cuthitime;
 189         time_t                  now;
 190         time_t                  t;
 191         time_t                  newt;
 192         struct tm               tm;
 193         struct tm               newtm;
 194         register struct tm      *tmp;
 195         register struct tm      *newtmp;
 196 
 197         INITIALIZE(cutlotime);
 198         INITIALIZE(cuthitime);
 199 
 200         (void) setlocale(LC_ALL, "");
 201 #if !defined(TEXT_DOMAIN)               /* Should be defined by cc -D */
 202 #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it weren't */
 203 #endif
 204         (void) textdomain(TEXT_DOMAIN);
 205 
 206         progname = argv[0];
 207         for (i = 1; i < argc; ++i)
 208                 if (strcmp(argv[i], "--version") == 0) {
 209                         (void) printf("%s\n", elsieid);
 210                         exit(EXIT_SUCCESS);
 211                 }
 212         vflag = 0;
 213         cutarg = NULL;
 214         while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
 215                 if (c == 'v')
 216                         vflag = 1;
 217                 else    cutarg = optarg;
 218         if (c != EOF ||
 219                 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
 220                         usage();
 221                         /* NOTREACHED */
 222         }
 223         if (vflag) {
 224                 if (cutarg != NULL) {
 225                         long    lo;
 226                         long    hi;
 227                         char    dummy;
 228 
 229                         if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
 230                                 cuthiyear = hi;
 231                         } else if (sscanf(cutarg, "%ld,%ld%c",
 232                                 &lo, &hi, &dummy) == 2) {
 233                                         cutloyear = lo;
 234                                         cuthiyear = hi;
 235                         } else {
 236 (void) fprintf(stderr, gettext("%s: wild -c argument %s\n"),
 237                                         progname, cutarg);
 238                                 exit(EXIT_FAILURE);
 239                         }
 240                 }
 241                 setabsolutes();
 242                 cutlotime = yeartot(cutloyear);
 243                 cuthitime = yeartot(cuthiyear);
 244         }
 245         (void) time(&now);
 246         longest = 0;
 247         for (i = optind; i < argc; ++i)
 248                 if (strlen(argv[i]) > longest)
 249                         longest = strlen(argv[i]);
 250 
 251         for (i = optind; i < argc; ++i) {
 252                 static char     buf[MAX_STRING_LENGTH];
 253                 static char     *tzp = NULL;
 254 
 255                 (void) unsetenv("TZ");
 256                 if (tzp != NULL)
 257                         free(tzp);
 258                 if ((tzp = malloc(3 + strlen(argv[i]) + 1)) == NULL) {
 259                         perror(progname);
 260                         exit(EXIT_FAILURE);
 261                 }
 262                 (void) strcpy(tzp, "TZ=");
 263                 (void) strcat(tzp, argv[i]);
 264                 if (putenv(tzp) != 0) {
 265                         perror(progname);
 266                         exit(EXIT_FAILURE);
 267                 }
 268                 if (!vflag) {
 269                         show(argv[i], now, FALSE);
 270                         continue;
 271                 }
 272 
 273 #if defined(sun)
 274                 /*
 275                  * We show the current time first, probably because we froze
 276                  * the behavior of zdump some time ago and then it got
 277                  * changed.
 278                  */
 279                 show(argv[i], now, TRUE);
 280 #endif
 281                 warned = FALSE;
 282                 t = absolute_min_time;
 283                 show(argv[i], t, TRUE);
 284                 t += SECSPERHOUR * HOURSPERDAY;
 285                 show(argv[i], t, TRUE);
 286                 if (t < cutlotime)
 287                         t = cutlotime;
 288                 tmp = my_localtime(&t);
 289                 if (tmp != NULL) {
 290                         tm = *tmp;
 291                         (void) strncpy(buf, abbr(&tm), sizeof (buf) - 1);
 292                 }
 293                 for (;;) {
 294                         if (t >= cuthitime)
 295                                 break;
 296                         /* check if newt will overrun maximum time_t value */
 297                         if (t > LONG_MAX - (SECSPERHOUR * 12))
 298                                 break;
 299                         newt = t + SECSPERHOUR * 12;
 300                         if (newt >= cuthitime)
 301                                 break;
 302                         newtmp = localtime(&newt);
 303                         if (newtmp != NULL)
 304                                 newtm = *newtmp;
 305                         if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
 306                                 (delta(&newtm, &tm) != (newt - t) ||
 307                                 newtm.tm_isdst != tm.tm_isdst ||
 308                                 strcmp(abbr(&newtm), buf) != 0)) {
 309                                         newt = hunt(argv[i], t, newt);
 310                                         newtmp = localtime(&newt);
 311                                         if (newtmp != NULL) {
 312                                                 newtm = *newtmp;
 313                                                 (void) strncpy(buf,
 314                                                         abbr(&newtm),
 315                                                         sizeof (buf) - 1);
 316                                         }
 317                         }
 318                         t = newt;
 319                         tm = newtm;
 320                         tmp = newtmp;
 321                 }
 322                 t = absolute_max_time;
 323 #if defined(sun)
 324                 show(argv[i], t, TRUE);
 325                 t -= SECSPERHOUR * HOURSPERDAY;
 326                 show(argv[i], t, TRUE);
 327 #else /* !defined(sun) */
 328                 t -= SECSPERHOUR * HOURSPERDAY;
 329                 show(argv[i], t, TRUE);
 330                 t += SECSPERHOUR * HOURSPERDAY;
 331                 show(argv[i], t, TRUE);
 332 #endif /* !defined(sun) */
 333         }
 334         if (fflush(stdout) || ferror(stdout)) {
 335                 (void) fprintf(stderr, "%s: ", progname);
 336                 (void) perror(gettext("Error writing standard output"));
 337                 exit(EXIT_FAILURE);
 338         }
 339         return (EXIT_SUCCESS);
 340 }
 341 
 342 static void
 343 setabsolutes()
 344 {
 345 #if defined(sun)
 346         absolute_min_time = LONG_MIN;
 347         absolute_max_time = LONG_MAX;
 348 #else
 349         if (0.5 == (time_t)0.5) {
 350                 /*
 351                  * time_t is floating.
 352                  */
 353                 if (sizeof (time_t) == sizeof (float)) {
 354                         absolute_min_time = (time_t)-FLT_MAX;
 355                         absolute_max_time = (time_t)FLT_MAX;
 356                 } else if (sizeof (time_t) == sizeof (double)) {
 357                         absolute_min_time = (time_t)-DBL_MAX;
 358                         absolute_max_time = (time_t)DBL_MAX;
 359                 } else {
 360                         (void) fprintf(stderr, gettext("%s: use of -v on "
 361                             "system with floating time_t other than float "
 362                             "or double\n"), progname);
 363                         exit(EXIT_FAILURE);
 364                 }
 365         } else
 366         /*CONSTANTCONDITION*/
 367         if (0 > (time_t)-1) {
 368                 /*
 369                  * time_t is signed.
 370                  */
 371                 register time_t hibit;
 372 
 373                 for (hibit = 1; (hibit * 2) != 0; hibit *= 2)
 374                         continue;
 375                 absolute_min_time = hibit;
 376                 absolute_max_time = -(hibit + 1);
 377         } else {
 378                 /*
 379                  * time_t is unsigned.
 380                  */
 381                 absolute_min_time = 0;
 382                 absolute_max_time = absolute_min_time - 1;
 383         }
 384 #endif
 385 }
 386 
 387 static time_t
 388 yeartot(y)
 389 const long      y;
 390 {
 391         register long   myy;
 392         register long   seconds;
 393         register time_t t;
 394 
 395         myy = EPOCH_YEAR;
 396         t = 0;
 397         while (myy != y) {
 398                 if (myy < y) {
 399                         seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
 400                         ++myy;
 401                         if (t > absolute_max_time - seconds) {
 402                                 t = absolute_max_time;
 403                                 break;
 404                         }
 405                         t += seconds;
 406                 } else {
 407                         --myy;
 408                         seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
 409                         if (t < absolute_min_time + seconds) {
 410                                 t = absolute_min_time;
 411                                 break;
 412                         }
 413                         t -= seconds;
 414                 }
 415         }
 416         return (t);
 417 }
 418 
 419 static time_t
 420 hunt(name, lot, hit)
 421 char    *name;
 422 time_t  lot;
 423 time_t  hit;
 424 {
 425         time_t                  t;
 426         long                    diff;
 427         struct tm               lotm;
 428         register struct tm      *lotmp;
 429         struct tm               tm;
 430         register struct tm      *tmp;
 431         char                    loab[MAX_STRING_LENGTH];
 432 
 433         lotmp = my_localtime(&lot);
 434         if (lotmp != NULL) {
 435                 lotm = *lotmp;
 436                 (void) strncpy(loab, abbr(&lotm), sizeof (loab) - 1);
 437         }
 438         for (;;) {
 439                 diff = (long)(hit - lot);
 440                 if (diff < 2)
 441                         break;
 442                 t = lot;
 443                 t += diff / 2;
 444                 if (t <= lot)
 445                         ++t;
 446                 else if (t >= hit)
 447                         --t;
 448                 tmp = my_localtime(&t);
 449                 if (tmp != NULL)
 450                         tm = *tmp;
 451                 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
 452                         (delta(&tm, &lotm) == (t - lot) &&
 453                         tm.tm_isdst == lotm.tm_isdst &&
 454                         strcmp(abbr(&tm), loab) == 0)) {
 455                                 lot = t;
 456                                 lotm = tm;
 457                                 lotmp = tmp;
 458                 } else  hit = t;
 459         }
 460         show(name, lot, TRUE);
 461         show(name, hit, TRUE);
 462         return (hit);
 463 }
 464 
 465 /*
 466  * Thanks to Paul Eggert for logic used in delta.
 467  */
 468 
 469 static long
 470 delta(newp, oldp)
 471 struct tm       *newp;
 472 struct tm       *oldp;
 473 {
 474         register long   result;
 475         register int    tmy;
 476 
 477         if (newp->tm_year < oldp->tm_year)
 478                 return (-delta(oldp, newp));
 479         result = 0;
 480         for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
 481                 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
 482         result += newp->tm_yday - oldp->tm_yday;
 483         result *= HOURSPERDAY;
 484         result += newp->tm_hour - oldp->tm_hour;
 485         result *= MINSPERHOUR;
 486         result += newp->tm_min - oldp->tm_min;
 487         result *= SECSPERMIN;
 488         result += newp->tm_sec - oldp->tm_sec;
 489         return (result);
 490 }
 491 
 492 static void
 493 show(zone, t, v)
 494 char    *zone;
 495 time_t  t;
 496 int     v;
 497 {
 498         register struct tm      *tmp;
 499 
 500         (void) printf("%-*s  ", (int)longest, zone);
 501         if (v) {
 502                 tmp = gmtime(&t);
 503                 if (tmp == NULL) {
 504                         (void) printf(tformat(), t);
 505                 } else {
 506                         dumptime(tmp);
 507                         (void) printf(" UTC");
 508                 }
 509                 (void) printf(" = ");
 510         }
 511         tmp = my_localtime(&t);
 512         dumptime(tmp);
 513         if (tmp != NULL) {
 514                 if (*abbr(tmp) != '\0')
 515                         (void) printf(" %s", abbr(tmp));
 516                 if (v) {
 517                         (void) printf(" isdst=%d", tmp->tm_isdst);
 518 #ifdef TM_GMTOFF
 519                         (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
 520 #endif /* defined TM_GMTOFF */
 521                 }
 522         }
 523         (void) printf("\n");
 524         if (tmp != NULL && *abbr(tmp) != '\0')
 525                 abbrok(abbr(tmp), zone);
 526 }
 527 
 528 static char *
 529 abbr(tmp)
 530 struct tm       *tmp;
 531 {
 532         register char   *result;
 533         static char     nada;
 534 
 535         if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
 536                 return (&nada);
 537         result = tzname[tmp->tm_isdst];
 538         return ((result == NULL) ? &nada : result);
 539 }
 540 
 541 /*
 542  * The code below can fail on certain theoretical systems;
 543  * it works on all known real-world systems as of 2004-12-30.
 544  */
 545 
 546 static const char *
 547 tformat()
 548 {
 549 #if defined(sun)
 550         /* time_t is signed long */
 551         return ("%ld");
 552 #else
 553         /*CONSTANTCONDITION*/
 554         if (0.5 == (time_t)0.5) {       /* floating */
 555                 /*CONSTANTCONDITION*/
 556                 if (sizeof (time_t) > sizeof (double))
 557                         return ("%Lg");
 558                 return ("%g");
 559         }
 560         /*CONSTANTCONDITION*/
 561         if (0 > (time_t)-1) {                /* signed */
 562                 /*CONSTANTCONDITION*/
 563                 if (sizeof (time_t) > sizeof (long))
 564                         return ("%lld");
 565                 /*CONSTANTCONDITION*/
 566                 if (sizeof (time_t) > sizeof (int))
 567                         return ("%ld");
 568                 return ("%d");
 569         }
 570         /*CONSTANTCONDITION*/
 571         if (sizeof (time_t) > sizeof (unsigned long))
 572                 return ("%llu");
 573         /*CONSTANTCONDITION*/
 574         if (sizeof (time_t) > sizeof (unsigned int))
 575                 return ("%lu");
 576         return ("%u");
 577 #endif
 578 }
 579 
 580 static void
 581 dumptime(timeptr)
 582 register const struct tm        *timeptr;
 583 {
 584         static const char       wday_name[][3] = {
 585                 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 586         };
 587         static const char       mon_name[][3] = {
 588                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 589                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 590         };
 591         register const char     *wn;
 592         register const char     *mn;
 593         register int            lead;
 594         register int            trail;
 595 
 596         if (timeptr == NULL) {
 597                 (void) printf("NULL");
 598                 return;
 599         }
 600         /*
 601          * The packaged versions of localtime and gmtime never put out-of-range
 602          * values in tm_wday or tm_mon, but since this code might be compiled
 603          * with other (perhaps experimental) versions, paranoia is in order.
 604          */
 605         if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
 606                 (int)(sizeof (wday_name) / sizeof (wday_name[0])))
 607                         wn = "???";
 608         else            wn = wday_name[timeptr->tm_wday];
 609         if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
 610                 (int)(sizeof (mon_name) / sizeof (mon_name[0])))
 611                         mn = "???";
 612         else            mn = mon_name[timeptr->tm_mon];
 613         (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
 614                 wn, mn,
 615                 timeptr->tm_mday, timeptr->tm_hour,
 616                 timeptr->tm_min, timeptr->tm_sec);
 617 #define DIVISOR 10
 618         trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
 619         lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
 620                 trail / DIVISOR;
 621         trail %= DIVISOR;
 622         if (trail < 0 && lead > 0) {
 623                 trail += DIVISOR;
 624                 --lead;
 625         } else if (lead < 0 && trail > 0) {
 626                 trail -= DIVISOR;
 627                 ++lead;
 628         }
 629         if (lead == 0)
 630                 (void) printf("%d", trail);
 631         else
 632                 (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
 633 }
 634 
 635 static void
 636 usage()
 637 {
 638         (void) fprintf(stderr, gettext(
 639             "%s: [ --version ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n"),
 640                 progname);
 641         exit(EXIT_FAILURE);
 642         /* NOTREACHED */
 643 }