localtime.c revision 8870
1224135Sdim#ifndef lint
2224135Sdim#ifndef NOID
3224135Sdimstatic char	elsieid[] = "@(#)localtime.c	7.19";
4224135Sdim#endif /* !defined NOID */
5224135Sdim#endif /* !defined lint */
6224135Sdim
7224135Sdim/*
8224135Sdim** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
9224135Sdim** POSIX-style TZ environment variable handling from Guy Harris
10224135Sdim** (guy@auspex.com).
11224135Sdim*/
12224135Sdim
13224135Sdim/*LINTLIBRARY*/
14263509Sdim
15224135Sdim#include "private.h"
16224135Sdim#include "tzfile.h"
17224135Sdim#include "fcntl.h"
18224135Sdim
19224135Sdim#define ACCESS_MODE	O_RDONLY
20224135Sdim
21252723Sdim#ifdef O_BINARY
22252723Sdim#define OPEN_MODE	(O_RDONLY | O_BINARY)
23252723Sdim#endif /* defined O_BINARY */
24224135Sdim#ifndef O_BINARY
25224135Sdim#define OPEN_MODE	O_RDONLY
26224135Sdim#endif /* !defined O_BINARY */
27224135Sdim
28252723Sdim#ifndef WILDABBR
29252723Sdim/*
30252723Sdim** Someone might make incorrect use of a time zone abbreviation:
31252723Sdim**	1.	They might reference tzname[0] before calling tzset (explicitly
32252723Sdim**	 	or implicitly).
33252723Sdim**	2.	They might reference tzname[1] before calling tzset (explicitly
34224135Sdim**	 	or implicitly).
35224135Sdim**	3.	They might reference tzname[1] after setting to a time zone
36252723Sdim**		in which Daylight Saving Time is never observed.
37252723Sdim**	4.	They might reference tzname[0] after setting to a time zone
38252723Sdim**		in which Standard Time is never observed.
39252723Sdim**	5.	They might reference tm.TM_ZONE after calling offtime.
40252723Sdim** What's best to do in the above cases is open to debate;
41224135Sdim** for now, we just set things up so that in any of the five cases
42224135Sdim** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
43224135Sdim** string "tzname[0] used before set", and similarly for the other cases.
44224135Sdim** And another:  initialize tzname[0] to "ERA", with an explanation in the
45224135Sdim** manual page of what this "time zone abbreviation" means (doing this so
46224135Sdim** that tzname[0] has the "normal" length of three characters).
47224135Sdim*/
48224135Sdim#define WILDABBR	"   "
49224135Sdim#endif /* !defined WILDABBR */
50235633Sdim
51224135Sdimstatic const char GMT[] = "GMT";
52224135Sdim
53224135Sdimstruct ttinfo {				/* time type information */
54224135Sdim	long		tt_gmtoff;	/* GMT offset in seconds */
55224135Sdim	int		tt_isdst;	/* used to set tm_isdst */
56224135Sdim	int		tt_abbrind;	/* abbreviation list index */
57224135Sdim	int		tt_ttisstd;	/* TRUE if transition is std time */
58224135Sdim};
59224135Sdim
60224135Sdimstruct lsinfo {				/* leap second information */
61224135Sdim	time_t		ls_trans;	/* transition time */
62224135Sdim	long		ls_corr;	/* correction to apply */
63224135Sdim};
64252723Sdim
65252723Sdim#define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
66252723Sdim
67224135Sdim#ifdef TZNAME_MAX
68224135Sdim#define MY_TZNAME_MAX	TZNAME_MAX
69224135Sdim#endif /* defined TZNAME_MAX */
70224135Sdim#ifndef TZNAME_MAX
71235633Sdim#define MY_TZNAME_MAX	255
72235633Sdim#endif /* !defined TZNAME_MAX */
73235633Sdim
74235633Sdimstruct state {
75235633Sdim	int		leapcnt;
76224135Sdim	int		timecnt;
77224135Sdim	int		typecnt;
78224135Sdim	int		charcnt;
79224135Sdim	time_t		ats[TZ_MAX_TIMES];
80224135Sdim	unsigned char	types[TZ_MAX_TIMES];
81224135Sdim	struct ttinfo	ttis[TZ_MAX_TYPES];
82224135Sdim	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof GMT),
83224135Sdim				(2 * (MY_TZNAME_MAX + 1)))];
84224135Sdim	struct lsinfo	lsis[TZ_MAX_LEAPS];
85224135Sdim};
86224135Sdim
87224135Sdimstruct rule {
88235633Sdim	int		r_type;		/* type of rule--see below */
89224135Sdim	int		r_day;		/* day number of rule */
90224135Sdim	int		r_week;		/* week number of rule */
91224135Sdim	int		r_mon;		/* month number of rule */
92224135Sdim	long		r_time;		/* transition time of rule */
93224135Sdim};
94224135Sdim
95224135Sdim#define JULIAN_DAY		0	/* Jn - Julian day */
96224135Sdim#define DAY_OF_YEAR		1	/* n - day of year */
97224135Sdim#define MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d - month, week, day of week */
98235633Sdim
99235633Sdim/*
100224135Sdim** Prototypes for static functions.
101224135Sdim*/
102224135Sdim
103224135Sdimstatic long		detzcode P((const char * codep));
104224135Sdimstatic const char *	getzname P((const char * strp));
105224135Sdimstatic const char *	getnum P((const char * strp, int * nump, int min,
106235633Sdim				int max));
107252723Sdimstatic const char *	getsecs P((const char * strp, long * secsp));
108224135Sdimstatic const char *	getoffset P((const char * strp, long * offsetp));
109224135Sdimstatic const char *	getrule P((const char * strp, struct rule * rulep));
110224135Sdimstatic void		gmtload P((struct state * sp));
111224135Sdimstatic void		gmtsub P((const time_t * timep, long offset,
112252723Sdim				struct tm * tmp));
113252723Sdimstatic void		localsub P((const time_t * timep, long offset,
114235633Sdim				struct tm * tmp));
115224135Sdimstatic int		increment_overflow P((int * number, int delta));
116224135Sdimstatic int		normalize_overflow P((int * tensptr, int * unitsptr,
117224135Sdim				int base));
118224135Sdimstatic void		settzname P((void));
119224135Sdimstatic time_t		time1 P((struct tm * tmp, void (* funcp)(),
120224135Sdim				long offset));
121224135Sdimstatic time_t		time2 P((struct tm *tmp, void (* funcp)(),
122224135Sdim				long offset, int * okayp));
123224135Sdimstatic void		timesub P((const time_t * timep, long offset,
124224135Sdim				const struct state * sp, struct tm * tmp));
125224135Sdimstatic int		tmcomp P((const struct tm * atmp,
126224135Sdim				const struct tm * btmp));
127252723Sdimstatic time_t		transtime P((time_t janfirst, int year,
128252723Sdim				const struct rule * rulep, long offset));
129252723Sdimstatic int		tzload P((const char * name, struct state * sp));
130252723Sdimstatic int		tzparse P((const char * name, struct state * sp,
131252723Sdim				int lastditch));
132252723Sdim
133252723Sdim#ifdef ALL_STATE
134252723Sdimstatic struct state *	lclptr;
135252723Sdimstatic struct state *	gmtptr;
136252723Sdim#endif /* defined ALL_STATE */
137252723Sdim
138252723Sdim#ifndef ALL_STATE
139252723Sdimstatic struct state	lclmem;
140252723Sdimstatic struct state	gmtmem;
141252723Sdim#define lclptr		(&lclmem)
142252723Sdim#define gmtptr		(&gmtmem)
143252723Sdim#endif /* State Farm */
144224135Sdim
145252723Sdimstatic int		lcl_is_set;
146252723Sdimstatic int		gmt_is_set;
147252723Sdim
148252723Sdimchar *			tzname[2] = {
149252723Sdim	WILDABBR,
150252723Sdim	WILDABBR
151252723Sdim};
152252723Sdim
153235633Sdim#ifdef USG_COMPAT
154235633Sdimtime_t			timezone = 0;
155224135Sdimint			daylight = 0;
156224135Sdim#endif /* defined USG_COMPAT */
157224135Sdim
158224135Sdim#ifdef ALTZONE
159235633Sdimtime_t			altzone = 0;
160235633Sdim#endif /* defined ALTZONE */
161252723Sdim
162252723Sdimstatic long
163224135Sdimdetzcode(codep)
164224135Sdimconst char * const	codep;
165224135Sdim{
166224135Sdim	register long	result;
167224135Sdim	register int	i;
168224135Sdim
169224135Sdim	result = 0;
170224135Sdim	for (i = 0; i < 4; ++i)
171224135Sdim		result = (result << 8) | (codep[i] & 0xff);
172224135Sdim	return result;
173224135Sdim}
174224135Sdim
175224135Sdimstatic void
176224135Sdimsettzname()
177224135Sdim{
178235633Sdim	register const struct state * const	sp = lclptr;
179252723Sdim	register int				i;
180252723Sdim
181252723Sdim	tzname[0] = WILDABBR;
182252723Sdim	tzname[1] = WILDABBR;
183252723Sdim#ifdef USG_COMPAT
184252723Sdim	daylight = 0;
185252723Sdim	timezone = 0;
186224135Sdim#endif /* defined USG_COMPAT */
187224135Sdim#ifdef ALTZONE
188224135Sdim	altzone = 0;
189224135Sdim#endif /* defined ALTZONE */
190252723Sdim#ifdef ALL_STATE
191235633Sdim	if (sp == NULL) {
192252723Sdim		tzname[0] = tzname[1] = GMT;
193252723Sdim		return;
194224135Sdim	}
195224135Sdim#endif /* defined ALL_STATE */
196224135Sdim	for (i = 0; i < sp->typecnt; ++i) {
197224135Sdim		register const struct ttinfo * const	ttisp = &sp->ttis[i];
198224135Sdim
199235633Sdim		tzname[ttisp->tt_isdst] =
200224135Sdim			(char *) &sp->chars[ttisp->tt_abbrind];
201235633Sdim#ifdef USG_COMPAT
202224135Sdim		if (ttisp->tt_isdst)
203224135Sdim			daylight = 1;
204224135Sdim		if (i == 0 || !ttisp->tt_isdst)
205224135Sdim			timezone = -(ttisp->tt_gmtoff);
206235633Sdim#endif /* defined USG_COMPAT */
207235633Sdim#ifdef ALTZONE
208235633Sdim		if (i == 0 || ttisp->tt_isdst)
209235633Sdim			altzone = -(ttisp->tt_gmtoff);
210235633Sdim#endif /* defined ALTZONE */
211235633Sdim	}
212235633Sdim	/*
213235633Sdim	** And to get the latest zone names into tzname. . .
214235633Sdim	*/
215235633Sdim	for (i = 0; i < sp->timecnt; ++i) {
216235633Sdim		register const struct ttinfo * const	ttisp =
217235633Sdim							&sp->ttis[
218224135Sdim								sp->types[i]];
219235633Sdim
220235633Sdim		tzname[ttisp->tt_isdst] =
221235633Sdim			(char *) &sp->chars[ttisp->tt_abbrind];
222235633Sdim	}
223235633Sdim}
224235633Sdim
225235633Sdimstatic int
226224135Sdimtzload(name, sp)
227235633Sdimregister const char *		name;
228235633Sdimregister struct state * const	sp;
229235633Sdim{
230235633Sdim	register const char *	p;
231224135Sdim	register int		i;
232224135Sdim	register int		fid;
233224135Sdim
234224135Sdim	if (name == NULL && (name = TZDEFAULT) == NULL)
235224135Sdim		return -1;
236235633Sdim	{
237224135Sdim		register int 	doaccess;
238224135Sdim		char		fullname[FILENAME_MAX + 1];
239224135Sdim
240224135Sdim		if (name[0] == ':')
241235633Sdim			++name;
242224135Sdim		doaccess = name[0] == '/';
243224135Sdim		if (!doaccess) {
244224135Sdim			if ((p = TZDIR) == NULL)
245224135Sdim				return -1;
246235633Sdim			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
247224135Sdim				return -1;
248235633Sdim			(void) strcpy(fullname, p);
249235633Sdim			(void) strcat(fullname, "/");
250235633Sdim			(void) strcat(fullname, name);
251235633Sdim			/*
252224135Sdim			** Set doaccess if '.' (as in "../") shows up in name.
253252723Sdim			*/
254224135Sdim			if (strchr(name, '.') != NULL)
255224135Sdim				doaccess = TRUE;
256235633Sdim			name = fullname;
257235633Sdim		}
258224135Sdim		if (doaccess && access(name, ACCESS_MODE) != 0)
259224135Sdim			return -1;
260224135Sdim		if ((fid = open(name, OPEN_MODE)) == -1)
261224135Sdim			return -1;
262235633Sdim	}
263224135Sdim	{
264224135Sdim		register const struct tzhead *	tzhp;
265224135Sdim		char				buf[sizeof *sp + sizeof *tzhp];
266224135Sdim		int				ttisstdcnt;
267224135Sdim
268224135Sdim		i = read(fid, buf, sizeof buf);
269224135Sdim		if (close(fid) != 0 || i < sizeof *tzhp)
270224135Sdim			return -1;
271224135Sdim		tzhp = (struct tzhead *) buf;
272224135Sdim		ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt);
273224135Sdim		sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt);
274224135Sdim		sp->timecnt = (int) detzcode(tzhp->tzh_timecnt);
275224135Sdim		sp->typecnt = (int) detzcode(tzhp->tzh_typecnt);
276224135Sdim		sp->charcnt = (int) detzcode(tzhp->tzh_charcnt);
277224135Sdim		if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
278224135Sdim			sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
279224135Sdim			sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
280224135Sdim			sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
281224135Sdim			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0))
282224135Sdim				return -1;
283224135Sdim		if (i < sizeof *tzhp +
284224135Sdim			sp->timecnt * (4 + sizeof (char)) +
285224135Sdim			sp->typecnt * (4 + 2 * sizeof (char)) +
286224135Sdim			sp->charcnt * sizeof (char) +
287224135Sdim			sp->leapcnt * 2 * 4 +
288224135Sdim			ttisstdcnt * sizeof (char))
289224135Sdim				return -1;
290224135Sdim		p = buf + sizeof *tzhp;
291224135Sdim		for (i = 0; i < sp->timecnt; ++i) {
292224135Sdim			sp->ats[i] = detzcode(p);
293224135Sdim			p += 4;
294224135Sdim		}
295224135Sdim		for (i = 0; i < sp->timecnt; ++i) {
296224135Sdim			sp->types[i] = (unsigned char) *p++;
297224135Sdim			if (sp->types[i] >= sp->typecnt)
298224135Sdim				return -1;
299224135Sdim		}
300224135Sdim		for (i = 0; i < sp->typecnt; ++i) {
301224135Sdim			register struct ttinfo *	ttisp;
302224135Sdim
303224135Sdim			ttisp = &sp->ttis[i];
304224135Sdim			ttisp->tt_gmtoff = detzcode(p);
305224135Sdim			p += 4;
306224135Sdim			ttisp->tt_isdst = (unsigned char) *p++;
307224135Sdim			if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
308224135Sdim				return -1;
309224135Sdim			ttisp->tt_abbrind = (unsigned char) *p++;
310224135Sdim			if (ttisp->tt_abbrind < 0 ||
311224135Sdim				ttisp->tt_abbrind > sp->charcnt)
312224135Sdim					return -1;
313224135Sdim		}
314224135Sdim		for (i = 0; i < sp->charcnt; ++i)
315224135Sdim			sp->chars[i] = *p++;
316224135Sdim		sp->chars[i] = '\0';	/* ensure '\0' at end */
317224135Sdim		for (i = 0; i < sp->leapcnt; ++i) {
318224135Sdim			register struct lsinfo *	lsisp;
319224135Sdim
320224135Sdim			lsisp = &sp->lsis[i];
321224135Sdim			lsisp->ls_trans = detzcode(p);
322224135Sdim			p += 4;
323224135Sdim			lsisp->ls_corr = detzcode(p);
324224135Sdim			p += 4;
325224135Sdim		}
326224135Sdim		for (i = 0; i < sp->typecnt; ++i) {
327226890Sdim			register struct ttinfo *	ttisp;
328224135Sdim
329224135Sdim			ttisp = &sp->ttis[i];
330224135Sdim			if (ttisstdcnt == 0)
331224135Sdim				ttisp->tt_ttisstd = FALSE;
332224135Sdim			else {
333224135Sdim				ttisp->tt_ttisstd = *p++;
334224135Sdim				if (ttisp->tt_ttisstd != TRUE &&
335224135Sdim					ttisp->tt_ttisstd != FALSE)
336224135Sdim						return -1;
337224135Sdim			}
338224135Sdim		}
339224135Sdim	}
340224135Sdim	return 0;
341224135Sdim}
342224135Sdim
343224135Sdimstatic const int	mon_lengths[2][MONSPERYEAR] = {
344224135Sdim	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
345224135Sdim	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
346224135Sdim};
347224135Sdim
348224135Sdimstatic const int	year_lengths[2] = {
349224135Sdim	DAYSPERNYEAR, DAYSPERLYEAR
350224135Sdim};
351224135Sdim
352224135Sdim/*
353224135Sdim** Given a pointer into a time zone string, scan until a character that is not
354224135Sdim** a valid character in a zone name is found.  Return a pointer to that
355224135Sdim** character.
356224135Sdim*/
357224135Sdim
358224135Sdimstatic const char *
359224135Sdimgetzname(strp)
360224135Sdimregister const char *	strp;
361224135Sdim{
362224135Sdim	register char	c;
363224135Sdim
364224135Sdim	while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' &&
365224135Sdim		c != '+')
366226890Sdim			++strp;
367245431Sdim	return strp;
368226890Sdim}
369226890Sdim
370226890Sdim/*
371226890Sdim** Given a pointer into a time zone string, extract a number from that string.
372224135Sdim** Check that the number is within a specified range; if it is not, return
373226890Sdim** NULL.
374224135Sdim** Otherwise, return a pointer to the first character not part of the number.
375224135Sdim*/
376224135Sdim
377224135Sdimstatic const char *
378224135Sdimgetnum(strp, nump, min, max)
379224135Sdimregister const char *	strp;
380224135Sdimint * const		nump;
381224135Sdimconst int		min;
382224135Sdimconst int		max;
383224135Sdim{
384224135Sdim	register char	c;
385224135Sdim	register int	num;
386224135Sdim
387224135Sdim	if (strp == NULL || !isdigit(*strp))
388224135Sdim		return NULL;
389224135Sdim	num = 0;
390224135Sdim	while ((c = *strp) != '\0' && isdigit(c)) {
391224135Sdim		num = num * 10 + (c - '0');
392224135Sdim		if (num > max)
393224135Sdim			return NULL;	/* illegal value */
394224135Sdim		++strp;
395224135Sdim	}
396224135Sdim	if (num < min)
397224135Sdim		return NULL;		/* illegal value */
398224135Sdim	*nump = num;
399224135Sdim	return strp;
400224135Sdim}
401224135Sdim
402224135Sdim/*
403224135Sdim** Given a pointer into a time zone string, extract a number of seconds,
404224135Sdim** in hh[:mm[:ss]] form, from the string.
405224135Sdim** If any error occurs, return NULL.
406224135Sdim** Otherwise, return a pointer to the first character not part of the number
407224135Sdim** of seconds.
408224135Sdim*/
409224135Sdim
410224135Sdimstatic const char *
411224135Sdimgetsecs(strp, secsp)
412224135Sdimregister const char *	strp;
413224135Sdimlong * const		secsp;
414224135Sdim{
415224135Sdim	int	num;
416224135Sdim
417224135Sdim	strp = getnum(strp, &num, 0, HOURSPERDAY);
418224135Sdim	if (strp == NULL)
419224135Sdim		return NULL;
420224135Sdim	*secsp = num * SECSPERHOUR;
421224135Sdim	if (*strp == ':') {
422224135Sdim		++strp;
423224135Sdim		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
424224135Sdim		if (strp == NULL)
425224135Sdim			return NULL;
426224135Sdim		*secsp += num * SECSPERMIN;
427224135Sdim		if (*strp == ':') {
428224135Sdim			++strp;
429224135Sdim			strp = getnum(strp, &num, 0, SECSPERMIN - 1);
430224135Sdim			if (strp == NULL)
431224135Sdim				return NULL;
432235633Sdim			*secsp += num;
433224135Sdim		}
434224135Sdim	}
435224135Sdim	return strp;
436224135Sdim}
437224135Sdim
438224135Sdim/*
439224135Sdim** Given a pointer into a time zone string, extract an offset, in
440224135Sdim** [+-]hh[:mm[:ss]] form, from the string.
441224135Sdim** If any error occurs, return NULL.
442224135Sdim** Otherwise, return a pointer to the first character not part of the time.
443224135Sdim*/
444224135Sdim
445224135Sdimstatic const char *
446224135Sdimgetoffset(strp, offsetp)
447235633Sdimregister const char *	strp;
448224135Sdimlong * const		offsetp;
449224135Sdim{
450245431Sdim	register int	neg;
451245431Sdim
452224135Sdim	if (*strp == '-') {
453224135Sdim		neg = 1;
454224135Sdim		++strp;
455224135Sdim	} else if (isdigit(*strp) || *strp++ == '+')
456224135Sdim		neg = 0;
457224135Sdim	else	return NULL;		/* illegal offset */
458224135Sdim	strp = getsecs(strp, offsetp);
459224135Sdim	if (strp == NULL)
460224135Sdim		return NULL;		/* illegal time */
461224135Sdim	if (neg)
462224135Sdim		*offsetp = -*offsetp;
463224135Sdim	return strp;
464224135Sdim}
465235633Sdim
466224135Sdim/*
467224135Sdim** Given a pointer into a time zone string, extract a rule in the form
468224135Sdim** date[/time].  See POSIX section 8 for the format of "date" and "time".
469224135Sdim** If a valid rule is not found, return NULL.
470224135Sdim** Otherwise, return a pointer to the first character not part of the rule.
471224135Sdim*/
472224135Sdim
473224135Sdimstatic const char *
474224135Sdimgetrule(strp, rulep)
475224135Sdimconst char *			strp;
476224135Sdimregister struct rule * const	rulep;
477224135Sdim{
478224135Sdim	if (*strp == 'J') {
479224135Sdim		/*
480224135Sdim		** Julian day.
481224135Sdim		*/
482235633Sdim		rulep->r_type = JULIAN_DAY;
483224135Sdim		++strp;
484224135Sdim		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
485224135Sdim	} else if (*strp == 'M') {
486224135Sdim		/*
487224135Sdim		** Month, week, day.
488224135Sdim		*/
489224135Sdim		rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
490224135Sdim		++strp;
491224135Sdim		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
492224135Sdim		if (strp == NULL)
493224135Sdim			return NULL;
494224135Sdim		if (*strp++ != '.')
495224135Sdim			return NULL;
496224135Sdim		strp = getnum(strp, &rulep->r_week, 1, 5);
497224135Sdim		if (strp == NULL)
498224135Sdim			return NULL;
499224135Sdim		if (*strp++ != '.')
500224135Sdim			return NULL;
501224135Sdim		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
502224135Sdim	} else if (isdigit(*strp)) {
503224135Sdim		/*
504252723Sdim		** Day of year.
505252723Sdim		*/
506252723Sdim		rulep->r_type = DAY_OF_YEAR;
507224135Sdim		strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
508224135Sdim	} else	return NULL;		/* invalid format */
509224135Sdim	if (strp == NULL)
510224135Sdim		return NULL;
511224135Sdim	if (*strp == '/') {
512224135Sdim		/*
513224135Sdim		** Time specified.
514224135Sdim		*/
515224135Sdim		++strp;
516224135Sdim		strp = getsecs(strp, &rulep->r_time);
517224135Sdim	} else	rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
518224135Sdim	return strp;
519224135Sdim}
520224135Sdim
521224135Sdim/*
522224135Sdim** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
523224135Sdim** year, a rule, and the offset from GMT at the time that rule takes effect,
524224135Sdim** calculate the Epoch-relative time that rule takes effect.
525224135Sdim*/
526224135Sdim
527252723Sdimstatic time_t
528252723Sdimtranstime(janfirst, year, rulep, offset)
529252723Sdimconst time_t				janfirst;
530224135Sdimconst int				year;
531224135Sdimregister const struct rule * const	rulep;
532224135Sdimconst long				offset;
533224135Sdim{
534224135Sdim	register int	leapyear;
535224135Sdim	register time_t	value;
536224135Sdim	register int	i;
537224135Sdim	int		d, m1, yy0, yy1, yy2, dow;
538224135Sdim
539235633Sdim	leapyear = isleap(year);
540235633Sdim	switch (rulep->r_type) {
541235633Sdim
542263509Sdim	case JULIAN_DAY:
543263509Sdim		/*
544235633Sdim		** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
545235633Sdim		** years.
546263509Sdim		** In non-leap years, or if the day number is 59 or less, just
547263509Sdim		** add SECSPERDAY times the day number-1 to the time of
548263509Sdim		** January 1, midnight, to get the day.
549235633Sdim		*/
550235633Sdim		value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
551263509Sdim		if (leapyear && rulep->r_day >= 60)
552263509Sdim			value += SECSPERDAY;
553263509Sdim		break;
554263509Sdim
555263509Sdim	case DAY_OF_YEAR:
556263509Sdim		/*
557263509Sdim		** n - day of year.
558263509Sdim		** Just add SECSPERDAY times the day number to the time of
559263509Sdim		** January 1, midnight, to get the day.
560263509Sdim		*/
561263509Sdim		value = janfirst + rulep->r_day * SECSPERDAY;
562263509Sdim		break;
563263509Sdim
564263509Sdim	case MONTH_NTH_DAY_OF_WEEK:
565263509Sdim		/*
566263509Sdim		** Mm.n.d - nth "dth day" of month m.
567263509Sdim		*/
568263509Sdim		value = janfirst;
569263509Sdim		for (i = 0; i < rulep->r_mon - 1; ++i)
570263509Sdim			value += mon_lengths[leapyear][i] * SECSPERDAY;
571263509Sdim
572263509Sdim		/*
573263509Sdim		** Use Zeller's Congruence to get day-of-week of first day of
574235633Sdim		** month.
575263509Sdim		*/
576263509Sdim		m1 = (rulep->r_mon + 9) % 12 + 1;
577263509Sdim		yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
578235633Sdim		yy1 = yy0 / 100;
579235633Sdim		yy2 = yy0 % 100;
580235633Sdim		dow = ((26 * m1 - 2) / 10 +
581235633Sdim			1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
582224135Sdim		if (dow < 0)
583235633Sdim			dow += DAYSPERWEEK;
584224135Sdim
585224135Sdim		/*
586224135Sdim		** "dow" is the day-of-week of the first day of the month.  Get
587224135Sdim		** the day-of-month (zero-origin) of the first "dow" day of the
588235633Sdim		** month.
589235633Sdim		*/
590245431Sdim		d = rulep->r_day - dow;
591245431Sdim		if (d < 0)
592224135Sdim			d += DAYSPERWEEK;
593224135Sdim		for (i = 1; i < rulep->r_week; ++i) {
594224135Sdim			if (d + DAYSPERWEEK >=
595224135Sdim				mon_lengths[leapyear][rulep->r_mon - 1])
596224135Sdim					break;
597224135Sdim			d += DAYSPERWEEK;
598224135Sdim		}
599224135Sdim
600224135Sdim		/*
601224135Sdim		** "d" is the day-of-month (zero-origin) of the day we want.
602224135Sdim		*/
603224135Sdim		value += d * SECSPERDAY;
604224135Sdim		break;
605224135Sdim	}
606224135Sdim
607224135Sdim	/*
608224135Sdim	** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
609224135Sdim	** question.  To get the Epoch-relative time of the specified local
610224135Sdim	** time on that day, add the transition time and the current offset
611224135Sdim	** from GMT.
612224135Sdim	*/
613224135Sdim	return value + rulep->r_time + offset;
614224135Sdim}
615224135Sdim
616224135Sdim/*
617224135Sdim** Given a POSIX section 8-style TZ string, fill in the rule tables as
618224135Sdim** appropriate.
619224135Sdim*/
620224135Sdim
621224135Sdimstatic int
622224135Sdimtzparse(name, sp, lastditch)
623224135Sdimconst char *			name;
624224135Sdimregister struct state * const	sp;
625224135Sdimconst int			lastditch;
626224135Sdim{
627224135Sdim	const char *			stdname;
628224135Sdim	const char *			dstname;
629224135Sdim	int				stdlen;
630224135Sdim	int				dstlen;
631224135Sdim	long				stdoffset;
632224135Sdim	long				dstoffset;
633263509Sdim	register time_t *		atp;
634224135Sdim	register unsigned char *	typep;
635224135Sdim	register char *			cp;
636235633Sdim	register int			load_result;
637224135Sdim
638263509Sdim	stdname = name;
639224135Sdim	if (lastditch) {
640263509Sdim		stdlen = strlen(name);	/* length of standard zone name */
641263509Sdim		name += stdlen;
642263509Sdim		if (stdlen >= sizeof sp->chars)
643263509Sdim			stdlen = (sizeof sp->chars) - 1;
644263509Sdim	} else {
645263509Sdim		name = getzname(name);
646263509Sdim		stdlen = name - stdname;
647263509Sdim		if (stdlen < 3)
648263509Sdim			return -1;
649263509Sdim	}
650263509Sdim	if (*name == '\0')
651263509Sdim		return -1;	/* was "stdoffset = 0;" */
652263509Sdim	else {
653263509Sdim		name = getoffset(name, &stdoffset);
654263509Sdim		if (name == NULL)
655263509Sdim			return -1;
656263509Sdim	}
657263509Sdim	load_result = tzload(TZDEFRULES, sp);
658224135Sdim	if (load_result != 0)
659224135Sdim		sp->leapcnt = 0;		/* so, we're off a little */
660224135Sdim	if (*name != '\0') {
661224135Sdim		dstname = name;
662224135Sdim		name = getzname(name);
663224135Sdim		dstlen = name - dstname;	/* length of DST zone name */
664224135Sdim		if (dstlen < 3)
665224135Sdim			return -1;
666224135Sdim		if (*name != '\0' && *name != ',' && *name != ';') {
667235633Sdim			name = getoffset(name, &dstoffset);
668224135Sdim			if (name == NULL)
669224135Sdim				return -1;
670224135Sdim		} else	dstoffset = stdoffset - SECSPERHOUR;
671224135Sdim		if (*name == ',' || *name == ';') {
672224135Sdim			struct rule	start;
673224135Sdim			struct rule	end;
674224135Sdim			register int	year;
675235633Sdim			register time_t	janfirst;
676224135Sdim			time_t		starttime;
677224135Sdim			time_t		endtime;
678224135Sdim
679224135Sdim			++name;
680224135Sdim			if ((name = getrule(name, &start)) == NULL)
681224135Sdim				return -1;
682224135Sdim			if (*name++ != ',')
683224135Sdim				return -1;
684224135Sdim			if ((name = getrule(name, &end)) == NULL)
685224135Sdim				return -1;
686224135Sdim			if (*name != '\0')
687224135Sdim				return -1;
688224135Sdim			sp->typecnt = 2;	/* standard time and DST */
689224135Sdim			/*
690224135Sdim			** Two transitions per year, from EPOCH_YEAR to 2037.
691224135Sdim			*/
692224135Sdim			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
693224135Sdim			if (sp->timecnt > TZ_MAX_TIMES)
694224135Sdim				return -1;
695224135Sdim			sp->ttis[0].tt_gmtoff = -dstoffset;
696224135Sdim			sp->ttis[0].tt_isdst = 1;
697224135Sdim			sp->ttis[0].tt_abbrind = stdlen + 1;
698224135Sdim			sp->ttis[1].tt_gmtoff = -stdoffset;
699224135Sdim			sp->ttis[1].tt_isdst = 0;
700224135Sdim			sp->ttis[1].tt_abbrind = 0;
701224135Sdim			atp = sp->ats;
702224135Sdim			typep = sp->types;
703224135Sdim			janfirst = 0;
704224135Sdim			for (year = EPOCH_YEAR; year <= 2037; ++year) {
705224135Sdim				starttime = transtime(janfirst, year, &start,
706245431Sdim					stdoffset);
707224135Sdim				endtime = transtime(janfirst, year, &end,
708224135Sdim					dstoffset);
709224135Sdim				if (starttime > endtime) {
710224135Sdim					*atp++ = endtime;
711224135Sdim					*typep++ = 1;	/* DST ends */
712224135Sdim					*atp++ = starttime;
713235633Sdim					*typep++ = 0;	/* DST begins */
714235633Sdim				} else {
715224135Sdim					*atp++ = starttime;
716224135Sdim					*typep++ = 0;	/* DST begins */
717224135Sdim					*atp++ = endtime;
718224135Sdim					*typep++ = 1;	/* DST ends */
719224135Sdim				}
720224135Sdim				janfirst += year_lengths[isleap(year)] *
721224135Sdim					SECSPERDAY;
722224135Sdim			}
723224135Sdim		} else {
724224135Sdim			int		sawstd;
725224135Sdim			int		sawdst;
726224135Sdim			long		stdfix;
727263509Sdim			long		dstfix;
728224135Sdim			long		oldfix;
729224135Sdim			int		isdst;
730224135Sdim			register int	i;
731224135Sdim
732224135Sdim			if (*name != '\0')
733224135Sdim				return -1;
734224135Sdim			if (load_result != 0)
735224135Sdim				return -1;
736224135Sdim			/*
737224135Sdim			** Compute the difference between the real and
738224135Sdim			** prototype standard and summer time offsets
739224135Sdim			** from GMT, and put the real standard and summer
740224135Sdim			** time offsets into the rules in place of the
741224135Sdim			** prototype offsets.
742224135Sdim			*/
743224135Sdim			sawstd = FALSE;
744224135Sdim			sawdst = FALSE;
745224135Sdim			stdfix = 0;
746224135Sdim			dstfix = 0;
747224135Sdim			for (i = 0; i < sp->typecnt; ++i) {
748224135Sdim				if (sp->ttis[i].tt_isdst) {
749224135Sdim					oldfix = dstfix;
750224135Sdim					dstfix = sp->ttis[i].tt_gmtoff +
751224135Sdim						dstoffset;
752224135Sdim					if (sawdst && (oldfix != dstfix))
753224135Sdim						return -1;
754224135Sdim					sp->ttis[i].tt_gmtoff = -dstoffset;
755224135Sdim					sp->ttis[i].tt_abbrind = stdlen + 1;
756224135Sdim					sawdst = TRUE;
757224135Sdim				} else {
758224135Sdim					oldfix = stdfix;
759224135Sdim					stdfix = sp->ttis[i].tt_gmtoff +
760224135Sdim						stdoffset;
761224135Sdim					if (sawstd && (oldfix != stdfix))
762224135Sdim						return -1;
763224135Sdim					sp->ttis[i].tt_gmtoff = -stdoffset;
764224135Sdim					sp->ttis[i].tt_abbrind = 0;
765224135Sdim					sawstd = TRUE;
766224135Sdim				}
767224135Sdim			}
768224135Sdim			/*
769224135Sdim			** Make sure we have both standard and summer time.
770224135Sdim			*/
771224135Sdim			if (!sawdst || !sawstd)
772224135Sdim				return -1;
773224135Sdim			/*
774224135Sdim			** Now correct the transition times by shifting
775224135Sdim			** them by the difference between the real and
776224135Sdim			** prototype offsets.  Note that this difference
777224135Sdim			** can be different in standard and summer time;
778224135Sdim			** the prototype probably has a 1-hour difference
779224135Sdim			** between standard and summer time, but a different
780235633Sdim			** difference can be specified in TZ.
781224135Sdim			*/
782224135Sdim			isdst = FALSE;	/* we start in standard time */
783224135Sdim			for (i = 0; i < sp->timecnt; ++i) {
784224135Sdim				register const struct ttinfo *	ttisp;
785224135Sdim
786224135Sdim				/*
787224135Sdim				** If summer time is in effect, and the
788235633Sdim				** transition time was not specified as
789224135Sdim				** standard time, add the summer time
790224135Sdim				** offset to the transition time;
791224135Sdim				** otherwise, add the standard time offset
792224135Sdim				** to the transition time.
793224135Sdim				*/
794224135Sdim				ttisp = &sp->ttis[sp->types[i]];
795224135Sdim				sp->ats[i] +=
796224135Sdim					(isdst && !ttisp->tt_ttisstd) ?
797224135Sdim						dstfix : stdfix;
798224135Sdim				isdst = ttisp->tt_isdst;
799224135Sdim			}
800224135Sdim		}
801224135Sdim	} else {
802224135Sdim		dstlen = 0;
803224135Sdim		sp->typecnt = 1;		/* only standard time */
804224135Sdim		sp->timecnt = 0;
805224135Sdim		sp->ttis[0].tt_gmtoff = -stdoffset;
806224135Sdim		sp->ttis[0].tt_isdst = 0;
807224135Sdim		sp->ttis[0].tt_abbrind = 0;
808224135Sdim	}
809224135Sdim	sp->charcnt = stdlen + 1;
810224135Sdim	if (dstlen != 0)
811263509Sdim		sp->charcnt += dstlen + 1;
812263509Sdim	if (sp->charcnt > sizeof sp->chars)
813263509Sdim		return -1;
814224135Sdim	cp = sp->chars;
815224135Sdim	(void) strncpy(cp, stdname, stdlen);
816224135Sdim	cp += stdlen;
817224135Sdim	*cp++ = '\0';
818224135Sdim	if (dstlen != 0) {
819224135Sdim		(void) strncpy(cp, dstname, dstlen);
820224135Sdim		*(cp + dstlen) = '\0';
821224135Sdim	}
822224135Sdim	return 0;
823224135Sdim}
824263509Sdim
825224135Sdimstatic void
826224135Sdimgmtload(sp)
827263509Sdimstruct state * const	sp;
828263509Sdim{
829263509Sdim	if (tzload(GMT, sp) != 0)
830224135Sdim		(void) tzparse(GMT, sp, TRUE);
831263509Sdim}
832263509Sdim
833224135Sdim#ifndef STD_INSPIRED
834224135Sdimstatic
835224135Sdim#endif /* !defined STD_INSPIRED */
836224135Sdimvoid
837224135Sdimtzsetwall()
838252723Sdim{
839252723Sdim	lcl_is_set = TRUE;
840252723Sdim#ifdef ALL_STATE
841252723Sdim	if (lclptr == NULL) {
842252723Sdim		lclptr = (struct state *) malloc(sizeof *lclptr);
843252723Sdim		if (lclptr == NULL) {
844252723Sdim			settzname();	/* all we can do */
845252723Sdim			return;
846252723Sdim		}
847252723Sdim	}
848252723Sdim#endif /* defined ALL_STATE */
849252723Sdim	if (tzload((char *) NULL, lclptr) != 0)
850252723Sdim		gmtload(lclptr);
851224135Sdim	settzname();
852235633Sdim}
853235633Sdim
854235633Sdimvoid
855235633Sdimtzset()
856235633Sdim{
857224135Sdim	register const char *	name;
858224135Sdim
859224135Sdim	name = getenv("TZ");
860224135Sdim	if (name == NULL) {
861224135Sdim		tzsetwall();
862235633Sdim		return;
863235633Sdim	}
864235633Sdim	lcl_is_set = TRUE;
865235633Sdim#ifdef ALL_STATE
866235633Sdim	if (lclptr == NULL) {
867235633Sdim		lclptr = (struct state *) malloc(sizeof *lclptr);
868235633Sdim		if (lclptr == NULL) {
869224135Sdim			settzname();	/* all we can do */
870224135Sdim			return;
871224135Sdim		}
872224135Sdim	}
873224135Sdim#endif /* defined ALL_STATE */
874224135Sdim	if (*name == '\0') {
875235633Sdim		/*
876224135Sdim		** User wants it fast rather than right.
877224135Sdim		*/
878245431Sdim		lclptr->leapcnt = 0;		/* so, we're off a little */
879245431Sdim		lclptr->timecnt = 0;
880245431Sdim		lclptr->ttis[0].tt_gmtoff = 0;
881224135Sdim		lclptr->ttis[0].tt_abbrind = 0;
882224135Sdim		(void) strcpy(lclptr->chars, GMT);
883224135Sdim	} else if (tzload(name, lclptr) != 0)
884235633Sdim		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
885224135Sdim			(void) gmtload(lclptr);
886224135Sdim	settzname();
887263509Sdim}
888263509Sdim
889263509Sdim/*
890263509Sdim** The easy way to behave "as if no library function calls" localtime
891263509Sdim** is to not call it--so we drop its guts into "localsub", which can be
892263509Sdim** freely called.  (And no, the PANS doesn't require the above behavior--
893263509Sdim** but it *is* desirable.)
894263509Sdim**
895263509Sdim** The unused offset argument is for the benefit of mktime variants.
896263509Sdim*/
897263509Sdim
898263509Sdim/*ARGSUSED*/
899263509Sdimstatic void
900263509Sdimlocalsub(timep, offset, tmp)
901263509Sdimconst time_t * const	timep;
902263509Sdimconst long		offset;
903263509Sdimstruct tm * const	tmp;
904263509Sdim{
905263509Sdim	register const struct state *	sp;
906263509Sdim	register const struct ttinfo *	ttisp;
907263509Sdim	register int			i;
908263509Sdim	const time_t			t = *timep;
909263509Sdim
910263509Sdim	if (!lcl_is_set)
911263509Sdim		tzset();
912263509Sdim	sp = lclptr;
913263509Sdim#ifdef ALL_STATE
914263509Sdim	if (sp == NULL) {
915263509Sdim		gmtsub(timep, offset, tmp);
916263509Sdim		return;
917263509Sdim	}
918263509Sdim#endif /* defined ALL_STATE */
919263509Sdim	if (sp->timecnt == 0 || t < sp->ats[0]) {
920263509Sdim		i = 0;
921263509Sdim		while (sp->ttis[i].tt_isdst)
922263509Sdim			if (++i >= sp->typecnt) {
923263509Sdim				i = 0;
924263509Sdim				break;
925263509Sdim			}
926263509Sdim	} else {
927263509Sdim		for (i = 1; i < sp->timecnt; ++i)
928263509Sdim			if (t < sp->ats[i])
929263509Sdim				break;
930263509Sdim		i = sp->types[i - 1];
931263509Sdim	}
932263509Sdim	ttisp = &sp->ttis[i];
933263509Sdim	/*
934263509Sdim	** To get (wrong) behavior that's compatible with System V Release 2.0
935224135Sdim	** you'd replace the statement below with
936224135Sdim	**	t += ttisp->tt_gmtoff;
937224135Sdim	**	timesub(&t, 0L, sp, tmp);
938224135Sdim	*/
939224135Sdim	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
940224135Sdim	tmp->tm_isdst = ttisp->tt_isdst;
941224135Sdim	tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
942224135Sdim#ifdef TM_ZONE
943224135Sdim	tmp->TM_ZONE = (char *)&sp->chars[ttisp->tt_abbrind];
944224135Sdim#endif /* defined TM_ZONE */
945224135Sdim}
946224135Sdim
947224135Sdimstruct tm *
948245431Sdimlocaltime(timep)
949245431Sdimconst time_t * const	timep;
950245431Sdim{
951245431Sdim	static struct tm	tm;
952245431Sdim
953245431Sdim	localsub(timep, 0L, &tm);
954245431Sdim	return &tm;
955245431Sdim}
956245431Sdim
957245431Sdim/*
958245431Sdim** gmtsub is to gmtime as localsub is to localtime.
959245431Sdim*/
960245431Sdim
961245431Sdimstatic void
962245431Sdimgmtsub(timep, offset, tmp)
963245431Sdimconst time_t * const	timep;
964245431Sdimconst long		offset;
965245431Sdimstruct tm * const	tmp;
966245431Sdim{
967245431Sdim	if (!gmt_is_set) {
968245431Sdim		gmt_is_set = TRUE;
969245431Sdim#ifdef ALL_STATE
970245431Sdim		gmtptr = (struct state *) malloc(sizeof *gmtptr);
971245431Sdim		if (gmtptr != NULL)
972245431Sdim#endif /* defined ALL_STATE */
973245431Sdim			gmtload(gmtptr);
974224135Sdim	}
975224135Sdim	timesub(timep, offset, gmtptr, tmp);
976224135Sdim#ifdef TM_ZONE
977224135Sdim	/*
978224135Sdim	** Could get fancy here and deliver something such as
979224135Sdim	** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
980224135Sdim	** but this is no time for a treasure hunt.
981224135Sdim	*/
982224135Sdim	if (offset != 0)
983224135Sdim		tmp->TM_ZONE = WILDABBR;
984224135Sdim	else {
985224135Sdim#ifdef ALL_STATE
986224135Sdim		if (gmtptr == NULL)
987224135Sdim			tmp->TM_ZONE = GMT;
988224135Sdim		else	tmp->TM_ZONE = gmtptr->chars;
989224135Sdim#endif /* defined ALL_STATE */
990224135Sdim#ifndef ALL_STATE
991224135Sdim		tmp->TM_ZONE = gmtptr->chars;
992224135Sdim#endif /* State Farm */
993235633Sdim	}
994224135Sdim#endif /* defined TM_ZONE */
995224135Sdim}
996224135Sdim
997224135Sdimstruct tm *
998224135Sdimgmtime(timep)
999224135Sdimconst time_t * const	timep;
1000224135Sdim{
1001224135Sdim	static struct tm	tm;
1002224135Sdim
1003224135Sdim	gmtsub(timep, 0L, &tm);
1004224135Sdim	return &tm;
1005224135Sdim}
1006235633Sdim
1007224135Sdim#ifdef STD_INSPIRED
1008224135Sdim
1009224135Sdimstruct tm *
1010224135Sdimofftime(timep, offset)
1011224135Sdimconst time_t * const	timep;
1012224135Sdimconst long		offset;
1013224135Sdim{
1014224135Sdim	static struct tm	tm;
1015224135Sdim
1016224135Sdim	gmtsub(timep, offset, &tm);
1017224135Sdim	return &tm;
1018224135Sdim}
1019224135Sdim
1020224135Sdim#endif /* defined STD_INSPIRED */
1021224135Sdim
1022224135Sdimstatic void
1023224135Sdimtimesub(timep, offset, sp, tmp)
1024224135Sdimconst time_t * const			timep;
1025224135Sdimconst long				offset;
1026224135Sdimregister const struct state * const	sp;
1027224135Sdimregister struct tm * const		tmp;
1028224135Sdim{
1029224135Sdim	register const struct lsinfo *	lp;
1030224135Sdim	register long			days;
1031224135Sdim	register long			rem;
1032224135Sdim	register int			y;
1033235633Sdim	register int			yleap;
1034224135Sdim	register const int *		ip;
1035224135Sdim	register long			corr;
1036224135Sdim	register int			hit;
1037224135Sdim	register int			i;
1038224135Sdim
1039224135Sdim	corr = 0;
1040224135Sdim	hit = 0;
1041224135Sdim#ifdef ALL_STATE
1042224135Sdim	i = (sp == NULL) ? 0 : sp->leapcnt;
1043224135Sdim#endif /* defined ALL_STATE */
1044224135Sdim#ifndef ALL_STATE
1045224135Sdim	i = sp->leapcnt;
1046224135Sdim#endif /* State Farm */
1047224135Sdim	while (--i >= 0) {
1048224135Sdim		lp = &sp->lsis[i];
1049224135Sdim		if (*timep >= lp->ls_trans) {
1050224135Sdim			if (*timep == lp->ls_trans) {
1051224135Sdim				hit = ((i == 0 && lp->ls_corr > 0) ||
1052224135Sdim					lp->ls_corr > sp->lsis[i - 1].ls_corr);
1053224135Sdim				if (hit)
1054224135Sdim					while (i > 0 &&
1055252723Sdim						sp->lsis[i].ls_trans ==
1056252723Sdim						sp->lsis[i - 1].ls_trans + 1 &&
1057252723Sdim						sp->lsis[i].ls_corr ==
1058252723Sdim						sp->lsis[i - 1].ls_corr + 1) {
1059224135Sdim							++hit;
1060224135Sdim							--i;
1061224135Sdim					}
1062224135Sdim			}
1063263509Sdim			corr = lp->ls_corr;
1064263509Sdim			break;
1065224135Sdim		}
1066224135Sdim	}
1067235633Sdim	days = *timep / SECSPERDAY;
1068235633Sdim	rem = *timep % SECSPERDAY;
1069224135Sdim#ifdef mc68k
1070224135Sdim	if (*timep == 0x80000000) {
1071224135Sdim		/*
1072224135Sdim		** A 3B1 muffs the division on the most negative number.
1073224135Sdim		*/
1074224135Sdim		days = -24855;
1075224135Sdim		rem = -11648;
1076224135Sdim	}
1077224135Sdim#endif /* mc68k */
1078224135Sdim	rem += (offset - corr);
1079224135Sdim	while (rem < 0) {
1080224135Sdim		rem += SECSPERDAY;
1081224135Sdim		--days;
1082224135Sdim	}
1083224135Sdim	while (rem >= SECSPERDAY) {
1084235633Sdim		rem -= SECSPERDAY;
1085235633Sdim		++days;
1086235633Sdim	}
1087235633Sdim	tmp->tm_hour = (int) (rem / SECSPERHOUR);
1088224135Sdim	rem = rem % SECSPERHOUR;
1089224135Sdim	tmp->tm_min = (int) (rem / SECSPERMIN);
1090224135Sdim	tmp->tm_sec = (int) (rem % SECSPERMIN);
1091224135Sdim	if (hit)
1092235633Sdim		/*
1093235633Sdim		** A positive leap second requires a special
1094224135Sdim		** representation.  This uses "... ??:59:60" et seq.
1095224135Sdim		*/
1096224135Sdim		tmp->tm_sec += hit;
1097224135Sdim	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
1098224135Sdim	if (tmp->tm_wday < 0)
1099224135Sdim		tmp->tm_wday += DAYSPERWEEK;
1100224135Sdim	y = EPOCH_YEAR;
1101224135Sdim	if (days >= 0)
1102224135Sdim		for ( ; ; ) {
1103224135Sdim			yleap = isleap(y);
1104224135Sdim			if (days < (long) year_lengths[yleap])
1105224135Sdim				break;
1106224135Sdim			++y;
1107224135Sdim			days = days - (long) year_lengths[yleap];
1108224135Sdim		}
1109224135Sdim	else do {
1110224135Sdim		--y;
1111224135Sdim		yleap = isleap(y);
1112224135Sdim		days = days + (long) year_lengths[yleap];
1113224135Sdim	} while (days < 0);
1114224135Sdim	tmp->tm_year = y - TM_YEAR_BASE;
1115224135Sdim	tmp->tm_yday = (int) days;
1116224135Sdim	ip = mon_lengths[yleap];
1117224135Sdim	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
1118224135Sdim		days = days - (long) ip[tmp->tm_mon];
1119224135Sdim	tmp->tm_mday = (int) (days + 1);
1120224135Sdim	tmp->tm_isdst = 0;
1121224135Sdim#ifdef TM_GMTOFF
1122224135Sdim	tmp->TM_GMTOFF = offset;
1123224135Sdim#endif /* defined TM_GMTOFF */
1124224135Sdim}
1125224135Sdim
1126224135Sdimchar *
1127224135Sdimctime(timep)
1128226890Sdimconst time_t * const	timep;
1129224135Sdim{
1130226890Sdim	return asctime(localtime(timep));
1131224135Sdim}
1132224135Sdim
1133224135Sdim/*
1134224135Sdim** Adapted from code provided by Robert Elz, who writes:
1135224135Sdim**	The "best" way to do mktime I think is based on an idea of Bob
1136224135Sdim**	Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
1137224135Sdim**	It does a binary search of the time_t space.  Since time_t's are
1138245431Sdim**	just 32 bits, its a max of 32 iterations (even at 64 bits it
1139224135Sdim**	would still be very reasonable).
1140224135Sdim*/
1141224135Sdim
1142224135Sdim#ifndef WRONG
1143224135Sdim#define WRONG	(-1)
1144224135Sdim#endif /* !defined WRONG */
1145226890Sdim
1146226890Sdim/*
1147226890Sdim** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
1148226890Sdim*/
1149226890Sdim
1150226890Sdimstatic int
1151226890Sdimincrement_overflow(number, delta)
1152226890Sdimint *	number;
1153226890Sdimint	delta;
1154235633Sdim{
1155235633Sdim	int number0;
1156235633Sdim
1157235633Sdim	number0 = *number;
1158235633Sdim	*number += delta;
1159235633Sdim	return (*number < number0) != (delta < 0);
1160235633Sdim}
1161235633Sdim
1162235633Sdimstatic int
1163224135Sdimnormalize_overflow(tensptr, unitsptr, base)
1164224135Sdimint * const	tensptr;
1165224135Sdimint * const	unitsptr;
1166224135Sdimconst int	base;
1167224135Sdim{
1168224135Sdim	register int	tensdelta;
1169224135Sdim
1170224135Sdim	tensdelta = (*unitsptr >= 0) ?
1171224135Sdim		(*unitsptr / base) :
1172224135Sdim		(-1 - (-1 - *unitsptr) / base);
1173224135Sdim	*unitsptr -= tensdelta * base;
1174224135Sdim	return increment_overflow(tensptr, tensdelta);
1175224135Sdim}
1176224135Sdim
1177224135Sdimstatic int
1178224135Sdimtmcomp(atmp, btmp)
1179224135Sdimregister const struct tm * const atmp;
1180224135Sdimregister const struct tm * const btmp;
1181235633Sdim{
1182226890Sdim	register int	result;
1183226890Sdim
1184224135Sdim	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
1185224135Sdim		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
1186224135Sdim		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
1187224135Sdim		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
1188224135Sdim		(result = (atmp->tm_min - btmp->tm_min)) == 0)
1189224135Sdim			result = atmp->tm_sec - btmp->tm_sec;
1190224135Sdim	return result;
1191224135Sdim}
1192224135Sdim
1193224135Sdimstatic time_t
1194224135Sdimtime2(tmp, funcp, offset, okayp)
1195224135Sdimstruct tm * const	tmp;
1196224135Sdimvoid (* const		funcp)();
1197224135Sdimconst long		offset;
1198224135Sdimint * const		okayp;
1199224135Sdim{
1200224135Sdim	register const struct state *	sp;
1201224135Sdim	register int			dir;
1202224135Sdim	register int			bits;
1203224135Sdim	register int			i, j ;
1204224135Sdim	register int			saved_seconds;
1205224135Sdim	time_t				newt;
1206224135Sdim	time_t				t;
1207263509Sdim	struct tm			yourtm, mytm;
1208263509Sdim
1209263509Sdim	*okayp = FALSE;
1210263509Sdim	yourtm = *tmp;
1211263509Sdim	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
1212263509Sdim		return WRONG;
1213263509Sdim	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
1214224135Sdim		return WRONG;
1215235633Sdim	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
1216224135Sdim		return WRONG;
1217224135Sdim	/*
1218224135Sdim	** Turn yourtm.tm_year into an actual year number for now.
1219224135Sdim	** It is converted back to an offset from TM_YEAR_BASE later.
1220224135Sdim	*/
1221224135Sdim	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
1222224135Sdim		return WRONG;
1223224135Sdim	while (yourtm.tm_mday <= 0) {
1224224135Sdim		if (increment_overflow(&yourtm.tm_year, -1))
1225224135Sdim			return WRONG;
1226224135Sdim		yourtm.tm_mday += year_lengths[isleap(yourtm.tm_year)];
1227224135Sdim	}
1228224135Sdim	while (yourtm.tm_mday > DAYSPERLYEAR) {
1229224135Sdim		yourtm.tm_mday -= year_lengths[isleap(yourtm.tm_year)];
1230224135Sdim		if (increment_overflow(&yourtm.tm_year, 1))
1231224135Sdim			return WRONG;
1232224135Sdim	}
1233224135Sdim	for ( ; ; ) {
1234224135Sdim		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
1235224135Sdim		if (yourtm.tm_mday <= i)
1236224135Sdim			break;
1237226890Sdim		yourtm.tm_mday -= i;
1238226890Sdim		if (++yourtm.tm_mon >= MONSPERYEAR) {
1239226890Sdim			yourtm.tm_mon = 0;
1240226890Sdim			if (increment_overflow(&yourtm.tm_year, 1))
1241226890Sdim				return WRONG;
1242226890Sdim		}
1243226890Sdim	}
1244224135Sdim	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
1245224135Sdim		return WRONG;
1246224135Sdim	if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
1247224135Sdim		/*
1248224135Sdim		** We can't set tm_sec to 0, because that might push the
1249224135Sdim		** time below the minimum representable time.
1250224135Sdim		** Set tm_sec to 59 instead.
1251224135Sdim		** This assumes that the minimum representable time is
1252235633Sdim		** not in the same minute that a leap second was deleted from,
1253224135Sdim		** which is a safer assumption than using 58 would be.
1254224135Sdim		*/
1255224135Sdim		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
1256224135Sdim			return WRONG;
1257245431Sdim		saved_seconds = yourtm.tm_sec;
1258252723Sdim		yourtm.tm_sec = SECSPERMIN - 1;
1259224135Sdim	} else {
1260252723Sdim		saved_seconds = yourtm.tm_sec;
1261224135Sdim		yourtm.tm_sec = 0;
1262224135Sdim	}
1263224135Sdim	/*
1264224135Sdim	** Calculate the number of magnitude bits in a time_t
1265224135Sdim	** (this works regardless of whether time_t is
1266224135Sdim	** signed or unsigned, though lint complains if unsigned).
1267245431Sdim	*/
1268245431Sdim	for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
1269245431Sdim		continue;
1270235633Sdim	/*
1271235633Sdim	** If time_t is signed, then 0 is the median value,
1272235633Sdim	** if time_t is unsigned, then 1 << bits is median.
1273224135Sdim	*/
1274224135Sdim	t = (t < 0) ? 0 : ((time_t) 1 << bits);
1275224135Sdim	for ( ; ; ) {
1276224135Sdim		(*funcp)(&t, offset, &mytm);
1277235633Sdim		dir = tmcomp(&mytm, &yourtm);
1278235633Sdim		if (dir != 0) {
1279235633Sdim			if (bits-- < 0)
1280235633Sdim				return WRONG;
1281235633Sdim			if (bits < 0)
1282235633Sdim				--t;
1283263509Sdim			else if (dir > 0)
1284263509Sdim				t -= (time_t) 1 << bits;
1285263509Sdim			else	t += (time_t) 1 << bits;
1286263509Sdim			continue;
1287235633Sdim		}
1288235633Sdim		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
1289235633Sdim			break;
1290235633Sdim		/*
1291235633Sdim		** Right time, wrong type.
1292235633Sdim		** Hunt for right time, right type.
1293224135Sdim		** It's okay to guess wrong since the guess
1294224135Sdim		** gets checked.
1295224135Sdim		*/
1296224135Sdim		/*
1297224135Sdim		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
1298224135Sdim		*/
1299224135Sdim		sp = (const struct state *)
1300224135Sdim			(((void *) funcp == (void *) localsub) ?
1301224135Sdim			lclptr : gmtptr);
1302224135Sdim#ifdef ALL_STATE
1303224135Sdim		if (sp == NULL)
1304224135Sdim			return WRONG;
1305224135Sdim#endif /* defined ALL_STATE */
1306224135Sdim		for (i = 0; i < sp->typecnt; ++i) {
1307224135Sdim			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
1308235633Sdim				continue;
1309235633Sdim			for (j = 0; j < sp->typecnt; ++j) {
1310224135Sdim				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
1311224135Sdim					continue;
1312224135Sdim				newt = t + sp->ttis[j].tt_gmtoff -
1313224135Sdim					sp->ttis[i].tt_gmtoff;
1314224135Sdim				(*funcp)(&newt, offset, &mytm);
1315224135Sdim				if (tmcomp(&mytm, &yourtm) != 0)
1316224135Sdim					continue;
1317224135Sdim				if (mytm.tm_isdst != yourtm.tm_isdst)
1318224135Sdim					continue;
1319224135Sdim				/*
1320224135Sdim				** We have a match.
1321224135Sdim				*/
1322224135Sdim				t = newt;
1323224135Sdim				goto label;
1324224135Sdim			}
1325224135Sdim		}
1326224135Sdim		return WRONG;
1327224135Sdim	}
1328224135Sdimlabel:
1329224135Sdim	newt = t + saved_seconds;
1330224135Sdim	if ((newt < t) != (saved_seconds < 0))
1331224135Sdim		return WRONG;
1332224135Sdim	t = newt;
1333224135Sdim	(*funcp)(&t, offset, tmp);
1334224135Sdim	*okayp = TRUE;
1335224135Sdim	return t;
1336224135Sdim}
1337224135Sdim
1338224135Sdimstatic time_t
1339235633Sdimtime1(tmp, funcp, offset)
1340235633Sdimstruct tm * const	tmp;
1341235633Sdimvoid (* const		funcp)();
1342235633Sdimconst long		offset;
1343235633Sdim{
1344235633Sdim	register time_t			t;
1345235633Sdim	register const struct state *	sp;
1346235633Sdim	register int			samei, otheri;
1347235633Sdim	int				okay;
1348224135Sdim
1349235633Sdim	if (tmp->tm_isdst > 1)
1350224135Sdim		tmp->tm_isdst = 1;
1351245431Sdim	t = time2(tmp, funcp, offset, &okay);
1352235633Sdim#ifdef PCTS
1353224135Sdim	/*
1354224135Sdim	** PCTS code courtesy Grant Sullivan (grant@osf.org).
1355224135Sdim	*/
1356224135Sdim	if (okay)
1357224135Sdim		return t;
1358224135Sdim	if (tmp->tm_isdst < 0)
1359245431Sdim		tmp->tm_isdst = 0;	/* reset to std and try again */
1360224135Sdim#endif /* defined PCTS */
1361245431Sdim#ifndef PCTS
1362245431Sdim	if (okay || tmp->tm_isdst < 0)
1363224135Sdim		return t;
1364245431Sdim#endif /* !defined PCTS */
1365245431Sdim	/*
1366245431Sdim	** We're supposed to assume that somebody took a time of one type
1367245431Sdim	** and did some math on it that yielded a "struct tm" that's bad.
1368252723Sdim	** We try to divine the type they started from and adjust to the
1369252723Sdim	** type they need.
1370245431Sdim	*/
1371245431Sdim	/*
1372245431Sdim	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
1373245431Sdim	*/
1374224135Sdim	sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
1375245431Sdim		lclptr : gmtptr);
1376252723Sdim#ifdef ALL_STATE
1377245431Sdim	if (sp == NULL)
1378245431Sdim		return WRONG;
1379245431Sdim#endif /* defined ALL_STATE */
1380245431Sdim	for (samei = 0; samei < sp->typecnt; ++samei) {
1381245431Sdim		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
1382245431Sdim			continue;
1383245431Sdim		for (otheri = 0; otheri < sp->typecnt; ++otheri) {
1384245431Sdim			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
1385245431Sdim				continue;
1386263509Sdim			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
1387245431Sdim					sp->ttis[samei].tt_gmtoff;
1388245431Sdim			tmp->tm_isdst = !tmp->tm_isdst;
1389245431Sdim			t = time2(tmp, funcp, offset, &okay);
1390245431Sdim			if (okay)
1391224135Sdim				return t;
1392224135Sdim			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
1393224135Sdim					sp->ttis[samei].tt_gmtoff;
1394224135Sdim			tmp->tm_isdst = !tmp->tm_isdst;
1395224135Sdim		}
1396226890Sdim	}
1397226890Sdim	return WRONG;
1398226890Sdim}
1399226890Sdim
1400226890Sdimtime_t
1401226890Sdimmktime(tmp)
1402226890Sdimstruct tm * const	tmp;
1403224135Sdim{
1404224135Sdim	return time1(tmp, localsub, 0L);
1405224135Sdim}
1406224135Sdim
1407224135Sdim#ifdef STD_INSPIRED
1408224135Sdim
1409224135Sdimtime_t
1410224135Sdimtimelocal(tmp)
1411224135Sdimstruct tm * const	tmp;
1412224135Sdim{
1413224135Sdim	tmp->tm_isdst = -1;	/* in case it wasn't initialized */
1414224135Sdim	return mktime(tmp);
1415224135Sdim}
1416224135Sdim
1417224135Sdimtime_t
1418224135Sdimtimegm(tmp)
1419224135Sdimstruct tm * const	tmp;
1420224135Sdim{
1421224135Sdim	tmp->tm_isdst = 0;
1422224135Sdim	return time1(tmp, gmtsub, 0L);
1423224135Sdim}
1424235633Sdim
1425235633Sdimtime_t
1426224135Sdimtimeoff(tmp, offset)
1427224135Sdimstruct tm * const	tmp;
1428224135Sdimconst long		offset;
1429224135Sdim{
1430224135Sdim	tmp->tm_isdst = 0;
1431224135Sdim	return time1(tmp, gmtsub, offset);
1432224135Sdim}
1433224135Sdim
1434224135Sdim#endif /* defined STD_INSPIRED */
1435224135Sdim
1436224135Sdim#ifdef CMUCS
1437263509Sdim
1438263509Sdim/*
1439263509Sdim** The following is supplied for compatibility with
1440224135Sdim** previous versions of the CMUCS runtime library.
1441224135Sdim*/
1442224135Sdim
1443224135Sdimlong
1444224135Sdimgtime(tmp)
1445235633Sdimstruct tm * const	tmp;
1446235633Sdim{
1447235633Sdim	const time_t	t = mktime(tmp);
1448224135Sdim
1449224135Sdim	if (t == WRONG)
1450224135Sdim		return -1;
1451224135Sdim	return t;
1452224135Sdim}
1453224135Sdim
1454224135Sdim#endif /* defined CMUCS */
1455224135Sdim
1456224135Sdim/*
1457224135Sdim** XXX--is the below the right way to conditionalize??
1458224135Sdim*/
1459224135Sdim
1460224135Sdim#ifdef STD_INSPIRED
1461224135Sdim
1462224135Sdim/*
1463224135Sdim** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
1464224135Sdim** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which
1465224135Sdim** is not the case if we are accounting for leap seconds.
1466224135Sdim** So, we provide the following conversion routines for use
1467224135Sdim** when exchanging timestamps with POSIX conforming systems.
1468224135Sdim*/
1469224135Sdim
1470224135Sdimstatic long
1471224135Sdimleapcorr(timep)
1472224135Sdimtime_t *	timep;
1473224135Sdim{
1474224135Sdim	register struct state *		sp;
1475224135Sdim	register struct lsinfo *	lp;
1476224135Sdim	register int			i;
1477224135Sdim
1478224135Sdim	if (!lcl_is_set)
1479224135Sdim		(void) tzset();
1480224135Sdim	sp = lclptr;
1481224135Sdim	i = sp->leapcnt;
1482224135Sdim	while (--i >= 0) {
1483224135Sdim		lp = &sp->lsis[i];
1484224135Sdim		if (*timep >= lp->ls_trans)
1485224135Sdim			return lp->ls_corr;
1486263509Sdim	}
1487263509Sdim	return 0;
1488263509Sdim}
1489224135Sdim
1490224135Sdimtime_t
1491224135Sdimtime2posix(t)
1492224135Sdimtime_t	t;
1493224135Sdim{
1494224135Sdim	return t - leapcorr(&t);
1495224135Sdim}
1496224135Sdim
1497224135Sdimtime_t
1498224135Sdimposix2time(t)
1499224135Sdimtime_t	t;
1500224135Sdim{
1501235633Sdim	time_t	x;
1502235633Sdim	time_t	y;
1503224135Sdim
1504224135Sdim	/*
1505224135Sdim	** For a positive leap second hit, the result
1506224135Sdim	** is not unique.  For a negative leap second
1507224135Sdim	** hit, the corresponding time doesn't exist,
1508224135Sdim	** so we return an adjacent second.
1509224135Sdim	*/
1510224135Sdim	x = t + leapcorr(&t);
1511224135Sdim	y = x - leapcorr(&x);
1512224135Sdim	if (y < t) {
1513224135Sdim		do {
1514224135Sdim			x++;
1515224135Sdim			y = x - leapcorr(&x);
1516224135Sdim		} while (y < t);
1517224135Sdim		if (t != y)
1518224135Sdim			return x - 1;
1519224135Sdim	} else if (y > t) {
1520224135Sdim		do {
1521224135Sdim			--x;
1522224135Sdim			y = x - leapcorr(&x);
1523224135Sdim		} while (y > t);
1524224135Sdim		if (t != y)
1525224135Sdim			return x + 1;
1526224135Sdim	}
1527224135Sdim	return x;
1528224135Sdim}
1529224135Sdim
1530224135Sdim#endif /* defined STD_INSPIRED */
1531224135Sdim