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