xref: /onnv-gate/usr/src/lib/libbc/libc/gen/common/localtime.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 1995-2002 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 	  /* from Arthur Olson's 6.1 */
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate /*LINTLIBRARY*/
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #include <tzfile.h>
33*0Sstevel@tonic-gate #include <time.h>
34*0Sstevel@tonic-gate #include <string.h>
35*0Sstevel@tonic-gate #include <ctype.h>
36*0Sstevel@tonic-gate #include <stdio.h>	/* for NULL */
37*0Sstevel@tonic-gate #include <fcntl.h>
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate #include <sys/param.h>	/* for MAXPATHLEN */
40*0Sstevel@tonic-gate 
41*0Sstevel@tonic-gate #undef	FILENAME_MAX
42*0Sstevel@tonic-gate #define	FILENAME_MAX	MAXPATHLEN
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate #ifdef __STDC__
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate #define P(s)		s
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate #else /* !defined __STDC__ */
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate /*
51*0Sstevel@tonic-gate ** Memory management functions
52*0Sstevel@tonic-gate */
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate extern char *	calloc();
55*0Sstevel@tonic-gate extern char *	malloc();
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate /*
58*0Sstevel@tonic-gate ** Communication with the environment
59*0Sstevel@tonic-gate */
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate extern char *	getenv();
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate #define ASTERISK	*
64*0Sstevel@tonic-gate #define P(s)		(/ASTERISK s ASTERISK/)
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate #define const
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate #endif /* !defined __STDC__ */
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate #ifndef TRUE
71*0Sstevel@tonic-gate #define TRUE		1
72*0Sstevel@tonic-gate #define FALSE		0
73*0Sstevel@tonic-gate #endif /* !defined TRUE */
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate #define ACCESS_MODE	O_RDONLY
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate #define OPEN_MODE	O_RDONLY
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate /*
80*0Sstevel@tonic-gate ** Someone might make incorrect use of a time zone abbreviation:
81*0Sstevel@tonic-gate **	1.	They might reference tzname[0] before calling tzset (explicitly
82*0Sstevel@tonic-gate **	 	or implicitly).
83*0Sstevel@tonic-gate **	2.	They might reference tzname[1] before calling tzset (explicitly
84*0Sstevel@tonic-gate **	 	or implicitly).
85*0Sstevel@tonic-gate **	3.	They might reference tzname[1] after setting to a time zone
86*0Sstevel@tonic-gate **		in which Daylight Saving Time is never observed.
87*0Sstevel@tonic-gate **	4.	They might reference tzname[0] after setting to a time zone
88*0Sstevel@tonic-gate **		in which Standard Time is never observed.
89*0Sstevel@tonic-gate **	5.	They might reference tm.TM_ZONE after calling offtime.
90*0Sstevel@tonic-gate ** What's best to do in the above cases is open to debate;
91*0Sstevel@tonic-gate ** for now, we just set things up so that in any of the five cases
92*0Sstevel@tonic-gate ** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
93*0Sstevel@tonic-gate ** string "tzname[0] used before set", and similarly for the other cases.
94*0Sstevel@tonic-gate ** And another:  initialize tzname[0] to "ERA", with an explanation in the
95*0Sstevel@tonic-gate ** manual page of what this "time zone abbreviation" means (doing this so
96*0Sstevel@tonic-gate ** that tzname[0] has the "normal" length of three characters).
97*0Sstevel@tonic-gate */
98*0Sstevel@tonic-gate static const char *WILDABBR = "   ";
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate static const char *GMT = "GMT";
101*0Sstevel@tonic-gate 
102*0Sstevel@tonic-gate struct ttinfo {				/* time type information */
103*0Sstevel@tonic-gate 	long		tt_gmtoff;	/* GMT offset in seconds */
104*0Sstevel@tonic-gate 	int		tt_isdst;	/* used to set tm_isdst */
105*0Sstevel@tonic-gate 	int		tt_abbrind;	/* abbreviation list index */
106*0Sstevel@tonic-gate 	int		tt_ttisstd;	/* TRUE if transition is std time */
107*0Sstevel@tonic-gate };
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate struct state {
110*0Sstevel@tonic-gate 	int		timecnt;
111*0Sstevel@tonic-gate 	int		typecnt;
112*0Sstevel@tonic-gate 	int		charcnt;
113*0Sstevel@tonic-gate 	time_t		*ats;
114*0Sstevel@tonic-gate 	unsigned char	*types;
115*0Sstevel@tonic-gate 	struct ttinfo	*ttis;
116*0Sstevel@tonic-gate 	char		*chars;
117*0Sstevel@tonic-gate 	char		*last_tzload;	/* name of file tzload() last opened */
118*0Sstevel@tonic-gate };
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate struct rule {
121*0Sstevel@tonic-gate 	int		r_type;		/* type of rule--see below */
122*0Sstevel@tonic-gate 	int		r_day;		/* day number of rule */
123*0Sstevel@tonic-gate 	int		r_week;		/* week number of rule */
124*0Sstevel@tonic-gate 	int		r_mon;		/* month number of rule */
125*0Sstevel@tonic-gate 	long		r_time;		/* transition time of rule */
126*0Sstevel@tonic-gate };
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate #define	JULIAN_DAY		0	/* Jn - Julian day */
129*0Sstevel@tonic-gate #define	DAY_OF_YEAR		1	/* n - day of year */
130*0Sstevel@tonic-gate #define	MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d - month, week, day of week */
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate /*
133*0Sstevel@tonic-gate ** Prototypes for static functions.
134*0Sstevel@tonic-gate */
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate static int		allocall P((register struct state * sp));
137*0Sstevel@tonic-gate static long		detzcode P((const char * codep));
138*0Sstevel@tonic-gate static void		freeall P((register struct state * sp));
139*0Sstevel@tonic-gate static const char *	getzname P((const char * strp, const int i));
140*0Sstevel@tonic-gate static const char *	getnum P((const char * strp, int * nump, int min,
141*0Sstevel@tonic-gate 				int max));
142*0Sstevel@tonic-gate static const char *	getsecs P((const char * strp, long * secsp));
143*0Sstevel@tonic-gate static const char *	getoffset P((const char * strp, long * offsetp));
144*0Sstevel@tonic-gate static const char *	getrule P((const char * strp, struct rule * rulep));
145*0Sstevel@tonic-gate static void		gmtload P((struct state * sp));
146*0Sstevel@tonic-gate static void		gmtsub P((const time_t * timep, long offset,
147*0Sstevel@tonic-gate 				struct tm * tmp));
148*0Sstevel@tonic-gate static void		localsub P((const time_t * timep, long offset,
149*0Sstevel@tonic-gate 				struct tm * tmp));
150*0Sstevel@tonic-gate static void		normalize P((int * tensptr, int * unitsptr, int base));
151*0Sstevel@tonic-gate static void		settzname P((void));
152*0Sstevel@tonic-gate static time_t		time1 P((struct tm * tmp, void (* funcp)(),
153*0Sstevel@tonic-gate 				long offset));
154*0Sstevel@tonic-gate static time_t		time2 P((struct tm *tmp, void (* funcp)(),
155*0Sstevel@tonic-gate 				long offset, int * okayp));
156*0Sstevel@tonic-gate static void		timesub P((const time_t * timep, long offset,
157*0Sstevel@tonic-gate 				struct tm * tmp));
158*0Sstevel@tonic-gate static int		tmcomp P((const struct tm * atmp,
159*0Sstevel@tonic-gate 				const struct tm * btmp));
160*0Sstevel@tonic-gate static time_t		transtime P((time_t janfirst, int year,
161*0Sstevel@tonic-gate 				const struct rule * rulep, long offset));
162*0Sstevel@tonic-gate static int		tzload P((const char * name, struct state * sp));
163*0Sstevel@tonic-gate static int		tzparse P((const char * name, struct state * sp,
164*0Sstevel@tonic-gate 				int lastditch));
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate static struct state *	lclptr;
167*0Sstevel@tonic-gate static struct state *	gmtptr;
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate static int		lcl_is_set;
170*0Sstevel@tonic-gate static int		gmt_is_set;
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate #ifdef S5EMUL
173*0Sstevel@tonic-gate char *			tzname[2] = {
174*0Sstevel@tonic-gate 	"GMT",
175*0Sstevel@tonic-gate 	"   ",
176*0Sstevel@tonic-gate };
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate time_t			timezone = 0;
179*0Sstevel@tonic-gate time_t			altzone = 0;
180*0Sstevel@tonic-gate int			daylight = 0;
181*0Sstevel@tonic-gate #endif /* defined S5EMUL */
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate static long
detzcode(codep)184*0Sstevel@tonic-gate detzcode(codep)
185*0Sstevel@tonic-gate const char * const	codep;
186*0Sstevel@tonic-gate {
187*0Sstevel@tonic-gate 	register long	result;
188*0Sstevel@tonic-gate 	register int	i;
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	result = 0;
191*0Sstevel@tonic-gate 	for (i = 0; i < 4; ++i)
192*0Sstevel@tonic-gate 		result = (result << 8) | (codep[i] & 0xff);
193*0Sstevel@tonic-gate 	return result;
194*0Sstevel@tonic-gate }
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate /*
197*0Sstevel@tonic-gate ** Free up existing items pointed to by the specified "state" structure,
198*0Sstevel@tonic-gate ** and allocate new ones of sizes specified by that "state" structure.
199*0Sstevel@tonic-gate ** Return 0 on success; return -1 and free all previously-allocated items
200*0Sstevel@tonic-gate ** on failure.
201*0Sstevel@tonic-gate */
202*0Sstevel@tonic-gate static int
allocall(sp)203*0Sstevel@tonic-gate allocall(sp)
204*0Sstevel@tonic-gate register struct state * const	sp;
205*0Sstevel@tonic-gate {
206*0Sstevel@tonic-gate 	freeall(sp);
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	if (sp->timecnt != 0) {
209*0Sstevel@tonic-gate 		sp->ats = (time_t *)calloc((unsigned)sp->timecnt,
210*0Sstevel@tonic-gate 		   (unsigned)sizeof (time_t));
211*0Sstevel@tonic-gate 		if (sp->ats == NULL)
212*0Sstevel@tonic-gate 			return -1;
213*0Sstevel@tonic-gate 		sp->types =
214*0Sstevel@tonic-gate 		   (unsigned char *)calloc((unsigned)sp->timecnt,
215*0Sstevel@tonic-gate 		   (unsigned)sizeof (unsigned char));
216*0Sstevel@tonic-gate 		if (sp->types == NULL) {
217*0Sstevel@tonic-gate 			freeall(sp);
218*0Sstevel@tonic-gate 			return -1;
219*0Sstevel@tonic-gate 		}
220*0Sstevel@tonic-gate 	}
221*0Sstevel@tonic-gate 	sp->ttis =
222*0Sstevel@tonic-gate 	  (struct ttinfo *)calloc((unsigned)sp->typecnt,
223*0Sstevel@tonic-gate 	  (unsigned)sizeof (struct ttinfo));
224*0Sstevel@tonic-gate 	if (sp->ttis == NULL) {
225*0Sstevel@tonic-gate 		freeall(sp);
226*0Sstevel@tonic-gate 		return -1;
227*0Sstevel@tonic-gate 	}
228*0Sstevel@tonic-gate 	sp->chars = (char *)calloc((unsigned)sp->charcnt + 1,
229*0Sstevel@tonic-gate 	  (unsigned)sizeof (char));
230*0Sstevel@tonic-gate 	if (sp->chars == NULL) {
231*0Sstevel@tonic-gate 		freeall(sp);
232*0Sstevel@tonic-gate 		return -1;
233*0Sstevel@tonic-gate 	}
234*0Sstevel@tonic-gate 	return 0;
235*0Sstevel@tonic-gate }
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate /*
238*0Sstevel@tonic-gate ** Free all the items pointed to by the specified "state" structure (except for
239*0Sstevel@tonic-gate ** "chars", which might have other references to it), and zero out all the
240*0Sstevel@tonic-gate ** pointers to those items.
241*0Sstevel@tonic-gate */
242*0Sstevel@tonic-gate static void
freeall(sp)243*0Sstevel@tonic-gate freeall(sp)
244*0Sstevel@tonic-gate register struct state * const	sp;
245*0Sstevel@tonic-gate {
246*0Sstevel@tonic-gate 	if (sp->ttis) {
247*0Sstevel@tonic-gate 		free((char *)sp->ttis);
248*0Sstevel@tonic-gate 		sp->ttis = 0;
249*0Sstevel@tonic-gate 	}
250*0Sstevel@tonic-gate 	if (sp->types) {
251*0Sstevel@tonic-gate 		free((char *)sp->types);
252*0Sstevel@tonic-gate 		sp->types = 0;
253*0Sstevel@tonic-gate 	}
254*0Sstevel@tonic-gate 	if (sp->ats) {
255*0Sstevel@tonic-gate 		free((char *)sp->ats);
256*0Sstevel@tonic-gate 		sp->ats = 0;
257*0Sstevel@tonic-gate 	}
258*0Sstevel@tonic-gate }
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate #ifdef S5EMUL
261*0Sstevel@tonic-gate static void
settzname()262*0Sstevel@tonic-gate settzname()
263*0Sstevel@tonic-gate {
264*0Sstevel@tonic-gate 	register const struct state * const	sp = lclptr;
265*0Sstevel@tonic-gate 	register int				i;
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	tzname[0] = (char *)GMT;
268*0Sstevel@tonic-gate 	tzname[1] = (char *)WILDABBR;
269*0Sstevel@tonic-gate 	daylight = 0;
270*0Sstevel@tonic-gate 	timezone = 0;
271*0Sstevel@tonic-gate 	altzone = 0;
272*0Sstevel@tonic-gate 	if (sp == NULL)
273*0Sstevel@tonic-gate 		return;
274*0Sstevel@tonic-gate 	for (i = 0; i < sp->typecnt; ++i) {
275*0Sstevel@tonic-gate 		register const struct ttinfo * const	ttisp = &sp->ttis[i];
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 		tzname[ttisp->tt_isdst] =
278*0Sstevel@tonic-gate 			(char *) &sp->chars[ttisp->tt_abbrind];
279*0Sstevel@tonic-gate 		if (ttisp->tt_isdst)
280*0Sstevel@tonic-gate 			daylight = 1;
281*0Sstevel@tonic-gate 		if (i == 0 || !ttisp->tt_isdst)
282*0Sstevel@tonic-gate 			timezone = -(ttisp->tt_gmtoff);
283*0Sstevel@tonic-gate 		if (i == 0 || ttisp->tt_isdst)
284*0Sstevel@tonic-gate 			altzone = -(ttisp->tt_gmtoff);
285*0Sstevel@tonic-gate 	}
286*0Sstevel@tonic-gate 	/*
287*0Sstevel@tonic-gate 	** And to get the latest zone names into tzname. . .
288*0Sstevel@tonic-gate 	*/
289*0Sstevel@tonic-gate 	for (i = 0; i < sp->timecnt; ++i) {
290*0Sstevel@tonic-gate 		register const struct ttinfo * const	ttisp =
291*0Sstevel@tonic-gate 							&sp->ttis[sp->types[i]];
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 		tzname[ttisp->tt_isdst] =
294*0Sstevel@tonic-gate 			(char *) &sp->chars[ttisp->tt_abbrind];
295*0Sstevel@tonic-gate 	}
296*0Sstevel@tonic-gate }
297*0Sstevel@tonic-gate #endif
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate /*
300*0Sstevel@tonic-gate ** Maximum size of a time zone file.
301*0Sstevel@tonic-gate */
302*0Sstevel@tonic-gate #define	MAX_TZFILESZ	(sizeof (struct tzhead) + \
303*0Sstevel@tonic-gate 			TZ_MAX_TIMES * (4 + sizeof (char)) + \
304*0Sstevel@tonic-gate 			TZ_MAX_TYPES * (4 + 2 * sizeof (char)) + \
305*0Sstevel@tonic-gate 			TZ_MAX_CHARS * sizeof (char) + \
306*0Sstevel@tonic-gate 			TZ_MAX_LEAPS * 2 * 4 + \
307*0Sstevel@tonic-gate 			TZ_MAX_TYPES * sizeof (char))
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate static int
tzload(name,sp)310*0Sstevel@tonic-gate tzload(name, sp)
311*0Sstevel@tonic-gate register const char *	name;
312*0Sstevel@tonic-gate register struct state * const	sp;
313*0Sstevel@tonic-gate {
314*0Sstevel@tonic-gate 	register const char *	p;
315*0Sstevel@tonic-gate 	register int		i;
316*0Sstevel@tonic-gate 	register int		fid;
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	if (name == NULL && (name = (const char *)TZDEFAULT) == NULL)
319*0Sstevel@tonic-gate 		return -1;
320*0Sstevel@tonic-gate 	{
321*0Sstevel@tonic-gate 		register int 	doaccess;
322*0Sstevel@tonic-gate 		char		fullname[FILENAME_MAX + 1];
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate 		if (name[0] == ':')
325*0Sstevel@tonic-gate 			++name;
326*0Sstevel@tonic-gate 		doaccess = name[0] == '/';
327*0Sstevel@tonic-gate 		if (!doaccess) {
328*0Sstevel@tonic-gate 			if ((p = TZDIR) == NULL)
329*0Sstevel@tonic-gate 				return -1;
330*0Sstevel@tonic-gate 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
331*0Sstevel@tonic-gate 				return -1;
332*0Sstevel@tonic-gate 			(void) strcpy(fullname, p);
333*0Sstevel@tonic-gate 			(void) strcat(fullname, "/");
334*0Sstevel@tonic-gate 			(void) strcat(fullname, name);
335*0Sstevel@tonic-gate 			/*
336*0Sstevel@tonic-gate 			** Set doaccess if '.' (as in "../") shows up in name.
337*0Sstevel@tonic-gate 			*/
338*0Sstevel@tonic-gate 			if (strchr(name, '.') != NULL)
339*0Sstevel@tonic-gate 				doaccess = TRUE;
340*0Sstevel@tonic-gate 			name = fullname;
341*0Sstevel@tonic-gate 		}
342*0Sstevel@tonic-gate 		if (sp->last_tzload && strcmp(sp->last_tzload, name) == 0)
343*0Sstevel@tonic-gate 			return (0);
344*0Sstevel@tonic-gate 		if (doaccess && access(name, ACCESS_MODE) != 0)
345*0Sstevel@tonic-gate 			return -1;
346*0Sstevel@tonic-gate 		if ((fid = open(name, OPEN_MODE)) == -1)
347*0Sstevel@tonic-gate 			return -1;
348*0Sstevel@tonic-gate 	}
349*0Sstevel@tonic-gate 	{
350*0Sstevel@tonic-gate 		register const struct tzhead *	tzhp;
351*0Sstevel@tonic-gate 		char				buf[MAX_TZFILESZ];
352*0Sstevel@tonic-gate 		int				leapcnt;
353*0Sstevel@tonic-gate 		int				ttisstdcnt;
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 		i = read(fid, buf, sizeof buf);
356*0Sstevel@tonic-gate 		if (close(fid) != 0 || i < sizeof *tzhp)
357*0Sstevel@tonic-gate 			return -1;
358*0Sstevel@tonic-gate 		tzhp = (struct tzhead *) buf;
359*0Sstevel@tonic-gate 		ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt);
360*0Sstevel@tonic-gate 		leapcnt = (int) detzcode(tzhp->tzh_leapcnt);
361*0Sstevel@tonic-gate 		sp->timecnt = (int) detzcode(tzhp->tzh_timecnt);
362*0Sstevel@tonic-gate 		sp->typecnt = (int) detzcode(tzhp->tzh_typecnt);
363*0Sstevel@tonic-gate 		sp->charcnt = (int) detzcode(tzhp->tzh_charcnt);
364*0Sstevel@tonic-gate 		if (leapcnt < 0 || leapcnt > TZ_MAX_LEAPS ||
365*0Sstevel@tonic-gate 			sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
366*0Sstevel@tonic-gate 			sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
367*0Sstevel@tonic-gate 			sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
368*0Sstevel@tonic-gate 			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0))
369*0Sstevel@tonic-gate 				return -1;
370*0Sstevel@tonic-gate 		if (i < sizeof *tzhp +
371*0Sstevel@tonic-gate 			sp->timecnt * (4 + sizeof (char)) +
372*0Sstevel@tonic-gate 			sp->typecnt * (4 + 2 * sizeof (char)) +
373*0Sstevel@tonic-gate 			sp->charcnt * sizeof (char) +
374*0Sstevel@tonic-gate 			leapcnt * 2 * 4 +
375*0Sstevel@tonic-gate 			ttisstdcnt * sizeof (char))
376*0Sstevel@tonic-gate 				return -1;
377*0Sstevel@tonic-gate 		if (allocall(sp) < 0)
378*0Sstevel@tonic-gate 			return -1;
379*0Sstevel@tonic-gate 		p = buf + sizeof *tzhp;
380*0Sstevel@tonic-gate 		for (i = 0; i < sp->timecnt; ++i) {
381*0Sstevel@tonic-gate 			sp->ats[i] = detzcode(p);
382*0Sstevel@tonic-gate 			p += 4;
383*0Sstevel@tonic-gate 		}
384*0Sstevel@tonic-gate 		for (i = 0; i < sp->timecnt; ++i) {
385*0Sstevel@tonic-gate 			sp->types[i] = (unsigned char) *p++;
386*0Sstevel@tonic-gate 			if (sp->types[i] >= sp->typecnt)
387*0Sstevel@tonic-gate 				return -1;
388*0Sstevel@tonic-gate 		}
389*0Sstevel@tonic-gate 		for (i = 0; i < sp->typecnt; ++i) {
390*0Sstevel@tonic-gate 			register struct ttinfo *	ttisp;
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 			ttisp = &sp->ttis[i];
393*0Sstevel@tonic-gate 			ttisp->tt_gmtoff = detzcode(p);
394*0Sstevel@tonic-gate 			p += 4;
395*0Sstevel@tonic-gate 			ttisp->tt_isdst = (unsigned char) *p++;
396*0Sstevel@tonic-gate 			if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
397*0Sstevel@tonic-gate 				return -1;
398*0Sstevel@tonic-gate 			ttisp->tt_abbrind = (unsigned char) *p++;
399*0Sstevel@tonic-gate 			if (ttisp->tt_abbrind < 0 ||
400*0Sstevel@tonic-gate 				ttisp->tt_abbrind > sp->charcnt)
401*0Sstevel@tonic-gate 					return -1;
402*0Sstevel@tonic-gate 		}
403*0Sstevel@tonic-gate 		for (i = 0; i < sp->charcnt-1; ++i)
404*0Sstevel@tonic-gate 			sp->chars[i] = *p++;
405*0Sstevel@tonic-gate 		sp->chars[i] = '\0';	/* ensure '\0' at end */
406*0Sstevel@tonic-gate 		p += (4 + 4) * leapcnt;	/* skip leap seconds list */
407*0Sstevel@tonic-gate 		for (i = 0; i < sp->typecnt; ++i) {
408*0Sstevel@tonic-gate 			register struct ttinfo *	ttisp;
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 			ttisp = &sp->ttis[i];
411*0Sstevel@tonic-gate 			if (ttisstdcnt == 0)
412*0Sstevel@tonic-gate 				ttisp->tt_ttisstd = FALSE;
413*0Sstevel@tonic-gate 			else {
414*0Sstevel@tonic-gate 				ttisp->tt_ttisstd = *p++;
415*0Sstevel@tonic-gate 				if (ttisp->tt_ttisstd != TRUE &&
416*0Sstevel@tonic-gate 					ttisp->tt_ttisstd != FALSE)
417*0Sstevel@tonic-gate 						return -1;
418*0Sstevel@tonic-gate 			}
419*0Sstevel@tonic-gate 		}
420*0Sstevel@tonic-gate 	}
421*0Sstevel@tonic-gate 	if (sp->last_tzload)
422*0Sstevel@tonic-gate 		free(sp->last_tzload);
423*0Sstevel@tonic-gate 	sp->last_tzload = strdup(name);
424*0Sstevel@tonic-gate 	return 0;
425*0Sstevel@tonic-gate }
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate static const int	mon_lengths[2][MONSPERYEAR] = {
428*0Sstevel@tonic-gate 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
429*0Sstevel@tonic-gate 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
430*0Sstevel@tonic-gate };
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate static const int	year_lengths[2] = {
433*0Sstevel@tonic-gate 	DAYSPERNYEAR, DAYSPERLYEAR
434*0Sstevel@tonic-gate };
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate /*
437*0Sstevel@tonic-gate ** Given a pointer into a time zone string, scan until a character that is not
438*0Sstevel@tonic-gate ** a valid character in a zone name is found.  Return a pointer to that
439*0Sstevel@tonic-gate ** character.
440*0Sstevel@tonic-gate ** Support both quoted and unquoted timezones.
441*0Sstevel@tonic-gate */
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate static const char *
getzname(strp,quoted)444*0Sstevel@tonic-gate getzname(strp, quoted)
445*0Sstevel@tonic-gate const char *	strp;
446*0Sstevel@tonic-gate int quoted;
447*0Sstevel@tonic-gate {
448*0Sstevel@tonic-gate 	unsigned char	c;
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 	if (quoted) {
451*0Sstevel@tonic-gate 		while ((c = (unsigned char)*strp) != '\0' &&
452*0Sstevel@tonic-gate 			(isalnum(c) || (c == '+') || (c == '-')))
453*0Sstevel@tonic-gate 				++strp;
454*0Sstevel@tonic-gate 	} else {
455*0Sstevel@tonic-gate 		while ((c = (unsigned char)*strp) != '\0' && !isdigit(c)
456*0Sstevel@tonic-gate 			&& (c != ',') && (c != '-') && (c != '+'))
457*0Sstevel@tonic-gate 				++strp;
458*0Sstevel@tonic-gate 	}
459*0Sstevel@tonic-gate 	return strp;
460*0Sstevel@tonic-gate }
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate /*
463*0Sstevel@tonic-gate ** Given a pointer into a time zone string, extract a number from that string.
464*0Sstevel@tonic-gate ** Check that the number is within a specified range; if it is not, return
465*0Sstevel@tonic-gate ** NULL.
466*0Sstevel@tonic-gate ** Otherwise, return a pointer to the first character not part of the number.
467*0Sstevel@tonic-gate */
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate static const char *
getnum(strp,nump,min,max)470*0Sstevel@tonic-gate getnum(strp, nump, min, max)
471*0Sstevel@tonic-gate register const char *	strp;
472*0Sstevel@tonic-gate int * const		nump;
473*0Sstevel@tonic-gate const int		min;
474*0Sstevel@tonic-gate const int		max;
475*0Sstevel@tonic-gate {
476*0Sstevel@tonic-gate 	register char	c;
477*0Sstevel@tonic-gate 	register int	num;
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	if (strp == NULL || !isdigit(*strp))
480*0Sstevel@tonic-gate 		return NULL;
481*0Sstevel@tonic-gate 	num = 0;
482*0Sstevel@tonic-gate 	while ((c = *strp) != '\0' && isdigit(c)) {
483*0Sstevel@tonic-gate 		num = num * 10 + (c - '0');
484*0Sstevel@tonic-gate 		if (num > max)
485*0Sstevel@tonic-gate 			return NULL;	/* illegal value */
486*0Sstevel@tonic-gate 		++strp;
487*0Sstevel@tonic-gate 	}
488*0Sstevel@tonic-gate 	if (num < min)
489*0Sstevel@tonic-gate 		return NULL;		/* illegal value */
490*0Sstevel@tonic-gate 	*nump = num;
491*0Sstevel@tonic-gate 	return strp;
492*0Sstevel@tonic-gate }
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate /*
495*0Sstevel@tonic-gate ** Given a pointer into a time zone string, extract a number of seconds,
496*0Sstevel@tonic-gate ** in hh[:mm[:ss]] form, from the string.
497*0Sstevel@tonic-gate ** If any error occurs, return NULL.
498*0Sstevel@tonic-gate ** Otherwise, return a pointer to the first character not part of the number
499*0Sstevel@tonic-gate ** of seconds.
500*0Sstevel@tonic-gate */
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate static const char *
getsecs(strp,secsp)503*0Sstevel@tonic-gate getsecs(strp, secsp)
504*0Sstevel@tonic-gate register const char *	strp;
505*0Sstevel@tonic-gate long * const		secsp;
506*0Sstevel@tonic-gate {
507*0Sstevel@tonic-gate 	int	num;
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 	strp = getnum(strp, &num, 0, HOURSPERDAY);
510*0Sstevel@tonic-gate 	if (strp == NULL)
511*0Sstevel@tonic-gate 		return NULL;
512*0Sstevel@tonic-gate 	*secsp = num * SECSPERHOUR;
513*0Sstevel@tonic-gate 	if (*strp == ':') {
514*0Sstevel@tonic-gate 		++strp;
515*0Sstevel@tonic-gate 		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
516*0Sstevel@tonic-gate 		if (strp == NULL)
517*0Sstevel@tonic-gate 			return NULL;
518*0Sstevel@tonic-gate 		*secsp += num * SECSPERMIN;
519*0Sstevel@tonic-gate 		if (*strp == ':') {
520*0Sstevel@tonic-gate 			++strp;
521*0Sstevel@tonic-gate 			strp = getnum(strp, &num, 0, SECSPERMIN - 1);
522*0Sstevel@tonic-gate 			if (strp == NULL)
523*0Sstevel@tonic-gate 				return NULL;
524*0Sstevel@tonic-gate 			*secsp += num;
525*0Sstevel@tonic-gate 		}
526*0Sstevel@tonic-gate 	}
527*0Sstevel@tonic-gate 	return strp;
528*0Sstevel@tonic-gate }
529*0Sstevel@tonic-gate 
530*0Sstevel@tonic-gate /*
531*0Sstevel@tonic-gate ** Given a pointer into a time zone string, extract an offset, in
532*0Sstevel@tonic-gate ** [+-]hh[:mm[:ss]] form, from the string.
533*0Sstevel@tonic-gate ** If any error occurs, return NULL.
534*0Sstevel@tonic-gate ** Otherwise, return a pointer to the first character not part of the time.
535*0Sstevel@tonic-gate */
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate static const char *
getoffset(strp,offsetp)538*0Sstevel@tonic-gate getoffset(strp, offsetp)
539*0Sstevel@tonic-gate register const char *	strp;
540*0Sstevel@tonic-gate long * const		offsetp;
541*0Sstevel@tonic-gate {
542*0Sstevel@tonic-gate 	register int	neg;
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate 	if (*strp == '-') {
545*0Sstevel@tonic-gate 		neg = 1;
546*0Sstevel@tonic-gate 		++strp;
547*0Sstevel@tonic-gate 	} else if (isdigit(*strp) || *strp++ == '+')
548*0Sstevel@tonic-gate 		neg = 0;
549*0Sstevel@tonic-gate 	else	return NULL;		/* illegal offset */
550*0Sstevel@tonic-gate 	strp = getsecs(strp, offsetp);
551*0Sstevel@tonic-gate 	if (strp == NULL)
552*0Sstevel@tonic-gate 		return NULL;		/* illegal time */
553*0Sstevel@tonic-gate 	if (neg)
554*0Sstevel@tonic-gate 		*offsetp = -*offsetp;
555*0Sstevel@tonic-gate 	return strp;
556*0Sstevel@tonic-gate }
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate /*
559*0Sstevel@tonic-gate ** Given a pointer into a time zone string, extract a rule in the form
560*0Sstevel@tonic-gate ** date[/time].  See POSIX section 8 for the format of "date" and "time".
561*0Sstevel@tonic-gate ** If a valid rule is not found, return NULL.
562*0Sstevel@tonic-gate ** Otherwise, return a pointer to the first character not part of the rule.
563*0Sstevel@tonic-gate */
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate static const char *
getrule(strp,rulep)566*0Sstevel@tonic-gate getrule(strp, rulep)
567*0Sstevel@tonic-gate const char *			strp;
568*0Sstevel@tonic-gate register struct rule * const	rulep;
569*0Sstevel@tonic-gate {
570*0Sstevel@tonic-gate 	if (*strp == 'J') {
571*0Sstevel@tonic-gate 		/*
572*0Sstevel@tonic-gate 		** Julian day.
573*0Sstevel@tonic-gate 		*/
574*0Sstevel@tonic-gate 		rulep->r_type = JULIAN_DAY;
575*0Sstevel@tonic-gate 		++strp;
576*0Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
577*0Sstevel@tonic-gate 	} else if (*strp == 'M') {
578*0Sstevel@tonic-gate 		/*
579*0Sstevel@tonic-gate 		** Month, week, day.
580*0Sstevel@tonic-gate 		*/
581*0Sstevel@tonic-gate 		rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
582*0Sstevel@tonic-gate 		++strp;
583*0Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
584*0Sstevel@tonic-gate 		if (strp == NULL)
585*0Sstevel@tonic-gate 			return NULL;
586*0Sstevel@tonic-gate 		if (*strp++ != '.')
587*0Sstevel@tonic-gate 			return NULL;
588*0Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_week, 1, 5);
589*0Sstevel@tonic-gate 		if (strp == NULL)
590*0Sstevel@tonic-gate 			return NULL;
591*0Sstevel@tonic-gate 		if (*strp++ != '.')
592*0Sstevel@tonic-gate 			return NULL;
593*0Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
594*0Sstevel@tonic-gate 	} else if (isdigit(*strp)) {
595*0Sstevel@tonic-gate 		/*
596*0Sstevel@tonic-gate 		** Day of year.
597*0Sstevel@tonic-gate 		*/
598*0Sstevel@tonic-gate 		rulep->r_type = DAY_OF_YEAR;
599*0Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
600*0Sstevel@tonic-gate 	} else	return NULL;		/* invalid format */
601*0Sstevel@tonic-gate 	if (strp == NULL)
602*0Sstevel@tonic-gate 		return NULL;
603*0Sstevel@tonic-gate 	if (*strp == '/') {
604*0Sstevel@tonic-gate 		/*
605*0Sstevel@tonic-gate 		** Time specified.
606*0Sstevel@tonic-gate 		*/
607*0Sstevel@tonic-gate 		++strp;
608*0Sstevel@tonic-gate 		strp = getsecs(strp, &rulep->r_time);
609*0Sstevel@tonic-gate 	} else	rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
610*0Sstevel@tonic-gate 	return strp;
611*0Sstevel@tonic-gate }
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate /*
614*0Sstevel@tonic-gate ** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
615*0Sstevel@tonic-gate ** year, a rule, and the offset from GMT at the time that rule takes effect,
616*0Sstevel@tonic-gate ** calculate the Epoch-relative time that rule takes effect.
617*0Sstevel@tonic-gate */
618*0Sstevel@tonic-gate 
619*0Sstevel@tonic-gate static time_t
transtime(janfirst,year,rulep,offset)620*0Sstevel@tonic-gate transtime(janfirst, year, rulep, offset)
621*0Sstevel@tonic-gate const time_t				janfirst;
622*0Sstevel@tonic-gate const int				year;
623*0Sstevel@tonic-gate register const struct rule * const	rulep;
624*0Sstevel@tonic-gate const long				offset;
625*0Sstevel@tonic-gate {
626*0Sstevel@tonic-gate 	register int	leapyear;
627*0Sstevel@tonic-gate 	register time_t	value;
628*0Sstevel@tonic-gate 	register int	i;
629*0Sstevel@tonic-gate 	int		d, m1, yy0, yy1, yy2, dow;
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	leapyear = isleap(year);
632*0Sstevel@tonic-gate 	switch (rulep->r_type) {
633*0Sstevel@tonic-gate 
634*0Sstevel@tonic-gate 	case JULIAN_DAY:
635*0Sstevel@tonic-gate 		/*
636*0Sstevel@tonic-gate 		** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
637*0Sstevel@tonic-gate 		** years.
638*0Sstevel@tonic-gate 		** In non-leap years, or if the day number is 59 or less, just
639*0Sstevel@tonic-gate 		** add SECSPERDAY times the day number-1 to the time of
640*0Sstevel@tonic-gate 		** January 1, midnight, to get the day.
641*0Sstevel@tonic-gate 		*/
642*0Sstevel@tonic-gate 		value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
643*0Sstevel@tonic-gate 		if (leapyear && rulep->r_day >= 60)
644*0Sstevel@tonic-gate 			value += SECSPERDAY;
645*0Sstevel@tonic-gate 		break;
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	case DAY_OF_YEAR:
648*0Sstevel@tonic-gate 		/*
649*0Sstevel@tonic-gate 		** n - day of year.
650*0Sstevel@tonic-gate 		** Just add SECSPERDAY times the day number to the time of
651*0Sstevel@tonic-gate 		** January 1, midnight, to get the day.
652*0Sstevel@tonic-gate 		*/
653*0Sstevel@tonic-gate 		value = janfirst + rulep->r_day * SECSPERDAY;
654*0Sstevel@tonic-gate 		break;
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate 	case MONTH_NTH_DAY_OF_WEEK:
657*0Sstevel@tonic-gate 		/*
658*0Sstevel@tonic-gate 		** Mm.n.d - nth "dth day" of month m.
659*0Sstevel@tonic-gate 		*/
660*0Sstevel@tonic-gate 		value = janfirst;
661*0Sstevel@tonic-gate 		for (i = 0; i < rulep->r_mon - 1; ++i)
662*0Sstevel@tonic-gate 			value += mon_lengths[leapyear][i] * SECSPERDAY;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 		/*
665*0Sstevel@tonic-gate 		** Use Zeller's Congruence to get day-of-week of first day of
666*0Sstevel@tonic-gate 		** month.
667*0Sstevel@tonic-gate 		*/
668*0Sstevel@tonic-gate 		m1 = (rulep->r_mon + 9) % 12 + 1;
669*0Sstevel@tonic-gate 		yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
670*0Sstevel@tonic-gate 		yy1 = yy0 / 100;
671*0Sstevel@tonic-gate 		yy2 = yy0 % 100;
672*0Sstevel@tonic-gate 		dow = ((26 * m1 - 2) / 10 +
673*0Sstevel@tonic-gate 			1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
674*0Sstevel@tonic-gate 		if (dow < 0)
675*0Sstevel@tonic-gate 			dow += DAYSPERWEEK;
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate 		/*
678*0Sstevel@tonic-gate 		** "dow" is the day-of-week of the first day of the month.  Get
679*0Sstevel@tonic-gate 		** the day-of-month (zero-origin) of the first "dow" day of the
680*0Sstevel@tonic-gate 		** month.
681*0Sstevel@tonic-gate 		*/
682*0Sstevel@tonic-gate 		d = rulep->r_day - dow;
683*0Sstevel@tonic-gate 		if (d < 0)
684*0Sstevel@tonic-gate 			d += DAYSPERWEEK;
685*0Sstevel@tonic-gate 		for (i = 1; i < rulep->r_week; ++i) {
686*0Sstevel@tonic-gate 			if (d + DAYSPERWEEK >=
687*0Sstevel@tonic-gate 				mon_lengths[leapyear][rulep->r_mon - 1])
688*0Sstevel@tonic-gate 					break;
689*0Sstevel@tonic-gate 			d += DAYSPERWEEK;
690*0Sstevel@tonic-gate 		}
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 		/*
693*0Sstevel@tonic-gate 		** "d" is the day-of-month (zero-origin) of the day we want.
694*0Sstevel@tonic-gate 		*/
695*0Sstevel@tonic-gate 		value += d * SECSPERDAY;
696*0Sstevel@tonic-gate 		break;
697*0Sstevel@tonic-gate 	}
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	/*
700*0Sstevel@tonic-gate 	** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
701*0Sstevel@tonic-gate 	** question.  To get the Epoch-relative time of the specified local
702*0Sstevel@tonic-gate 	** time on that day, add the transition time and the current offset
703*0Sstevel@tonic-gate 	** from GMT.
704*0Sstevel@tonic-gate 	*/
705*0Sstevel@tonic-gate 	return value + rulep->r_time + offset;
706*0Sstevel@tonic-gate }
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate /*
709*0Sstevel@tonic-gate ** Given a POSIX section 8-style TZ string, fill in the rule tables as
710*0Sstevel@tonic-gate ** appropriate.
711*0Sstevel@tonic-gate */
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate static int
tzparse(name,sp,lastditch)714*0Sstevel@tonic-gate tzparse(name, sp, lastditch)
715*0Sstevel@tonic-gate const char *			name;
716*0Sstevel@tonic-gate struct state * const	sp;
717*0Sstevel@tonic-gate const int			lastditch;
718*0Sstevel@tonic-gate {
719*0Sstevel@tonic-gate 	const char *			stdname;
720*0Sstevel@tonic-gate 	const char *			dstname;
721*0Sstevel@tonic-gate 	int				stdlen;
722*0Sstevel@tonic-gate 	int				dstlen;
723*0Sstevel@tonic-gate 	long				stdoffset;
724*0Sstevel@tonic-gate 	long				dstoffset;
725*0Sstevel@tonic-gate 	time_t *			atp;
726*0Sstevel@tonic-gate 	unsigned char *			typep;
727*0Sstevel@tonic-gate 	char *				cp;
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 	freeall(sp);			/* */
730*0Sstevel@tonic-gate 	stdname = name;
731*0Sstevel@tonic-gate 	if (lastditch) {
732*0Sstevel@tonic-gate 		stdlen = strlen(name);	/* length of standard zone name */
733*0Sstevel@tonic-gate 		name += stdlen;
734*0Sstevel@tonic-gate 		if (stdlen >= sizeof sp->chars)
735*0Sstevel@tonic-gate 			stdlen = (sizeof sp->chars) - 1;
736*0Sstevel@tonic-gate 	} else {
737*0Sstevel@tonic-gate 		if (*name == '<') {
738*0Sstevel@tonic-gate 			name++;
739*0Sstevel@tonic-gate 			stdname++;
740*0Sstevel@tonic-gate 			name = getzname(name, 1);
741*0Sstevel@tonic-gate 			if (*name != '>') {
742*0Sstevel@tonic-gate 				return (-1);
743*0Sstevel@tonic-gate 			}
744*0Sstevel@tonic-gate 			stdlen = name - stdname;
745*0Sstevel@tonic-gate 			name++;
746*0Sstevel@tonic-gate 		} else {
747*0Sstevel@tonic-gate 			name = getzname(name, 0);
748*0Sstevel@tonic-gate 			stdlen = name - stdname;
749*0Sstevel@tonic-gate 		}
750*0Sstevel@tonic-gate 		if (stdlen < 3)
751*0Sstevel@tonic-gate 			return -1;
752*0Sstevel@tonic-gate 	}
753*0Sstevel@tonic-gate 	if (*name == '\0')
754*0Sstevel@tonic-gate 		stdoffset = 0;
755*0Sstevel@tonic-gate 	else {
756*0Sstevel@tonic-gate 		name = getoffset(name, &stdoffset);
757*0Sstevel@tonic-gate 		if (name == NULL)
758*0Sstevel@tonic-gate 			return -1;
759*0Sstevel@tonic-gate 	}
760*0Sstevel@tonic-gate 	if (*name != '\0') {
761*0Sstevel@tonic-gate 		dstname = name;
762*0Sstevel@tonic-gate 		if (*name == '<') {
763*0Sstevel@tonic-gate 			name++;
764*0Sstevel@tonic-gate 			dstname++;
765*0Sstevel@tonic-gate 			name = getzname(name, 1);
766*0Sstevel@tonic-gate 			if (*name != '>') {
767*0Sstevel@tonic-gate 				return (-1);
768*0Sstevel@tonic-gate 			}
769*0Sstevel@tonic-gate 			dstlen = name - dstname;
770*0Sstevel@tonic-gate 			name++;
771*0Sstevel@tonic-gate 		} else {
772*0Sstevel@tonic-gate 			name = getzname(name, 0);
773*0Sstevel@tonic-gate 			dstlen = name - dstname;
774*0Sstevel@tonic-gate 		}
775*0Sstevel@tonic-gate 		if (dstlen < 3)
776*0Sstevel@tonic-gate 			return -1;
777*0Sstevel@tonic-gate 		if (*name != '\0' && *name != ',' && *name != ';') {
778*0Sstevel@tonic-gate 			name = getoffset(name, &dstoffset);
779*0Sstevel@tonic-gate 			if (name == NULL)
780*0Sstevel@tonic-gate 				return -1;
781*0Sstevel@tonic-gate 		} else	dstoffset = stdoffset - SECSPERHOUR;
782*0Sstevel@tonic-gate 		if (*name == ',' || *name == ';') {
783*0Sstevel@tonic-gate 			struct rule	start;
784*0Sstevel@tonic-gate 			struct rule	end;
785*0Sstevel@tonic-gate 			register int	year;
786*0Sstevel@tonic-gate 			register time_t	janfirst;
787*0Sstevel@tonic-gate 			time_t		starttime;
788*0Sstevel@tonic-gate 			time_t		endtime;
789*0Sstevel@tonic-gate 
790*0Sstevel@tonic-gate 			++name;
791*0Sstevel@tonic-gate 			if ((name = getrule(name, &start)) == NULL)
792*0Sstevel@tonic-gate 				return -1;
793*0Sstevel@tonic-gate 			if (*name++ != ',')
794*0Sstevel@tonic-gate 				return -1;
795*0Sstevel@tonic-gate 			if ((name = getrule(name, &end)) == NULL)
796*0Sstevel@tonic-gate 				return -1;
797*0Sstevel@tonic-gate 			if (*name != '\0')
798*0Sstevel@tonic-gate 				return -1;
799*0Sstevel@tonic-gate 			sp->typecnt = 2;	/* standard time and DST */
800*0Sstevel@tonic-gate 			/*
801*0Sstevel@tonic-gate 			** Two transitions per year, from EPOCH_YEAR to 2037.
802*0Sstevel@tonic-gate 			*/
803*0Sstevel@tonic-gate 			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
804*0Sstevel@tonic-gate 			if (sp->timecnt > TZ_MAX_TIMES)
805*0Sstevel@tonic-gate 				return -1;
806*0Sstevel@tonic-gate 			sp->charcnt = stdlen + 1 + dstlen + 1;
807*0Sstevel@tonic-gate 			if (allocall(sp) < 0)
808*0Sstevel@tonic-gate 				return -1;
809*0Sstevel@tonic-gate 			sp->ttis[0].tt_gmtoff = -dstoffset;
810*0Sstevel@tonic-gate 			sp->ttis[0].tt_isdst = 1;
811*0Sstevel@tonic-gate 			sp->ttis[0].tt_abbrind = stdlen + 1;
812*0Sstevel@tonic-gate 			sp->ttis[1].tt_gmtoff = -stdoffset;
813*0Sstevel@tonic-gate 			sp->ttis[1].tt_isdst = 0;
814*0Sstevel@tonic-gate 			sp->ttis[1].tt_abbrind = 0;
815*0Sstevel@tonic-gate 			atp = sp->ats;
816*0Sstevel@tonic-gate 			typep = sp->types;
817*0Sstevel@tonic-gate 			janfirst = 0;
818*0Sstevel@tonic-gate 			for (year = EPOCH_YEAR; year <= 2037; ++year) {
819*0Sstevel@tonic-gate 				starttime = transtime(janfirst, year, &start,
820*0Sstevel@tonic-gate 					stdoffset);
821*0Sstevel@tonic-gate 				endtime = transtime(janfirst, year, &end,
822*0Sstevel@tonic-gate 					dstoffset);
823*0Sstevel@tonic-gate 				if (starttime > endtime) {
824*0Sstevel@tonic-gate 					*atp++ = endtime;
825*0Sstevel@tonic-gate 					*typep++ = 1;	/* DST ends */
826*0Sstevel@tonic-gate 					*atp++ = starttime;
827*0Sstevel@tonic-gate 					*typep++ = 0;	/* DST begins */
828*0Sstevel@tonic-gate 				} else {
829*0Sstevel@tonic-gate 					*atp++ = starttime;
830*0Sstevel@tonic-gate 					*typep++ = 0;	/* DST begins */
831*0Sstevel@tonic-gate 					*atp++ = endtime;
832*0Sstevel@tonic-gate 					*typep++ = 1;	/* DST ends */
833*0Sstevel@tonic-gate 				}
834*0Sstevel@tonic-gate 				janfirst +=
835*0Sstevel@tonic-gate 					year_lengths[isleap(year)] * SECSPERDAY;
836*0Sstevel@tonic-gate 			}
837*0Sstevel@tonic-gate 		} else {
838*0Sstevel@tonic-gate 			int		sawstd;
839*0Sstevel@tonic-gate 			int		sawdst;
840*0Sstevel@tonic-gate 			long		stdfix;
841*0Sstevel@tonic-gate 			long		dstfix;
842*0Sstevel@tonic-gate 			long		oldfix;
843*0Sstevel@tonic-gate 			int		isdst;
844*0Sstevel@tonic-gate 			register int	i;
845*0Sstevel@tonic-gate 
846*0Sstevel@tonic-gate 			if (*name != '\0')
847*0Sstevel@tonic-gate 				return -1;
848*0Sstevel@tonic-gate 			if (tzload(TZDEFRULES, sp) != 0) {
849*0Sstevel@tonic-gate 				freeall(sp);
850*0Sstevel@tonic-gate 				return -1;
851*0Sstevel@tonic-gate 			}
852*0Sstevel@tonic-gate 			/*
853*0Sstevel@tonic-gate 			** Discard zone abbreviations from file, and allocate
854*0Sstevel@tonic-gate 			** space for the ones from TZ.
855*0Sstevel@tonic-gate 			*/
856*0Sstevel@tonic-gate 			free(sp->chars);
857*0Sstevel@tonic-gate 			sp->charcnt = stdlen + 1 + dstlen + 1;
858*0Sstevel@tonic-gate 			sp->chars = (char *)calloc((unsigned)sp->charcnt,
859*0Sstevel@tonic-gate 			  (unsigned)sizeof (char));
860*0Sstevel@tonic-gate 			/*
861*0Sstevel@tonic-gate 			** Compute the difference between the real and
862*0Sstevel@tonic-gate 			** prototype standard and summer time offsets
863*0Sstevel@tonic-gate 			** from GMT, and put the real standard and summer
864*0Sstevel@tonic-gate 			** time offsets into the rules in place of the
865*0Sstevel@tonic-gate 			** prototype offsets.
866*0Sstevel@tonic-gate 			*/
867*0Sstevel@tonic-gate 			sawstd = FALSE;
868*0Sstevel@tonic-gate 			sawdst = FALSE;
869*0Sstevel@tonic-gate 			stdfix = 0;
870*0Sstevel@tonic-gate 			dstfix = 0;
871*0Sstevel@tonic-gate 			for (i = 0; i < sp->typecnt; ++i) {
872*0Sstevel@tonic-gate 				if (sp->ttis[i].tt_isdst) {
873*0Sstevel@tonic-gate 					oldfix = dstfix;
874*0Sstevel@tonic-gate 					dstfix =
875*0Sstevel@tonic-gate 					    sp->ttis[i].tt_gmtoff + dstoffset;
876*0Sstevel@tonic-gate 					if (sawdst && (oldfix != dstfix))
877*0Sstevel@tonic-gate 						return -1;
878*0Sstevel@tonic-gate 					sp->ttis[i].tt_gmtoff = -dstoffset;
879*0Sstevel@tonic-gate 					sp->ttis[i].tt_abbrind = stdlen + 1;
880*0Sstevel@tonic-gate 					sawdst = TRUE;
881*0Sstevel@tonic-gate 				} else {
882*0Sstevel@tonic-gate 					oldfix = stdfix;
883*0Sstevel@tonic-gate 					stdfix =
884*0Sstevel@tonic-gate 					    sp->ttis[i].tt_gmtoff + stdoffset;
885*0Sstevel@tonic-gate 					if (sawstd && (oldfix != stdfix))
886*0Sstevel@tonic-gate 						return -1;
887*0Sstevel@tonic-gate 					sp->ttis[i].tt_gmtoff = -stdoffset;
888*0Sstevel@tonic-gate 					sp->ttis[i].tt_abbrind = 0;
889*0Sstevel@tonic-gate 					sawstd = TRUE;
890*0Sstevel@tonic-gate 				}
891*0Sstevel@tonic-gate 			}
892*0Sstevel@tonic-gate 			/*
893*0Sstevel@tonic-gate 			** Make sure we have both standard and summer time.
894*0Sstevel@tonic-gate 			*/
895*0Sstevel@tonic-gate 			if (!sawdst || !sawstd)
896*0Sstevel@tonic-gate 				return -1;
897*0Sstevel@tonic-gate 			/*
898*0Sstevel@tonic-gate 			** Now correct the transition times by shifting
899*0Sstevel@tonic-gate 			** them by the difference between the real and
900*0Sstevel@tonic-gate 			** prototype offsets.  Note that this difference
901*0Sstevel@tonic-gate 			** can be different in standard and summer time;
902*0Sstevel@tonic-gate 			** the prototype probably has a 1-hour difference
903*0Sstevel@tonic-gate 			** between standard and summer time, but a different
904*0Sstevel@tonic-gate 			** difference can be specified in TZ.
905*0Sstevel@tonic-gate 			*/
906*0Sstevel@tonic-gate 			isdst = FALSE;	/* we start in standard time */
907*0Sstevel@tonic-gate 			for (i = 0; i < sp->timecnt; ++i) {
908*0Sstevel@tonic-gate 				register const struct ttinfo *	ttisp;
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 				/*
911*0Sstevel@tonic-gate 				** If summer time is in effect, and the
912*0Sstevel@tonic-gate 				** transition time was not specified as
913*0Sstevel@tonic-gate 				** standard time, add the summer time
914*0Sstevel@tonic-gate 				** offset to the transition time;
915*0Sstevel@tonic-gate 				** otherwise, add the standard time offset
916*0Sstevel@tonic-gate 				** to the transition time.
917*0Sstevel@tonic-gate 				*/
918*0Sstevel@tonic-gate 				ttisp = &sp->ttis[sp->types[i]];
919*0Sstevel@tonic-gate 				sp->ats[i] +=
920*0Sstevel@tonic-gate 					(isdst && !ttisp->tt_ttisstd) ?
921*0Sstevel@tonic-gate 						dstfix : stdfix;
922*0Sstevel@tonic-gate 				isdst = ttisp->tt_isdst;
923*0Sstevel@tonic-gate 			}
924*0Sstevel@tonic-gate 		}
925*0Sstevel@tonic-gate 	} else {
926*0Sstevel@tonic-gate 		dstlen = 0;
927*0Sstevel@tonic-gate 		sp->typecnt = 1;		/* only standard time */
928*0Sstevel@tonic-gate 		sp->timecnt = 0;
929*0Sstevel@tonic-gate 		sp->charcnt = stdlen + 1;
930*0Sstevel@tonic-gate 		if (allocall(sp) < 0)
931*0Sstevel@tonic-gate 			return -1;
932*0Sstevel@tonic-gate 		sp->ttis[0].tt_gmtoff = -stdoffset;
933*0Sstevel@tonic-gate 		sp->ttis[0].tt_isdst = 0;
934*0Sstevel@tonic-gate 		sp->ttis[0].tt_abbrind = 0;
935*0Sstevel@tonic-gate 	}
936*0Sstevel@tonic-gate 	cp = sp->chars;
937*0Sstevel@tonic-gate 	(void) strncpy(cp, stdname, stdlen);
938*0Sstevel@tonic-gate 	cp += stdlen;
939*0Sstevel@tonic-gate 	*cp++ = '\0';
940*0Sstevel@tonic-gate 	if (dstlen != 0) {
941*0Sstevel@tonic-gate 		(void) strncpy(cp, dstname, dstlen);
942*0Sstevel@tonic-gate 		*(cp + dstlen) = '\0';
943*0Sstevel@tonic-gate 	}
944*0Sstevel@tonic-gate 	return 0;
945*0Sstevel@tonic-gate }
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate static void
gmtload(sp)948*0Sstevel@tonic-gate gmtload(sp)
949*0Sstevel@tonic-gate struct state * const	sp;
950*0Sstevel@tonic-gate {
951*0Sstevel@tonic-gate 	if (tzload(GMT, sp) != 0)
952*0Sstevel@tonic-gate 		(void) tzparse(GMT, sp, TRUE);
953*0Sstevel@tonic-gate }
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate void
tzsetwall()956*0Sstevel@tonic-gate tzsetwall()
957*0Sstevel@tonic-gate {
958*0Sstevel@tonic-gate 	lcl_is_set = TRUE;
959*0Sstevel@tonic-gate 	if (lclptr == NULL) {
960*0Sstevel@tonic-gate 		lclptr = (struct state *) calloc(1, (unsigned)sizeof *lclptr);
961*0Sstevel@tonic-gate 		if (lclptr == NULL) {
962*0Sstevel@tonic-gate #ifdef S5EMUL
963*0Sstevel@tonic-gate 			settzname();	/* all we can do */
964*0Sstevel@tonic-gate #endif
965*0Sstevel@tonic-gate 			return;
966*0Sstevel@tonic-gate 		}
967*0Sstevel@tonic-gate 	}
968*0Sstevel@tonic-gate 	if (tzload((char *) NULL, lclptr) != 0)
969*0Sstevel@tonic-gate 		gmtload(lclptr);
970*0Sstevel@tonic-gate #ifdef S5EMUL
971*0Sstevel@tonic-gate 	settzname();
972*0Sstevel@tonic-gate #endif
973*0Sstevel@tonic-gate }
974*0Sstevel@tonic-gate 
975*0Sstevel@tonic-gate void
tzset()976*0Sstevel@tonic-gate tzset()
977*0Sstevel@tonic-gate {
978*0Sstevel@tonic-gate 	register const char *	name;
979*0Sstevel@tonic-gate 
980*0Sstevel@tonic-gate 	name = (const char *)getenv("TZ");
981*0Sstevel@tonic-gate 	if (name == NULL) {
982*0Sstevel@tonic-gate 		tzsetwall();
983*0Sstevel@tonic-gate 		return;
984*0Sstevel@tonic-gate 	}
985*0Sstevel@tonic-gate 	lcl_is_set = TRUE;
986*0Sstevel@tonic-gate 	if (lclptr == NULL) {
987*0Sstevel@tonic-gate 		lclptr = (struct state *) calloc(1, (unsigned)sizeof *lclptr);
988*0Sstevel@tonic-gate 		if (lclptr == NULL) {
989*0Sstevel@tonic-gate #ifdef S5EMUL
990*0Sstevel@tonic-gate 			settzname();	/* all we can do */
991*0Sstevel@tonic-gate #endif
992*0Sstevel@tonic-gate 			return;
993*0Sstevel@tonic-gate 		}
994*0Sstevel@tonic-gate 	}
995*0Sstevel@tonic-gate 	if (*name == '\0') {
996*0Sstevel@tonic-gate 		/*
997*0Sstevel@tonic-gate 		** User wants it fast rather than right.
998*0Sstevel@tonic-gate 		*/
999*0Sstevel@tonic-gate 		lclptr->timecnt = 0;
1000*0Sstevel@tonic-gate 		lclptr->typecnt = 1;
1001*0Sstevel@tonic-gate 		lclptr->charcnt = sizeof GMT;
1002*0Sstevel@tonic-gate 		if (allocall(lclptr) < 0)
1003*0Sstevel@tonic-gate 			return;
1004*0Sstevel@tonic-gate 		lclptr->ttis[0].tt_gmtoff = 0;
1005*0Sstevel@tonic-gate 		lclptr->ttis[0].tt_abbrind = 0;
1006*0Sstevel@tonic-gate 		(void) strcpy(lclptr->chars, GMT);
1007*0Sstevel@tonic-gate 	} else if (tzload(name, lclptr) != 0)
1008*0Sstevel@tonic-gate 		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
1009*0Sstevel@tonic-gate 			(void) tzparse(name, lclptr, TRUE);
1010*0Sstevel@tonic-gate #ifdef S5EMUL
1011*0Sstevel@tonic-gate 	settzname();
1012*0Sstevel@tonic-gate #endif
1013*0Sstevel@tonic-gate }
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate /*
1016*0Sstevel@tonic-gate ** The easy way to behave "as if no library function calls" localtime
1017*0Sstevel@tonic-gate ** is to not call it--so we drop its guts into "localsub", which can be
1018*0Sstevel@tonic-gate ** freely called.  (And no, the PANS doesn't require the above behavior--
1019*0Sstevel@tonic-gate ** but it *is* desirable.)
1020*0Sstevel@tonic-gate **
1021*0Sstevel@tonic-gate ** The unused offset argument is for the benefit of mktime variants.
1022*0Sstevel@tonic-gate */
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate static struct tm	tm;
1025*0Sstevel@tonic-gate 
1026*0Sstevel@tonic-gate /*ARGSUSED*/
1027*0Sstevel@tonic-gate static void
localsub(timep,offset,tmp)1028*0Sstevel@tonic-gate localsub(timep, offset, tmp)
1029*0Sstevel@tonic-gate const time_t * const	timep;
1030*0Sstevel@tonic-gate const long		offset;
1031*0Sstevel@tonic-gate struct tm * const	tmp;
1032*0Sstevel@tonic-gate {
1033*0Sstevel@tonic-gate 	register const struct state *	sp;
1034*0Sstevel@tonic-gate 	register const struct ttinfo *	ttisp;
1035*0Sstevel@tonic-gate 	register int			i;
1036*0Sstevel@tonic-gate 	const time_t			t = *timep;
1037*0Sstevel@tonic-gate 
1038*0Sstevel@tonic-gate 	if (!lcl_is_set)
1039*0Sstevel@tonic-gate 		tzset();
1040*0Sstevel@tonic-gate 	sp = lclptr;
1041*0Sstevel@tonic-gate 	if (sp == NULL) {
1042*0Sstevel@tonic-gate 		gmtsub(timep, offset, tmp);
1043*0Sstevel@tonic-gate 		return;
1044*0Sstevel@tonic-gate 	}
1045*0Sstevel@tonic-gate 	if (sp->timecnt == 0 || t < sp->ats[0]) {
1046*0Sstevel@tonic-gate 		i = 0;
1047*0Sstevel@tonic-gate 		while (sp->ttis[i].tt_isdst)
1048*0Sstevel@tonic-gate 			if (++i >= sp->typecnt) {
1049*0Sstevel@tonic-gate 				i = 0;
1050*0Sstevel@tonic-gate 				break;
1051*0Sstevel@tonic-gate 			}
1052*0Sstevel@tonic-gate 	} else {
1053*0Sstevel@tonic-gate 		for (i = 1; i < sp->timecnt; ++i)
1054*0Sstevel@tonic-gate 			if (t < sp->ats[i])
1055*0Sstevel@tonic-gate 				break;
1056*0Sstevel@tonic-gate 		i = sp->types[i - 1];
1057*0Sstevel@tonic-gate 	}
1058*0Sstevel@tonic-gate 	ttisp = &sp->ttis[i];
1059*0Sstevel@tonic-gate 	timesub(&t, ttisp->tt_gmtoff, tmp);
1060*0Sstevel@tonic-gate 	tmp->tm_isdst = ttisp->tt_isdst;
1061*0Sstevel@tonic-gate #ifdef S5EMUL
1062*0Sstevel@tonic-gate 	tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
1063*0Sstevel@tonic-gate #endif /* S5EMUL */
1064*0Sstevel@tonic-gate 	tmp->tm_zone = &sp->chars[ttisp->tt_abbrind];
1065*0Sstevel@tonic-gate }
1066*0Sstevel@tonic-gate 
1067*0Sstevel@tonic-gate struct tm *
localtime(timep)1068*0Sstevel@tonic-gate localtime(timep)
1069*0Sstevel@tonic-gate const time_t * const	timep;
1070*0Sstevel@tonic-gate {
1071*0Sstevel@tonic-gate 	time_t		temp_time = *(const time_t*)timep;
1072*0Sstevel@tonic-gate 
1073*0Sstevel@tonic-gate 	_ltzset(&temp_time);	/*
1074*0Sstevel@tonic-gate 				 * base localtime calls this to initialize
1075*0Sstevel@tonic-gate 				 * some things, so we'll do it here, too.
1076*0Sstevel@tonic-gate 				 */
1077*0Sstevel@tonic-gate 	localsub(timep, 0L, &tm);
1078*0Sstevel@tonic-gate 	return &tm;
1079*0Sstevel@tonic-gate }
1080*0Sstevel@tonic-gate 
1081*0Sstevel@tonic-gate /*
1082*0Sstevel@tonic-gate ** gmtsub is to gmtime as localsub is to localtime.
1083*0Sstevel@tonic-gate */
1084*0Sstevel@tonic-gate 
1085*0Sstevel@tonic-gate static void
gmtsub(timep,offset,tmp)1086*0Sstevel@tonic-gate gmtsub(timep, offset, tmp)
1087*0Sstevel@tonic-gate const time_t * const	timep;
1088*0Sstevel@tonic-gate const long		offset;
1089*0Sstevel@tonic-gate struct tm * const	tmp;
1090*0Sstevel@tonic-gate {
1091*0Sstevel@tonic-gate 	if (!gmt_is_set) {
1092*0Sstevel@tonic-gate 		gmt_is_set = TRUE;
1093*0Sstevel@tonic-gate 		gmtptr = (struct state *) calloc(1, (unsigned)sizeof *gmtptr);
1094*0Sstevel@tonic-gate 		if (gmtptr != NULL)
1095*0Sstevel@tonic-gate 			gmtload(gmtptr);
1096*0Sstevel@tonic-gate 	}
1097*0Sstevel@tonic-gate 	timesub(timep, offset, tmp);
1098*0Sstevel@tonic-gate 	/*
1099*0Sstevel@tonic-gate 	** Could get fancy here and deliver something such as
1100*0Sstevel@tonic-gate 	** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
1101*0Sstevel@tonic-gate 	** but this is no time for a treasure hunt.
1102*0Sstevel@tonic-gate 	*/
1103*0Sstevel@tonic-gate 	if (offset != 0)
1104*0Sstevel@tonic-gate 		tmp->tm_zone = (char *)WILDABBR;
1105*0Sstevel@tonic-gate 	else {
1106*0Sstevel@tonic-gate 		if (gmtptr == NULL)
1107*0Sstevel@tonic-gate 			tmp->tm_zone = (char *)GMT;
1108*0Sstevel@tonic-gate 		else	tmp->tm_zone = gmtptr->chars;
1109*0Sstevel@tonic-gate 	}
1110*0Sstevel@tonic-gate }
1111*0Sstevel@tonic-gate 
1112*0Sstevel@tonic-gate struct tm *
gmtime(timep)1113*0Sstevel@tonic-gate gmtime(timep)
1114*0Sstevel@tonic-gate const time_t * const	timep;
1115*0Sstevel@tonic-gate {
1116*0Sstevel@tonic-gate 	gmtsub(timep, 0L, &tm);
1117*0Sstevel@tonic-gate 	return &tm;
1118*0Sstevel@tonic-gate }
1119*0Sstevel@tonic-gate 
1120*0Sstevel@tonic-gate struct tm *
offtime(timep,offset)1121*0Sstevel@tonic-gate offtime(timep, offset)
1122*0Sstevel@tonic-gate const time_t * const	timep;
1123*0Sstevel@tonic-gate const long		offset;
1124*0Sstevel@tonic-gate {
1125*0Sstevel@tonic-gate 	gmtsub(timep, offset, &tm);
1126*0Sstevel@tonic-gate 	return &tm;
1127*0Sstevel@tonic-gate }
1128*0Sstevel@tonic-gate 
1129*0Sstevel@tonic-gate static void
timesub(timep,offset,tmp)1130*0Sstevel@tonic-gate timesub(timep, offset, tmp)
1131*0Sstevel@tonic-gate const time_t * const			timep;
1132*0Sstevel@tonic-gate const long				offset;
1133*0Sstevel@tonic-gate register struct tm * const		tmp;
1134*0Sstevel@tonic-gate {
1135*0Sstevel@tonic-gate 	register long			days;
1136*0Sstevel@tonic-gate 	register long			rem;
1137*0Sstevel@tonic-gate 	register int			y;
1138*0Sstevel@tonic-gate 	register int			yleap;
1139*0Sstevel@tonic-gate 	register const int *		ip;
1140*0Sstevel@tonic-gate 
1141*0Sstevel@tonic-gate 	days = *timep / SECSPERDAY;
1142*0Sstevel@tonic-gate 	rem = *timep % SECSPERDAY;
1143*0Sstevel@tonic-gate 	rem += offset;
1144*0Sstevel@tonic-gate 	while (rem < 0) {
1145*0Sstevel@tonic-gate 		rem += SECSPERDAY;
1146*0Sstevel@tonic-gate 		--days;
1147*0Sstevel@tonic-gate 	}
1148*0Sstevel@tonic-gate 	while (rem >= SECSPERDAY) {
1149*0Sstevel@tonic-gate 		rem -= SECSPERDAY;
1150*0Sstevel@tonic-gate 		++days;
1151*0Sstevel@tonic-gate 	}
1152*0Sstevel@tonic-gate 	tmp->tm_hour = (int) (rem / SECSPERHOUR);
1153*0Sstevel@tonic-gate 	rem = rem % SECSPERHOUR;
1154*0Sstevel@tonic-gate 	tmp->tm_min = (int) (rem / SECSPERMIN);
1155*0Sstevel@tonic-gate 	tmp->tm_sec = (int) (rem % SECSPERMIN);
1156*0Sstevel@tonic-gate 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
1157*0Sstevel@tonic-gate 	if (tmp->tm_wday < 0)
1158*0Sstevel@tonic-gate 		tmp->tm_wday += DAYSPERWEEK;
1159*0Sstevel@tonic-gate 	y = EPOCH_YEAR;
1160*0Sstevel@tonic-gate 	if (days >= 0)
1161*0Sstevel@tonic-gate 		for ( ; ; ) {
1162*0Sstevel@tonic-gate 			yleap = isleap(y);
1163*0Sstevel@tonic-gate 			if (days < (long) year_lengths[yleap])
1164*0Sstevel@tonic-gate 				break;
1165*0Sstevel@tonic-gate 			++y;
1166*0Sstevel@tonic-gate 			days = days - (long) year_lengths[yleap];
1167*0Sstevel@tonic-gate 		}
1168*0Sstevel@tonic-gate 	else do {
1169*0Sstevel@tonic-gate 		--y;
1170*0Sstevel@tonic-gate 		yleap = isleap(y);
1171*0Sstevel@tonic-gate 		days = days + (long) year_lengths[yleap];
1172*0Sstevel@tonic-gate 	} while (days < 0);
1173*0Sstevel@tonic-gate 	tmp->tm_year = y - TM_YEAR_BASE;
1174*0Sstevel@tonic-gate 	tmp->tm_yday = (int) days;
1175*0Sstevel@tonic-gate 	ip = mon_lengths[yleap];
1176*0Sstevel@tonic-gate 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
1177*0Sstevel@tonic-gate 		days = days - (long) ip[tmp->tm_mon];
1178*0Sstevel@tonic-gate 	tmp->tm_mday = (int) (days + 1);
1179*0Sstevel@tonic-gate 	tmp->tm_isdst = 0;
1180*0Sstevel@tonic-gate 	tmp->tm_gmtoff = offset;
1181*0Sstevel@tonic-gate }
1182*0Sstevel@tonic-gate 
1183*0Sstevel@tonic-gate /*
1184*0Sstevel@tonic-gate ** Adapted from code provided by Robert Elz, who writes:
1185*0Sstevel@tonic-gate **	The "best" way to do mktime I think is based on an idea of Bob
1186*0Sstevel@tonic-gate **	Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
1187*0Sstevel@tonic-gate **	It does a binary search of the time_t space.  Since time_t's are
1188*0Sstevel@tonic-gate **	just 32 bits, its a max of 32 iterations (even at 64 bits it
1189*0Sstevel@tonic-gate **	would still be very reasonable).
1190*0Sstevel@tonic-gate */
1191*0Sstevel@tonic-gate 
1192*0Sstevel@tonic-gate #ifndef WRONG
1193*0Sstevel@tonic-gate #define WRONG	(-1)
1194*0Sstevel@tonic-gate #endif /* !defined WRONG */
1195*0Sstevel@tonic-gate 
1196*0Sstevel@tonic-gate static void
normalize(tensptr,unitsptr,base)1197*0Sstevel@tonic-gate normalize(tensptr, unitsptr, base)
1198*0Sstevel@tonic-gate int * const	tensptr;
1199*0Sstevel@tonic-gate int * const	unitsptr;
1200*0Sstevel@tonic-gate const int	base;
1201*0Sstevel@tonic-gate {
1202*0Sstevel@tonic-gate 	int tmp;
1203*0Sstevel@tonic-gate 
1204*0Sstevel@tonic-gate 	if (*unitsptr >= base) {
1205*0Sstevel@tonic-gate 		*tensptr += *unitsptr / base;
1206*0Sstevel@tonic-gate 		*unitsptr %= base;
1207*0Sstevel@tonic-gate 	} else if (*unitsptr < 0) {
1208*0Sstevel@tonic-gate 		/* tmp has the range 0 to abs(*unitptr) -1 */
1209*0Sstevel@tonic-gate 		tmp = -1 - (*unitsptr);
1210*0Sstevel@tonic-gate 		*tensptr -= (tmp/base + 1);
1211*0Sstevel@tonic-gate 		*unitsptr = (base - 1) - (tmp % base);
1212*0Sstevel@tonic-gate 	}
1213*0Sstevel@tonic-gate }
1214*0Sstevel@tonic-gate 
1215*0Sstevel@tonic-gate static int
tmcomp(atmp,btmp)1216*0Sstevel@tonic-gate tmcomp(atmp, btmp)
1217*0Sstevel@tonic-gate register const struct tm * const atmp;
1218*0Sstevel@tonic-gate register const struct tm * const btmp;
1219*0Sstevel@tonic-gate {
1220*0Sstevel@tonic-gate 	register int	result;
1221*0Sstevel@tonic-gate 
1222*0Sstevel@tonic-gate 	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
1223*0Sstevel@tonic-gate 		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
1224*0Sstevel@tonic-gate 		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
1225*0Sstevel@tonic-gate 		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
1226*0Sstevel@tonic-gate 		(result = (atmp->tm_min - btmp->tm_min)) == 0)
1227*0Sstevel@tonic-gate 			result = atmp->tm_sec - btmp->tm_sec;
1228*0Sstevel@tonic-gate 	return result;
1229*0Sstevel@tonic-gate }
1230*0Sstevel@tonic-gate 
1231*0Sstevel@tonic-gate static time_t
time2(tmp,funcp,offset,okayp)1232*0Sstevel@tonic-gate time2(tmp, funcp, offset, okayp)
1233*0Sstevel@tonic-gate struct tm * const	tmp;
1234*0Sstevel@tonic-gate void (* const		funcp)();
1235*0Sstevel@tonic-gate const long		offset;
1236*0Sstevel@tonic-gate int * const		okayp;
1237*0Sstevel@tonic-gate {
1238*0Sstevel@tonic-gate 	register const struct state *	sp;
1239*0Sstevel@tonic-gate 	register int			dir;
1240*0Sstevel@tonic-gate 	register int			bits;
1241*0Sstevel@tonic-gate 	register int			i, j ;
1242*0Sstevel@tonic-gate 	register int			saved_seconds;
1243*0Sstevel@tonic-gate 	time_t				newt;
1244*0Sstevel@tonic-gate 	time_t				t;
1245*0Sstevel@tonic-gate 	struct tm			yourtm, mytm;
1246*0Sstevel@tonic-gate 
1247*0Sstevel@tonic-gate 	*okayp = FALSE;
1248*0Sstevel@tonic-gate 	yourtm = *tmp;
1249*0Sstevel@tonic-gate 	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
1250*0Sstevel@tonic-gate 		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
1251*0Sstevel@tonic-gate 	normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
1252*0Sstevel@tonic-gate 	normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
1253*0Sstevel@tonic-gate 	normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
1254*0Sstevel@tonic-gate 	while (yourtm.tm_mday <= 0) {
1255*0Sstevel@tonic-gate 		if (yourtm.tm_mon == 0) {
1256*0Sstevel@tonic-gate 			yourtm.tm_mon = 12;
1257*0Sstevel@tonic-gate 			--yourtm.tm_year;
1258*0Sstevel@tonic-gate 		}
1259*0Sstevel@tonic-gate 		yourtm.tm_mday +=
1260*0Sstevel@tonic-gate 			mon_lengths[isleap(yourtm.tm_year +
1261*0Sstevel@tonic-gate 			   TM_YEAR_BASE)][--yourtm.tm_mon];
1262*0Sstevel@tonic-gate 		if (yourtm.tm_mon >= MONSPERYEAR) {
1263*0Sstevel@tonic-gate 			yourtm.tm_mon = 0;
1264*0Sstevel@tonic-gate 			--yourtm.tm_year;
1265*0Sstevel@tonic-gate 		}
1266*0Sstevel@tonic-gate 	}
1267*0Sstevel@tonic-gate 	for ( ; ; ) {
1268*0Sstevel@tonic-gate 		i = mon_lengths[isleap(yourtm.tm_year +
1269*0Sstevel@tonic-gate 			TM_YEAR_BASE)][yourtm.tm_mon];
1270*0Sstevel@tonic-gate 		if (yourtm.tm_mday <= i)
1271*0Sstevel@tonic-gate 			break;
1272*0Sstevel@tonic-gate 		yourtm.tm_mday -= i;
1273*0Sstevel@tonic-gate 		if (++yourtm.tm_mon >= MONSPERYEAR) {
1274*0Sstevel@tonic-gate 			yourtm.tm_mon = 0;
1275*0Sstevel@tonic-gate 			++yourtm.tm_year;
1276*0Sstevel@tonic-gate 		}
1277*0Sstevel@tonic-gate 	}
1278*0Sstevel@tonic-gate 	saved_seconds = yourtm.tm_sec;
1279*0Sstevel@tonic-gate 	yourtm.tm_sec = 0;
1280*0Sstevel@tonic-gate 	/*
1281*0Sstevel@tonic-gate 	** Calculate the number of magnitude bits in a time_t
1282*0Sstevel@tonic-gate 	** (this works regardless of whether time_t is
1283*0Sstevel@tonic-gate 	** signed or unsigned, though lint complains if unsigned).
1284*0Sstevel@tonic-gate 	*/
1285*0Sstevel@tonic-gate 	for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
1286*0Sstevel@tonic-gate 		;
1287*0Sstevel@tonic-gate 	/*
1288*0Sstevel@tonic-gate 	** If time_t is signed, then 0 is the median value,
1289*0Sstevel@tonic-gate 	** if time_t is unsigned, then 1 << bits is median.
1290*0Sstevel@tonic-gate 	*/
1291*0Sstevel@tonic-gate 	t = (t < 0) ? 0 : ((time_t) 1 << bits);
1292*0Sstevel@tonic-gate 	for ( ; ; ) {
1293*0Sstevel@tonic-gate 		(*funcp)(&t, offset, &mytm);
1294*0Sstevel@tonic-gate 		dir = tmcomp(&mytm, &yourtm);
1295*0Sstevel@tonic-gate 		if (dir != 0) {
1296*0Sstevel@tonic-gate 			if (bits-- < 0)
1297*0Sstevel@tonic-gate 				return WRONG;
1298*0Sstevel@tonic-gate 			if (bits < 0)
1299*0Sstevel@tonic-gate 				--t;
1300*0Sstevel@tonic-gate 			else if (dir > 0)
1301*0Sstevel@tonic-gate 				t -= (time_t) 1 << bits;
1302*0Sstevel@tonic-gate 			else	t += (time_t) 1 << bits;
1303*0Sstevel@tonic-gate 			continue;
1304*0Sstevel@tonic-gate 		}
1305*0Sstevel@tonic-gate 		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
1306*0Sstevel@tonic-gate 			break;
1307*0Sstevel@tonic-gate 		/*
1308*0Sstevel@tonic-gate 		** Right time, wrong type.
1309*0Sstevel@tonic-gate 		** Hunt for right time, right type.
1310*0Sstevel@tonic-gate 		** It's okay to guess wrong since the guess
1311*0Sstevel@tonic-gate 		** gets checked.
1312*0Sstevel@tonic-gate 		*/
1313*0Sstevel@tonic-gate 		sp = (const struct state *)
1314*0Sstevel@tonic-gate 			((funcp == localsub) ? lclptr : gmtptr);
1315*0Sstevel@tonic-gate 		if (sp == NULL)
1316*0Sstevel@tonic-gate 			return WRONG;
1317*0Sstevel@tonic-gate 		for (i = 0; i < sp->typecnt; ++i) {
1318*0Sstevel@tonic-gate 			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
1319*0Sstevel@tonic-gate 				continue;
1320*0Sstevel@tonic-gate 			for (j = 0; j < sp->typecnt; ++j) {
1321*0Sstevel@tonic-gate 				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
1322*0Sstevel@tonic-gate 					continue;
1323*0Sstevel@tonic-gate 				newt = t + sp->ttis[j].tt_gmtoff -
1324*0Sstevel@tonic-gate 					sp->ttis[i].tt_gmtoff;
1325*0Sstevel@tonic-gate 				(*funcp)(&newt, offset, &mytm);
1326*0Sstevel@tonic-gate 				if (tmcomp(&mytm, &yourtm) != 0)
1327*0Sstevel@tonic-gate 					continue;
1328*0Sstevel@tonic-gate 				if (mytm.tm_isdst != yourtm.tm_isdst)
1329*0Sstevel@tonic-gate 					continue;
1330*0Sstevel@tonic-gate 				/*
1331*0Sstevel@tonic-gate 				** We have a match.
1332*0Sstevel@tonic-gate 				*/
1333*0Sstevel@tonic-gate 				t = newt;
1334*0Sstevel@tonic-gate 				goto label;
1335*0Sstevel@tonic-gate 			}
1336*0Sstevel@tonic-gate 		}
1337*0Sstevel@tonic-gate 		return WRONG;
1338*0Sstevel@tonic-gate 	}
1339*0Sstevel@tonic-gate label:
1340*0Sstevel@tonic-gate 	t += saved_seconds;
1341*0Sstevel@tonic-gate 	(*funcp)(&t, offset, tmp);
1342*0Sstevel@tonic-gate 	*okayp = TRUE;
1343*0Sstevel@tonic-gate 	return t;
1344*0Sstevel@tonic-gate }
1345*0Sstevel@tonic-gate 
1346*0Sstevel@tonic-gate static time_t
time1(tmp,funcp,offset)1347*0Sstevel@tonic-gate time1(tmp, funcp, offset)
1348*0Sstevel@tonic-gate struct tm * const	tmp;
1349*0Sstevel@tonic-gate void (* const		funcp)();
1350*0Sstevel@tonic-gate const long		offset;
1351*0Sstevel@tonic-gate {
1352*0Sstevel@tonic-gate 	register time_t			t;
1353*0Sstevel@tonic-gate 	register const struct state *	sp;
1354*0Sstevel@tonic-gate 	register int			samei, otheri;
1355*0Sstevel@tonic-gate 	int				okay;
1356*0Sstevel@tonic-gate 
1357*0Sstevel@tonic-gate 
1358*0Sstevel@tonic-gate         if (tmp->tm_isdst > 1)
1359*0Sstevel@tonic-gate                 tmp->tm_isdst = 1;
1360*0Sstevel@tonic-gate 	t = time2(tmp, funcp, offset, &okay);
1361*0Sstevel@tonic-gate 	if (okay || tmp->tm_isdst < 0)
1362*0Sstevel@tonic-gate 		return t;
1363*0Sstevel@tonic-gate 	/*
1364*0Sstevel@tonic-gate 	** We're supposed to assume that somebody took a time of one type
1365*0Sstevel@tonic-gate 	** and did some math on it that yielded a "struct tm" that's bad.
1366*0Sstevel@tonic-gate 	** We try to divine the type they started from and adjust to the
1367*0Sstevel@tonic-gate 	** type they need.
1368*0Sstevel@tonic-gate 	*/
1369*0Sstevel@tonic-gate 	sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr);
1370*0Sstevel@tonic-gate 	if (sp == NULL)
1371*0Sstevel@tonic-gate 		return WRONG;
1372*0Sstevel@tonic-gate 	for (samei = 0; samei < sp->typecnt; ++samei) {
1373*0Sstevel@tonic-gate 		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
1374*0Sstevel@tonic-gate 			continue;
1375*0Sstevel@tonic-gate 		for (otheri = 0; otheri < sp->typecnt; ++otheri) {
1376*0Sstevel@tonic-gate 			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
1377*0Sstevel@tonic-gate 				continue;
1378*0Sstevel@tonic-gate 			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
1379*0Sstevel@tonic-gate 					sp->ttis[samei].tt_gmtoff;
1380*0Sstevel@tonic-gate 			tmp->tm_isdst = !tmp->tm_isdst;
1381*0Sstevel@tonic-gate 			t = time2(tmp, funcp, offset, &okay);
1382*0Sstevel@tonic-gate 			if (okay)
1383*0Sstevel@tonic-gate 				return t;
1384*0Sstevel@tonic-gate 			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
1385*0Sstevel@tonic-gate 					sp->ttis[samei].tt_gmtoff;
1386*0Sstevel@tonic-gate 			tmp->tm_isdst = !tmp->tm_isdst;
1387*0Sstevel@tonic-gate 		}
1388*0Sstevel@tonic-gate 	}
1389*0Sstevel@tonic-gate 	return WRONG;
1390*0Sstevel@tonic-gate }
1391*0Sstevel@tonic-gate 
1392*0Sstevel@tonic-gate time_t
mktime(tmp)1393*0Sstevel@tonic-gate mktime(tmp)
1394*0Sstevel@tonic-gate struct tm * const	tmp;
1395*0Sstevel@tonic-gate {
1396*0Sstevel@tonic-gate 	return time1(tmp, localsub, 0L);
1397*0Sstevel@tonic-gate }
1398*0Sstevel@tonic-gate 
1399*0Sstevel@tonic-gate time_t
timelocal(tmp)1400*0Sstevel@tonic-gate timelocal(tmp)
1401*0Sstevel@tonic-gate struct tm * const	tmp;
1402*0Sstevel@tonic-gate {
1403*0Sstevel@tonic-gate 	tmp->tm_isdst = -1;
1404*0Sstevel@tonic-gate 	return mktime(tmp);
1405*0Sstevel@tonic-gate }
1406*0Sstevel@tonic-gate 
1407*0Sstevel@tonic-gate time_t
timegm(tmp)1408*0Sstevel@tonic-gate timegm(tmp)
1409*0Sstevel@tonic-gate struct tm * const	tmp;
1410*0Sstevel@tonic-gate {
1411*0Sstevel@tonic-gate 	return time1(tmp, gmtsub, 0L);
1412*0Sstevel@tonic-gate }
1413*0Sstevel@tonic-gate 
1414*0Sstevel@tonic-gate time_t
timeoff(tmp,offset)1415*0Sstevel@tonic-gate timeoff(tmp, offset)
1416*0Sstevel@tonic-gate struct tm * const	tmp;
1417*0Sstevel@tonic-gate const long		offset;
1418*0Sstevel@tonic-gate {
1419*0Sstevel@tonic-gate 
1420*0Sstevel@tonic-gate 	return time1(tmp, gmtsub, offset);
1421*0Sstevel@tonic-gate }
1422