xref: /openbsd/usr.sbin/zdump/zdump.c (revision fcefcf04)
1*fcefcf04Smillert /*	$OpenBSD: zdump.c,v 1.14 2016/03/15 19:50:48 millert Exp $ */
22059035fStedu /*
32059035fStedu ** This file is in the public domain, so clarified as of
42059035fStedu ** 2009-05-17 by Arthur David Olson.
52059035fStedu */
62059035fStedu 
72059035fStedu /*
82059035fStedu ** This code has been made independent of the rest of the time
92059035fStedu ** conversion package to increase confidence in the verification it provides.
102059035fStedu ** You can use this code to help in verifying other implementations.
112059035fStedu */
122059035fStedu 
13f0dc9fbaSderaadt #include <ctype.h>
14f0dc9fbaSderaadt #include <stdio.h>
15f0dc9fbaSderaadt #include <string.h>
16f0dc9fbaSderaadt #include <stdlib.h>
17f0dc9fbaSderaadt #include <unistd.h>
18f0dc9fbaSderaadt #include <time.h>
192059035fStedu 
202059035fStedu #define ZDUMP_LO_YEAR	(-500)
212059035fStedu #define ZDUMP_HI_YEAR	2500
222059035fStedu 
232059035fStedu #define MAX_STRING_LENGTH	1024
242059035fStedu 
252059035fStedu #define TRUE		1
262059035fStedu #define FALSE		0
272059035fStedu 
282059035fStedu #define SECSPERMIN	60
292059035fStedu #define MINSPERHOUR	60
302059035fStedu #define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
312059035fStedu #define HOURSPERDAY	24
322059035fStedu #define EPOCH_YEAR	1970
332059035fStedu #define TM_YEAR_BASE	1900
342059035fStedu #define DAYSPERNYEAR	365
352059035fStedu 
36f0dc9fbaSderaadt #define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
37f0dc9fbaSderaadt #define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
38f0dc9fbaSderaadt #define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
39f0dc9fbaSderaadt 
402059035fStedu #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
412059035fStedu 
422059035fStedu #ifndef isleap_sum
432059035fStedu /*
442059035fStedu ** See tzfile.h for details on isleap_sum.
452059035fStedu */
462059035fStedu #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
472059035fStedu #endif /* !defined isleap_sum */
482059035fStedu 
492059035fStedu extern char	**environ;
502059035fStedu extern char	*tzname[2];
51f0dc9fbaSderaadt extern char 	*__progname;
522059035fStedu 
53f0dc9fbaSderaadt time_t		absolute_min_time;
54f0dc9fbaSderaadt time_t		absolute_max_time;
55f0dc9fbaSderaadt size_t		longest;
56f0dc9fbaSderaadt int		warned;
572059035fStedu 
582059035fStedu static char 		*abbr(struct tm *tmp);
592059035fStedu static void		abbrok(const char *abbrp, const char *zone);
602059035fStedu static long		delta(struct tm *newp, struct tm *oldp);
612059035fStedu static void		dumptime(const struct tm *tmp);
622059035fStedu static time_t		hunt(char *name, time_t lot, time_t	hit);
632059035fStedu static void		setabsolutes(void);
642059035fStedu static void		show(char *zone, time_t t, int v);
652059035fStedu static const char 	*tformat(void);
662059035fStedu static time_t		yeartot(long y);
67f0dc9fbaSderaadt static void		usage(void);
682059035fStedu 
692059035fStedu static void
abbrok(const char * const abbrp,const char * const zone)70f0dc9fbaSderaadt abbrok(const char * const abbrp, const char * const zone)
712059035fStedu {
7266e58127Stedu 	const char 	*cp;
7366e58127Stedu 	char 		*wp;
742059035fStedu 
752059035fStedu 	if (warned)
762059035fStedu 		return;
772059035fStedu 	cp = abbrp;
782059035fStedu 	wp = NULL;
79*fcefcf04Smillert 	while (isascii((unsigned char)*cp) &&
80*fcefcf04Smillert 	    (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
812059035fStedu 		++cp;
82*fcefcf04Smillert 	if (cp - abbrp < 3)
83*fcefcf04Smillert 		wp = "has fewer than 3 characters";
842059035fStedu 	else if (cp - abbrp > 6)
85*fcefcf04Smillert 		wp = "has more than 6 characters";
86*fcefcf04Smillert 	else if (*cp)
87*fcefcf04Smillert 		wp = "has characters other than ASCII alphanumerics, '-' or '+'";
88*fcefcf04Smillert 	else
892059035fStedu 		return;
90e3b8c046Stedu 	fflush(stdout);
91f0dc9fbaSderaadt 	fprintf(stderr, "%s: warning: zone \"%s\" abbreviation \"%s\" %s\n",
92f0dc9fbaSderaadt 		__progname, zone, abbrp, wp);
932059035fStedu 	warned = TRUE;
942059035fStedu }
952059035fStedu 
962059035fStedu static void
usage(void)97b3cc38c2Stedu usage(void)
982059035fStedu {
99f0dc9fbaSderaadt 	fprintf(stderr, "usage: %s [-v] [-c [loyear,]hiyear] zonename ...\n",
100f0dc9fbaSderaadt 	    __progname);
101b3cc38c2Stedu 	exit(EXIT_FAILURE);
1022059035fStedu }
1032059035fStedu 
1042059035fStedu int
main(int argc,char * argv[])105f0dc9fbaSderaadt main(int argc, char *argv[])
1062059035fStedu {
107f0dc9fbaSderaadt 	int		i, c, vflag = 0;
108f0dc9fbaSderaadt 	char		*cutarg = NULL;
10966e58127Stedu 	long		cutloyear = ZDUMP_LO_YEAR;
11066e58127Stedu 	long		cuthiyear = ZDUMP_HI_YEAR;
111f0dc9fbaSderaadt 	time_t		cutlotime = 0, cuthitime = 0;
112f0dc9fbaSderaadt 	time_t		now, t, newt;
113f0dc9fbaSderaadt 	struct tm	tm, newtm, *tmp, *newtmp;
11466e58127Stedu 	char		**fakeenv;
1152059035fStedu 
116244b8f99Smestre 	if (pledge("stdio rpath", NULL) == -1) {
117efe28ca9Sderaadt 		perror("pledge");
1189b32031cSderaadt 		exit(1);
1199b32031cSderaadt 	}
120efe28ca9Sderaadt 
121f0dc9fbaSderaadt 	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') {
122f0dc9fbaSderaadt 		switch (c) {
123f0dc9fbaSderaadt 		case 'v':
1242059035fStedu 			vflag = 1;
125f0dc9fbaSderaadt 			break;
126f0dc9fbaSderaadt 		case 'c':
127f0dc9fbaSderaadt 			cutarg = optarg;
128f0dc9fbaSderaadt 			break;
129f0dc9fbaSderaadt 		default:
130f0dc9fbaSderaadt 			usage();
131f0dc9fbaSderaadt 			break;
132f0dc9fbaSderaadt 		}
133f0dc9fbaSderaadt 	}
134f0dc9fbaSderaadt 	if (c != -1 ||
1352059035fStedu 	    (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
136b3cc38c2Stedu 		usage();
1372059035fStedu 	}
1382059035fStedu 	if (vflag) {
1392059035fStedu 		if (cutarg != NULL) {
140f0dc9fbaSderaadt 			long	lo, hi;
1412059035fStedu 			char	dummy;
1422059035fStedu 
1432059035fStedu 			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
1442059035fStedu 				cuthiyear = hi;
1452059035fStedu 			} else if (sscanf(cutarg, "%ld,%ld%c",
1462059035fStedu 			    &lo, &hi, &dummy) == 2) {
1472059035fStedu 				cutloyear = lo;
1482059035fStedu 				cuthiyear = hi;
1492059035fStedu 			} else {
150f0dc9fbaSderaadt 				fprintf(stderr, "%s: wild -c argument %s\n",
151f0dc9fbaSderaadt 				    __progname, cutarg);
1522059035fStedu 				exit(EXIT_FAILURE);
1532059035fStedu 			}
1542059035fStedu 		}
1552059035fStedu 		setabsolutes();
1562059035fStedu 		cutlotime = yeartot(cutloyear);
1572059035fStedu 		cuthitime = yeartot(cuthiyear);
1582059035fStedu 	}
159e3b8c046Stedu 	time(&now);
1602059035fStedu 	longest = 0;
1612059035fStedu 	for (i = optind; i < argc; ++i)
1622059035fStedu 		if (strlen(argv[i]) > longest)
1632059035fStedu 			longest = strlen(argv[i]);
164f0dc9fbaSderaadt 
1652059035fStedu 	{
166f0dc9fbaSderaadt 		int	from, to;
1672059035fStedu 
1682059035fStedu 		for (i = 0; environ[i] != NULL; ++i)
1692059035fStedu 			continue;
170753cfe0cSderaadt 		fakeenv = reallocarray(NULL, i + 2, sizeof *fakeenv);
1712059035fStedu 		if (fakeenv == NULL ||
172e3b8c046Stedu 		    (fakeenv[0] = malloc(longest + 4)) == NULL) {
173f0dc9fbaSderaadt 			perror(__progname);
1742059035fStedu 			exit(EXIT_FAILURE);
1752059035fStedu 		}
1762059035fStedu 		to = 0;
1772059035fStedu 		strlcpy(fakeenv[to++], "TZ=", longest + 4);
1782059035fStedu 		for (from = 0; environ[from] != NULL; ++from)
1792059035fStedu 			if (strncmp(environ[from], "TZ=", 3) != 0)
1802059035fStedu 				fakeenv[to++] = environ[from];
1812059035fStedu 		fakeenv[to] = NULL;
1822059035fStedu 		environ = fakeenv;
1832059035fStedu 	}
1842059035fStedu 	for (i = optind; i < argc; ++i) {
185f0dc9fbaSderaadt 		char	buf[MAX_STRING_LENGTH];
1862059035fStedu 
1872059035fStedu 		strlcpy(&fakeenv[0][3], argv[i], longest + 1);
1882059035fStedu 		if (!vflag) {
1892059035fStedu 			show(argv[i], now, FALSE);
1902059035fStedu 			continue;
1912059035fStedu 		}
1922059035fStedu 		warned = FALSE;
1932059035fStedu 		t = absolute_min_time;
1942059035fStedu 		show(argv[i], t, TRUE);
1952059035fStedu 		t += SECSPERHOUR * HOURSPERDAY;
1962059035fStedu 		show(argv[i], t, TRUE);
1972059035fStedu 		if (t < cutlotime)
1982059035fStedu 			t = cutlotime;
199f0dc9fbaSderaadt 		tmp = localtime(&t);
2002059035fStedu 		if (tmp != NULL) {
2012059035fStedu 			tm = *tmp;
2022059035fStedu 			strlcpy(buf, abbr(&tm), sizeof buf);
2032059035fStedu 		}
2042059035fStedu 		for ( ; ; ) {
2052059035fStedu 			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
2062059035fStedu 				break;
2072059035fStedu 			newt = t + SECSPERHOUR * 12;
2082059035fStedu 			newtmp = localtime(&newt);
2092059035fStedu 			if (newtmp != NULL)
2102059035fStedu 				newtm = *newtmp;
2112059035fStedu 			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
2122059035fStedu 			    (delta(&newtm, &tm) != (newt - t) ||
2132059035fStedu 			    newtm.tm_isdst != tm.tm_isdst ||
2142059035fStedu 			    strcmp(abbr(&newtm), buf) != 0)) {
2152059035fStedu 				newt = hunt(argv[i], t, newt);
2162059035fStedu 				newtmp = localtime(&newt);
2172059035fStedu 				if (newtmp != NULL) {
2182059035fStedu 					newtm = *newtmp;
219f0dc9fbaSderaadt 					strlcpy(buf, abbr(&newtm), sizeof buf);
2202059035fStedu 				}
2212059035fStedu 			}
2222059035fStedu 			t = newt;
2232059035fStedu 			tm = newtm;
2242059035fStedu 			tmp = newtmp;
2252059035fStedu 		}
2262059035fStedu 		t = absolute_max_time;
2272059035fStedu 		t -= SECSPERHOUR * HOURSPERDAY;
2282059035fStedu 		show(argv[i], t, TRUE);
2292059035fStedu 		t += SECSPERHOUR * HOURSPERDAY;
2302059035fStedu 		show(argv[i], t, TRUE);
2312059035fStedu 	}
2322059035fStedu 	if (fflush(stdout) || ferror(stdout)) {
233f0dc9fbaSderaadt 		fprintf(stderr, "%s: ", __progname);
234f0dc9fbaSderaadt 		perror("Error writing to standard output");
2352059035fStedu 		exit(EXIT_FAILURE);
2362059035fStedu 	}
237b3cc38c2Stedu 	return 0;
2382059035fStedu }
2392059035fStedu 
2402059035fStedu static void
setabsolutes(void)2412059035fStedu setabsolutes(void)
2422059035fStedu {
243f0dc9fbaSderaadt 	time_t t = 0, t1 = 1;
2442059035fStedu 
2452059035fStedu 	while (t < t1) {
2462059035fStedu 		t = t1;
2472059035fStedu 		t1 = 2 * t1 + 1;
2482059035fStedu 	}
2492059035fStedu 
2502059035fStedu 	absolute_max_time = t;
2512059035fStedu 	t = -t;
2522059035fStedu 	absolute_min_time = t - 1;
2532059035fStedu 	if (t < absolute_min_time)
2542059035fStedu 		absolute_min_time = t;
2552059035fStedu }
2562059035fStedu 
2572059035fStedu static time_t
yeartot(const long y)258f0dc9fbaSderaadt yeartot(const long y)
2592059035fStedu {
260f0dc9fbaSderaadt 	long	myy = EPOCH_YEAR, seconds;
261f0dc9fbaSderaadt 	time_t	t = 0;
2622059035fStedu 
2632059035fStedu 	while (myy != y) {
2642059035fStedu 		if (myy < y) {
2652059035fStedu 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
2662059035fStedu 			++myy;
2672059035fStedu 			if (t > absolute_max_time - seconds) {
2682059035fStedu 				t = absolute_max_time;
2692059035fStedu 				break;
2702059035fStedu 			}
2712059035fStedu 			t += seconds;
2722059035fStedu 		} else {
2732059035fStedu 			--myy;
2742059035fStedu 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
2752059035fStedu 			if (t < absolute_min_time + seconds) {
2762059035fStedu 				t = absolute_min_time;
2772059035fStedu 				break;
2782059035fStedu 			}
2792059035fStedu 			t -= seconds;
2802059035fStedu 		}
2812059035fStedu 	}
2822059035fStedu 	return t;
2832059035fStedu }
2842059035fStedu 
2852059035fStedu static time_t
hunt(char * name,time_t lot,time_t hit)2862059035fStedu hunt(char *name, time_t lot, time_t hit)
2872059035fStedu {
2882059035fStedu 	time_t			t;
2892059035fStedu 	long			diff;
290f0dc9fbaSderaadt 	struct tm		lotm, *lotmp;
291f0dc9fbaSderaadt 	struct tm		tm, *tmp;
2922059035fStedu 	char			loab[MAX_STRING_LENGTH];
2932059035fStedu 
294f0dc9fbaSderaadt 	lotmp = localtime(&lot);
2952059035fStedu 	if (lotmp != NULL) {
2962059035fStedu 		lotm = *lotmp;
297e3b8c046Stedu 		strlcpy(loab, abbr(&lotm), sizeof loab);
2982059035fStedu 	}
2992059035fStedu 	for ( ; ; ) {
3002059035fStedu 		diff = (long) (hit - lot);
3012059035fStedu 		if (diff < 2)
3022059035fStedu 			break;
3032059035fStedu 		t = lot;
3042059035fStedu 		t += diff / 2;
3052059035fStedu 		if (t <= lot)
3062059035fStedu 			++t;
3072059035fStedu 		else if (t >= hit)
3082059035fStedu 			--t;
309f0dc9fbaSderaadt 		tmp = localtime(&t);
3102059035fStedu 		if (tmp != NULL)
3112059035fStedu 			tm = *tmp;
3122059035fStedu 		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
3132059035fStedu 		    (delta(&tm, &lotm) == (t - lot) &&
3142059035fStedu 		    tm.tm_isdst == lotm.tm_isdst &&
3152059035fStedu 		    strcmp(abbr(&tm), loab) == 0)) {
3162059035fStedu 			lot = t;
3172059035fStedu 			lotm = tm;
3182059035fStedu 			lotmp = tmp;
319f0dc9fbaSderaadt 		} else
320f0dc9fbaSderaadt 			hit = t;
3212059035fStedu 	}
3222059035fStedu 	show(name, lot, TRUE);
3232059035fStedu 	show(name, hit, TRUE);
3242059035fStedu 	return hit;
3252059035fStedu }
3262059035fStedu 
3272059035fStedu /*
3282059035fStedu ** Thanks to Paul Eggert for logic used in delta.
3292059035fStedu */
3302059035fStedu 
3312059035fStedu static long
delta(struct tm * newp,struct tm * oldp)332f0dc9fbaSderaadt delta(struct tm *newp, struct tm *oldp)
3332059035fStedu {
33466e58127Stedu 	long	result;
33566e58127Stedu 	int	tmy;
3362059035fStedu 
3372059035fStedu 	if (newp->tm_year < oldp->tm_year)
3382059035fStedu 		return -delta(oldp, newp);
3392059035fStedu 	result = 0;
3402059035fStedu 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
3412059035fStedu 		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
3422059035fStedu 	result += newp->tm_yday - oldp->tm_yday;
3432059035fStedu 	result *= HOURSPERDAY;
3442059035fStedu 	result += newp->tm_hour - oldp->tm_hour;
3452059035fStedu 	result *= MINSPERHOUR;
3462059035fStedu 	result += newp->tm_min - oldp->tm_min;
3472059035fStedu 	result *= SECSPERMIN;
3482059035fStedu 	result += newp->tm_sec - oldp->tm_sec;
3492059035fStedu 	return result;
3502059035fStedu }
3512059035fStedu 
3522059035fStedu static void
show(char * zone,time_t t,int v)3532059035fStedu show(char *zone, time_t t, int v)
3542059035fStedu {
35566e58127Stedu 	struct tm 	*tmp;
3562059035fStedu 
357e3b8c046Stedu 	printf("%-*s  ", (int) longest, zone);
3582059035fStedu 	if (v) {
3592059035fStedu 		tmp = gmtime(&t);
3602059035fStedu 		if (tmp == NULL) {
361e3b8c046Stedu 			printf(tformat(), t);
3622059035fStedu 		} else {
3632059035fStedu 			dumptime(tmp);
364e3b8c046Stedu 			printf(" UTC");
3652059035fStedu 		}
366e3b8c046Stedu 		printf(" = ");
3672059035fStedu 	}
368f0dc9fbaSderaadt 	tmp = localtime(&t);
3692059035fStedu 	dumptime(tmp);
3702059035fStedu 	if (tmp != NULL) {
3712059035fStedu 		if (*abbr(tmp) != '\0')
372e3b8c046Stedu 			printf(" %s", abbr(tmp));
3732059035fStedu 		if (v) {
374e3b8c046Stedu 			printf(" isdst=%d", tmp->tm_isdst);
3752059035fStedu #ifdef TM_GMTOFF
376e3b8c046Stedu 			printf(" gmtoff=%ld", tmp->TM_GMTOFF);
3772059035fStedu #endif /* defined TM_GMTOFF */
3782059035fStedu 		}
3792059035fStedu 	}
380e3b8c046Stedu 	printf("\n");
3812059035fStedu 	if (tmp != NULL && *abbr(tmp) != '\0')
3822059035fStedu 		abbrok(abbr(tmp), zone);
3832059035fStedu }
3842059035fStedu 
3852059035fStedu static char *
abbr(struct tm * tmp)386f0dc9fbaSderaadt abbr(struct tm *tmp)
3872059035fStedu {
38866e58127Stedu 	char 		*result;
3892059035fStedu 	static char	nada;
3902059035fStedu 
3912059035fStedu 	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
3922059035fStedu 		return &nada;
3932059035fStedu 	result = tzname[tmp->tm_isdst];
3942059035fStedu 	return (result == NULL) ? &nada : result;
3952059035fStedu }
3962059035fStedu 
3972059035fStedu /*
3982059035fStedu ** The code below can fail on certain theoretical systems;
3992059035fStedu ** it works on all known real-world systems as of 2004-12-30.
4002059035fStedu */
4012059035fStedu 
4022059035fStedu static const char *
tformat(void)4032059035fStedu tformat(void)
4042059035fStedu {
4052059035fStedu 	return "%lld";
4062059035fStedu }
4072059035fStedu 
4082059035fStedu static void
dumptime(const struct tm * timeptr)409f0dc9fbaSderaadt dumptime(const struct tm *timeptr)
4102059035fStedu {
4112059035fStedu 	static const char wday_name[][3] = {
4122059035fStedu 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
4132059035fStedu 	};
4142059035fStedu 	static const char mon_name[][3] = {
4152059035fStedu 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
4162059035fStedu 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
4172059035fStedu 	};
418f0dc9fbaSderaadt 	const char	*wn, *mn;
419f0dc9fbaSderaadt 	int		lead, trail;
4202059035fStedu 
4212059035fStedu 	if (timeptr == NULL) {
422e3b8c046Stedu 		printf("NULL");
4232059035fStedu 		return;
4242059035fStedu 	}
4252059035fStedu 	/*
4262059035fStedu 	** The packaged versions of localtime and gmtime never put out-of-range
4272059035fStedu 	** values in tm_wday or tm_mon, but since this code might be compiled
4282059035fStedu 	** with other (perhaps experimental) versions, paranoia is in order.
4292059035fStedu 	*/
4302059035fStedu 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
4312059035fStedu 	    (int) (sizeof wday_name / sizeof wday_name[0]))
4322059035fStedu 		wn = "???";
433f0dc9fbaSderaadt 	else
434f0dc9fbaSderaadt 		wn = wday_name[timeptr->tm_wday];
4352059035fStedu 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
4362059035fStedu 	    (int) (sizeof mon_name / sizeof mon_name[0]))
4372059035fStedu 		mn = "???";
438f0dc9fbaSderaadt 	else
439f0dc9fbaSderaadt 		mn = mon_name[timeptr->tm_mon];
440e3b8c046Stedu 	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
4412059035fStedu 	    wn, mn,
4422059035fStedu 	    timeptr->tm_mday, timeptr->tm_hour,
4432059035fStedu 	    timeptr->tm_min, timeptr->tm_sec);
4442059035fStedu #define DIVISOR	10
4452059035fStedu 	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
4462059035fStedu 	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
4472059035fStedu 		trail / DIVISOR;
4482059035fStedu 	trail %= DIVISOR;
4492059035fStedu 	if (trail < 0 && lead > 0) {
4502059035fStedu 		trail += DIVISOR;
4512059035fStedu 		--lead;
4522059035fStedu 	} else if (lead < 0 && trail > 0) {
4532059035fStedu 		trail -= DIVISOR;
4542059035fStedu 		++lead;
4552059035fStedu 	}
4562059035fStedu 	if (lead == 0)
457e3b8c046Stedu 		printf("%d", trail);
458e3b8c046Stedu 	else
459e3b8c046Stedu 		printf("%d%d", lead, ((trail < 0) ? -trail : trail));
4602059035fStedu }
461