GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/zdump/zdump.c Lines: 0 219 0.0 %
Date: 2017-11-13 Branches: 0 167 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: zdump.c,v 1.14 2016/03/15 19:50:48 millert 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
/*
8
** This code has been made independent of the rest of the time
9
** conversion package to increase confidence in the verification it provides.
10
** You can use this code to help in verifying other implementations.
11
*/
12
13
#include <ctype.h>
14
#include <stdio.h>
15
#include <string.h>
16
#include <stdlib.h>
17
#include <unistd.h>
18
#include <time.h>
19
20
#define ZDUMP_LO_YEAR	(-500)
21
#define ZDUMP_HI_YEAR	2500
22
23
#define MAX_STRING_LENGTH	1024
24
25
#define TRUE		1
26
#define FALSE		0
27
28
#define SECSPERMIN	60
29
#define MINSPERHOUR	60
30
#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
31
#define HOURSPERDAY	24
32
#define EPOCH_YEAR	1970
33
#define TM_YEAR_BASE	1900
34
#define DAYSPERNYEAR	365
35
36
#define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
37
#define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
38
#define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
39
40
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
41
42
#ifndef isleap_sum
43
/*
44
** See tzfile.h for details on isleap_sum.
45
*/
46
#define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
47
#endif /* !defined isleap_sum */
48
49
extern char	**environ;
50
extern char	*tzname[2];
51
extern char 	*__progname;
52
53
time_t		absolute_min_time;
54
time_t		absolute_max_time;
55
size_t		longest;
56
int		warned;
57
58
static char 		*abbr(struct tm *tmp);
59
static void		abbrok(const char *abbrp, const char *zone);
60
static long		delta(struct tm *newp, struct tm *oldp);
61
static void		dumptime(const struct tm *tmp);
62
static time_t		hunt(char *name, time_t lot, time_t	hit);
63
static void		setabsolutes(void);
64
static void		show(char *zone, time_t t, int v);
65
static const char 	*tformat(void);
66
static time_t		yeartot(long y);
67
static void		usage(void);
68
69
static void
70
abbrok(const char * const abbrp, const char * const zone)
71
{
72
	const char 	*cp;
73
	char 		*wp;
74
75
	if (warned)
76
		return;
77
	cp = abbrp;
78
	wp = NULL;
79
	while (isascii((unsigned char)*cp) &&
80
	    (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
81
		++cp;
82
	if (cp - abbrp < 3)
83
		wp = "has fewer than 3 characters";
84
	else if (cp - abbrp > 6)
85
		wp = "has more than 6 characters";
86
	else if (*cp)
87
		wp = "has characters other than ASCII alphanumerics, '-' or '+'";
88
	else
89
		return;
90
	fflush(stdout);
91
	fprintf(stderr, "%s: warning: zone \"%s\" abbreviation \"%s\" %s\n",
92
		__progname, zone, abbrp, wp);
93
	warned = TRUE;
94
}
95
96
static void
97
usage(void)
98
{
99
	fprintf(stderr, "usage: %s [-v] [-c [loyear,]hiyear] zonename ...\n",
100
	    __progname);
101
	exit(EXIT_FAILURE);
102
}
103
104
int
105
main(int argc, char *argv[])
106
{
107
	int		i, c, vflag = 0;
108
	char		*cutarg = NULL;
109
	long		cutloyear = ZDUMP_LO_YEAR;
110
	long		cuthiyear = ZDUMP_HI_YEAR;
111
	time_t		cutlotime = 0, cuthitime = 0;
112
	time_t		now, t, newt;
113
	struct tm	tm, newtm, *tmp, *newtmp;
114
	char		**fakeenv;
115
116
	if (pledge("stdio rpath flock cpath wpath", NULL) == -1) {
117
		perror("pledge");
118
		exit(1);
119
	}
120
121
	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') {
122
		switch (c) {
123
		case 'v':
124
			vflag = 1;
125
			break;
126
		case 'c':
127
			cutarg = optarg;
128
			break;
129
		default:
130
			usage();
131
			break;
132
		}
133
	}
134
	if (c != -1 ||
135
	    (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
136
		usage();
137
	}
138
	if (vflag) {
139
		if (cutarg != NULL) {
140
			long	lo, hi;
141
			char	dummy;
142
143
			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
144
				cuthiyear = hi;
145
			} else if (sscanf(cutarg, "%ld,%ld%c",
146
			    &lo, &hi, &dummy) == 2) {
147
				cutloyear = lo;
148
				cuthiyear = hi;
149
			} else {
150
				fprintf(stderr, "%s: wild -c argument %s\n",
151
				    __progname, cutarg);
152
				exit(EXIT_FAILURE);
153
			}
154
		}
155
		setabsolutes();
156
		cutlotime = yeartot(cutloyear);
157
		cuthitime = yeartot(cuthiyear);
158
	}
159
	time(&now);
160
	longest = 0;
161
	for (i = optind; i < argc; ++i)
162
		if (strlen(argv[i]) > longest)
163
			longest = strlen(argv[i]);
164
165
	{
166
		int	from, to;
167
168
		for (i = 0; environ[i] != NULL; ++i)
169
			continue;
170
		fakeenv = reallocarray(NULL, i + 2, sizeof *fakeenv);
171
		if (fakeenv == NULL ||
172
		    (fakeenv[0] = malloc(longest + 4)) == NULL) {
173
			perror(__progname);
174
			exit(EXIT_FAILURE);
175
		}
176
		to = 0;
177
		strlcpy(fakeenv[to++], "TZ=", longest + 4);
178
		for (from = 0; environ[from] != NULL; ++from)
179
			if (strncmp(environ[from], "TZ=", 3) != 0)
180
				fakeenv[to++] = environ[from];
181
		fakeenv[to] = NULL;
182
		environ = fakeenv;
183
	}
184
	for (i = optind; i < argc; ++i) {
185
		char	buf[MAX_STRING_LENGTH];
186
187
		strlcpy(&fakeenv[0][3], argv[i], longest + 1);
188
		if (!vflag) {
189
			show(argv[i], now, FALSE);
190
			continue;
191
		}
192
		warned = FALSE;
193
		t = absolute_min_time;
194
		show(argv[i], t, TRUE);
195
		t += SECSPERHOUR * HOURSPERDAY;
196
		show(argv[i], t, TRUE);
197
		if (t < cutlotime)
198
			t = cutlotime;
199
		tmp = localtime(&t);
200
		if (tmp != NULL) {
201
			tm = *tmp;
202
			strlcpy(buf, abbr(&tm), sizeof buf);
203
		}
204
		for ( ; ; ) {
205
			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
206
				break;
207
			newt = t + SECSPERHOUR * 12;
208
			newtmp = localtime(&newt);
209
			if (newtmp != NULL)
210
				newtm = *newtmp;
211
			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
212
			    (delta(&newtm, &tm) != (newt - t) ||
213
			    newtm.tm_isdst != tm.tm_isdst ||
214
			    strcmp(abbr(&newtm), buf) != 0)) {
215
				newt = hunt(argv[i], t, newt);
216
				newtmp = localtime(&newt);
217
				if (newtmp != NULL) {
218
					newtm = *newtmp;
219
					strlcpy(buf, abbr(&newtm), sizeof buf);
220
				}
221
			}
222
			t = newt;
223
			tm = newtm;
224
			tmp = newtmp;
225
		}
226
		t = absolute_max_time;
227
		t -= SECSPERHOUR * HOURSPERDAY;
228
		show(argv[i], t, TRUE);
229
		t += SECSPERHOUR * HOURSPERDAY;
230
		show(argv[i], t, TRUE);
231
	}
232
	if (fflush(stdout) || ferror(stdout)) {
233
		fprintf(stderr, "%s: ", __progname);
234
		perror("Error writing to standard output");
235
		exit(EXIT_FAILURE);
236
	}
237
	return 0;
238
}
239
240
static void
241
setabsolutes(void)
242
{
243
	time_t t = 0, t1 = 1;
244
245
	while (t < t1) {
246
		t = t1;
247
		t1 = 2 * t1 + 1;
248
	}
249
250
	absolute_max_time = t;
251
	t = -t;
252
	absolute_min_time = t - 1;
253
	if (t < absolute_min_time)
254
		absolute_min_time = t;
255
}
256
257
static time_t
258
yeartot(const long y)
259
{
260
	long	myy = EPOCH_YEAR, seconds;
261
	time_t	t = 0;
262
263
	while (myy != y) {
264
		if (myy < y) {
265
			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
266
			++myy;
267
			if (t > absolute_max_time - seconds) {
268
				t = absolute_max_time;
269
				break;
270
			}
271
			t += seconds;
272
		} else {
273
			--myy;
274
			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
275
			if (t < absolute_min_time + seconds) {
276
				t = absolute_min_time;
277
				break;
278
			}
279
			t -= seconds;
280
		}
281
	}
282
	return t;
283
}
284
285
static time_t
286
hunt(char *name, time_t lot, time_t hit)
287
{
288
	time_t			t;
289
	long			diff;
290
	struct tm		lotm, *lotmp;
291
	struct tm		tm, *tmp;
292
	char			loab[MAX_STRING_LENGTH];
293
294
	lotmp = localtime(&lot);
295
	if (lotmp != NULL) {
296
		lotm = *lotmp;
297
		strlcpy(loab, abbr(&lotm), sizeof loab);
298
	}
299
	for ( ; ; ) {
300
		diff = (long) (hit - lot);
301
		if (diff < 2)
302
			break;
303
		t = lot;
304
		t += diff / 2;
305
		if (t <= lot)
306
			++t;
307
		else if (t >= hit)
308
			--t;
309
		tmp = localtime(&t);
310
		if (tmp != NULL)
311
			tm = *tmp;
312
		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
313
		    (delta(&tm, &lotm) == (t - lot) &&
314
		    tm.tm_isdst == lotm.tm_isdst &&
315
		    strcmp(abbr(&tm), loab) == 0)) {
316
			lot = t;
317
			lotm = tm;
318
			lotmp = tmp;
319
		} else
320
			hit = t;
321
	}
322
	show(name, lot, TRUE);
323
	show(name, hit, TRUE);
324
	return hit;
325
}
326
327
/*
328
** Thanks to Paul Eggert for logic used in delta.
329
*/
330
331
static long
332
delta(struct tm *newp, struct tm *oldp)
333
{
334
	long	result;
335
	int	tmy;
336
337
	if (newp->tm_year < oldp->tm_year)
338
		return -delta(oldp, newp);
339
	result = 0;
340
	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
341
		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
342
	result += newp->tm_yday - oldp->tm_yday;
343
	result *= HOURSPERDAY;
344
	result += newp->tm_hour - oldp->tm_hour;
345
	result *= MINSPERHOUR;
346
	result += newp->tm_min - oldp->tm_min;
347
	result *= SECSPERMIN;
348
	result += newp->tm_sec - oldp->tm_sec;
349
	return result;
350
}
351
352
static void
353
show(char *zone, time_t t, int v)
354
{
355
	struct tm 	*tmp;
356
357
	printf("%-*s  ", (int) longest, zone);
358
	if (v) {
359
		tmp = gmtime(&t);
360
		if (tmp == NULL) {
361
			printf(tformat(), t);
362
		} else {
363
			dumptime(tmp);
364
			printf(" UTC");
365
		}
366
		printf(" = ");
367
	}
368
	tmp = localtime(&t);
369
	dumptime(tmp);
370
	if (tmp != NULL) {
371
		if (*abbr(tmp) != '\0')
372
			printf(" %s", abbr(tmp));
373
		if (v) {
374
			printf(" isdst=%d", tmp->tm_isdst);
375
#ifdef TM_GMTOFF
376
			printf(" gmtoff=%ld", tmp->TM_GMTOFF);
377
#endif /* defined TM_GMTOFF */
378
		}
379
	}
380
	printf("\n");
381
	if (tmp != NULL && *abbr(tmp) != '\0')
382
		abbrok(abbr(tmp), zone);
383
}
384
385
static char *
386
abbr(struct tm *tmp)
387
{
388
	char 		*result;
389
	static char	nada;
390
391
	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
392
		return &nada;
393
	result = tzname[tmp->tm_isdst];
394
	return (result == NULL) ? &nada : result;
395
}
396
397
/*
398
** The code below can fail on certain theoretical systems;
399
** it works on all known real-world systems as of 2004-12-30.
400
*/
401
402
static const char *
403
tformat(void)
404
{
405
	return "%lld";
406
}
407
408
static void
409
dumptime(const struct tm *timeptr)
410
{
411
	static const char wday_name[][3] = {
412
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
413
	};
414
	static const char mon_name[][3] = {
415
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
416
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
417
	};
418
	const char	*wn, *mn;
419
	int		lead, trail;
420
421
	if (timeptr == NULL) {
422
		printf("NULL");
423
		return;
424
	}
425
	/*
426
	** The packaged versions of localtime and gmtime never put out-of-range
427
	** values in tm_wday or tm_mon, but since this code might be compiled
428
	** with other (perhaps experimental) versions, paranoia is in order.
429
	*/
430
	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
431
	    (int) (sizeof wday_name / sizeof wday_name[0]))
432
		wn = "???";
433
	else
434
		wn = wday_name[timeptr->tm_wday];
435
	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
436
	    (int) (sizeof mon_name / sizeof mon_name[0]))
437
		mn = "???";
438
	else
439
		mn = mon_name[timeptr->tm_mon];
440
	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
441
	    wn, mn,
442
	    timeptr->tm_mday, timeptr->tm_hour,
443
	    timeptr->tm_min, timeptr->tm_sec);
444
#define DIVISOR	10
445
	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
446
	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
447
		trail / DIVISOR;
448
	trail %= DIVISOR;
449
	if (trail < 0 && lead > 0) {
450
		trail += DIVISOR;
451
		--lead;
452
	} else if (lead < 0 && trail > 0) {
453
		trail -= DIVISOR;
454
		++lead;
455
	}
456
	if (lead == 0)
457
		printf("%d", trail);
458
	else
459
		printf("%d%d", lead, ((trail < 0) ? -trail : trail));
460
}