]>
Commit | Line | Data |
---|---|---|
2fc1e207 A |
1 | static const char elsieid[] = "@(#)zdump.c 7.31"; |
2 | ||
3 | #ifndef lint | |
cf37c299 A |
4 | #include <sys/cdefs.h> |
5 | __unused static const char rcsid[] = | |
ef8ad44b | 6 | "$FreeBSD: src/usr.sbin/zic/zdump.c,v 1.10 2008/02/19 07:09:19 ru Exp $"; |
2fc1e207 | 7 | #endif /* not lint */ |
1815bff5 A |
8 | |
9 | /* | |
10 | ** This code has been made independent of the rest of the time | |
11 | ** conversion package to increase confidence in the verification it provides. | |
12 | ** You can use this code to help in verifying other implementations. | |
13 | */ | |
14 | ||
2fc1e207 A |
15 | #include <err.h> |
16 | #include <stdio.h> /* for stdout, stderr */ | |
17 | #include <stdlib.h> /* for exit, malloc, atoi */ | |
18 | #include <string.h> /* for strcpy */ | |
19 | #include <sys/types.h> /* for time_t */ | |
20 | #include <time.h> /* for struct tm */ | |
21 | #include <unistd.h> | |
ef8ad44b | 22 | #include <limits.h> |
1815bff5 A |
23 | |
24 | #ifndef MAX_STRING_LENGTH | |
25 | #define MAX_STRING_LENGTH 1024 | |
26 | #endif /* !defined MAX_STRING_LENGTH */ | |
27 | ||
28 | #ifndef TRUE | |
29 | #define TRUE 1 | |
30 | #endif /* !defined TRUE */ | |
31 | ||
32 | #ifndef FALSE | |
33 | #define FALSE 0 | |
34 | #endif /* !defined FALSE */ | |
35 | ||
36 | #ifndef EXIT_SUCCESS | |
37 | #define EXIT_SUCCESS 0 | |
38 | #endif /* !defined EXIT_SUCCESS */ | |
39 | ||
40 | #ifndef EXIT_FAILURE | |
41 | #define EXIT_FAILURE 1 | |
42 | #endif /* !defined EXIT_FAILURE */ | |
43 | ||
44 | #ifndef SECSPERMIN | |
45 | #define SECSPERMIN 60 | |
46 | #endif /* !defined SECSPERMIN */ | |
47 | ||
48 | #ifndef MINSPERHOUR | |
49 | #define MINSPERHOUR 60 | |
50 | #endif /* !defined MINSPERHOUR */ | |
51 | ||
52 | #ifndef SECSPERHOUR | |
53 | #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) | |
54 | #endif /* !defined SECSPERHOUR */ | |
55 | ||
56 | #ifndef HOURSPERDAY | |
57 | #define HOURSPERDAY 24 | |
58 | #endif /* !defined HOURSPERDAY */ | |
59 | ||
60 | #ifndef EPOCH_YEAR | |
61 | #define EPOCH_YEAR 1970 | |
62 | #endif /* !defined EPOCH_YEAR */ | |
63 | ||
64 | #ifndef TM_YEAR_BASE | |
65 | #define TM_YEAR_BASE 1900 | |
66 | #endif /* !defined TM_YEAR_BASE */ | |
67 | ||
68 | #ifndef DAYSPERNYEAR | |
69 | #define DAYSPERNYEAR 365 | |
70 | #endif /* !defined DAYSPERNYEAR */ | |
71 | ||
72 | #ifndef isleap | |
73 | #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) | |
74 | #endif /* !defined isleap */ | |
75 | ||
76 | #if HAVE_GETTEXT - 0 | |
77 | #include "locale.h" /* for setlocale */ | |
78 | #include "libintl.h" | |
79 | #endif /* HAVE_GETTEXT - 0 */ | |
80 | ||
81 | #ifndef GNUC_or_lint | |
82 | #ifdef lint | |
83 | #define GNUC_or_lint | |
84 | #endif /* defined lint */ | |
85 | #ifndef lint | |
86 | #ifdef __GNUC__ | |
87 | #define GNUC_or_lint | |
88 | #endif /* defined __GNUC__ */ | |
89 | #endif /* !defined lint */ | |
90 | #endif /* !defined GNUC_or_lint */ | |
91 | ||
92 | #ifndef INITIALIZE | |
93 | #ifdef GNUC_or_lint | |
94 | #define INITIALIZE(x) ((x) = 0) | |
95 | #endif /* defined GNUC_or_lint */ | |
96 | #ifndef GNUC_or_lint | |
97 | #define INITIALIZE(x) | |
98 | #endif /* !defined GNUC_or_lint */ | |
99 | #endif /* !defined INITIALIZE */ | |
100 | ||
101 | /* | |
102 | ** For the benefit of GNU folk... | |
103 | ** `_(MSGID)' uses the current locale's message library string for MSGID. | |
104 | ** The default is to use gettext if available, and use MSGID otherwise. | |
105 | */ | |
106 | ||
107 | #ifndef _ | |
108 | #if HAVE_GETTEXT - 0 | |
109 | #define _(msgid) gettext(msgid) | |
110 | #else /* !(HAVE_GETTEXT - 0) */ | |
111 | #define _(msgid) msgid | |
112 | #endif /* !(HAVE_GETTEXT - 0) */ | |
113 | #endif /* !defined _ */ | |
114 | ||
115 | #ifndef TZ_DOMAIN | |
116 | #define TZ_DOMAIN "tz" | |
117 | #endif /* !defined TZ_DOMAIN */ | |
118 | ||
2fc1e207 A |
119 | #ifndef P |
120 | #ifdef __STDC__ | |
121 | #define P(x) x | |
122 | #endif /* defined __STDC__ */ | |
123 | #ifndef __STDC__ | |
124 | #define P(x) () | |
125 | #endif /* !defined __STDC__ */ | |
126 | #endif /* !defined P */ | |
127 | ||
1815bff5 | 128 | extern char ** environ; |
1815bff5 A |
129 | extern char * tzname[2]; |
130 | ||
2fc1e207 A |
131 | static char * abbr P((struct tm * tmp)); |
132 | static long delta P((struct tm * newp, struct tm * oldp)); | |
133 | static time_t hunt P((char * name, time_t lot, time_t hit)); | |
134 | static size_t longest; | |
135 | static void show P((char * zone, time_t t, int v)); | |
136 | static void usage(void); | |
1815bff5 A |
137 | |
138 | int | |
cf37c299 | 139 | main(int argc, char *argv[]) |
1815bff5 | 140 | { |
cf37c299 A |
141 | int i; |
142 | int c; | |
143 | int vflag; | |
144 | char * cutoff; | |
145 | int cutyear; | |
146 | long cuttime; | |
1815bff5 A |
147 | char ** fakeenv; |
148 | time_t now; | |
149 | time_t t; | |
150 | time_t newt; | |
ef8ad44b A |
151 | #ifndef __APPLE__ |
152 | // <rdar://problem/6013740> | |
153 | // The approach of walking through every day from the minimum | |
154 | // possible time_t value to the maximum possible time_t value | |
155 | // falls apart with 64-bit time_t (takes too long to iterate, | |
156 | // and causes gmtime(3) and localtime(3) to return EOVERFLOW | |
157 | // which this code does not anticipate). Limiting the time_t | |
158 | // range to [INT_MIN:INT_MAX] even on LP64. | |
1815bff5 | 159 | time_t hibit; |
ef8ad44b | 160 | #endif |
1815bff5 A |
161 | struct tm tm; |
162 | struct tm newtm; | |
163 | ||
164 | INITIALIZE(cuttime); | |
165 | #if HAVE_GETTEXT - 0 | |
166 | (void) setlocale(LC_MESSAGES, ""); | |
167 | #ifdef TZ_DOMAINDIR | |
168 | (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); | |
169 | #endif /* defined(TEXTDOMAINDIR) */ | |
170 | (void) textdomain(TZ_DOMAIN); | |
171 | #endif /* HAVE_GETTEXT - 0 */ | |
2fc1e207 A |
172 | for (i = 1; i < argc; ++i) |
173 | if (strcmp(argv[i], "--version") == 0) { | |
174 | errx(EXIT_SUCCESS, "%s", elsieid); | |
175 | } | |
1815bff5 A |
176 | vflag = 0; |
177 | cutoff = NULL; | |
178 | while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') | |
179 | if (c == 'v') | |
180 | vflag = 1; | |
181 | else cutoff = optarg; | |
ef8ad44b | 182 | if ((c != -1) || |
1815bff5 | 183 | (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { |
2fc1e207 | 184 | usage(); |
1815bff5 A |
185 | } |
186 | if (cutoff != NULL) { | |
187 | int y; | |
188 | ||
189 | cutyear = atoi(cutoff); | |
190 | cuttime = 0; | |
191 | for (y = EPOCH_YEAR; y < cutyear; ++y) | |
192 | cuttime += DAYSPERNYEAR + isleap(y); | |
193 | cuttime *= SECSPERHOUR * HOURSPERDAY; | |
194 | } | |
195 | (void) time(&now); | |
196 | longest = 0; | |
197 | for (i = optind; i < argc; ++i) | |
198 | if (strlen(argv[i]) > longest) | |
199 | longest = strlen(argv[i]); | |
ef8ad44b | 200 | #ifndef __APPLE__ |
1815bff5 A |
201 | for (hibit = 1; (hibit << 1) != 0; hibit <<= 1) |
202 | continue; | |
ef8ad44b | 203 | #endif |
1815bff5 | 204 | { |
cf37c299 A |
205 | int from; |
206 | int to; | |
1815bff5 A |
207 | |
208 | for (i = 0; environ[i] != NULL; ++i) | |
209 | continue; | |
210 | fakeenv = (char **) malloc((size_t) ((i + 2) * | |
211 | sizeof *fakeenv)); | |
212 | if (fakeenv == NULL || | |
213 | (fakeenv[0] = (char *) malloc((size_t) (longest + | |
2fc1e207 A |
214 | 4))) == NULL) |
215 | errx(EXIT_FAILURE, | |
216 | _("malloc() failed")); | |
1815bff5 A |
217 | to = 0; |
218 | (void) strcpy(fakeenv[to++], "TZ="); | |
219 | for (from = 0; environ[from] != NULL; ++from) | |
220 | if (strncmp(environ[from], "TZ=", 3) != 0) | |
221 | fakeenv[to++] = environ[from]; | |
222 | fakeenv[to] = NULL; | |
223 | environ = fakeenv; | |
224 | } | |
225 | for (i = optind; i < argc; ++i) { | |
226 | static char buf[MAX_STRING_LENGTH]; | |
227 | ||
228 | (void) strcpy(&fakeenv[0][3], argv[i]); | |
2fc1e207 A |
229 | if (!vflag) { |
230 | show(argv[i], now, FALSE); | |
1815bff5 | 231 | continue; |
2fc1e207 | 232 | } |
1815bff5 A |
233 | /* |
234 | ** Get lowest value of t. | |
235 | */ | |
ef8ad44b A |
236 | #ifdef __APPLE__ |
237 | t = INT_MIN; | |
238 | #else | |
1815bff5 A |
239 | t = hibit; |
240 | if (t > 0) /* time_t is unsigned */ | |
241 | t = 0; | |
ef8ad44b | 242 | #endif |
1815bff5 A |
243 | show(argv[i], t, TRUE); |
244 | t += SECSPERHOUR * HOURSPERDAY; | |
245 | show(argv[i], t, TRUE); | |
246 | tm = *localtime(&t); | |
247 | (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); | |
248 | for ( ; ; ) { | |
249 | if (cutoff != NULL && t >= cuttime) | |
250 | break; | |
251 | newt = t + SECSPERHOUR * 12; | |
252 | if (cutoff != NULL && newt >= cuttime) | |
253 | break; | |
ef8ad44b A |
254 | #ifdef __APPLE__ |
255 | if (newt > INT_MAX) | |
256 | break; | |
257 | #else | |
1815bff5 A |
258 | if (newt <= t) |
259 | break; | |
ef8ad44b | 260 | #endif |
1815bff5 A |
261 | newtm = *localtime(&newt); |
262 | if (delta(&newtm, &tm) != (newt - t) || | |
263 | newtm.tm_isdst != tm.tm_isdst || | |
264 | strcmp(abbr(&newtm), buf) != 0) { | |
265 | newt = hunt(argv[i], t, newt); | |
266 | newtm = *localtime(&newt); | |
267 | (void) strncpy(buf, abbr(&newtm), | |
268 | (sizeof buf) - 1); | |
269 | } | |
270 | t = newt; | |
271 | tm = newtm; | |
272 | } | |
273 | /* | |
274 | ** Get highest value of t. | |
275 | */ | |
ef8ad44b A |
276 | #ifdef __APPLE__ |
277 | t = INT_MAX; | |
278 | #else | |
1815bff5 A |
279 | t = ~((time_t) 0); |
280 | if (t < 0) /* time_t is signed */ | |
281 | t &= ~hibit; | |
ef8ad44b | 282 | #endif |
1815bff5 A |
283 | t -= SECSPERHOUR * HOURSPERDAY; |
284 | show(argv[i], t, TRUE); | |
285 | t += SECSPERHOUR * HOURSPERDAY; | |
286 | show(argv[i], t, TRUE); | |
287 | } | |
2fc1e207 A |
288 | if (fflush(stdout) || ferror(stdout)) |
289 | errx(EXIT_FAILURE, _("error writing standard output")); | |
1815bff5 A |
290 | exit(EXIT_SUCCESS); |
291 | ||
292 | /* gcc -Wall pacifier */ | |
293 | for ( ; ; ) | |
294 | continue; | |
295 | } | |
296 | ||
2fc1e207 A |
297 | static void |
298 | usage(void) | |
299 | { | |
300 | fprintf(stderr, | |
301 | _("usage: zdump [--version] [-v] [-c cutoff] zonename ...\n")); | |
302 | exit(EXIT_FAILURE); | |
303 | } | |
304 | ||
1815bff5 | 305 | static time_t |
cf37c299 | 306 | hunt(char *name, time_t lot, time_t hit) |
1815bff5 A |
307 | { |
308 | time_t t; | |
309 | struct tm lotm; | |
310 | struct tm tm; | |
311 | static char loab[MAX_STRING_LENGTH]; | |
312 | ||
313 | lotm = *localtime(&lot); | |
314 | (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); | |
1815bff5 A |
315 | while ((hit - lot) >= 2) { |
316 | t = lot / 2 + hit / 2; | |
317 | if (t <= lot) | |
318 | ++t; | |
319 | else if (t >= hit) | |
320 | --t; | |
321 | tm = *localtime(&t); | |
322 | if (delta(&tm, &lotm) == (t - lot) && | |
323 | tm.tm_isdst == lotm.tm_isdst && | |
324 | strcmp(abbr(&tm), loab) == 0) { | |
325 | lot = t; | |
326 | lotm = tm; | |
327 | } else hit = t; | |
328 | } | |
329 | show(name, lot, TRUE); | |
330 | show(name, hit, TRUE); | |
331 | return hit; | |
332 | } | |
333 | ||
334 | /* | |
335 | ** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta. | |
336 | */ | |
337 | ||
338 | static long | |
cf37c299 | 339 | delta(struct tm *newp, struct tm *oldp) |
1815bff5 A |
340 | { |
341 | long result; | |
342 | int tmy; | |
343 | ||
344 | if (newp->tm_year < oldp->tm_year) | |
345 | return -delta(oldp, newp); | |
346 | result = 0; | |
347 | for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) | |
348 | result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE); | |
349 | result += newp->tm_yday - oldp->tm_yday; | |
350 | result *= HOURSPERDAY; | |
351 | result += newp->tm_hour - oldp->tm_hour; | |
352 | result *= MINSPERHOUR; | |
353 | result += newp->tm_min - oldp->tm_min; | |
354 | result *= SECSPERMIN; | |
355 | result += newp->tm_sec - oldp->tm_sec; | |
356 | return result; | |
357 | } | |
358 | ||
1815bff5 | 359 | static void |
cf37c299 | 360 | show(char *zone, time_t t, int v) |
1815bff5 A |
361 | { |
362 | struct tm * tmp; | |
363 | ||
2fc1e207 | 364 | (void) printf("%-*s ", (int) longest, zone); |
1815bff5 | 365 | if (v) |
2fc1e207 | 366 | (void) printf("%.24s UTC = ", asctime(gmtime(&t))); |
1815bff5 A |
367 | tmp = localtime(&t); |
368 | (void) printf("%.24s", asctime(tmp)); | |
369 | if (*abbr(tmp) != '\0') | |
370 | (void) printf(" %s", abbr(tmp)); | |
371 | if (v) { | |
372 | (void) printf(" isdst=%d", tmp->tm_isdst); | |
373 | #ifdef TM_GMTOFF | |
374 | (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); | |
375 | #endif /* defined TM_GMTOFF */ | |
376 | } | |
377 | (void) printf("\n"); | |
378 | } | |
379 | ||
380 | static char * | |
cf37c299 | 381 | abbr(struct tm *tmp) |
1815bff5 | 382 | { |
cf37c299 | 383 | char * result; |
1815bff5 A |
384 | static char nada; |
385 | ||
386 | if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) | |
387 | return &nada; | |
388 | result = tzname[tmp->tm_isdst]; | |
389 | return (result == NULL) ? &nada : result; | |
390 | } |