zdump.c revision 1.34 1 /* $NetBSD: zdump.c,v 1.34 2014/08/15 11:04:07 christos Exp $ */
2 /*
3 ** This file is in the public domain, so clarified as of
4 ** 2009-05-17 by Arthur David Olson.
5 */
6
7 #include <sys/cdefs.h>
8 #ifndef lint
9 __RCSID("$NetBSD: zdump.c,v 1.34 2014/08/15 11:04:07 christos Exp $");
10 #endif /* !defined lint */
11
12 #include "version.h"
13 /*
14 ** This code has been made independent of the rest of the time
15 ** conversion package to increase confidence in the verification it provides.
16 ** You can use this code to help in verifying other implementations.
17 **
18 ** However, include private.h when debugging, so that it overrides
19 ** time_t consistently with the rest of the package.
20 */
21
22 #include "private.h"
23
24 #include "stdio.h" /* for stdout, stderr */
25 #include "string.h" /* for strcpy */
26 #include "sys/types.h" /* for time_t */
27 #include "time.h" /* for struct tm */
28 #include "stdlib.h" /* for exit, malloc, atoi */
29 #include <err.h>
30
31 /*
32 ** Substitutes for pre-C99 compilers.
33 ** Much of this section of code is stolen from private.h.
34 */
35
36 #ifndef HAVE_STDINT_H
37 # define HAVE_STDINT_H \
38 (199901 <= __STDC_VERSION__ \
39 || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
40 || __CYGWIN__)
41 #endif
42 #if HAVE_STDINT_H
43 # include "stdint.h"
44 #endif
45 #ifndef HAVE_INTTYPES_H
46 # define HAVE_INTTYPES_H HAVE_STDINT_H
47 #endif
48 #if HAVE_INTTYPES_H
49 # include <inttypes.h>
50 #endif
51
52 #ifndef INT_FAST32_MAX
53 # if INT_MAX >> 31 == 0
54 typedef long int_fast32_t;
55 # else
56 typedef int int_fast32_t;
57 # endif
58 #endif
59
60 #ifndef INTMAX_MAX
61 # if defined LLONG_MAX || defined __LONG_LONG_MAX__
62 typedef long long intmax_t;
63 # define strtoimax strtoll
64 # define PRIdMAX "lld"
65 # ifdef LLONG_MAX
66 # define INTMAX_MAX LLONG_MAX
67 # else
68 # define INTMAX_MAX __LONG_LONG_MAX__
69 # endif
70 # else
71 typedef long intmax_t;
72 # define strtoimax strtol
73 # define PRIdMAX "ld"
74 # define INTMAX_MAX LONG_MAX
75 # endif
76 #endif
77
78
79 #ifndef ZDUMP_LO_YEAR
80 #define ZDUMP_LO_YEAR (-500)
81 #endif /* !defined ZDUMP_LO_YEAR */
82
83 #ifndef ZDUMP_HI_YEAR
84 #define ZDUMP_HI_YEAR 2500
85 #endif /* !defined ZDUMP_HI_YEAR */
86
87 #ifndef MAX_STRING_LENGTH
88 #define MAX_STRING_LENGTH 1024
89 #endif /* !defined MAX_STRING_LENGTH */
90
91 #ifndef TRUE
92 #define TRUE 1
93 #endif /* !defined TRUE */
94
95 #ifndef FALSE
96 #define FALSE 0
97 #endif /* !defined FALSE */
98
99 #ifndef EXIT_SUCCESS
100 #define EXIT_SUCCESS 0
101 #endif /* !defined EXIT_SUCCESS */
102
103 #ifndef EXIT_FAILURE
104 #define EXIT_FAILURE 1
105 #endif /* !defined EXIT_FAILURE */
106
107 #ifndef SECSPERMIN
108 #define SECSPERMIN 60
109 #endif /* !defined SECSPERMIN */
110
111 #ifndef MINSPERHOUR
112 #define MINSPERHOUR 60
113 #endif /* !defined MINSPERHOUR */
114
115 #ifndef SECSPERHOUR
116 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
117 #endif /* !defined SECSPERHOUR */
118
119 #ifndef HOURSPERDAY
120 #define HOURSPERDAY 24
121 #endif /* !defined HOURSPERDAY */
122
123 #ifndef EPOCH_YEAR
124 #define EPOCH_YEAR 1970
125 #endif /* !defined EPOCH_YEAR */
126
127 #ifndef TM_YEAR_BASE
128 #define TM_YEAR_BASE 1900
129 #endif /* !defined TM_YEAR_BASE */
130
131 #ifndef DAYSPERNYEAR
132 #define DAYSPERNYEAR 365
133 #endif /* !defined DAYSPERNYEAR */
134
135 #ifndef isleap
136 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
137 #endif /* !defined isleap */
138
139 #ifndef isleap_sum
140 /*
141 ** See tzfile.h for details on isleap_sum.
142 */
143 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
144 #endif /* !defined isleap_sum */
145
146 #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
147 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
148 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
149 #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
150 + SECSPERLYEAR * (intmax_t) (100 - 3))
151
152 /*
153 ** True if SECSPER400YEARS is known to be representable as an
154 ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
155 ** even if SECSPER400YEARS is representable, because when that happens
156 ** the code merely runs a bit more slowly, and this slowness doesn't
157 ** occur on any practical platform.
158 */
159 enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
160
161 #ifndef HAVE_GETTEXT
162 #define HAVE_GETTEXT 0
163 #endif
164 #if HAVE_GETTEXT
165 #include "locale.h" /* for setlocale */
166 #include "libintl.h"
167 #endif /* HAVE_GETTEXT */
168
169 #ifndef ATTRIBUTE_PURE
170 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
171 # define ATTRIBUTE_PURE __attribute__ ((ATTRIBUTE_PURE__))
172 #else
173 # define ATTRIBUTE_PURE /* empty */
174 #endif
175 #endif
176
177 #ifndef INITIALIZE
178 #ifdef GNUC_or_lint
179 #define INITIALIZE(x) ((x) = 0)
180 #else /* !defined GNUC_or_lint */
181 #define INITIALIZE(x)
182 #endif /* !defined GNUC_or_lint */
183 #endif /* !defined INITIALIZE */
184
185 /*
186 ** For the benefit of GNU folk...
187 ** '_(MSGID)' uses the current locale's message library string for MSGID.
188 ** The default is to use gettext if available, and use MSGID otherwise.
189 */
190
191 #ifndef _
192 #if HAVE_GETTEXT
193 #define _(msgid) gettext(msgid)
194 #else /* !HAVE_GETTEXT */
195 #define _(msgid) msgid
196 #endif /* !HAVE_GETTEXT */
197 #endif /* !defined _ */
198
199 #if !defined TZ_DOMAIN && defined HAVE_GETTEXT
200 # define TZ_DOMAIN "tz"
201 #endif
202
203 extern char ** environ;
204 extern int getopt(int argc, char * const argv[],
205 const char * options);
206 extern char * optarg;
207 extern int optind;
208
209 /* The minimum and maximum finite time values. */
210 static time_t absolute_min_time =
211 ((time_t) -1 < 0
212 ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
213 : 0);
214 static time_t absolute_max_time =
215 ((time_t) -1 < 0
216 ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
217 : -1);
218 static size_t longest;
219 static char * progname;
220 static int warned;
221
222 static const char * abbr(struct tm * tmp);
223 static void abbrok(const char * abbrp, const char * zone);
224 static intmax_t delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE;
225 static void dumptime(const struct tm * tmp);
226 static time_t hunt(char * name, time_t lot, time_t hit);
227 static void show(char * zone, time_t t, int v);
228 static const char * tformat(void);
229 static time_t yeartot(long y) ATTRIBUTE_PURE;
230
231 /* Is A an alphabetic character in the C locale? */
232 static int
233 is_alpha(char a)
234 {
235 switch (a) {
236 default:
237 return 0;
238 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
239 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
240 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
241 case 'V': case 'W': case 'X': case 'Y': case 'Z':
242 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
243 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
244 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
245 case 'v': case 'w': case 'x': case 'y': case 'z':
246 return 1;
247 }
248 }
249
250 #ifndef TYPECHECK
251 #define my_localtime localtime
252 #else /* !defined TYPECHECK */
253 static struct tm *
254 my_localtime(time_t *tp)
255 {
256 struct tm *tmp;
257
258 tmp = localtime(tp);
259 if (tp != NULL && tmp != NULL) {
260 struct tm tm;
261 time_t t;
262
263 tm = *tmp;
264 t = mktime(&tm);
265 if (t != *tp) {
266 (void) fflush(stdout);
267 (void) fprintf(stderr, "\n%s: ", progname);
268 (void) fprintf(stderr, tformat(), *tp);
269 (void) fprintf(stderr, " ->");
270 (void) fprintf(stderr, " year=%d", tmp->tm_year);
271 (void) fprintf(stderr, " mon=%d", tmp->tm_mon);
272 (void) fprintf(stderr, " mday=%d", tmp->tm_mday);
273 (void) fprintf(stderr, " hour=%d", tmp->tm_hour);
274 (void) fprintf(stderr, " min=%d", tmp->tm_min);
275 (void) fprintf(stderr, " sec=%d", tmp->tm_sec);
276 (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
277 (void) fprintf(stderr, " -> ");
278 (void) fprintf(stderr, tformat(), t);
279 (void) fprintf(stderr, "\n");
280 }
281 }
282 return tmp;
283 }
284 #endif /* !defined TYPECHECK */
285
286 static void
287 abbrok(const char *const abbrp, const char *const zone)
288 {
289 const char *cp;
290 const char *wp;
291
292 if (warned)
293 return;
294 cp = abbrp;
295 wp = NULL;
296 while (is_alpha(*cp))
297 ++cp;
298 if (cp - abbrp == 0)
299 wp = _("lacks alphabetic at start");
300 else if (cp - abbrp < 3)
301 wp = _("has fewer than 3 alphabetics");
302 else if (cp - abbrp > 6)
303 wp = _("has more than 6 alphabetics");
304 if (wp == NULL && (*cp == '+' || *cp == '-')) {
305 ++cp;
306 if ('0' <= *cp && *cp <= '9')
307 if (*cp++ == '1' && '0' <= *cp && *cp <= '4')
308 cp++;
309 if (*cp != '\0')
310 wp = _("differs from POSIX standard");
311 }
312 if (wp == NULL)
313 return;
314 (void) fflush(stdout);
315 (void) fprintf(stderr,
316 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
317 progname, zone, abbrp, wp);
318 warned = TRUE;
319 }
320
321 __dead static void
322 usage(FILE *const stream, const int status)
323 {
324 (void) fprintf(stream,
325 _("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
326 "\n"
327 "Report bugs to %s.\n"),
328 progname, progname, REPORT_BUGS_TO);
329 exit(status);
330 }
331
332 int
333 main(int argc, char *argv[])
334 {
335 int i;
336 int vflag;
337 int Vflag;
338 char * cutarg;
339 char * cuttimes;
340 time_t cutlotime;
341 time_t cuthitime;
342 char ** fakeenv;
343 time_t now;
344 time_t t;
345 time_t newt;
346 struct tm tm;
347 struct tm newtm;
348 struct tm * tmp;
349 struct tm * newtmp;
350
351 cutlotime = absolute_min_time;
352 cuthitime = absolute_max_time;
353 #if HAVE_GETTEXT
354 (void) setlocale(LC_ALL, "");
355 #ifdef TZ_DOMAINDIR
356 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
357 #endif /* defined TEXTDOMAINDIR */
358 (void) textdomain(TZ_DOMAIN);
359 #endif /* HAVE_GETTEXT */
360 progname = argv[0];
361 for (i = 1; i < argc; ++i)
362 if (strcmp(argv[i], "--version") == 0) {
363 (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION);
364 exit(EXIT_SUCCESS);
365 } else if (strcmp(argv[i], "--help") == 0) {
366 usage(stdout, EXIT_SUCCESS);
367 }
368 vflag = Vflag = 0;
369 cutarg = cuttimes = NULL;
370 for (;;)
371 switch (getopt(argc, argv, "c:t:vV")) {
372 case 'c': cutarg = optarg; break;
373 case 't': cuttimes = optarg; break;
374 case 'v': vflag = 1; break;
375 case 'V': Vflag = 1; break;
376 case -1:
377 if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
378 goto arg_processing_done;
379 /* Fall through. */
380 default:
381 usage(stderr, EXIT_FAILURE);
382 }
383 arg_processing_done:;
384
385 if (vflag | Vflag) {
386 intmax_t lo;
387 intmax_t hi;
388 char *loend, *hiend;
389 intmax_t cutloyear = ZDUMP_LO_YEAR;
390 intmax_t cuthiyear = ZDUMP_HI_YEAR;
391 if (cutarg != NULL) {
392 lo = strtoimax(cutarg, &loend, 10);
393 if (cutarg != loend && !*loend) {
394 hi = lo;
395 cuthiyear = hi;
396 } else if (cutarg != loend && *loend == ','
397 && (hi = strtoimax(loend + 1, &hiend, 10),
398 loend + 1 != hiend && !*hiend)) {
399 cutloyear = lo;
400 cuthiyear = hi;
401 } else {
402 (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
403 progname, cutarg);
404 exit(EXIT_FAILURE);
405 }
406 }
407 if (cutarg != NULL || cuttimes == NULL) {
408 cutlotime = yeartot(cutloyear);
409 cuthitime = yeartot(cuthiyear);
410 }
411 if (cuttimes != NULL) {
412 lo = strtoimax(cuttimes, &loend, 10);
413 if (cuttimes != loend && !*loend) {
414 hi = lo;
415 if (hi < cuthitime) {
416 if (hi < absolute_min_time)
417 hi = absolute_min_time;
418 cuthitime = hi;
419 }
420 } else if (cuttimes != loend && *loend == ','
421 && (hi = strtoimax(loend + 1, &hiend, 10),
422 loend + 1 != hiend && !*hiend)) {
423 if (cutlotime < lo) {
424 if (absolute_max_time < lo)
425 lo = absolute_max_time;
426 cutlotime = lo;
427 }
428 if (hi < cuthitime) {
429 if (hi < absolute_min_time)
430 hi = absolute_min_time;
431 cuthitime = hi;
432 }
433 } else {
434 (void) fprintf(stderr,
435 _("%s: wild -t argument %s\n"),
436 progname, cuttimes);
437 exit(EXIT_FAILURE);
438 }
439 }
440 }
441 (void) time(&now);
442 longest = 0;
443 for (i = optind; i < argc; ++i)
444 if (strlen(argv[i]) > longest)
445 longest = strlen(argv[i]);
446 {
447 int from;
448 int to;
449
450 for (i = 0; environ[i] != NULL; ++i)
451 continue;
452 fakeenv = malloc((i + 2) * sizeof *fakeenv);
453 if (fakeenv == NULL ||
454 (fakeenv[0] = malloc(longest + 4)) == NULL) {
455 err(EXIT_FAILURE, "Can't allocated %zu bytes",
456 longest + 4);
457 }
458 to = 0;
459 (void)strcpy(fakeenv[to++], "TZ="); /* XXX strcpy is safe */
460 for (from = 0; environ[from] != NULL; ++from)
461 if (strncmp(environ[from], "TZ=", 3) != 0)
462 fakeenv[to++] = environ[from];
463 fakeenv[to] = NULL;
464 environ = fakeenv;
465 }
466 for (i = optind; i < argc; ++i) {
467 static char buf[MAX_STRING_LENGTH];
468
469 (void) strcpy(&fakeenv[0][3], argv[i]); /* XXX strcpy is safe */
470 if (! (vflag | Vflag)) {
471 show(argv[i], now, FALSE);
472 continue;
473 }
474 warned = FALSE;
475 t = absolute_min_time;
476 if (!Vflag) {
477 show(argv[i], t, TRUE);
478 t += SECSPERDAY;
479 show(argv[i], t, TRUE);
480 }
481 if (t < cutlotime)
482 t = cutlotime;
483 tmp = my_localtime(&t);
484 if (tmp != NULL) {
485 tm = *tmp;
486 (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
487 }
488 for ( ; ; ) {
489 newt = (t < absolute_max_time - SECSPERDAY / 2
490 ? t + SECSPERDAY / 2
491 : absolute_max_time);
492 if (cuthitime <= newt)
493 break;
494 newtmp = localtime(&newt);
495 if (newtmp != NULL)
496 newtm = *newtmp;
497 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
498 (delta(&newtm, &tm) != (newt - t) ||
499 newtm.tm_isdst != tm.tm_isdst ||
500 strcmp(abbr(&newtm), buf) != 0)) {
501 newt = hunt(argv[i], t, newt);
502 newtmp = localtime(&newt);
503 if (newtmp != NULL) {
504 newtm = *newtmp;
505 (void) strncpy(buf,
506 abbr(&newtm),
507 (sizeof buf) - 1);
508 }
509 }
510 t = newt;
511 tm = newtm;
512 tmp = newtmp;
513 }
514 if (!Vflag) {
515 t = absolute_max_time;
516 t -= SECSPERDAY;
517 show(argv[i], t, TRUE);
518 t += SECSPERDAY;
519 show(argv[i], t, TRUE);
520 }
521 }
522 if (fflush(stdout) || ferror(stdout)) {
523 err(EXIT_FAILURE, _("Error writing standard output"));
524 }
525 exit(EXIT_SUCCESS);
526 /* If exit fails to exit... */
527 return EXIT_FAILURE;
528 }
529
530 static time_t
531 yeartot(const long y)
532 {
533 intmax_t myy, seconds, years;
534 time_t t;
535
536 myy = EPOCH_YEAR;
537 t = 0;
538 while (myy < y) {
539 if (SECSPER400YEARS_FITS && 400 <= y - myy) {
540 intmax_t diff400 = (y - myy) / 400;
541 if (INTMAX_MAX / SECSPER400YEARS < diff400)
542 return absolute_max_time;
543 seconds = diff400 * SECSPER400YEARS;
544 years = diff400 * 400;
545 } else {
546 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
547 years = 1;
548 }
549 myy += years;
550 if (t > absolute_max_time - seconds)
551 return absolute_max_time;
552 t += seconds;
553 }
554 while (y < myy) {
555 if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
556 intmax_t diff400 = (myy - y) / 400;
557 if (INTMAX_MAX / SECSPER400YEARS < diff400)
558 return absolute_min_time;
559 seconds = diff400 * SECSPER400YEARS;
560 years = diff400 * 400;
561 } else {
562 seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
563 years = 1;
564 }
565 myy -= years;
566 if (t < absolute_min_time + seconds)
567 return absolute_min_time;
568 t -= seconds;
569 }
570 return t;
571 }
572
573 static time_t
574 hunt(char *name, time_t lot, time_t hit)
575 {
576 time_t t;
577 struct tm lotm;
578 struct tm * lotmp;
579 struct tm tm;
580 struct tm * tmp;
581 char loab[MAX_STRING_LENGTH];
582
583 lotmp = my_localtime(&lot);
584 if (lotmp != NULL) {
585 lotm = *lotmp;
586 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
587 }
588 for ( ; ; ) {
589 time_t diff = hit - lot;
590 if (diff < 2)
591 break;
592 t = lot;
593 t += diff / 2;
594 if (t <= lot)
595 ++t;
596 else if (t >= hit)
597 --t;
598 tmp = my_localtime(&t);
599 if (tmp != NULL)
600 tm = *tmp;
601 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
602 (delta(&tm, &lotm) == (t - lot) &&
603 tm.tm_isdst == lotm.tm_isdst &&
604 strcmp(abbr(&tm), loab) == 0)) {
605 lot = t;
606 lotm = tm;
607 lotmp = tmp;
608 } else hit = t;
609 }
610 show(name, lot, TRUE);
611 show(name, hit, TRUE);
612 return hit;
613 }
614
615 /*
616 ** Thanks to Paul Eggert for logic used in delta.
617 */
618
619 static intmax_t
620 delta(struct tm *newp, struct tm *oldp)
621 {
622 intmax_t result;
623 int tmy;
624
625 if (newp->tm_year < oldp->tm_year)
626 return -delta(oldp, newp);
627 result = 0;
628 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
629 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
630 result += newp->tm_yday - oldp->tm_yday;
631 result *= HOURSPERDAY;
632 result += newp->tm_hour - oldp->tm_hour;
633 result *= MINSPERHOUR;
634 result += newp->tm_min - oldp->tm_min;
635 result *= SECSPERMIN;
636 result += newp->tm_sec - oldp->tm_sec;
637 return result;
638 }
639
640 static void
641 show(char *zone, time_t t, int v)
642 {
643 struct tm * tmp;
644
645 (void) printf("%-*s ", (int) longest, zone);
646 if (v) {
647 tmp = gmtime(&t);
648 if (tmp == NULL) {
649 (void) printf(tformat(), t);
650 } else {
651 dumptime(tmp);
652 (void) printf(" UT");
653 }
654 (void) printf(" = ");
655 }
656 tmp = my_localtime(&t);
657 dumptime(tmp);
658 if (tmp != NULL) {
659 if (*abbr(tmp) != '\0')
660 (void) printf(" %s", abbr(tmp));
661 if (v) {
662 (void) printf(" isdst=%d", tmp->tm_isdst);
663 #ifdef TM_GMTOFF
664 (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
665 #endif /* defined TM_GMTOFF */
666 }
667 }
668 (void) printf("\n");
669 if (tmp != NULL && *abbr(tmp) != '\0')
670 abbrok(abbr(tmp), zone);
671 }
672
673 static const char *
674 abbr(struct tm *tmp)
675 {
676 const char * result;
677 static const char nada;
678
679 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
680 return &nada;
681 result = tzname[tmp->tm_isdst];
682 return (result == NULL) ? &nada : result;
683 }
684
685 /*
686 ** The code below can fail on certain theoretical systems;
687 ** it works on all known real-world systems as of 2004-12-30.
688 */
689
690 static const char *
691 tformat(void)
692 {
693 if (0 > (time_t) -1) { /* signed */
694 if (sizeof (time_t) == sizeof (intmax_t))
695 return "%"PRIdMAX;
696 if (sizeof (time_t) > sizeof (long))
697 return "%lld";
698 if (sizeof (time_t) > sizeof (int))
699 return "%ld";
700 return "%d";
701 }
702 #ifdef PRIuMAX
703 if (sizeof (time_t) == sizeof (uintmax_t))
704 return "%"PRIuMAX;
705 #endif
706 if (sizeof (time_t) > sizeof (unsigned long))
707 return "%llu";
708 if (sizeof (time_t) > sizeof (unsigned int))
709 return "%lu";
710 return "%u";
711 }
712
713 static void
714 dumptime(const struct tm *timeptr)
715 {
716 static const char wday_name[][3] = {
717 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
718 };
719 static const char mon_name[][3] = {
720 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
721 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
722 };
723 const char * wn;
724 const char * mn;
725 int lead;
726 int trail;
727
728 if (timeptr == NULL) {
729 (void) printf("NULL");
730 return;
731 }
732 /*
733 ** The packaged versions of localtime and gmtime never put out-of-range
734 ** values in tm_wday or tm_mon, but since this code might be compiled
735 ** with other (perhaps experimental) versions, paranoia is in order.
736 */
737 if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
738 (int) (sizeof wday_name / sizeof wday_name[0]))
739 wn = "???";
740 else wn = wday_name[timeptr->tm_wday];
741 if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
742 (int) (sizeof mon_name / sizeof mon_name[0]))
743 mn = "???";
744 else mn = mon_name[timeptr->tm_mon];
745 (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
746 wn, mn,
747 timeptr->tm_mday, timeptr->tm_hour,
748 timeptr->tm_min, timeptr->tm_sec);
749 #define DIVISOR 10
750 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
751 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
752 trail / DIVISOR;
753 trail %= DIVISOR;
754 if (trail < 0 && lead > 0) {
755 trail += DIVISOR;
756 --lead;
757 } else if (lead < 0 && trail > 0) {
758 trail -= DIVISOR;
759 ++lead;
760 }
761 if (lead == 0)
762 (void) printf("%d", trail);
763 else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
764 }
765