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