xref: /titanic_50/usr/src/lib/libc/port/gen/localtime.c (revision 210938ebfcf386cf4819f16c23979173b0d78097)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 /*
34  * A part of this file comes from public domain source, so
35  * clarified as of June 5, 1996 by Arthur David Olson
36  * (arthur_david_olson@nih.gov).
37  */
38 
39 /*
40  * localtime.c
41  *
42  * This file contains routines to convert struct tm to time_t and
43  * back as well as adjust time values based on their timezone, which
44  * is a local offset from GMT (Greenwich Mean Time).
45  *
46  * Many timezones actually consist of more than one offset from GMT.
47  * The GMT offset that is considered the normal offset is referred
48  * to as standard time.  The other offset is referred to as alternate
49  * time, but is better known as daylight savings time or summer time.
50  *
51  * The current timezone for an application is derived from the TZ
52  * environment variable either as defined in the environment or in
53  * /etc/default/init.  As defined by IEEE 1003.1-1990 (POSIX), the
54  * TZ variable can either be:
55  *    :<characters>
56  * or
57  *    <std><offset1>[<dst>[<offset2>]][,<start>[/<time>],<end>[/<time>]
58  *
59  * <characters> is an implementation-defined string that somehow describes
60  * a timezone.  The implementation-defined description of a timezone used
61  * in Solaris is based on the public domain zoneinfo code available from
62  * elsie.nci.nih.gov and a timezone that is specified in this way is
63  * referred to as a zoneinfo timezone.  An example of this is ":US/Pacific".
64  *
65  * The precise definition of the second format can be found in POSIX,
66  * but, basically, <std> is the abbreviation for the timezone in standard
67  * (not daylight savings time), <offset1> is the standard offset from GMT,
68  * <dst> is the abbreviation for the timezone in daylight savings time and
69  * <offset2> is the daylight savings time offset from GMT.  The remainder
70  * specifies when daylight savings time begins and ends.  A timezone
71  * specified in this way is referred to as a POSIX timezone.  An example
72  * of this is "PST7PDT".
73  *
74  * In Solaris, there is an extension to this.  If the timezone is not
75  * preceded by a ":" and it does not parse as a POSIX timezone, then it
76  * will be treated as a zoneinfo timezone.  Much usage of zoneinfo
77  * timezones in Solaris is done without the leading ":".
78  *
79  * A zoneinfo timezone is a reference to a file that contains a set of
80  * rules that describe the timezone.  In Solaris, the file is in
81  * /usr/share/lib/zoneinfo.  The file is generated by zic(1M), based
82  * on zoneinfo rules "source" files.  This is all described on the zic(1M)
83  * man page.
84  */
85 
86 /*
87  * Functions that are common to ctime(3C) and cftime(3C)
88  */
89 
90 #pragma weak tzset = _tzset
91 #pragma weak localtime_r = _localtime_r
92 #pragma weak gmtime_r = _gmtime_r
93 
94 #include "synonyms.h"
95 #include "libc.h"
96 #include "tsd.h"
97 #include <stdarg.h>
98 #include <mtlib.h>
99 #include <sys/types.h>
100 #include <ctype.h>
101 #include <stdio.h>
102 #include <limits.h>
103 #include <sys/param.h>
104 #include <time.h>
105 #include <unistd.h>
106 #include <stdlib.h>
107 #include <string.h>
108 #include <tzfile.h>
109 #include <thread.h>
110 #include <synch.h>
111 #include <fcntl.h>
112 #include <errno.h>
113 #include <sys/stat.h>
114 
115 /* JAN_01_1902 cast to (int) - negative number of seconds from 1970 */
116 #define	JAN_01_1902		(int)0x8017E880
117 #define	LEN_TZDIR		(sizeof (TZDIR) - 1)
118 #define	TIMEZONE		"/etc/default/init"
119 #define	TZSTRING		"TZ="
120 #define	HASHTABLE		109
121 
122 #define	LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
123 
124 /* Days since 1/1/70 to 12/31/(1900 + Y - 1) */
125 #define	DAYS_SINCE_70(Y) (YR((Y)-1L) - YR(70-1))
126 #define	YR(X) /* Calc # days since 0 A.D. X = curr. yr - 1900 */ \
127 	((1900L + (X)) * 365L + (1900L + (X)) / 4L - \
128 	(1900L + (X)) / 100L + ((1900L + (X)) - 1600L) / 400L)
129 
130 
131 /*
132  * The following macros are replacements for detzcode(), which has
133  * been in the public domain versions of the localtime.c code for
134  * a long time. The primatives supporting the CVTZCODE macro are
135  * implemented differently for different endianness (ie. little
136  * vs. big endian) out of necessity, to account for the different
137  * byte ordering of the quantities being fetched.  Both versions
138  * are substantially faster than the detzcode() macro.  The big
139  * endian version is approx. 6.8x faster than detzcode(), the
140  * little endian version is approximately 3x faster, due to the
141  * extra shifting requiring to change byte order.  The micro
142  * benchmarks used to compare were based on the SUNWSpro SC6.1
143  * (and later) compilers.
144  */
145 
146 #if defined(__sparc) || defined(__sparcv9)  /* big endian */
147 
148 #define	GET_LONG(p) \
149 	    *(uint_t *)(p)
150 
151 #define	GET_SHORTS(p) \
152 	    *(ushort_t *)(p) << 16 |\
153 	    *(ushort_t *)((p) + 2)
154 
155 #define	GET_CHARS(p) \
156 	    *(uchar_t *)(p) << 24 |\
157 	    *(uchar_t *)((p) + 1) << 16 |\
158 	    *(uchar_t *)((p) + 2) << 8  |\
159 	    *(uchar_t *)((p) + 3)
160 
161 #else /* little endian */
162 
163 #define	GET_BYTE(x) \
164 	    ((x) & 0xff)
165 
166 #define	SWAP_BYTES(x) ((\
167 	    GET_BYTE(x) << 8) |\
168 	    GET_BYTE((x) >> 8))
169 
170 #define	SWAP_WORDS(x) ((\
171 	    SWAP_BYTES(x) << 16) |\
172 	    SWAP_BYTES((x) >> 16))
173 
174 #define	GET_LONG(p) \
175 	    SWAP_WORDS(*(uint_t *)(p))
176 
177 #define	GET_SHORTS(p) \
178 	    SWAP_BYTES(*(ushort_t *)(p)) << 16 |\
179 	    SWAP_BYTES(*(ushort_t *)((p) + 2))
180 
181 #define	GET_CHARS(p) \
182 	    GET_BYTE(*(uchar_t *)(p)) << 24 |\
183 	    GET_BYTE(*(uchar_t *)((p) + 1)) << 16 |\
184 	    GET_BYTE(*(uchar_t *)((p) + 2)) << 8 |\
185 	    GET_BYTE(*(uchar_t *)((p) + 3))
186 
187 #endif
188 
189 
190 #define	IF_ALIGNED(ptr, byte_alignment) \
191 			!((uintptr_t)(ptr) & (byte_alignment - 1))
192 
193 #define	CVTZCODE(p) (int)(\
194 	    IF_ALIGNED(p, 4) ? GET_LONG(p) :\
195 	    IF_ALIGNED(p, 2) ? GET_SHORTS(p) : GET_CHARS(p));\
196 	    p += 4;
197 
198 #ifndef	FALSE
199 #define	FALSE	(0)
200 #endif
201 
202 #ifndef	TRUE
203 #define	TRUE	(1)
204 #endif
205 
206 extern	mutex_t		_time_lock;
207 
208 extern const int	__lyday_to_month[];
209 extern const int	__yday_to_month[];
210 extern const int	__mon_lengths[2][MONS_PER_YEAR];
211 extern const int	__year_lengths[2];
212 
213 const char	_tz_gmt[4] = "GMT";	/* "GMT"  */
214 const char	_tz_spaces[4] = "   ";	/* "   "  */
215 static const char	_posix_gmt0[5] = "GMT0";	/* "GMT0" */
216 
217 typedef struct ttinfo {			/* Time type information */
218 	long		tt_gmtoff;	/* GMT offset in seconds */
219 	int		tt_isdst;	/* used to set tm_isdst */
220 	int		tt_abbrind;	/* abbreviation list index */
221 	int		tt_ttisstd;	/* TRUE if trans is std time */
222 	int		tt_ttisgmt;	/* TRUE if transition is GMT */
223 } ttinfo_t;
224 
225 typedef struct lsinfo {			/* Leap second information */
226 	time_t		ls_trans;	/* transition time */
227 	long		ls_corr;	/* correction to apply */
228 } lsinfo_t;
229 
230 typedef struct previnfo {		/* Info about *prev* trans */
231 	ttinfo_t	*std;		/* Most recent std type */
232 	ttinfo_t	*alt;		/* Most recent alt type */
233 } prev_t;
234 
235 typedef enum {
236 	MON_WEEK_DOW,		/* Mm.n.d - month, week, day of week */
237 	JULIAN_DAY,		/* Jn - Julian day */
238 	DAY_OF_YEAR		/* n - day of year */
239 } posrule_type_t;
240 
241 typedef struct {
242 	posrule_type_t	r_type;		/* type of rule */
243 	int		r_day;		/* day number of rule */
244 	int		r_week;		/* week number of rule */
245 	int		r_mon;		/* month number of rule */
246 	long		r_time;		/* transition time of rule */
247 } rule_t;
248 
249 typedef struct {
250 	rule_t		*rules[2];
251 	long		offset[2];
252 	long long	rtime[2];
253 } posix_daylight_t;
254 
255 /*
256  * Note: ZONERULES_INVALID used for global curr_zonerules variable, but not
257  * for zonerules field of state_t.
258  */
259 typedef enum {
260 	ZONERULES_INVALID, POSIX, POSIX_USA, ZONEINFO
261 } zone_rules_t;
262 
263 /*
264  * The following members are allocated from the libc-internal malloc:
265  *
266  *	zonename
267  *	chars
268  */
269 typedef struct state {
270 	const char	*zonename;		/* Timezone */
271 	struct state	*next;			/* next state */
272 	zone_rules_t	zonerules;		/* Type of zone */
273 	int		daylight;		/* daylight global */
274 	long		default_timezone;	/* Def. timezone val */
275 	long		default_altzone;	/* Def. altzone val */
276 	const char	*default_tzname0;	/* Def tz..[0] val */
277 	const char	*default_tzname1;	/* Def tz..[1] val  */
278 	int		leapcnt;		/* # leap sec trans */
279 	int		timecnt;		/* # transitions */
280 	int		typecnt;		/* # zone types */
281 	int		charcnt;		/* # zone abbv. chars */
282 	char		*chars;			/* Zone abbv. chars */
283 	size_t		charsbuf_size;		/* malloc'ed buflen */
284 	prev_t		prev[TZ_MAX_TIMES];	/* Pv. trans info */
285 	time_t		ats[TZ_MAX_TIMES];	/* Trans.  times */
286 	uchar_t		types[TZ_MAX_TIMES];	/* Type indices */
287 	ttinfo_t	ttis[TZ_MAX_TYPES];	/* Zone types */
288 	lsinfo_t	lsis[TZ_MAX_LEAPS];	/* Leap sec trans */
289 	rule_t		start_rule;		/* For POSIX w/rules */
290 	rule_t		end_rule;		/* For POSIX w/rules */
291 } state_t;
292 
293 typedef struct systemtz {
294 	const char	*tz;
295 	state_t		*entry;
296 	int		flag;
297 } systemtz_t;
298 
299 static const char	*namecache;
300 
301 static state_t	*tzcache[HASHTABLE];
302 
303 static state_t	*lclzonep;
304 
305 static struct tm	tm;		/* For non-reentrant use */
306 static int		is_in_dst;	/* Set if t is in DST */
307 static zone_rules_t	curr_zonerules = ZONERULES_INVALID;
308 static int		cached_year;	/* mktime() perf. enhancement */
309 static long long	cached_secs_since_1970;	/* mktime() perf. */
310 static int		year_is_cached = FALSE;	/* mktime() perf. */
311 
312 
313 #define	_2AM		(2 * SECS_PER_HOUR)
314 #define	FIRSTWEEK	1
315 #define	LASTWEEK	5
316 
317 enum wks {
318 	_1st_week = 1,
319 	_2nd_week,
320 	_3rd_week,
321 	_4th_week,
322 	_Last_week
323 };
324 
325 enum dwk {
326 	Sun,
327 	Mon,
328 	Tue,
329 	Wed,
330 	Thu,
331 	Fri,
332 	Sat
333 };
334 
335 enum mth {
336 	Jan = 1,
337 	Feb,
338 	Mar,
339 	Apr,
340 	May,
341 	Jun,
342 	Jul,
343 	Aug,
344 	Sep,
345 	Oct,
346 	Nov,
347 	Dec
348 };
349 
350 /*
351  * The following table defines standard USA DST transitions
352  * as they have been declared throughout history, disregarding
353  * the legally sanctioned local variants.
354  *
355  * Note:  At some point, this table may be supplanted by
356  * more popular 'posixrules' logic.
357  */
358 typedef struct {
359 	int	s_year;
360 	int	e_year;
361 	rule_t	start;
362 	rule_t	end;
363 } __usa_rules_t;
364 
365 static const __usa_rules_t	__usa_rules[] = {
366 	{
367 		2007, 2037,
368 		{ MON_WEEK_DOW, Sun, _2nd_week, Mar, _2AM },
369 		{ MON_WEEK_DOW, Sun, _1st_week, Nov, _2AM },
370 	},
371 	{
372 		1987, 2006,
373 		{ MON_WEEK_DOW, Sun, _1st_week,  Apr, _2AM },
374 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
375 	},
376 	{
377 		1976, 1986,
378 		{ MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
379 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
380 	},
381 	{
382 		1975, 1975,
383 		{ MON_WEEK_DOW, Sun, _Last_week, Feb, _2AM },
384 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
385 	},
386 
387 	{
388 		1974, 1974,
389 		{ MON_WEEK_DOW, Sun, _1st_week,  Jan, _2AM },
390 		{ MON_WEEK_DOW, Sun, _Last_week, Nov, _2AM },
391 	},
392 	/*
393 	 * The entry below combines two previously separate entries for
394 	 * 1969-1973 and 1902-1968
395 	 */
396 	{
397 		1902, 1973,
398 		{ MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
399 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
400 	}
401 };
402 #define	MAX_RULE_TABLE	(sizeof (__usa_rules) / sizeof (__usa_rules_t) - 1)
403 
404 /*
405  * Prototypes for static functions.
406  */
407 static systemtz_t *getsystemTZ(systemtz_t *);
408 static const char *getzname(const char *, int);
409 static const char *getnum(const char *, int *, int, int);
410 static const char *getsecs(const char *, long *);
411 static const char *getoffset(const char *, long *);
412 static const char *getrule(const char *, rule_t *, int);
413 static int	load_posixinfo(const char *, state_t *);
414 static int	load_zoneinfo(const char *, state_t *);
415 static void	ltzset_u(time_t, systemtz_t *);
416 static struct tm *offtime_u(time_t, long, struct tm *);
417 static int	posix_check_dst(long long, state_t *);
418 static int	posix_daylight(long long *, int, posix_daylight_t *);
419 static void	set_zone_context(time_t);
420 
421 /*
422  * definition of difftime
423  *
424  * This code assumes time_t is type long.  Note the difference of two
425  * longs in absolute value is representable as an unsigned long.  So,
426  * compute the absolute value of the difference, cast the result to
427  * double and attach the sign back on.
428  *
429  * Note this code assumes 2's complement arithmetic.  The subtraction
430  * operation may overflow when using signed operands, but when the
431  * result is cast to unsigned long, it yields the desired value
432  * (ie, the absolute value of the difference).  The cast to unsigned
433  * long is done using pointers to avoid undefined behavior if casting
434  * a negative value to unsigned.
435  */
436 double
437 difftime(time_t time1, time_t time0)
438 {
439 	if (time1 < time0) {
440 		time0 -= time1;
441 		return (-(double)*(unsigned long *) &time0);
442 	} else {
443 		time1 -= time0;
444 		return ((double)*(unsigned long *) &time1);
445 	}
446 }
447 
448 /*
449  * Accepts a time_t, returns a tm struct based on it, with
450  * no local timezone adjustment.
451  *
452  * This routine is the thread-safe variant of gmtime(), and
453  * requires that the call provide the address of their own tm
454  * struct.
455  *
456  * Locking is not done here because set_zone_context()
457  * is not called, thus timezone, altzone, and tzname[] are not
458  * accessed, no memory is allocated, and no common dynamic
459  * data is accessed.
460  *
461  * See ctime(3C)
462  */
463 struct tm *
464 _gmtime_r(const time_t *timep, struct tm *p_tm)
465 {
466 	return (offtime_u((time_t)*timep, 0L, p_tm));
467 }
468 
469 /*
470  * Accepts a time_t, returns a tm struct based on it, with
471  * no local timezone adjustment.
472  *
473  * This function is explicitly NOT THREAD-SAFE.  The standards
474  * indicate it should provide its results in its own statically
475  * allocated tm struct that gets overwritten. The thread-safe
476  * variant is gmtime_r().  We make it mostly thread-safe by
477  * allocating its buffer in thread-specific data.
478  *
479  * See ctime(3C)
480  */
481 struct tm *
482 gmtime(const time_t *timep)
483 {
484 	struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
485 
486 	if (p_tm == NULL)	/* memory allocation failure */
487 		p_tm = &tm;	/* use static buffer and hope for the best */
488 	return (_gmtime_r(timep, p_tm));
489 }
490 
491 /*
492  * This is the hashing function, based on the input timezone name.
493  */
494 static int
495 get_hashid(const char *id)
496 {
497 	const unsigned char	*s = (const unsigned char *)id;
498 	unsigned char	c;
499 	unsigned int	h;
500 
501 	h = *s++;
502 	while ((c = *s++) != '\0') {
503 		h = (h << 5) - h + c;
504 	}
505 	return ((int)(h % HASHTABLE));
506 }
507 
508 /*
509  * find_zone() gets the hashid for zonename, then uses the hashid
510  * to search the hash table for the appropriate timezone entry.  If
511  * the entry for zonename is found in the hash table, return a pointer
512  * to the entry.  Otherwise, update the input link_prev and link_next
513  * to the addresses of pointers for the caller to update to add the new
514  * entry to the hash table.
515  */
516 static state_t *
517 find_zone(const char *zonename, state_t ***link_prev, state_t **link_next)
518 {
519 	int	hashid;
520 	state_t	*cur, *prv;
521 
522 	hashid = get_hashid(zonename);
523 	cur = tzcache[hashid];
524 	prv = NULL;
525 	while (cur) {
526 		int	res;
527 		res = strcmp(cur->zonename, zonename);
528 		if (res == 0) {
529 			return (cur);
530 		} else if (res > 0) {
531 			break;
532 		}
533 		prv = cur;
534 		cur = cur->next;
535 	}
536 	if (prv) {
537 		*link_prev = &prv->next;
538 		*link_next = cur;
539 	} else {
540 		*link_prev = &tzcache[hashid];
541 		*link_next = NULL;
542 	}
543 	return (NULL);
544 }
545 
546 
547 /*
548  * Returns tm struct based on input time_t argument, correcting
549  * for the local timezone, producing documented side-effects
550  * to extern global state, timezone, altzone, daylight and tzname[].
551  *
552  * localtime_r() is the thread-safe variant of localtime().
553  *
554  * IMPLEMENTATION NOTE:
555  *
556  *	Locking slows multithreaded access and is probably ultimately
557  *	unnecessary here. The POSIX specification is a bit vague
558  *	as to whether the extern variables set by tzset() need to
559  *	set as a result of a call to localtime_r()
560  *
561  *	Currently, the spec only mentions that tzname[] doesn't
562  *	need to be set.  As soon as it becomes unequivocal
563  *	that the external zone state doesn't need to be asserted
564  *	for this call, and it really doesn't make much sense
565  *	to set common state from multi-threaded calls made to this
566  *	function, locking can be dispensed with here.
567  *
568  *	local zone state would still need to be aquired for the
569  *	time in question in order for calculations elicited here
570  *	to be correct, but that state wouldn't need to be shared,
571  *	thus no multi-threaded synchronization would be required.
572  *
573  *	It would be nice if POSIX would approve an ltzset_r()
574  *	function, but if not, it wouldn't stop us from making one
575  *	privately.
576  *
577  *	localtime_r() can now return NULL if overflow is detected.
578  *	offtime_u() is the function that detects overflow, and sets
579  *	errno appropriately.  We unlock before the call to offtime_u(),
580  *	so that lmutex_unlock() does not reassign errno.  The function
581  *	offtime_u() is MT-safe and does not have to be locked.  Use
582  *	my_is_in_dst to reference local copy of is_in_dst outside locks.
583  *
584  * See ctime(3C)
585  */
586 struct tm *
587 _localtime_r(const time_t *timep, struct tm *p_tm)
588 {
589 	long	offset;
590 	struct tm *rt;
591 	int	my_is_in_dst;
592 	systemtz_t	stz;
593 	systemtz_t	*tzp;
594 
595 	tzp = getsystemTZ(&stz);
596 
597 	lmutex_lock(&_time_lock);
598 	ltzset_u(*timep, tzp);
599 	if (lclzonep == NULL) {
600 		lmutex_unlock(&_time_lock);
601 		if (tzp->flag)
602 			free(tzp->entry);
603 		return (offtime_u(*timep, 0L, p_tm));
604 	}
605 	my_is_in_dst = is_in_dst;
606 	offset = (my_is_in_dst) ? -altzone : -timezone;
607 	lmutex_unlock(&_time_lock);
608 	rt = offtime_u(*timep, offset, p_tm);
609 	p_tm->tm_isdst = my_is_in_dst;
610 	if (tzp->flag)
611 		free(tzp->entry);
612 	return (rt);
613 }
614 
615 /*
616  * Accepts a time_t, returns a tm struct based on it, correcting
617  * for the local timezone.  Produces documented side-effects to
618  * extern global timezone state data.
619  *
620  * This function is explicitly NOT THREAD-SAFE.  The standards
621  * indicate it should provide its results in its own statically
622  * allocated tm struct that gets overwritten. The thread-safe
623  * variant is localtime_r().  We make it mostly thread-safe by
624  * allocating its buffer in thread-specific data.
625  *
626  * localtime() can now return NULL if overflow is detected.
627  * offtime_u() is the function that detects overflow, and sets
628  * errno appropriately.
629  *
630  * See ctime(3C)
631  */
632 struct tm *
633 localtime(const time_t *timep)
634 {
635 	struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
636 
637 	if (p_tm == NULL)	/* memory allocation failure */
638 		p_tm = &tm;	/* use static buffer and hope for the best */
639 	return (_localtime_r(timep, p_tm));
640 }
641 
642 /*
643  * This function takes a pointer to a tm struct and returns a
644  * normalized time_t, also inducing documented side-effects in
645  * extern global zone state variables.  (See mktime(3C)).
646  */
647 time_t
648 mktime(struct tm *tmptr)
649 {
650 	struct tm _tm;
651 	long long t;		/* must hold more than 32-bit time_t */
652 	int	temp;
653 	int	mketimerrno;
654 	int	overflow;
655 	systemtz_t	stz;
656 	systemtz_t	*tzp;
657 
658 	mketimerrno = errno;
659 
660 	tzp = getsystemTZ(&stz);
661 
662 	/* mktime leaves errno unchanged if no error is encountered */
663 
664 	lmutex_lock(&_time_lock);
665 
666 	/* Calculate time_t from tm arg.  tm may need to be normalized. */
667 	t = tmptr->tm_sec + SECSPERMIN * tmptr->tm_min +
668 	    SECSPERHOUR * tmptr->tm_hour +
669 	    SECSPERDAY * (tmptr->tm_mday - 1);
670 
671 	if (tmptr->tm_mon >= 12) {
672 		tmptr->tm_year += tmptr->tm_mon / 12;
673 		tmptr->tm_mon %= 12;
674 	} else if (tmptr->tm_mon < 0) {
675 		temp = -tmptr->tm_mon;
676 		tmptr->tm_mon = 0;	/* If tm_mon divides by 12. */
677 		tmptr->tm_year -= (temp / 12);
678 		if (temp %= 12) {	/* Remainder... */
679 			tmptr->tm_year--;
680 			tmptr->tm_mon = 12 - temp;
681 		}
682 	}
683 
684 	/* Avoid numerous calculations embedded in macro if possible */
685 	if (!year_is_cached || (cached_year != tmptr->tm_year))	 {
686 		cached_year = tmptr->tm_year;
687 		year_is_cached = TRUE;
688 		/* For boundry values of tm_year, typecasting required */
689 		cached_secs_since_1970 =
690 		    (long long)SECSPERDAY * DAYS_SINCE_70(cached_year);
691 	}
692 	t += cached_secs_since_1970;
693 
694 	if (isleap(tmptr->tm_year + TM_YEAR_BASE))
695 		t += SECSPERDAY * __lyday_to_month[tmptr->tm_mon];
696 	else
697 		t += SECSPERDAY * __yday_to_month[tmptr->tm_mon];
698 
699 	ltzset_u((time_t)t, tzp);
700 	/* Attempt to convert time to GMT based on tm_isdst setting */
701 	t += (tmptr->tm_isdst > 0) ? altzone : timezone;
702 
703 #ifdef _ILP32
704 	overflow = t > LONG_MAX || t < LONG_MIN ||
705 			tmptr->tm_year < 1 || tmptr->tm_year > 138;
706 #else
707 	overflow = t > LONG_MAX || t < LONG_MIN;
708 #endif
709 	set_zone_context((time_t)t);
710 	if (tmptr->tm_isdst < 0) {
711 		long dst_delta = timezone - altzone;
712 		switch (curr_zonerules) {
713 		case ZONEINFO:
714 			if (is_in_dst) {
715 				t -= dst_delta;
716 				set_zone_context((time_t)t);
717 				if (is_in_dst) {
718 					(void) offtime_u((time_t)t,
719 						    -altzone, &_tm);
720 					_tm.tm_isdst = 1;
721 				} else {
722 					(void) offtime_u((time_t)t,
723 						    -timezone, &_tm);
724 				}
725 			} else {
726 				(void) offtime_u((time_t)t, -timezone, &_tm);
727 			}
728 			break;
729 		case POSIX_USA:
730 		case POSIX:
731 			if (is_in_dst) {
732 				t -= dst_delta;
733 				set_zone_context((time_t)t);
734 				if (is_in_dst) {
735 					(void) offtime_u((time_t)t,
736 						    -altzone, &_tm);
737 					_tm.tm_isdst = 1;
738 				} else {
739 					(void) offtime_u((time_t)t,
740 						    -timezone, &_tm);
741 				}
742 			} else { /* check for ambiguous 'fallback' transition */
743 				set_zone_context((time_t)t - dst_delta);
744 				if (is_in_dst) {  /* In fallback, force DST */
745 					t -= dst_delta;
746 					(void) offtime_u((time_t)t,
747 						    -altzone, &_tm);
748 					_tm.tm_isdst = 1;
749 				} else {
750 					(void) offtime_u((time_t)t,
751 						    -timezone, &_tm);
752 				}
753 			}
754 			break;
755 
756 		case ZONERULES_INVALID:
757 			(void) offtime_u((time_t)t, 0L, &_tm);
758 			break;
759 
760 		}
761 	} else if (is_in_dst) {
762 		(void) offtime_u((time_t)t, -altzone, &_tm);
763 		_tm.tm_isdst = 1;
764 	} else {
765 		(void) offtime_u((time_t)t, -timezone, &_tm);
766 	}
767 
768 	if (overflow || t > LONG_MAX || t < LONG_MIN) {
769 		mketimerrno = EOVERFLOW;
770 		t = -1;
771 	} else {
772 		*tmptr = _tm;
773 	}
774 
775 	lmutex_unlock(&_time_lock);
776 
777 	if (tzp->flag)
778 		free(tzp->entry);
779 	errno = mketimerrno;
780 	return ((time_t)t);
781 }
782 
783 /*
784  * Sets extern global zone state variables based on the current
785  * time.  Specifically, tzname[], timezone, altzone, and daylight
786  * are updated.  See ctime(3C) manpage.
787  */
788 void
789 _tzset(void)
790 {
791 	systemtz_t	stz;
792 	systemtz_t	*tzp;
793 
794 	tzp = getsystemTZ(&stz);
795 
796 	lmutex_lock(&_time_lock);
797 	ltzset_u(time(NULL), tzp);
798 	lmutex_unlock(&_time_lock);
799 	if (tzp->flag)
800 		free(tzp->entry);
801 }
802 
803 void
804 _ltzset(time_t tim)
805 {
806 	systemtz_t	stz;
807 	systemtz_t	*tzp;
808 
809 	tzp = getsystemTZ(&stz);
810 
811 	lmutex_lock(&_time_lock);
812 	ltzset_u(tim, tzp);
813 	lmutex_unlock(&_time_lock);
814 	if (tzp->flag)
815 		free(tzp->entry);
816 }
817 
818 /*
819  * Loads local zone information if TZ changed since last time zone
820  * information was loaded, or if this is the first time thru.
821  * We already hold _time_lock; no further locking is required.
822  */
823 static void
824 ltzset_u(time_t t, systemtz_t *tzp)
825 {
826 	const char	*zonename = tzp->tz;
827 	state_t	*entry, **p, *q;
828 
829 	if (zonename == NULL || *zonename == '\0')
830 		zonename = _posix_gmt0;
831 
832 	if (curr_zonerules != ZONERULES_INVALID &&
833 	    strcmp(namecache, zonename) == 0) {
834 		set_zone_context(t);
835 		return;
836 	}
837 
838 	entry = find_zone(zonename, &p, &q);
839 	if (entry == NULL) {
840 		/*
841 		 * No timezone entry found in hash table, so load it,
842 		 * and create a new timezone entry.
843 		 */
844 		char	*newzonename, *charsbuf;
845 
846 		/* Invalidate the current timezone */
847 		curr_zonerules = ZONERULES_INVALID;
848 
849 		newzonename = libc_strdup(zonename);
850 		daylight = 0;
851 		entry = tzp->entry;
852 
853 		if (entry == NULL || newzonename == NULL) {
854 			/* something wrong happened. */
855 			if (newzonename != NULL)
856 				libc_free(newzonename);
857 			timezone = altzone = 0;
858 			is_in_dst = 0;
859 			tzname[0] = (char *)_tz_gmt;
860 			tzname[1] = (char *)_tz_spaces;
861 			return;
862 		}
863 
864 		/*
865 		 * Builds transition cache and sets up zone state data for zone
866 		 * specified in TZ, which can be specified as a POSIX zone or an
867 		 * Olson zoneinfo file reference.
868 		 *
869 		 * If local data cannot be parsed or loaded, the local zone
870 		 * tables are set up for GMT.
871 		 *
872 		 * Unless a leading ':' is prepended to TZ, TZ is initially
873 		 * parsed as a POSIX zone;  failing that, it reverts to
874 		 * a zoneinfo check.
875 		 * However, if a ':' is prepended, the zone will *only* be
876 		 * parsed as zoneinfo.  If any failure occurs parsing or
877 		 * loading a zoneinfo TZ, GMT data is loaded for the local zone.
878 		 *
879 		 * Example:  There is a zoneinfo file in the standard
880 		 * distribution called 'PST8PDT'.  The only way the user can
881 		 * specify that file under Solaris is to set TZ to ":PST8PDT".
882 		 * Otherwise the initial parse of PST8PDT as a POSIX zone will
883 		 * succeed and be used.
884 		 */
885 		if ((charsbuf = libc_malloc(TZ_MAX_CHARS)) == NULL) {
886 			libc_free(newzonename);
887 
888 			timezone = altzone = 0;
889 			is_in_dst = 0;
890 			tzname[0] = (char *)_tz_gmt;
891 			tzname[1] = (char *)_tz_spaces;
892 			return;
893 		}
894 		entry->charsbuf_size = TZ_MAX_CHARS;
895 		entry->chars = charsbuf;
896 		entry->default_tzname0 = _tz_gmt;
897 		entry->default_tzname1 = _tz_spaces;
898 		entry->zonename = newzonename;
899 
900 		if (*zonename == ':') {
901 			if (load_zoneinfo(zonename + 1, entry) != 0) {
902 				(void) load_posixinfo(_posix_gmt0, entry);
903 			}
904 		} else if (load_posixinfo(zonename, entry) != 0) {
905 			if (load_zoneinfo(zonename, entry) != 0) {
906 				(void) load_posixinfo(_posix_gmt0, entry);
907 			}
908 		}
909 		/*
910 		 * The pre-allocated buffer is used; reset the free flag
911 		 * so the buffer won't be freed.
912 		 */
913 		tzp->flag = 0;
914 		entry->next = q;
915 		*p = entry;
916 	}
917 
918 	curr_zonerules = entry->zonerules;
919 	namecache = entry->zonename;
920 	daylight = entry->daylight;
921 	lclzonep = entry;
922 
923 	set_zone_context(t);
924 }
925 
926 /*
927  * Sets timezone, altzone, tzname[], extern globals, to represent
928  * disposition of t with respect to TZ; See ctime(3C). is_in_dst,
929  * internal global is also set.  daylight is set at zone load time.
930  *
931  * Issues:
932  *
933  *	In this function, any time_t not located in the cache is handled
934  *	as a miss.  To build/update transition cache, load_zoneinfo()
935  *	must be called prior to this routine.
936  *
937  *	If POSIX zone, cache miss penalty is slightly degraded
938  *	performance.  For zoneinfo, penalty is decreased is_in_dst
939  *	accuracy.
940  *
941  *	POSIX, despite its chicken/egg problem, ie. not knowing DST
942  *	until time known, and not knowing time until DST known, at
943  *	least uses the same algorithm for 64-bit time as 32-bit.
944  *
945  *	The fact that zoneinfo files only contain transistions for 32-bit
946  *	time space is a well known problem, as yet unresolved.
947  *	Without an official standard for coping with out-of-range
948  *	zoneinfo times,  assumptions must be made.  For now
949  *	the assumption is:   If t exceeds 32-bit boundries and local zone
950  *	is zoneinfo type, is_in_dst is set to to 0 for negative values
951  *	of t, and set to the same DST state as the highest ordered
952  * 	transition in cache for positive values of t.
953  */
954 static void
955 set_zone_context(time_t t)
956 {
957 	prev_t		*prevp;
958 	int    		lo, hi, tidx;
959 	ttinfo_t	*ttisp, *std, *alt;
960 
961 	/* If state data not loaded or TZ busted, just use GMT */
962 	if (lclzonep == NULL || curr_zonerules == ZONERULES_INVALID) {
963 		timezone = altzone = 0;
964 		daylight = is_in_dst = 0;
965 		tzname[0] = (char *)_tz_gmt;
966 		tzname[1] = (char *)_tz_spaces;
967 		return;
968 	}
969 
970 	/* Retrieve suitable defaults for this zone */
971 	altzone = lclzonep->default_altzone;
972 	timezone = lclzonep->default_timezone;
973 	tzname[0] = (char *)lclzonep->default_tzname0;
974 	tzname[1] = (char *)lclzonep->default_tzname1;
975 	is_in_dst = 0;
976 
977 	if (lclzonep->timecnt <= 0 || lclzonep->typecnt < 2)
978 		/* Loaded zone incapable of transitioning. */
979 		return;
980 
981 	/*
982 	 * At least one alt. zone and one transistion exist. Locate
983 	 * state for 't' quickly as possible.  Use defaults as necessary.
984 	 */
985 	lo = 0;
986 	hi = lclzonep->timecnt - 1;
987 
988 	if (t < lclzonep->ats[0] || t >= lclzonep->ats[hi]) {
989 
990 		/*  CACHE MISS.  Calculate DST as best as possible */
991 		if (lclzonep->zonerules == POSIX_USA ||
992 		    lclzonep->zonerules == POSIX) {
993 			/* Must nvoke calculations to determine DST */
994 			is_in_dst = (daylight) ?
995 			    posix_check_dst(t, lclzonep) : 0;
996 			return;
997 		} else if (t < lclzonep->ats[0]) {   /* zoneinfo... */
998 			/* t precedes 1st transition.  Use defaults */
999 			return;
1000 		} else	{    /* zoneinfo */
1001 			/* t follows final transistion.  Use final */
1002 			tidx = hi;
1003 		}
1004 
1005 	} else {
1006 
1007 		/*  CACHE HIT.  Locate transition using binary search. */
1008 
1009 		while (lo <= hi) {
1010 			tidx = (lo + hi) / 2;
1011 			if (t == lclzonep->ats[tidx])
1012 				break;
1013 			else if (t < lclzonep->ats[tidx])
1014 				hi = tidx - 1;
1015 			else
1016 				lo = tidx + 1;
1017 		}
1018 		if (lo > hi)
1019 			tidx = hi;
1020 	}
1021 
1022 	/*
1023 	 * Set extern globals based on located transition and summary of
1024 	 * its previous state, which were cached when zone was loaded
1025 	 */
1026 	ttisp = &lclzonep->ttis[lclzonep->types[tidx]];
1027 	prevp = &lclzonep->prev[tidx];
1028 
1029 	if ((is_in_dst = ttisp->tt_isdst) == 0) { /* std. time */
1030 		timezone = -ttisp->tt_gmtoff;
1031 		tzname[0] = &lclzonep->chars[ttisp->tt_abbrind];
1032 		if ((alt = prevp->alt) != NULL) {
1033 			altzone = -alt->tt_gmtoff;
1034 			tzname[1] = &lclzonep->chars[alt->tt_abbrind];
1035 		}
1036 	} else { /* alt. time */
1037 		altzone = -ttisp->tt_gmtoff;
1038 		tzname[1] = &lclzonep->chars[ttisp->tt_abbrind];
1039 		if ((std = prevp->std) != NULL) {
1040 			timezone = -std->tt_gmtoff;
1041 			tzname[0] = &lclzonep->chars[std->tt_abbrind];
1042 		}
1043 	}
1044 }
1045 
1046 /*
1047  * This function takes a time_t and gmt offset and produces a
1048  * tm struct based on specified time.
1049  *
1050  * The the following fields are calculated, based entirely
1051  * on the offset-adjusted value of t:
1052  *
1053  * tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec
1054  * tm_yday. tm_wday.  (tm_isdst is ALWAYS set to 0).
1055  */
1056 
1057 static struct tm *
1058 offtime_u(time_t t, long offset, struct tm *tmptr)
1059 {
1060 	long		days;
1061 	long		rem;
1062 	long		y;
1063 	int		yleap;
1064 	const int	*ip;
1065 
1066 	days = t / SECSPERDAY;
1067 	rem = t % SECSPERDAY;
1068 	rem += offset;
1069 	while (rem < 0) {
1070 		rem += SECSPERDAY;
1071 		--days;
1072 	}
1073 	while (rem >= SECSPERDAY) {
1074 		rem -= SECSPERDAY;
1075 		++days;
1076 	}
1077 	tmptr->tm_hour = (int)(rem / SECSPERHOUR);
1078 	rem = rem % SECSPERHOUR;
1079 	tmptr->tm_min = (int)(rem / SECSPERMIN);
1080 	tmptr->tm_sec = (int)(rem % SECSPERMIN);
1081 
1082 	tmptr->tm_wday = (int)((EPOCH_WDAY + days) % DAYSPERWEEK);
1083 	if (tmptr->tm_wday < 0)
1084 		tmptr->tm_wday += DAYSPERWEEK;
1085 	y = EPOCH_YEAR;
1086 	while (days < 0 || days >= (long)__year_lengths[yleap = isleap(y)]) {
1087 		long newy;
1088 
1089 		newy = y + days / DAYSPERNYEAR;
1090 		if (days < 0)
1091 			--newy;
1092 		days -= ((long)newy - (long)y) * DAYSPERNYEAR +
1093 			LEAPS_THRU_END_OF(newy > 0 ? newy - 1L : newy) -
1094 			LEAPS_THRU_END_OF(y > 0 ? y - 1L : y);
1095 		y = newy;
1096 	}
1097 	tmptr->tm_year = (int)(y - TM_YEAR_BASE);
1098 	tmptr->tm_yday = (int)days;
1099 	ip = __mon_lengths[yleap];
1100 	for (tmptr->tm_mon = 0; days >=
1101 		(long)ip[tmptr->tm_mon]; ++(tmptr->tm_mon))
1102 			days = days - (long)ip[tmptr->tm_mon];
1103 	tmptr->tm_mday = (int)(days + 1);
1104 	tmptr->tm_isdst = 0;
1105 
1106 #ifdef _LP64
1107 	/* do as much as possible before checking for error. */
1108 	if ((y > (long)INT_MAX + TM_YEAR_BASE) ||
1109 	    (y < (long)INT_MIN + TM_YEAR_BASE)) {
1110 		errno = EOVERFLOW;
1111 		return (NULL);
1112 	}
1113 #endif
1114 	return (tmptr);
1115 }
1116 
1117 /*
1118  * Check whether DST is set for time in question.  Only applies to
1119  * POSIX timezones.  If explicit POSIX transition rules were provided
1120  * for the current zone, use those, otherwise use default USA POSIX
1121  * transitions.
1122  */
1123 static int
1124 posix_check_dst(long long t, state_t *sp)
1125 {
1126 	struct tm	gmttm;
1127 	long long	jan01;
1128 	int		year, i, idx, ridx;
1129 	posix_daylight_t	pdaylight;
1130 
1131 	(void) offtime_u(t, 0L, &gmttm);
1132 
1133 	year = gmttm.tm_year + 1900;
1134 	jan01 = t - ((gmttm.tm_yday * SECSPERDAY) +
1135 			(gmttm.tm_hour * SECSPERHOUR) +
1136 			(gmttm.tm_min * SECSPERMIN) + gmttm.tm_sec);
1137 	/*
1138 	 * If transition rules were provided for this zone,
1139 	 * use them, otherwise, default to USA daylight rules,
1140 	 * which are historically correct for the continental USA,
1141 	 * excluding local provisions.  (This logic may be replaced
1142 	 * at some point in the future with "posixrules" to offer
1143 	 * more flexibility to the system administrator).
1144 	 */
1145 	if (sp->zonerules == POSIX)	 {	/* POSIX rules */
1146 		pdaylight.rules[0] = &sp->start_rule;
1147 		pdaylight.rules[1] = &sp->end_rule;
1148 	} else { 			/* POSIX_USA: USA */
1149 		i = 0;
1150 		while (year < __usa_rules[i].s_year && i < MAX_RULE_TABLE) {
1151 			i++;
1152 		}
1153 		pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
1154 		pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
1155 	}
1156 	pdaylight.offset[0] = timezone;
1157 	pdaylight.offset[1] = altzone;
1158 
1159 	idx = posix_daylight(&jan01, year, &pdaylight);
1160 	ridx = !idx;
1161 
1162 	/*
1163 	 * Note:  t, rtime[0], and rtime[1] are all bounded within 'year'
1164 	 * beginning on 'jan01'
1165 	 */
1166 	if (t >= pdaylight.rtime[idx] && t < pdaylight.rtime[ridx]) {
1167 		return (ridx);
1168 	} else {
1169 		return (idx);
1170 	}
1171 }
1172 
1173 /*
1174  * Given January 1, 00:00:00 GMT for a year as an Epoch-relative time,
1175  * along with the integer year #, a posix_daylight_t that is composed
1176  * of two rules, and two GMT offsets (timezone and altzone), calculate
1177  * the two Epoch-relative times the two rules take effect, and return
1178  * them in the two rtime fields of the posix_daylight_t structure.
1179  * Also update janfirst by a year, by adding the appropriate number of
1180  * seconds depending on whether the year is a leap year or not.  (We take
1181  * advantage that this routine knows the leap year status.)
1182  */
1183 static int
1184 posix_daylight(long long *janfirst, int year, posix_daylight_t *pdaylightp)
1185 {
1186 	rule_t	*rulep;
1187 	long	offset;
1188 	int	idx;
1189 	int	i, d, m1, yy0, yy1, yy2, dow;
1190 	long	leapyear;
1191 	long long	value;
1192 
1193 	static const int	__secs_year_lengths[2] = {
1194 		DAYS_PER_NYEAR * SECSPERDAY,
1195 		DAYS_PER_LYEAR * SECSPERDAY
1196 	};
1197 
1198 	leapyear = isleap(year);
1199 
1200 	for (idx = 0; idx < 2; idx++) {
1201 		rulep = pdaylightp->rules[idx];
1202 		offset = pdaylightp->offset[idx];
1203 
1204 		switch (rulep->r_type) {
1205 
1206 		case MON_WEEK_DOW:
1207 			/*
1208 			 * Mm.n.d - nth "dth day" of month m.
1209 			 */
1210 			value = *janfirst;
1211 			for (i = 0; i < rulep->r_mon - 1; ++i)
1212 				value += __mon_lengths[leapyear][i] *
1213 				    SECSPERDAY;
1214 
1215 			/*
1216 			 * Use Zeller's Congruence to get day-of-week of first
1217 			 * day of month.
1218 			 */
1219 			m1 = (rulep->r_mon + 9) % 12 + 1;
1220 			yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
1221 			yy1 = yy0 / 100;
1222 			yy2 = yy0 % 100;
1223 			dow = ((26 * m1 - 2) / 10 +
1224 			    1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
1225 
1226 			if (dow < 0)
1227 				dow += DAYSPERWEEK;
1228 
1229 			/*
1230 			 * Following heuristic increases accuracy of USA rules
1231 			 * for negative years.
1232 			 */
1233 			if (year < 1 && leapyear)
1234 				++dow;
1235 			/*
1236 			 * "dow" is the day-of-week of the first day of the
1237 			 * month.  Get the day-of-month, zero-origin, of the
1238 			 * first "dow" day of the month.
1239 			 */
1240 			d = rulep->r_day - dow;
1241 			if (d < 0)
1242 				d += DAYSPERWEEK;
1243 			for (i = 1; i < rulep->r_week; ++i) {
1244 				if (d + DAYSPERWEEK >=
1245 				    __mon_lengths[leapyear][rulep->r_mon - 1])
1246 					break;
1247 				d += DAYSPERWEEK;
1248 			}
1249 			/*
1250 			 * "d" is the day-of-month, zero-origin, of the day
1251 			 * we want.
1252 			 */
1253 			value += d * SECSPERDAY;
1254 			break;
1255 
1256 		case JULIAN_DAY:
1257 			/*
1258 			 * Jn - Julian day, 1 == Jan 1, 60 == March 1 even
1259 			 * in leap yrs.
1260 			 */
1261 			value = *janfirst + (rulep->r_day - 1) * SECSPERDAY;
1262 			if (leapyear && rulep->r_day >= 60)
1263 				value += SECSPERDAY;
1264 			break;
1265 
1266 		case DAY_OF_YEAR:
1267 			/*
1268 			 * n - day of year.
1269 			 */
1270 			value = *janfirst + rulep->r_day * SECSPERDAY;
1271 			break;
1272 		}
1273 		pdaylightp->rtime[idx] = value + rulep->r_time + offset;
1274 	}
1275 	*janfirst += __secs_year_lengths[leapyear];
1276 
1277 	return ((pdaylightp->rtime[0] > pdaylightp->rtime[1]) ? 1 : 0);
1278 }
1279 
1280 /*
1281  * Try to load zoneinfo file into internal transition tables using name
1282  * indicated in TZ, and do validity checks.  The format of zic(1M)
1283  * compiled zoneinfo files isdescribed in tzfile.h
1284  */
1285 static int
1286 load_zoneinfo(const char *name, state_t *sp)
1287 {
1288 	char	*cp;
1289 	char	*cp2;
1290 	int	i;
1291 	long	cnt;
1292 	int	fid;
1293 	int	ttisstdcnt;
1294 	int	ttisgmtcnt;
1295 	char	*fullname;
1296 	size_t	namelen;
1297 	char	*bufp;
1298 	size_t	flen;
1299 	prev_t	*prevp;
1300 /* LINTED */
1301 	struct	tzhead *tzhp;
1302 	struct	stat64	stbuf;
1303 	ttinfo_t	*most_recent_alt = NULL;
1304 	ttinfo_t	*most_recent_std = NULL;
1305 	ttinfo_t	*ttisp;
1306 
1307 
1308 	if (name == NULL && (name = TZDEFAULT) == NULL)
1309 		return (-1);
1310 
1311 	if ((name[0] == '/') || strstr(name, "../"))
1312 		return (-1);
1313 
1314 	/*
1315 	 * We allocate fullname this way to avoid having
1316 	 * a PATH_MAX size buffer in our stack frame.
1317 	 */
1318 	namelen = LEN_TZDIR + 1 + strlen(name) + 1;
1319 	if ((fullname = lmalloc(namelen)) == NULL)
1320 		return (-1);
1321 	(void) strcpy(fullname, TZDIR "/");
1322 	(void) strcpy(fullname + LEN_TZDIR + 1, name);
1323 	if ((fid = open(fullname, O_RDONLY)) == -1) {
1324 		lfree(fullname, namelen);
1325 		return (-1);
1326 	}
1327 	lfree(fullname, namelen);
1328 
1329 	if (fstat64(fid, &stbuf) == -1) {
1330 		(void) close(fid);
1331 		return (-1);
1332 	}
1333 
1334 	flen = (size_t)stbuf.st_size;
1335 	if (flen < sizeof (struct tzhead)) {
1336 		(void) close(fid);
1337 		return (-1);
1338 	}
1339 
1340 	/*
1341 	 * It would be nice to use alloca() to allocate bufp but,
1342 	 * as above, we wish to avoid allocating a big buffer in
1343 	 * our stack frame, and also because alloca() gives us no
1344 	 * opportunity to fail gracefully on allocation failure.
1345 	 */
1346 	cp = bufp = lmalloc(flen);
1347 	if (bufp == NULL) {
1348 		(void) close(fid);
1349 		return (-1);
1350 	}
1351 
1352 	if ((cnt = read(fid, bufp, flen)) != flen) {
1353 		lfree(bufp, flen);
1354 		(void) close(fid);
1355 		return (-1);
1356 	}
1357 
1358 	if (close(fid) != 0) {
1359 		lfree(bufp, flen);
1360 		return (-1);
1361 	}
1362 
1363 	cp += (sizeof (tzhp->tzh_magic)) + (sizeof (tzhp->tzh_reserved));
1364 
1365 /* LINTED: alignment */
1366 	ttisstdcnt = CVTZCODE(cp);
1367 /* LINTED: alignment */
1368 	ttisgmtcnt = CVTZCODE(cp);
1369 /* LINTED: alignment */
1370 	sp->leapcnt = CVTZCODE(cp);
1371 /* LINTED: alignment */
1372 	sp->timecnt = CVTZCODE(cp);
1373 /* LINTED: alignment */
1374 	sp->typecnt = CVTZCODE(cp);
1375 /* LINTED: alignment */
1376 	sp->charcnt = CVTZCODE(cp);
1377 
1378 	if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
1379 	    sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
1380 	    sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
1381 	    sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
1382 	    (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
1383 	    (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) {
1384 		lfree(bufp, flen);
1385 		return (-1);
1386 	}
1387 
1388 	if (cnt - (cp - bufp) < (long)(sp->timecnt * 4 +	/* ats */
1389 	    sp->timecnt +			/* types */
1390 	    sp->typecnt * (4 + 2) +		/* ttinfos */
1391 	    sp->charcnt +			/* chars */
1392 	    sp->leapcnt * (4 + 4) +		/* lsinfos */
1393 	    ttisstdcnt +			/* ttisstds */
1394 	    ttisgmtcnt)) {			/* ttisgmts */
1395 		lfree(bufp, flen);
1396 		return (-1);
1397 	}
1398 
1399 
1400 	for (i = 0; i < sp->timecnt; ++i) {
1401 /* LINTED: alignment */
1402 		sp->ats[i] = CVTZCODE(cp);
1403 	}
1404 
1405 	/*
1406 	 * Skip over types[] for now and load ttis[] so that when
1407 	 * types[] are loaded we can check for transitions to STD & DST.
1408 	 * This allows us to shave cycles in ltzset_u(), including
1409 	 * eliminating the need to check set 'daylight' later.
1410 	 */
1411 
1412 	cp2 = (char *)((uintptr_t)cp + sp->timecnt);
1413 
1414 	for (i = 0; i < sp->typecnt; ++i) {
1415 		ttisp = &sp->ttis[i];
1416 /* LINTED: alignment */
1417 		ttisp->tt_gmtoff = CVTZCODE(cp2);
1418 		ttisp->tt_isdst = (uchar_t)*cp2++;
1419 
1420 		if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) {
1421 			lfree(bufp, flen);
1422 			return (-1);
1423 		}
1424 
1425 		ttisp->tt_abbrind = (uchar_t)*cp2++;
1426 		if (ttisp->tt_abbrind < 0 ||
1427 		    ttisp->tt_abbrind > sp->charcnt) {
1428 			lfree(bufp, flen);
1429 			return (-1);
1430 		}
1431 	}
1432 
1433 	/*
1434 	 * Since ttis were loaded ahead of types, it is possible to
1435 	 * detect whether daylight is ever set for this zone now, and
1436 	 * also preload other information to avoid repeated lookups later.
1437 	 * This logic facilitates keeping a running tab on the state of
1438 	 * std zone and alternate zone transitions such that timezone,
1439 	 * altzone and tzname[] can be determined quickly via an
1440 	 * index to any transition.
1441 	 *
1442 	 * For transition #0 there are no previous transitions,
1443 	 * so prev->std and prev->alt will be null, but that's OK,
1444 	 * because null prev->std/prev->alt effectively
1445 	 * indicates none existed prior.
1446 	 */
1447 
1448 	prevp = &sp->prev[0];
1449 
1450 	for (i = 0; i < sp->timecnt; ++i) {
1451 
1452 		sp->types[i] = (uchar_t)*cp++;
1453 		ttisp = &sp->ttis[sp->types[i]];
1454 
1455 		prevp->std = most_recent_std;
1456 		prevp->alt = most_recent_alt;
1457 
1458 		if (ttisp->tt_isdst == 1) {
1459 			most_recent_alt = ttisp;
1460 		} else {
1461 			most_recent_std = ttisp;
1462 		}
1463 
1464 		if ((int)sp->types[i] >= sp->typecnt) {
1465 			lfree(bufp, flen);
1466 			return (-1);
1467 		}
1468 
1469 		++prevp;
1470 	}
1471 	if (most_recent_alt == NULL)
1472 		sp->daylight = 0;
1473 	else
1474 		sp->daylight = 1;
1475 
1476 	/*
1477 	 * Set pointer ahead to where it would have been if we
1478 	 * had read types[] and ttis[] in the same order they
1479 	 * occurred in the file.
1480 	 */
1481 	cp = cp2;
1482 	for (i = 0; i < sp->charcnt; ++i)
1483 		sp->chars[i] = *cp++;
1484 
1485 	sp->chars[i] = '\0';	/* ensure '\0' at end */
1486 
1487 	for (i = 0; i < sp->leapcnt; ++i) {
1488 		struct lsinfo *lsisp;
1489 
1490 		lsisp = &sp->lsis[i];
1491 /* LINTED: alignment */
1492 		lsisp->ls_trans = CVTZCODE(cp);
1493 /* LINTED: alignment */
1494 		lsisp->ls_corr = CVTZCODE(cp);
1495 	}
1496 
1497 	for (i = 0; i < sp->typecnt; ++i) {
1498 		ttisp = &sp->ttis[i];
1499 		if (ttisstdcnt == 0) {
1500 			ttisp->tt_ttisstd = FALSE;
1501 		} else {
1502 			ttisp->tt_ttisstd = *cp++;
1503 			if (ttisp->tt_ttisstd != TRUE &&
1504 			    ttisp->tt_ttisstd != FALSE) {
1505 				lfree(bufp, flen);
1506 				return (-1);
1507 			}
1508 		}
1509 	}
1510 
1511 	for (i = 0; i < sp->typecnt; ++i) {
1512 		ttisp = &sp->ttis[i];
1513 		if (ttisgmtcnt == 0) {
1514 			ttisp->tt_ttisgmt = FALSE;
1515 		} else {
1516 			ttisp->tt_ttisgmt = *cp++;
1517 			if (ttisp->tt_ttisgmt != TRUE &&
1518 			    ttisp->tt_ttisgmt != FALSE) {
1519 				lfree(bufp, flen);
1520 				return (-1);
1521 			}
1522 		}
1523 	}
1524 
1525 	/*
1526 	 * Other defaults set at beginning of this routine
1527 	 * to cover case where zoneinfo file cannot be loaded
1528 	 */
1529 	sp->default_timezone = -sp->ttis[0].tt_gmtoff;
1530 	sp->default_altzone  = 0;
1531 	sp->default_tzname0  = &sp->chars[0];
1532 	sp->default_tzname1  = _tz_spaces;
1533 
1534 	lfree(bufp, flen);
1535 
1536 	sp->zonerules = ZONEINFO;
1537 
1538 	return (0);
1539 }
1540 
1541 /*
1542  * Given a POSIX section 8-style TZ string, fill in transition tables.
1543  *
1544  * Examples:
1545  *
1546  * TZ = PST8 or GMT0
1547  *	Timecnt set to 0 and typecnt set to 1, reflecting std time only.
1548  *
1549  * TZ = PST8PDT or PST8PDT7
1550  *	Create transition times by applying USA transitions from
1551  *	Jan 1 of each year covering 1902-2038.  POSIX offsets
1552  *	as specified in the TZ are used to calculate the tt_gmtoff
1553  *	for each of the two zones.  If ommitted, DST defaults to
1554  *	std. time minus one hour.
1555  *
1556  * TZ = <PST8>8PDT  or <PST8>8<PDT9>
1557  *      Quoted transition.  The values in angled brackets are treated
1558  *      as zone name text, not parsed as offsets.  The offsets
1559  *      occuring following the zonename section.  In this way,
1560  *      instead of PST being displayed for standard time, it could
1561  *      be displayed as PST8 to give an indication of the offset
1562  *      of that zone to GMT.
1563  *
1564  * TZ = GMT0BST, M3.5.0/1, M10.5.0/2   or  GMT0BST, J23953, J23989
1565  *	Create transition times based on the application new-year
1566  *	relative POSIX transitions, parsed from TZ, from Jan 1
1567  *	for each year covering 1902-2038.  POSIX offsets specified
1568  *	in TZ are used to calculate tt_gmtoff for each of the two
1569  *	zones.
1570  *
1571  */
1572 static int
1573 load_posixinfo(const char *name, state_t *sp)
1574 {
1575 	const char	*stdname;
1576 	const char	*dstname = 0;
1577 	size_t		stdlen;
1578 	size_t		dstlen;
1579 	long		stdoff = 0;
1580 	long		dstoff = 0;
1581 	time_t		*tranp;
1582 	uchar_t		*typep;
1583 	prev_t		*prevp;
1584 	char		*cp;
1585 	int		year;
1586 	int		i;
1587 	long long	janfirst;
1588 	ttinfo_t	*dst;
1589 	ttinfo_t	*std;
1590 	int		quoted;
1591 	zone_rules_t	zonetype;
1592 	posix_daylight_t	pdaylight;
1593 
1594 	zonetype = POSIX_USA;
1595 	stdname = name;
1596 
1597 	if ((quoted = (*stdname == '<')) != 0)
1598 		++stdname;
1599 
1600 	/* Parse/extract STD zone name, len and GMT offset */
1601 	if (*name != '\0') {
1602 		if ((name = getzname(name, quoted)) == NULL)
1603 			return (-1);
1604 		stdlen = name - stdname;
1605 		if (*name == '>')
1606 			++name;
1607 		if (*name == '\0' || stdlen < 1) {
1608 			return (-1);
1609 		} else {
1610 			if ((name = getoffset(name, &stdoff)) == NULL)
1611 				return (-1);
1612 		}
1613 	}
1614 
1615 	/* If DST specified in TZ, extract DST zone details */
1616 	if (*name != '\0') {
1617 
1618 		dstname = name;
1619 		if ((quoted = (*dstname == '<')) != 0)
1620 			++dstname;
1621 		if ((name = getzname(name, quoted)) == NULL)
1622 			return (-1);
1623 		dstlen = name - dstname;
1624 		if (dstlen < 1)
1625 		    return (-1);
1626 		if (*name == '>')
1627 			++name;
1628 		if (*name != '\0' && *name != ',' && *name != ';') {
1629 			if ((name = getoffset(name, &dstoff)) == NULL)
1630 				return (-1);
1631 		} else {
1632 			dstoff = stdoff - SECSPERHOUR;
1633 		}
1634 
1635 		/* If any present, extract POSIX transitions from TZ */
1636 		if (*name == ',' || *name == ';') {
1637 			/* Backward compatibility using ';' separator */
1638 			int	compat_flag = (*name == ';');
1639 			++name;
1640 			if ((name = getrule(name, &sp->start_rule, compat_flag))
1641 			    == NULL)
1642 				return (-1);
1643 			if (*name++ != ',')
1644 				return (-1);
1645 			if ((name = getrule(name, &sp->end_rule, compat_flag))
1646 			    == NULL)
1647 				return (-1);
1648 			if (*name != '\0')
1649 				return (-1);
1650 			zonetype = POSIX;
1651 		}
1652 
1653 		/*
1654 		 * We know STD and DST zones are specified with this timezone
1655 		 * therefore the cache will be set up with 2 transitions per
1656 		 * year transitioning to their respective std and dst zones.
1657 		 */
1658 		sp->daylight = 1;
1659 		sp->typecnt = 2;
1660 		sp->timecnt = 272;
1661 
1662 		/*
1663 		 * Insert zone data from POSIX TZ into state table
1664 		 * The Olson public domain POSIX code sets up ttis[0] to be DST,
1665 		 * as we are doing here.  It seems to be the correct behavior.
1666 		 * The US/Pacific zoneinfo file also lists DST as first type.
1667 		 */
1668 		dst = &sp->ttis[0];
1669 		dst->tt_gmtoff = -dstoff;
1670 		dst->tt_isdst = 1;
1671 
1672 		std = &sp->ttis[1];
1673 		std->tt_gmtoff = -stdoff;
1674 		std->tt_isdst = 0;
1675 
1676 		sp->prev[0].std = NULL;
1677 		sp->prev[0].alt = NULL;
1678 
1679 		/* Create transition data based on POSIX TZ */
1680 		tranp = sp->ats;
1681 		prevp  = &sp->prev[1];
1682 		typep  = sp->types;
1683 
1684 		/*
1685 		 * We only cache from 1902 to 2037 to avoid transistions
1686 		 * that wrap at the 32-bit boundries, since 1901 and 2038
1687 		 * are not full years in 32-bit time.  The rough edges
1688 		 * will be handled as transition cache misses.
1689 		 */
1690 
1691 		janfirst = JAN_01_1902;
1692 
1693 		pdaylight.rules[0] = &sp->start_rule;
1694 		pdaylight.rules[1] = &sp->end_rule;
1695 		pdaylight.offset[0] = stdoff;
1696 		pdaylight.offset[1] = dstoff;
1697 
1698 		for (i = MAX_RULE_TABLE; i >= 0; i--) {
1699 			if (zonetype == POSIX_USA) {
1700 				pdaylight.rules[0] =
1701 				    (rule_t *)&__usa_rules[i].start;
1702 				pdaylight.rules[1] =
1703 				    (rule_t *)&__usa_rules[i].end;
1704 			}
1705 			for (year = __usa_rules[i].s_year;
1706 			    year <= __usa_rules[i].e_year;
1707 			    year++) {
1708 				int	idx, ridx;
1709 				idx =
1710 				    posix_daylight(&janfirst, year, &pdaylight);
1711 				ridx = !idx;
1712 
1713 				/*
1714 				 * Two transitions per year. Since there are
1715 				 * only two zone types for this POSIX zone,
1716 				 * previous std and alt are always set to
1717 				 * &ttis[0] and &ttis[1].
1718 				 */
1719 				*tranp++ = (time_t)pdaylight.rtime[idx];
1720 				*typep++ = idx;
1721 				prevp->std = std;
1722 				prevp->alt = dst;
1723 				++prevp;
1724 
1725 				*tranp++ = (time_t)pdaylight.rtime[ridx];
1726 				*typep++ = ridx;
1727 				prevp->std = std;
1728 				prevp->alt = dst;
1729 				++prevp;
1730 			}
1731 		}
1732 	} else {  /* DST wasn't specified in POSIX TZ */
1733 
1734 		/*  Since we only have STD time, there are no transitions */
1735 		dstlen = 0;
1736 		sp->daylight = 0;
1737 		sp->typecnt = 1;
1738 		sp->timecnt = 0;
1739 		std = &sp->ttis[0];
1740 		std->tt_gmtoff = -stdoff;
1741 		std->tt_isdst = 0;
1742 
1743 	}
1744 
1745 	/* Setup zone name character data for state table */
1746 	sp->charcnt = (int)(stdlen + 1);
1747 	if (dstlen != 0)
1748 		sp->charcnt += dstlen + 1;
1749 
1750 	/* If bigger than zone name abbv. buffer, grow it */
1751 	if ((size_t)sp->charcnt > sp->charsbuf_size) {
1752 		if ((cp = libc_realloc(sp->chars, sp->charcnt)) == NULL)
1753 			return (-1);
1754 		sp->chars = cp;
1755 		sp->charsbuf_size = sp->charcnt;
1756 	}
1757 
1758 	/*
1759 	 * Copy zone name text null-terminatedly into state table.
1760 	 * By doing the copy once during zone loading, setting
1761 	 * tzname[] subsequently merely involves setting pointer
1762 	 *
1763 	 * If either or both std. or alt. zone name < 3 chars,
1764 	 * space pad the deficient name(s) to right.
1765 	 */
1766 
1767 	std->tt_abbrind = 0;
1768 	cp = sp->chars;
1769 	(void) strncpy(cp, stdname, stdlen);
1770 	while (stdlen < 3)
1771 		cp[stdlen++] = ' ';
1772 	cp[stdlen] = '\0';
1773 
1774 	i = (int)(stdlen + 1);
1775 	if (dstlen != 0) {
1776 		dst->tt_abbrind = i;
1777 		cp += i;
1778 		(void) strncpy(cp, dstname, dstlen);
1779 		while (dstlen < 3)
1780 			cp[dstlen++] = ' ';
1781 		cp[dstlen] = '\0';
1782 	}
1783 
1784 	/* Save default values */
1785 	if (sp->typecnt == 1) {
1786 		sp->default_timezone = stdoff;
1787 		sp->default_altzone = stdoff;
1788 		sp->default_tzname0 = &sp->chars[0];
1789 		sp->default_tzname1 = _tz_spaces;
1790 	} else {
1791 		sp->default_timezone = -std->tt_gmtoff;
1792 		sp->default_altzone = -dst->tt_gmtoff;
1793 		sp->default_tzname0 = &sp->chars[std->tt_abbrind];
1794 		sp->default_tzname1 = &sp->chars[dst->tt_abbrind];
1795 	}
1796 
1797 	sp->zonerules = zonetype;
1798 
1799 	return (0);
1800 }
1801 
1802 
1803 /*
1804  * Given a pointer into a time zone string, scan until a character that is not
1805  * a valid character in a zone name is found.  Return ptr to that character.
1806  * Return NULL if error (ie. non-printable character located in name)
1807  */
1808 static const char *
1809 getzname(const char *strp, int quoted)
1810 {
1811 	char	c;
1812 
1813 	if (quoted) {
1814 		while ((c = *strp) != '\0' && c != '>' &&
1815 			isgraph((unsigned char)c))
1816 				++strp;
1817 	} else {
1818 		while ((c = *strp) != '\0' && isgraph((unsigned char)c) &&
1819 		    !isdigit((unsigned char)c) && c != ',' && c != '-' &&
1820 			    c != '+')
1821 				++strp;
1822 	}
1823 
1824 	/* Found an excessively invalid character.  Discredit whole name */
1825 	if (c != '\0' && !isgraph((unsigned char)c))
1826 		return (NULL);
1827 
1828 	return (strp);
1829 }
1830 
1831 /*
1832  * Given pointer into time zone string, extract first
1833  * number pointed to.  Validate number within range specified,
1834  * Return ptr to first char following valid numeric sequence.
1835  */
1836 static const char *
1837 getnum(const char *strp, int *nump, int min, int max)
1838 {
1839 	char	c;
1840 	int	num;
1841 
1842 	if (strp == NULL || !isdigit((unsigned char)(c = *strp)))
1843 		return (NULL);
1844 	num = 0;
1845 	do {
1846 		num = num * 10 + (c - '0');
1847 		if (num > max)
1848 			return (NULL);	/* illegal value */
1849 		c = *++strp;
1850 	} while (isdigit((unsigned char)c));
1851 	if (num < min)
1852 		return (NULL);		/* illegal value */
1853 	*nump = num;
1854 	return (strp);
1855 }
1856 
1857 /*
1858  * Given a pointer into a time zone string, extract a number of seconds,
1859  * in hh[:mm[:ss]] form, from the string.  If an error occurs, return NULL,
1860  * otherwise, return a pointer to the first character not part of the number
1861  * of seconds.
1862  */
1863 static const char *
1864 getsecs(const char *strp, long *secsp)
1865 {
1866 	int	num;
1867 
1868 	/*
1869 	 * `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
1870 	 * "M10.4.6/26", which does not conform to Posix,
1871 	 * but which specifies the equivalent of
1872 	 * ``02:00 on the first Sunday on or after 23 Oct''.
1873 	 */
1874 	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
1875 	if (strp == NULL)
1876 		return (NULL);
1877 	*secsp = num * (long)SECSPERHOUR;
1878 	if (*strp == ':') {
1879 		++strp;
1880 		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
1881 		if (strp == NULL)
1882 			return (NULL);
1883 		*secsp += num * SECSPERMIN;
1884 		if (*strp == ':') {
1885 			++strp;
1886 			/* `SECSPERMIN' allows for leap seconds.  */
1887 			strp = getnum(strp, &num, 0, SECSPERMIN);
1888 			if (strp == NULL)
1889 				return (NULL);
1890 			*secsp += num;
1891 		}
1892 	}
1893 	return (strp);
1894 }
1895 
1896 /*
1897  * Given a pointer into a time zone string, extract an offset, in
1898  * [+-]hh[:mm[:ss]] form, from the string.
1899  * If any error occurs, return NULL.
1900  * Otherwise, return a pointer to the first character not part of the time.
1901  */
1902 static const char *
1903 getoffset(const char *strp, long *offsetp)
1904 {
1905 	int	neg = 0;
1906 
1907 	if (*strp == '-') {
1908 		neg = 1;
1909 		++strp;
1910 	} else if (*strp == '+') {
1911 		++strp;
1912 	}
1913 	strp = getsecs(strp, offsetp);
1914 	if (strp == NULL)
1915 		return (NULL);		/* illegal time */
1916 	if (neg)
1917 		*offsetp = -*offsetp;
1918 	return (strp);
1919 }
1920 
1921 /*
1922  * Given a pointer into a time zone string, extract a rule in the form
1923  * date[/time].  See POSIX section 8 for the format of "date" and "time".
1924  * If a valid rule is not found, return NULL.
1925  * Otherwise, return a pointer to the first character not part of the rule.
1926  *
1927  * If compat_flag is set, support old 1-based day of year values.
1928  */
1929 static const char *
1930 getrule(const char *strp, rule_t *rulep, int compat_flag)
1931 {
1932 	if (compat_flag == 0 && *strp == 'M') {
1933 		/*
1934 		 * Month, week, day.
1935 		 */
1936 		rulep->r_type = MON_WEEK_DOW;
1937 		++strp;
1938 		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
1939 		if (strp == NULL)
1940 			return (NULL);
1941 		if (*strp++ != '.')
1942 			return (NULL);
1943 		strp = getnum(strp, &rulep->r_week, 1, 5);
1944 		if (strp == NULL)
1945 			return (NULL);
1946 		if (*strp++ != '.')
1947 			return (NULL);
1948 		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
1949 	} else if (compat_flag == 0 && *strp == 'J') {
1950 		/*
1951 		 * Julian day.
1952 		 */
1953 		rulep->r_type = JULIAN_DAY;
1954 		++strp;
1955 		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
1956 
1957 	} else if (isdigit((unsigned char)*strp)) {
1958 		/*
1959 		 * Day of year.
1960 		 */
1961 		rulep->r_type = DAY_OF_YEAR;
1962 		if (compat_flag == 0) {
1963 			/* zero-based day of year */
1964 			strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
1965 		} else {
1966 			/* one-based day of year */
1967 			strp = getnum(strp, &rulep->r_day, 1, DAYSPERLYEAR);
1968 			rulep->r_day--;
1969 		}
1970 	} else {
1971 		return (NULL);		/* ZONERULES_INVALID format */
1972 	}
1973 	if (strp == NULL)
1974 		return (NULL);
1975 	if (*strp == '/') {
1976 		/*
1977 		 * Time specified.
1978 		 */
1979 		++strp;
1980 		strp = getsecs(strp, &rulep->r_time);
1981 	} else	{
1982 		rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
1983 	}
1984 	return (strp);
1985 }
1986 
1987 /*
1988  * Returns default value for TZ as specified in /etc/default/init file, if
1989  * a default value for TZ is provided there.
1990  *
1991  * To strip quotes:  if a '"', or '\''is found, then transfer the following
1992  * bytes forward, and overwrite the double or single quote.  The tzS
1993  * pointer is used to keep track of the location in the buffer, to which bytes
1994  * pointed to by tzq, will be transferred.  Then, the <value> string
1995  * should be NULL terminated.  If no '"' or '\'' characters were encountered,
1996  * tzS will be NULL, so tzq should already be NULL-terminated.
1997  * However, if '"' or '\'' characters were encountered, then tzS will
1998  * be non-NULL, and *tzS should be set to the NULL character.
1999  */
2000 static char *
2001 get_default_tz(void)
2002 {
2003 	char	*tz;
2004 	int	in;
2005 
2006 	tz = NULL;
2007 	in = open(TIMEZONE, O_RDONLY);
2008 	if (in != -1) {
2009 		int	eof = 0;
2010 		char	tzFilebuf[BUFSIZ+1];
2011 		char	*p, *q;
2012 		size_t	bufsize;
2013 
2014 		p = q = tzFilebuf;
2015 		bufsize = BUFSIZ;
2016 		for (;;) {
2017 			char	*lineE, *nextp;
2018 			size_t	mlen;
2019 			int	r;
2020 
2021 			if (eof == 0) {
2022 				r = read(in, q, bufsize);
2023 				if (r <= 0)
2024 					break;
2025 				if (r < bufsize)
2026 					eof = 1;
2027 				*(q + r) = '\0';
2028 			}
2029 
2030 			if ((lineE = strchr(p, '\n')) == NULL) {
2031 				/* line too long */
2032 				break;
2033 			}
2034 			*lineE = '\0';
2035 
2036 			if (strncmp(TZSTRING, p, sizeof (TZSTRING) - 1) == 0) {
2037 				unsigned char	*tzp, *tzq, *tzS;
2038 
2039 				tzp = (unsigned char *)p +
2040 				    sizeof (TZSTRING) - 1;
2041 				while (isspace(*tzp))
2042 					tzp++;
2043 				tzq = tzp;
2044 				tzS = NULL;
2045 				while (isspace(*tzq) == 0 &&
2046 				    *tzq != ';' &&
2047 				    *tzq != '#' &&
2048 				    *tzq != '\0') {
2049 					if (*tzq == '"' ||
2050 					    *tzq == '\'') {
2051 						if (tzS == NULL) {
2052 							tzS = tzq;
2053 						}
2054 					} else {
2055 						if (tzS != NULL) {
2056 							*tzS = *tzq;
2057 							tzS++;
2058 						}
2059 					}
2060 					tzq++;
2061 				}
2062 				if (tzS != NULL)
2063 					*tzS = '\0';
2064 				else
2065 					*tzq = '\0';
2066 				tz = strdup((char *)tzp);
2067 				break;
2068 			}
2069 			nextp = lineE + 1;
2070 			if (eof == 0) {
2071 				mlen = (q + r) - nextp;
2072 				(void) memmove(p, nextp, mlen);
2073 				q = p + mlen;
2074 				bufsize = BUFSIZ - mlen;
2075 			} else {
2076 				p = nextp;
2077 			}
2078 		}
2079 		(void) close(in);
2080 	}
2081 	return (tz);
2082 }
2083 
2084 static state_t *
2085 get_zone(systemtz_t *tzp)
2086 {
2087 	int	hashid;
2088 	state_t	*m, *p;
2089 	const char *zonename = tzp->tz;
2090 
2091 	hashid = get_hashid(zonename);
2092 	m = tzcache[hashid];
2093 	while (m) {
2094 		int	r;
2095 		r = strcmp(m->zonename, zonename);
2096 		if (r == 0) {
2097 			/* matched */
2098 			return (NULL);
2099 		} else if (r > 0) {
2100 			break;
2101 		}
2102 		m = m->next;
2103 	}
2104 	/* malloc() return value is also checked for NULL in ltzset_u() */
2105 	p = malloc(sizeof (state_t));
2106 
2107 	/* ltzset_u() resets the free flag to 0 if it uses the p buffer */
2108 	if (p != NULL)
2109 		tzp->flag = 1;
2110 	return (p);
2111 }
2112 
2113 /*
2114  * getsystemTZ() returns the TZ value if it is set in the environment, or
2115  * it returns the system TZ;  if the systemTZ has not yet been set,
2116  * get_default_tz() is called to read the /etc/default/init file to get
2117  * the value.
2118  *
2119  * getsystemTZ() also calls get_zone() to do an initial check to see if the
2120  * timezone is the current timezone, or one that is already loaded in the
2121  * hash table.  If get_zone() determines the timezone has not yet been loaded,
2122  * it pre-allocates a buffer for a state_t struct, which ltzset_u() can use
2123  * later to load the timezone and add to the hash table.
2124  *
2125  * The large state_t buffer is allocated here to avoid calls to malloc()
2126  * within mutex_locks.
2127  */
2128 static systemtz_t *
2129 getsystemTZ(systemtz_t *stzp)
2130 {
2131 	static const char	*systemTZ = NULL;
2132 	char	*tz;
2133 
2134 	assert_no_libc_locks_held();
2135 
2136 	stzp->flag = 0;
2137 
2138 	tz = getenv("TZ");
2139 	if (tz != NULL && *tz != '\0') {
2140 		stzp->tz = (const char *)tz;
2141 		goto get_entry;
2142 	}
2143 
2144 	if (systemTZ != NULL) {
2145 		stzp->tz = systemTZ;
2146 		goto get_entry;
2147 	}
2148 
2149 	tz = get_default_tz();
2150 	lmutex_lock(&_time_lock);
2151 	if (systemTZ == NULL) {
2152 		if ((systemTZ = tz) != NULL)	/* found TZ entry in the file */
2153 			tz = NULL;
2154 		else
2155 			systemTZ = _posix_gmt0;	/* no TZ entry in the file */
2156 	}
2157 	lmutex_unlock(&_time_lock);
2158 
2159 	if (tz != NULL)		/* someone beat us to it; free our entry */
2160 		free(tz);
2161 
2162 	stzp->tz = systemTZ;
2163 
2164 get_entry:
2165 	/*
2166 	 * The object referred to by the 1st 'namecache'
2167 	 * may be different from the one by the 2nd 'namecache' below.
2168 	 * But, it does not matter.  The bottomline is at this point
2169 	 * 'namecache' points to non-NULL and whether the string pointed
2170 	 * to by 'namecache' is equivalent to stzp->tz or not.
2171 	 */
2172 	if (namecache != NULL && strcmp(namecache, stzp->tz) == 0) {
2173 		/*
2174 		 * At this point, we found the entry having the same
2175 		 * zonename as stzp->tz exists.  Later we will find
2176 		 * the exact one, so we don't need to allocate
2177 		 * the memory here.
2178 		 */
2179 		stzp->entry = NULL;
2180 	} else {
2181 		/*
2182 		 * At this point, we could not get the evidence that this
2183 		 * zonename had been cached.  We will look into the cache
2184 		 * further.
2185 		 */
2186 		stzp->entry = get_zone(stzp);
2187 	}
2188 	return (stzp);
2189 }
2190