Back to index

glibc  2.9
zdump.c
Go to the documentation of this file.
00001 static char   elsieid[] = "@(#)zdump.c    8.6";
00002 
00003 /*
00004 ** This code has been made independent of the rest of the time
00005 ** conversion package to increase confidence in the verification it provides.
00006 ** You can use this code to help in verifying other implementations.
00007 */
00008 
00009 #include "stdio.h"   /* for stdout, stderr, perror */
00010 #include "string.h"  /* for strcpy */
00011 #include "sys/types.h"      /* for time_t */
00012 #include "time.h"    /* for struct tm */
00013 #include "stdlib.h"  /* for exit, malloc, atoi */
00014 #include "float.h"   /* for FLT_MAX and DBL_MAX */
00015 #include "ctype.h"   /* for isalpha et al. */
00016 #ifndef isascii
00017 #define isascii(x) 1
00018 #endif /* !defined isascii */
00019 
00020 #ifndef ZDUMP_LO_YEAR
00021 #define ZDUMP_LO_YEAR       (-500)
00022 #endif /* !defined ZDUMP_LO_YEAR */
00023 
00024 #ifndef ZDUMP_HI_YEAR
00025 #define ZDUMP_HI_YEAR       2500
00026 #endif /* !defined ZDUMP_HI_YEAR */
00027 
00028 #ifndef MAX_STRING_LENGTH
00029 #define MAX_STRING_LENGTH   1024
00030 #endif /* !defined MAX_STRING_LENGTH */
00031 
00032 #ifndef TRUE
00033 #define TRUE         1
00034 #endif /* !defined TRUE */
00035 
00036 #ifndef FALSE
00037 #define FALSE        0
00038 #endif /* !defined FALSE */
00039 
00040 #ifndef EXIT_SUCCESS
00041 #define EXIT_SUCCESS 0
00042 #endif /* !defined EXIT_SUCCESS */
00043 
00044 #ifndef EXIT_FAILURE
00045 #define EXIT_FAILURE 1
00046 #endif /* !defined EXIT_FAILURE */
00047 
00048 #ifndef SECSPERMIN
00049 #define SECSPERMIN   60
00050 #endif /* !defined SECSPERMIN */
00051 
00052 #ifndef MINSPERHOUR
00053 #define MINSPERHOUR  60
00054 #endif /* !defined MINSPERHOUR */
00055 
00056 #ifndef SECSPERHOUR
00057 #define SECSPERHOUR  (SECSPERMIN * MINSPERHOUR)
00058 #endif /* !defined SECSPERHOUR */
00059 
00060 #ifndef HOURSPERDAY
00061 #define HOURSPERDAY  24
00062 #endif /* !defined HOURSPERDAY */
00063 
00064 #ifndef EPOCH_YEAR
00065 #define EPOCH_YEAR   1970
00066 #endif /* !defined EPOCH_YEAR */
00067 
00068 #ifndef TM_YEAR_BASE
00069 #define TM_YEAR_BASE 1900
00070 #endif /* !defined TM_YEAR_BASE */
00071 
00072 #ifndef DAYSPERNYEAR
00073 #define DAYSPERNYEAR 365
00074 #endif /* !defined DAYSPERNYEAR */
00075 
00076 #ifndef isleap
00077 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
00078 #endif /* !defined isleap */
00079 
00080 #ifndef isleap_sum
00081 /*
00082 ** See tzfile.h for details on isleap_sum.
00083 */
00084 #define isleap_sum(a, b)    isleap((a) % 400 + (b) % 400)
00085 #endif /* !defined isleap_sum */
00086 
00087 #define SECSPERDAY   ((long) SECSPERHOUR * HOURSPERDAY)
00088 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
00089 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
00090 
00091 #ifndef HAVE_GETTEXT
00092 #define HAVE_GETTEXT 0
00093 #endif
00094 #if HAVE_GETTEXT
00095 #include "locale.h"  /* for setlocale */
00096 #include "libintl.h"
00097 #endif /* HAVE_GETTEXT */
00098 
00099 #ifndef GNUC_or_lint
00100 #ifdef lint
00101 #define GNUC_or_lint
00102 #else /* !defined lint */
00103 #ifdef __GNUC__
00104 #define GNUC_or_lint
00105 #endif /* defined __GNUC__ */
00106 #endif /* !defined lint */
00107 #endif /* !defined GNUC_or_lint */
00108 
00109 #ifndef INITIALIZE
00110 #ifdef GNUC_or_lint
00111 #define INITIALIZE(x)       ((x) = 0)
00112 #else /* !defined GNUC_or_lint */
00113 #define INITIALIZE(x)
00114 #endif /* !defined GNUC_or_lint */
00115 #endif /* !defined INITIALIZE */
00116 
00117 /*
00118 ** For the benefit of GNU folk...
00119 ** `_(MSGID)' uses the current locale's message library string for MSGID.
00120 ** The default is to use gettext if available, and use MSGID otherwise.
00121 */
00122 
00123 #ifndef _
00124 #if HAVE_GETTEXT
00125 #define _(msgid) gettext(msgid)
00126 #else /* !HAVE_GETTEXT */
00127 #define _(msgid) msgid
00128 #endif /* !HAVE_GETTEXT */
00129 #endif /* !defined _ */
00130 
00131 #ifndef TZ_DOMAIN
00132 #define TZ_DOMAIN "tz"
00133 #endif /* !defined TZ_DOMAIN */
00134 
00135 extern char **       environ;
00136 extern int    getopt(int argc, char * const argv[],
00137                      const char * options);
00138 extern char * optarg;
00139 extern int    optind;
00140 extern char * tzname[2];
00141 
00142 static time_t absolute_min_time;
00143 static time_t absolute_max_time;
00144 static size_t longest;
00145 static char * progname;
00146 static int    warned;
00147 
00148 static char * abbr(struct tm * tmp);
00149 static void   abbrok(const char * abbrp, const char * zone);
00150 static long   delta(struct tm * newp, struct tm * oldp);
00151 static void   dumptime(const struct tm * tmp);
00152 static time_t hunt(char * name, time_t lot, time_t      hit);
00153 static void   setabsolutes(void);
00154 static void   show(char * zone, time_t t, int v);
00155 static const char *  tformat(void);
00156 static time_t yeartot(long y);
00157 
00158 #ifndef TYPECHECK
00159 #define my_localtime localtime
00160 #else /* !defined TYPECHECK */
00161 static struct tm *
00162 my_localtime(tp)
00163 time_t *      tp;
00164 {
00165        register struct tm * tmp;
00166 
00167        tmp = localtime(tp);
00168        if (tp != NULL && tmp != NULL) {
00169               struct tm     tm;
00170               register time_t      t;
00171 
00172               tm = *tmp;
00173               t = mktime(&tm);
00174               if (t - *tp >= 1 || *tp - t >= 1) {
00175                      (void) fflush(stdout);
00176                      (void) fprintf(stderr, "\n%s: ", progname);
00177                      (void) fprintf(stderr, tformat(), *tp);
00178                      (void) fprintf(stderr, " ->");
00179                      (void) fprintf(stderr, " year=%d", tmp->tm_year);
00180                      (void) fprintf(stderr, " mon=%d", tmp->tm_mon);
00181                      (void) fprintf(stderr, " mday=%d", tmp->tm_mday);
00182                      (void) fprintf(stderr, " hour=%d", tmp->tm_hour);
00183                      (void) fprintf(stderr, " min=%d", tmp->tm_min);
00184                      (void) fprintf(stderr, " sec=%d", tmp->tm_sec);
00185                      (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
00186                      (void) fprintf(stderr, " -> ");
00187                      (void) fprintf(stderr, tformat(), t);
00188                      (void) fprintf(stderr, "\n");
00189               }
00190        }
00191        return tmp;
00192 }
00193 #endif /* !defined TYPECHECK */
00194 
00195 static void
00196 abbrok(abbrp, zone)
00197 const char * const   abbrp;
00198 const char * const   zone;
00199 {
00200        register const char *       cp;
00201        register char *             wp;
00202 
00203        if (warned)
00204               return;
00205        cp = abbrp;
00206        wp = NULL;
00207        while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
00208               ++cp;
00209        if (cp - abbrp == 0)
00210               wp = _("lacks alphabetic at start");
00211        else if (cp - abbrp < 3)
00212               wp = _("has fewer than 3 alphabetics");
00213        else if (cp - abbrp > 6)
00214               wp = _("has more than 6 alphabetics");
00215        if (wp == NULL && (*cp == '+' || *cp == '-')) {
00216               ++cp;
00217               if (isascii((unsigned char) *cp) &&
00218                      isdigit((unsigned char) *cp))
00219                             if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
00220                                    ++cp;
00221               if (*cp != '\0')
00222                      wp = _("differs from POSIX standard");
00223        }
00224        if (wp == NULL)
00225               return;
00226        (void) fflush(stdout);
00227        (void) fprintf(stderr,
00228               _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
00229               progname, zone, abbrp, wp);
00230        warned = TRUE;
00231 }
00232 
00233 int
00234 main(argc, argv)
00235 int    argc;
00236 char * argv[];
00237 {
00238        register int         i;
00239        register int         c;
00240        register int         vflag;
00241        register char *             cutarg;
00242        register long        cutloyear = ZDUMP_LO_YEAR;
00243        register long        cuthiyear = ZDUMP_HI_YEAR;
00244        register time_t             cutlotime;
00245        register time_t             cuthitime;
00246        register char **     fakeenv;
00247        time_t               now;
00248        time_t               t;
00249        time_t               newt;
00250        struct tm            tm;
00251        struct tm            newtm;
00252        register struct tm * tmp;
00253        register struct tm * newtmp;
00254 
00255        INITIALIZE(cutlotime);
00256        INITIALIZE(cuthitime);
00257 #if HAVE_GETTEXT
00258        (void) setlocale(LC_ALL, "");
00259 #ifdef TZ_DOMAINDIR
00260        (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
00261 #endif /* defined TEXTDOMAINDIR */
00262        (void) textdomain(TZ_DOMAIN);
00263 #endif /* HAVE_GETTEXT */
00264        progname = argv[0];
00265        for (i = 1; i < argc; ++i)
00266               if (strcmp(argv[i], "--version") == 0) {
00267                      (void) printf("%s\n", elsieid);
00268                      exit(EXIT_SUCCESS);
00269               }
00270        vflag = 0;
00271        cutarg = NULL;
00272        while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
00273               if (c == 'v')
00274                      vflag = 1;
00275               else   cutarg = optarg;
00276        if ((c != EOF && c != -1) ||
00277               (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
00278                      (void) fprintf(stderr,
00279 _("%s: usage is %s [ --version ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n"),
00280                             progname, progname);
00281                      exit(EXIT_FAILURE);
00282        }
00283        if (vflag) {
00284               if (cutarg != NULL) {
00285                      long   lo;
00286                      long   hi;
00287                      char   dummy;
00288 
00289                      if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
00290                             cuthiyear = hi;
00291                      } else if (sscanf(cutarg, "%ld,%ld%c",
00292                             &lo, &hi, &dummy) == 2) {
00293                                    cutloyear = lo;
00294                                    cuthiyear = hi;
00295                      } else {
00296 (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
00297                                    progname, cutarg);
00298                             exit(EXIT_FAILURE);
00299                      }
00300               }
00301               setabsolutes();
00302               cutlotime = yeartot(cutloyear);
00303               cuthitime = yeartot(cuthiyear);
00304        }
00305        (void) time(&now);
00306        longest = 0;
00307        for (i = optind; i < argc; ++i)
00308               if (strlen(argv[i]) > longest)
00309                      longest = strlen(argv[i]);
00310        {
00311               register int  from;
00312               register int  to;
00313 
00314               for (i = 0; environ[i] != NULL; ++i)
00315                      continue;
00316               fakeenv = (char **) malloc((size_t) ((i + 2) *
00317                      sizeof *fakeenv));
00318               if (fakeenv == NULL ||
00319                      (fakeenv[0] = (char *) malloc(longest + 4)) == NULL) {
00320                                    (void) perror(progname);
00321                                    exit(EXIT_FAILURE);
00322               }
00323               to = 0;
00324               (void) strcpy(fakeenv[to++], "TZ=");
00325               for (from = 0; environ[from] != NULL; ++from)
00326                      if (strncmp(environ[from], "TZ=", 3) != 0)
00327                             fakeenv[to++] = environ[from];
00328               fakeenv[to] = NULL;
00329               environ = fakeenv;
00330        }
00331        for (i = optind; i < argc; ++i) {
00332               static char   buf[MAX_STRING_LENGTH];
00333 
00334               (void) strcpy(&fakeenv[0][3], argv[i]);
00335               if (!vflag) {
00336                      show(argv[i], now, FALSE);
00337                      continue;
00338               }
00339               warned = FALSE;
00340               t = absolute_min_time;
00341               show(argv[i], t, TRUE);
00342               t += SECSPERHOUR * HOURSPERDAY;
00343               show(argv[i], t, TRUE);
00344               if (t < cutlotime)
00345                      t = cutlotime;
00346               tmp = my_localtime(&t);
00347               if (tmp != NULL) {
00348                      tm = *tmp;
00349                      (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
00350               }
00351               for ( ; ; ) {
00352                      if (t >= cuthitime)
00353                             break;
00354                      newt = t + SECSPERHOUR * 12;
00355                      if (newt >= cuthitime)
00356                             break;
00357                      if (newt <= t)
00358                             break;
00359                      newtmp = localtime(&newt);
00360                      if (newtmp != NULL)
00361                             newtm = *newtmp;
00362                      if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
00363                             (delta(&newtm, &tm) != (newt - t) ||
00364                             newtm.tm_isdst != tm.tm_isdst ||
00365                             strcmp(abbr(&newtm), buf) != 0)) {
00366                                    newt = hunt(argv[i], t, newt);
00367                                    newtmp = localtime(&newt);
00368                                    if (newtmp != NULL) {
00369                                           newtm = *newtmp;
00370                                           (void) strncpy(buf,
00371                                                  abbr(&newtm),
00372                                                  (sizeof buf) - 1);
00373                                    }
00374                      }
00375                      t = newt;
00376                      tm = newtm;
00377                      tmp = newtmp;
00378               }
00379               t = absolute_max_time;
00380               t -= SECSPERHOUR * HOURSPERDAY;
00381               show(argv[i], t, TRUE);
00382               t += SECSPERHOUR * HOURSPERDAY;
00383               show(argv[i], t, TRUE);
00384        }
00385        if (fflush(stdout) || ferror(stdout)) {
00386               (void) fprintf(stderr, "%s: ", progname);
00387               (void) perror(_("Error writing to standard output"));
00388               exit(EXIT_FAILURE);
00389        }
00390        exit(EXIT_SUCCESS);
00391        /* If exit fails to exit... */
00392        return EXIT_FAILURE;
00393 }
00394 
00395 static void
00396 setabsolutes(void)
00397 {
00398        if (0.5 == (time_t) 0.5) {
00399               /*
00400               ** time_t is floating.
00401               */
00402               if (sizeof (time_t) == sizeof (float)) {
00403                      absolute_min_time = (time_t) -FLT_MAX;
00404                      absolute_max_time = (time_t) FLT_MAX;
00405               } else if (sizeof (time_t) == sizeof (double)) {
00406                      absolute_min_time = (time_t) -DBL_MAX;
00407                      absolute_max_time = (time_t) DBL_MAX;
00408               } else {
00409                      (void) fprintf(stderr,
00410 _("%s: use of -v on system with floating time_t other than float or double\n"),
00411                             progname);
00412                      exit(EXIT_FAILURE);
00413               }
00414        } else if (0 > (time_t) -1) {
00415               /*
00416               ** time_t is signed.  Assume overflow wraps around.
00417               */
00418               time_t t = 0;
00419               time_t t1 = 1;
00420 
00421               while (t < t1) {
00422                      t = t1;
00423                      t1 = 2 * t1 + 1;
00424               }
00425 
00426               absolute_max_time = t;
00427               t = -t;
00428               absolute_min_time = t - 1;
00429               if (t < absolute_min_time)
00430                      absolute_min_time = t;
00431        } else {
00432               /*
00433               ** time_t is unsigned.
00434               */
00435               absolute_min_time = 0;
00436               absolute_max_time = absolute_min_time - 1;
00437        }
00438 }
00439 
00440 static time_t
00441 yeartot(y)
00442 const long    y;
00443 {
00444        register long myy;
00445        register long seconds;
00446        register time_t      t;
00447 
00448        myy = EPOCH_YEAR;
00449        t = 0;
00450        while (myy != y) {
00451               if (myy < y) {
00452                      seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
00453                      ++myy;
00454                      if (t > absolute_max_time - seconds) {
00455                             t = absolute_max_time;
00456                             break;
00457                      }
00458                      t += seconds;
00459               } else {
00460                      --myy;
00461                      seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
00462                      if (t < absolute_min_time + seconds) {
00463                             t = absolute_min_time;
00464                             break;
00465                      }
00466                      t -= seconds;
00467               }
00468        }
00469        return t;
00470 }
00471 
00472 static time_t
00473 hunt(char *name, time_t lot, time_t hit)
00474 {
00475        time_t               t;
00476        long                 diff;
00477        struct tm            lotm;
00478        register struct tm * lotmp;
00479        struct tm            tm;
00480        register struct tm * tmp;
00481        char                 loab[MAX_STRING_LENGTH];
00482 
00483        lotmp = my_localtime(&lot);
00484        if (lotmp != NULL) {
00485               lotm = *lotmp;
00486               (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
00487        }
00488        for ( ; ; ) {
00489               diff = (long) (hit - lot);
00490               if (diff < 2)
00491                      break;
00492               t = lot;
00493               t += diff / 2;
00494               if (t <= lot)
00495                      ++t;
00496               else if (t >= hit)
00497                      --t;
00498               tmp = my_localtime(&t);
00499               if (tmp != NULL)
00500                      tm = *tmp;
00501               if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
00502                      (delta(&tm, &lotm) == (t - lot) &&
00503                      tm.tm_isdst == lotm.tm_isdst &&
00504                      strcmp(abbr(&tm), loab) == 0)) {
00505                             lot = t;
00506                             lotm = tm;
00507                             lotmp = tmp;
00508               } else hit = t;
00509        }
00510        show(name, lot, TRUE);
00511        show(name, hit, TRUE);
00512        return hit;
00513 }
00514 
00515 /*
00516 ** Thanks to Paul Eggert for logic used in delta.
00517 */
00518 
00519 static long
00520 delta(newp, oldp)
00521 struct tm *   newp;
00522 struct tm *   oldp;
00523 {
00524        register long result;
00525        register int  tmy;
00526 
00527        if (newp->tm_year < oldp->tm_year)
00528               return -delta(oldp, newp);
00529        result = 0;
00530        for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
00531               result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
00532        result += newp->tm_yday - oldp->tm_yday;
00533        result *= HOURSPERDAY;
00534        result += newp->tm_hour - oldp->tm_hour;
00535        result *= MINSPERHOUR;
00536        result += newp->tm_min - oldp->tm_min;
00537        result *= SECSPERMIN;
00538        result += newp->tm_sec - oldp->tm_sec;
00539        return result;
00540 }
00541 
00542 static void
00543 show(char *zone, time_t t, int v)
00544 {
00545        register struct tm * tmp;
00546 
00547        (void) printf("%-*s  ", (int) longest, zone);
00548        if (v) {
00549               tmp = gmtime(&t);
00550               if (tmp == NULL) {
00551                      (void) printf(tformat(), t);
00552               } else {
00553                      dumptime(tmp);
00554                      (void) printf(" UTC");
00555               }
00556               (void) printf(" = ");
00557        }
00558        tmp = my_localtime(&t);
00559        dumptime(tmp);
00560        if (tmp != NULL) {
00561               if (*abbr(tmp) != '\0')
00562                      (void) printf(" %s", abbr(tmp));
00563               if (v) {
00564                      (void) printf(" isdst=%d", tmp->tm_isdst);
00565 #ifdef TM_GMTOFF
00566                      (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
00567 #endif /* defined TM_GMTOFF */
00568               }
00569        }
00570        (void) printf("\n");
00571        if (tmp != NULL && *abbr(tmp) != '\0')
00572               abbrok(abbr(tmp), zone);
00573 }
00574 
00575 static char *
00576 abbr(tmp)
00577 struct tm *   tmp;
00578 {
00579        register char *      result;
00580        static char   nada;
00581 
00582        if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
00583               return &nada;
00584        result = tzname[tmp->tm_isdst];
00585        return (result == NULL) ? &nada : result;
00586 }
00587 
00588 /*
00589 ** The code below can fail on certain theoretical systems;
00590 ** it works on all known real-world systems as of 2004-12-30.
00591 */
00592 
00593 static const char *
00594 tformat(void)
00595 {
00596        if (0.5 == (time_t) 0.5) {  /* floating */
00597               if (sizeof (time_t) > sizeof (double))
00598                      return "%Lg";
00599               return "%g";
00600        }
00601        if (0 > (time_t) -1) {             /* signed */
00602               if (sizeof (time_t) > sizeof (long))
00603                      return "%lld";
00604               if (sizeof (time_t) > sizeof (int))
00605                      return "%ld";
00606               return "%d";
00607        }
00608        if (sizeof (time_t) > sizeof (unsigned long))
00609               return "%llu";
00610        if (sizeof (time_t) > sizeof (unsigned int))
00611               return "%lu";
00612        return "%u";
00613 }
00614 
00615 static void
00616 dumptime(timeptr)
00617 register const struct tm *  timeptr;
00618 {
00619        static const char    wday_name[][3] = {
00620               "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00621        };
00622        static const char    mon_name[][3] = {
00623               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00624               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00625        };
00626        register const char *       wn;
00627        register const char *       mn;
00628        register int         lead;
00629        register int         trail;
00630 
00631        if (timeptr == NULL) {
00632               (void) printf("NULL");
00633               return;
00634        }
00635        /*
00636        ** The packaged versions of localtime and gmtime never put out-of-range
00637        ** values in tm_wday or tm_mon, but since this code might be compiled
00638        ** with other (perhaps experimental) versions, paranoia is in order.
00639        */
00640        if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
00641               (int) (sizeof wday_name / sizeof wday_name[0]))
00642                      wn = "???";
00643        else          wn = wday_name[timeptr->tm_wday];
00644        if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
00645               (int) (sizeof mon_name / sizeof mon_name[0]))
00646                      mn = "???";
00647        else          mn = mon_name[timeptr->tm_mon];
00648        (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
00649               wn, mn,
00650               timeptr->tm_mday, timeptr->tm_hour,
00651               timeptr->tm_min, timeptr->tm_sec);
00652 #define DIVISOR      10
00653        trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
00654        lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
00655               trail / DIVISOR;
00656        trail %= DIVISOR;
00657        if (trail < 0 && lead > 0) {
00658               trail += DIVISOR;
00659               --lead;
00660        } else if (lead < 0 && trail > 0) {
00661               trail -= DIVISOR;
00662               ++lead;
00663        }
00664        if (lead == 0)
00665               (void) printf("%d", trail);
00666        else   (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
00667 }