xref: /titanic_44/usr/src/lib/libc/port/gen/localtime.c (revision 6b3dcaba4b523f95f84b332a8d270c4bc8f1cc55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
506e1a714Sraf  * Common Development and Distribution License (the "License").
606e1a714Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2106e1a714Sraf 
227c478bd9Sstevel@tonic-gate /*
23*6b3dcabaSAlexander Stetsenko  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
246eaad1d3SGarrett D'Amore  */
256eaad1d3SGarrett D'Amore 
266eaad1d3SGarrett D'Amore /*
27b9175c69SKenjiro Tsuji  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
287c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
327c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate /*
357c478bd9Sstevel@tonic-gate  * A part of this file comes from public domain source, so
367c478bd9Sstevel@tonic-gate  * clarified as of June 5, 1996 by Arthur David Olson
377c478bd9Sstevel@tonic-gate  * (arthur_david_olson@nih.gov).
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /*
417c478bd9Sstevel@tonic-gate  * localtime.c
427c478bd9Sstevel@tonic-gate  *
437c478bd9Sstevel@tonic-gate  * This file contains routines to convert struct tm to time_t and
447c478bd9Sstevel@tonic-gate  * back as well as adjust time values based on their timezone, which
457c478bd9Sstevel@tonic-gate  * is a local offset from GMT (Greenwich Mean Time).
467c478bd9Sstevel@tonic-gate  *
477c478bd9Sstevel@tonic-gate  * Many timezones actually consist of more than one offset from GMT.
487c478bd9Sstevel@tonic-gate  * The GMT offset that is considered the normal offset is referred
497c478bd9Sstevel@tonic-gate  * to as standard time.  The other offset is referred to as alternate
507c478bd9Sstevel@tonic-gate  * time, but is better known as daylight savings time or summer time.
517c478bd9Sstevel@tonic-gate  *
527c478bd9Sstevel@tonic-gate  * The current timezone for an application is derived from the TZ
537c478bd9Sstevel@tonic-gate  * environment variable either as defined in the environment or in
547c478bd9Sstevel@tonic-gate  * /etc/default/init.  As defined by IEEE 1003.1-1990 (POSIX), the
557c478bd9Sstevel@tonic-gate  * TZ variable can either be:
567c478bd9Sstevel@tonic-gate  *    :<characters>
577c478bd9Sstevel@tonic-gate  * or
587c478bd9Sstevel@tonic-gate  *    <std><offset1>[<dst>[<offset2>]][,<start>[/<time>],<end>[/<time>]
597c478bd9Sstevel@tonic-gate  *
607c478bd9Sstevel@tonic-gate  * <characters> is an implementation-defined string that somehow describes
617c478bd9Sstevel@tonic-gate  * a timezone.  The implementation-defined description of a timezone used
627c478bd9Sstevel@tonic-gate  * in Solaris is based on the public domain zoneinfo code available from
637c478bd9Sstevel@tonic-gate  * elsie.nci.nih.gov and a timezone that is specified in this way is
647c478bd9Sstevel@tonic-gate  * referred to as a zoneinfo timezone.  An example of this is ":US/Pacific".
657c478bd9Sstevel@tonic-gate  *
667c478bd9Sstevel@tonic-gate  * The precise definition of the second format can be found in POSIX,
677c478bd9Sstevel@tonic-gate  * but, basically, <std> is the abbreviation for the timezone in standard
687c478bd9Sstevel@tonic-gate  * (not daylight savings time), <offset1> is the standard offset from GMT,
697c478bd9Sstevel@tonic-gate  * <dst> is the abbreviation for the timezone in daylight savings time and
707c478bd9Sstevel@tonic-gate  * <offset2> is the daylight savings time offset from GMT.  The remainder
717c478bd9Sstevel@tonic-gate  * specifies when daylight savings time begins and ends.  A timezone
727c478bd9Sstevel@tonic-gate  * specified in this way is referred to as a POSIX timezone.  An example
737c478bd9Sstevel@tonic-gate  * of this is "PST7PDT".
747c478bd9Sstevel@tonic-gate  *
757c478bd9Sstevel@tonic-gate  * In Solaris, there is an extension to this.  If the timezone is not
767c478bd9Sstevel@tonic-gate  * preceded by a ":" and it does not parse as a POSIX timezone, then it
777c478bd9Sstevel@tonic-gate  * will be treated as a zoneinfo timezone.  Much usage of zoneinfo
787c478bd9Sstevel@tonic-gate  * timezones in Solaris is done without the leading ":".
797c478bd9Sstevel@tonic-gate  *
807c478bd9Sstevel@tonic-gate  * A zoneinfo timezone is a reference to a file that contains a set of
817c478bd9Sstevel@tonic-gate  * rules that describe the timezone.  In Solaris, the file is in
827c478bd9Sstevel@tonic-gate  * /usr/share/lib/zoneinfo.  The file is generated by zic(1M), based
837c478bd9Sstevel@tonic-gate  * on zoneinfo rules "source" files.  This is all described on the zic(1M)
847c478bd9Sstevel@tonic-gate  * man page.
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * Functions that are common to ctime(3C) and cftime(3C)
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate 
917257d1b4Sraf #pragma weak _tzset = tzset
927c478bd9Sstevel@tonic-gate 
937257d1b4Sraf #include "lint.h"
947c478bd9Sstevel@tonic-gate #include "libc.h"
957c478bd9Sstevel@tonic-gate #include "tsd.h"
967c478bd9Sstevel@tonic-gate #include <stdarg.h>
977c478bd9Sstevel@tonic-gate #include <mtlib.h>
987c478bd9Sstevel@tonic-gate #include <sys/types.h>
997c478bd9Sstevel@tonic-gate #include <ctype.h>
1007c478bd9Sstevel@tonic-gate #include <stdio.h>
1017c478bd9Sstevel@tonic-gate #include <limits.h>
1027c478bd9Sstevel@tonic-gate #include <sys/param.h>
1037c478bd9Sstevel@tonic-gate #include <time.h>
1047c478bd9Sstevel@tonic-gate #include <unistd.h>
1057c478bd9Sstevel@tonic-gate #include <stdlib.h>
1067c478bd9Sstevel@tonic-gate #include <string.h>
1077c478bd9Sstevel@tonic-gate #include <tzfile.h>
1087c478bd9Sstevel@tonic-gate #include <thread.h>
1097c478bd9Sstevel@tonic-gate #include <synch.h>
1107c478bd9Sstevel@tonic-gate #include <fcntl.h>
1117c478bd9Sstevel@tonic-gate #include <errno.h>
11206e1a714Sraf #include <deflt.h>
1137c478bd9Sstevel@tonic-gate #include <sys/stat.h>
114d1419d5aSNobutomo Nakano #include <sys/mman.h>
1157c478bd9Sstevel@tonic-gate 
1166065583aSrobbin /* JAN_01_1902 cast to (int) - negative number of seconds from 1970 */
1176065583aSrobbin #define	JAN_01_1902		(int)0x8017E880
1187c478bd9Sstevel@tonic-gate #define	LEN_TZDIR		(sizeof (TZDIR) - 1)
1197c478bd9Sstevel@tonic-gate #define	TIMEZONE		"/etc/default/init"
1207c478bd9Sstevel@tonic-gate #define	TZSTRING		"TZ="
121d1419d5aSNobutomo Nakano #define	HASHTABLE		31
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate #define	LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate /* Days since 1/1/70 to 12/31/(1900 + Y - 1) */
1267c478bd9Sstevel@tonic-gate #define	DAYS_SINCE_70(Y) (YR((Y)-1L) - YR(70-1))
1277c478bd9Sstevel@tonic-gate #define	YR(X) /* Calc # days since 0 A.D. X = curr. yr - 1900 */ \
1287c478bd9Sstevel@tonic-gate 	((1900L + (X)) * 365L + (1900L + (X)) / 4L - \
1297c478bd9Sstevel@tonic-gate 	(1900L + (X)) / 100L + ((1900L + (X)) - 1600L) / 400L)
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate  * The following macros are replacements for detzcode(), which has
1347c478bd9Sstevel@tonic-gate  * been in the public domain versions of the localtime.c code for
1357c478bd9Sstevel@tonic-gate  * a long time. The primatives supporting the CVTZCODE macro are
1367c478bd9Sstevel@tonic-gate  * implemented differently for different endianness (ie. little
1377c478bd9Sstevel@tonic-gate  * vs. big endian) out of necessity, to account for the different
1387c478bd9Sstevel@tonic-gate  * byte ordering of the quantities being fetched.  Both versions
1397c478bd9Sstevel@tonic-gate  * are substantially faster than the detzcode() macro.  The big
1407c478bd9Sstevel@tonic-gate  * endian version is approx. 6.8x faster than detzcode(), the
1417c478bd9Sstevel@tonic-gate  * little endian version is approximately 3x faster, due to the
1427c478bd9Sstevel@tonic-gate  * extra shifting requiring to change byte order.  The micro
1437c478bd9Sstevel@tonic-gate  * benchmarks used to compare were based on the SUNWSpro SC6.1
1447c478bd9Sstevel@tonic-gate  * (and later) compilers.
1457c478bd9Sstevel@tonic-gate  */
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate #if defined(__sparc) || defined(__sparcv9)  /* big endian */
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate #define	GET_LONG(p) \
1507c478bd9Sstevel@tonic-gate 	    *(uint_t *)(p)
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate #define	GET_SHORTS(p) \
1537c478bd9Sstevel@tonic-gate 	    *(ushort_t *)(p) << 16 |\
1547c478bd9Sstevel@tonic-gate 	    *(ushort_t *)((p) + 2)
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate #define	GET_CHARS(p) \
1577c478bd9Sstevel@tonic-gate 	    *(uchar_t *)(p) << 24 |\
1587c478bd9Sstevel@tonic-gate 	    *(uchar_t *)((p) + 1) << 16 |\
1597c478bd9Sstevel@tonic-gate 	    *(uchar_t *)((p) + 2) << 8  |\
1607c478bd9Sstevel@tonic-gate 	    *(uchar_t *)((p) + 3)
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate #else /* little endian */
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate #define	GET_BYTE(x) \
1657c478bd9Sstevel@tonic-gate 	    ((x) & 0xff)
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate #define	SWAP_BYTES(x) ((\
1687c478bd9Sstevel@tonic-gate 	    GET_BYTE(x) << 8) |\
1697c478bd9Sstevel@tonic-gate 	    GET_BYTE((x) >> 8))
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate #define	SWAP_WORDS(x) ((\
1727c478bd9Sstevel@tonic-gate 	    SWAP_BYTES(x) << 16) |\
1737c478bd9Sstevel@tonic-gate 	    SWAP_BYTES((x) >> 16))
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate #define	GET_LONG(p) \
1767c478bd9Sstevel@tonic-gate 	    SWAP_WORDS(*(uint_t *)(p))
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate #define	GET_SHORTS(p) \
1797c478bd9Sstevel@tonic-gate 	    SWAP_BYTES(*(ushort_t *)(p)) << 16 |\
1807c478bd9Sstevel@tonic-gate 	    SWAP_BYTES(*(ushort_t *)((p) + 2))
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate #define	GET_CHARS(p) \
1837c478bd9Sstevel@tonic-gate 	    GET_BYTE(*(uchar_t *)(p)) << 24 |\
1847c478bd9Sstevel@tonic-gate 	    GET_BYTE(*(uchar_t *)((p) + 1)) << 16 |\
1857c478bd9Sstevel@tonic-gate 	    GET_BYTE(*(uchar_t *)((p) + 2)) << 8 |\
1867c478bd9Sstevel@tonic-gate 	    GET_BYTE(*(uchar_t *)((p) + 3))
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate #endif
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate #define	IF_ALIGNED(ptr, byte_alignment) \
1927c478bd9Sstevel@tonic-gate 			!((uintptr_t)(ptr) & (byte_alignment - 1))
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate #define	CVTZCODE(p) (int)(\
1957c478bd9Sstevel@tonic-gate 	    IF_ALIGNED(p, 4) ? GET_LONG(p) :\
1967c478bd9Sstevel@tonic-gate 	    IF_ALIGNED(p, 2) ? GET_SHORTS(p) : GET_CHARS(p));\
1977c478bd9Sstevel@tonic-gate 	    p += 4;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate #ifndef	FALSE
2007c478bd9Sstevel@tonic-gate #define	FALSE	(0)
2017c478bd9Sstevel@tonic-gate #endif
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate #ifndef	TRUE
2047c478bd9Sstevel@tonic-gate #define	TRUE	(1)
2057c478bd9Sstevel@tonic-gate #endif
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate extern	mutex_t		_time_lock;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate extern const int	__lyday_to_month[];
2107c478bd9Sstevel@tonic-gate extern const int	__yday_to_month[];
2117c478bd9Sstevel@tonic-gate extern const int	__mon_lengths[2][MONS_PER_YEAR];
2127c478bd9Sstevel@tonic-gate extern const int	__year_lengths[2];
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate const char	_tz_gmt[4] = "GMT";	/* "GMT"  */
2157c478bd9Sstevel@tonic-gate const char	_tz_spaces[4] = "   ";	/* "   "  */
2167c478bd9Sstevel@tonic-gate static const char	_posix_gmt0[5] = "GMT0";	/* "GMT0" */
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate typedef struct ttinfo {			/* Time type information */
2197c478bd9Sstevel@tonic-gate 	long		tt_gmtoff;	/* GMT offset in seconds */
2207c478bd9Sstevel@tonic-gate 	int		tt_isdst;	/* used to set tm_isdst */
2217c478bd9Sstevel@tonic-gate 	int		tt_abbrind;	/* abbreviation list index */
2227c478bd9Sstevel@tonic-gate 	int		tt_ttisstd;	/* TRUE if trans is std time */
2237c478bd9Sstevel@tonic-gate 	int		tt_ttisgmt;	/* TRUE if transition is GMT */
2247c478bd9Sstevel@tonic-gate } ttinfo_t;
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate typedef struct lsinfo {			/* Leap second information */
2277c478bd9Sstevel@tonic-gate 	time_t		ls_trans;	/* transition time */
2287c478bd9Sstevel@tonic-gate 	long		ls_corr;	/* correction to apply */
2297c478bd9Sstevel@tonic-gate } lsinfo_t;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate typedef struct previnfo {		/* Info about *prev* trans */
2327c478bd9Sstevel@tonic-gate 	ttinfo_t	*std;		/* Most recent std type */
2337c478bd9Sstevel@tonic-gate 	ttinfo_t	*alt;		/* Most recent alt type */
2347c478bd9Sstevel@tonic-gate } prev_t;
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate typedef enum {
2377c478bd9Sstevel@tonic-gate 	MON_WEEK_DOW,		/* Mm.n.d - month, week, day of week */
2387c478bd9Sstevel@tonic-gate 	JULIAN_DAY,		/* Jn - Julian day */
2397c478bd9Sstevel@tonic-gate 	DAY_OF_YEAR		/* n - day of year */
2407c478bd9Sstevel@tonic-gate } posrule_type_t;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate typedef struct {
2437c478bd9Sstevel@tonic-gate 	posrule_type_t	r_type;		/* type of rule */
2447c478bd9Sstevel@tonic-gate 	int		r_day;		/* day number of rule */
2457c478bd9Sstevel@tonic-gate 	int		r_week;		/* week number of rule */
2467c478bd9Sstevel@tonic-gate 	int		r_mon;		/* month number of rule */
2477c478bd9Sstevel@tonic-gate 	long		r_time;		/* transition time of rule */
2487c478bd9Sstevel@tonic-gate } rule_t;
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate typedef struct {
2517c478bd9Sstevel@tonic-gate 	rule_t		*rules[2];
2527c478bd9Sstevel@tonic-gate 	long		offset[2];
2537c478bd9Sstevel@tonic-gate 	long long	rtime[2];
2547c478bd9Sstevel@tonic-gate } posix_daylight_t;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate /*
2577c478bd9Sstevel@tonic-gate  * Note: ZONERULES_INVALID used for global curr_zonerules variable, but not
2587c478bd9Sstevel@tonic-gate  * for zonerules field of state_t.
2597c478bd9Sstevel@tonic-gate  */
2607c478bd9Sstevel@tonic-gate typedef enum {
2617c478bd9Sstevel@tonic-gate 	ZONERULES_INVALID, POSIX, POSIX_USA, ZONEINFO
2627c478bd9Sstevel@tonic-gate } zone_rules_t;
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate /*
2657c478bd9Sstevel@tonic-gate  * The following members are allocated from the libc-internal malloc:
2667c478bd9Sstevel@tonic-gate  *
2677c478bd9Sstevel@tonic-gate  *	zonename
2687c478bd9Sstevel@tonic-gate  *	chars
2697c478bd9Sstevel@tonic-gate  */
2707c478bd9Sstevel@tonic-gate typedef struct state {
2717c478bd9Sstevel@tonic-gate 	const char	*zonename;		/* Timezone */
2727c478bd9Sstevel@tonic-gate 	struct state	*next;			/* next state */
2737c478bd9Sstevel@tonic-gate 	zone_rules_t	zonerules;		/* Type of zone */
2747c478bd9Sstevel@tonic-gate 	int		daylight;		/* daylight global */
2757c478bd9Sstevel@tonic-gate 	long		default_timezone;	/* Def. timezone val */
2767c478bd9Sstevel@tonic-gate 	long		default_altzone;	/* Def. altzone val */
2777c478bd9Sstevel@tonic-gate 	const char	*default_tzname0;	/* Def tz..[0] val */
2787c478bd9Sstevel@tonic-gate 	const char	*default_tzname1;	/* Def tz..[1] val  */
2797c478bd9Sstevel@tonic-gate 	int		leapcnt;		/* # leap sec trans */
2807c478bd9Sstevel@tonic-gate 	int		timecnt;		/* # transitions */
2817c478bd9Sstevel@tonic-gate 	int		typecnt;		/* # zone types */
2827c478bd9Sstevel@tonic-gate 	int		charcnt;		/* # zone abbv. chars */
2837c478bd9Sstevel@tonic-gate 	char		*chars;			/* Zone abbv. chars */
2847c478bd9Sstevel@tonic-gate 	size_t		charsbuf_size;		/* malloc'ed buflen */
2857c478bd9Sstevel@tonic-gate 	prev_t		prev[TZ_MAX_TIMES];	/* Pv. trans info */
2867c478bd9Sstevel@tonic-gate 	time_t		ats[TZ_MAX_TIMES];	/* Trans.  times */
2877c478bd9Sstevel@tonic-gate 	uchar_t		types[TZ_MAX_TIMES];	/* Type indices */
2887c478bd9Sstevel@tonic-gate 	ttinfo_t	ttis[TZ_MAX_TYPES];	/* Zone types */
2897c478bd9Sstevel@tonic-gate 	lsinfo_t	lsis[TZ_MAX_LEAPS];	/* Leap sec trans */
290d1419d5aSNobutomo Nakano 	int		last_ats_idx;		/* last ats index */
2917c478bd9Sstevel@tonic-gate 	rule_t		start_rule;		/* For POSIX w/rules */
2927c478bd9Sstevel@tonic-gate 	rule_t		end_rule;		/* For POSIX w/rules */
2937c478bd9Sstevel@tonic-gate } state_t;
2947c478bd9Sstevel@tonic-gate 
295d1419d5aSNobutomo Nakano typedef struct tznmlist {
296d1419d5aSNobutomo Nakano 	struct tznmlist *link;
297d1419d5aSNobutomo Nakano 	char	name[1];
298d1419d5aSNobutomo Nakano } tznmlist_t;
299d1419d5aSNobutomo Nakano 
300d1419d5aSNobutomo Nakano static const char	*systemTZ;
301d1419d5aSNobutomo Nakano static tznmlist_t	*systemTZrec;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate static const char	*namecache;
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate static state_t		*tzcache[HASHTABLE];
3067c478bd9Sstevel@tonic-gate 
307d1419d5aSNobutomo Nakano #define	TZNMC_SZ	43
308d1419d5aSNobutomo Nakano static tznmlist_t	*tznmhash[TZNMC_SZ];
309d1419d5aSNobutomo Nakano static const char	*last_tzname[2];
310d1419d5aSNobutomo Nakano 
3117c478bd9Sstevel@tonic-gate static state_t		*lclzonep;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate static struct tm	tm;		/* For non-reentrant use */
3147c478bd9Sstevel@tonic-gate static int		is_in_dst;	/* Set if t is in DST */
3157c478bd9Sstevel@tonic-gate static zone_rules_t	curr_zonerules = ZONERULES_INVALID;
3167c478bd9Sstevel@tonic-gate static int		cached_year;	/* mktime() perf. enhancement */
3177c478bd9Sstevel@tonic-gate static long long	cached_secs_since_1970;	/* mktime() perf. */
3187c478bd9Sstevel@tonic-gate static int		year_is_cached = FALSE;	/* mktime() perf. */
3197c478bd9Sstevel@tonic-gate 
320d1419d5aSNobutomo Nakano #define	TZSYNC_FILE	"/var/run/tzsync"
321d1419d5aSNobutomo Nakano static uint32_t		zoneinfo_seqno;
322d1419d5aSNobutomo Nakano static uint32_t		zoneinfo_seqno_init = 1;
323d1419d5aSNobutomo Nakano static uint32_t		*zoneinfo_seqadr = &zoneinfo_seqno_init;
324d1419d5aSNobutomo Nakano #define	RELOAD_INFO()	(zoneinfo_seqno != *zoneinfo_seqadr)
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate #define	_2AM		(2 * SECS_PER_HOUR)
3277c478bd9Sstevel@tonic-gate #define	FIRSTWEEK	1
3287c478bd9Sstevel@tonic-gate #define	LASTWEEK	5
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate enum wks {
3317c478bd9Sstevel@tonic-gate 	_1st_week = 1,
3327c478bd9Sstevel@tonic-gate 	_2nd_week,
3337c478bd9Sstevel@tonic-gate 	_3rd_week,
3347c478bd9Sstevel@tonic-gate 	_4th_week,
3357c478bd9Sstevel@tonic-gate 	_Last_week
3367c478bd9Sstevel@tonic-gate };
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate enum dwk {
3397c478bd9Sstevel@tonic-gate 	Sun,
3407c478bd9Sstevel@tonic-gate 	Mon,
3417c478bd9Sstevel@tonic-gate 	Tue,
3427c478bd9Sstevel@tonic-gate 	Wed,
3437c478bd9Sstevel@tonic-gate 	Thu,
3447c478bd9Sstevel@tonic-gate 	Fri,
3457c478bd9Sstevel@tonic-gate 	Sat
3467c478bd9Sstevel@tonic-gate };
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate enum mth {
3497c478bd9Sstevel@tonic-gate 	Jan = 1,
3507c478bd9Sstevel@tonic-gate 	Feb,
3517c478bd9Sstevel@tonic-gate 	Mar,
3527c478bd9Sstevel@tonic-gate 	Apr,
3537c478bd9Sstevel@tonic-gate 	May,
3547c478bd9Sstevel@tonic-gate 	Jun,
3557c478bd9Sstevel@tonic-gate 	Jul,
3567c478bd9Sstevel@tonic-gate 	Aug,
3577c478bd9Sstevel@tonic-gate 	Sep,
3587c478bd9Sstevel@tonic-gate 	Oct,
3597c478bd9Sstevel@tonic-gate 	Nov,
3607c478bd9Sstevel@tonic-gate 	Dec
3617c478bd9Sstevel@tonic-gate };
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate /*
3647c478bd9Sstevel@tonic-gate  * The following table defines standard USA DST transitions
3657c478bd9Sstevel@tonic-gate  * as they have been declared throughout history, disregarding
3667c478bd9Sstevel@tonic-gate  * the legally sanctioned local variants.
3677c478bd9Sstevel@tonic-gate  *
3687c478bd9Sstevel@tonic-gate  * Note:  At some point, this table may be supplanted by
3697c478bd9Sstevel@tonic-gate  * more popular 'posixrules' logic.
3707c478bd9Sstevel@tonic-gate  */
3717c478bd9Sstevel@tonic-gate typedef struct {
3727c478bd9Sstevel@tonic-gate 	int	s_year;
3737c478bd9Sstevel@tonic-gate 	int	e_year;
3747c478bd9Sstevel@tonic-gate 	rule_t	start;
3757c478bd9Sstevel@tonic-gate 	rule_t	end;
3767c478bd9Sstevel@tonic-gate } __usa_rules_t;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate static const __usa_rules_t	__usa_rules[] = {
3797c478bd9Sstevel@tonic-gate 	{
38080868c53Srobbin 		2007, 2037,
38180868c53Srobbin 		{ MON_WEEK_DOW, Sun, _2nd_week, Mar, _2AM },
38280868c53Srobbin 		{ MON_WEEK_DOW, Sun, _1st_week, Nov, _2AM },
38380868c53Srobbin 	},
38480868c53Srobbin 	{
38580868c53Srobbin 		1987, 2006,
3867c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _1st_week,  Apr, _2AM },
3877c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
3887c478bd9Sstevel@tonic-gate 	},
3897c478bd9Sstevel@tonic-gate 	{
3907c478bd9Sstevel@tonic-gate 		1976, 1986,
3917c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
3927c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
3937c478bd9Sstevel@tonic-gate 	},
3947c478bd9Sstevel@tonic-gate 	{
3957c478bd9Sstevel@tonic-gate 		1975, 1975,
3967c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Feb, _2AM },
3977c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
3987c478bd9Sstevel@tonic-gate 	},
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	{
4017c478bd9Sstevel@tonic-gate 		1974, 1974,
4027c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _1st_week,  Jan, _2AM },
4037c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Nov, _2AM },
4047c478bd9Sstevel@tonic-gate 	},
4057c478bd9Sstevel@tonic-gate 	/*
4067c478bd9Sstevel@tonic-gate 	 * The entry below combines two previously separate entries for
4077c478bd9Sstevel@tonic-gate 	 * 1969-1973 and 1902-1968
4087c478bd9Sstevel@tonic-gate 	 */
4097c478bd9Sstevel@tonic-gate 	{
4107c478bd9Sstevel@tonic-gate 		1902, 1973,
4117c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
4127c478bd9Sstevel@tonic-gate 		{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate };
4157c478bd9Sstevel@tonic-gate #define	MAX_RULE_TABLE	(sizeof (__usa_rules) / sizeof (__usa_rules_t) - 1)
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate  * Prototypes for static functions.
4197c478bd9Sstevel@tonic-gate  */
420d1419d5aSNobutomo Nakano static const char *getsystemTZ(void);
4217c478bd9Sstevel@tonic-gate static const char *getzname(const char *, int);
4227c478bd9Sstevel@tonic-gate static const char *getnum(const char *, int *, int, int);
4237c478bd9Sstevel@tonic-gate static const char *getsecs(const char *, long *);
4247c478bd9Sstevel@tonic-gate static const char *getoffset(const char *, long *);
4257c478bd9Sstevel@tonic-gate static const char *getrule(const char *, rule_t *, int);
4267c478bd9Sstevel@tonic-gate static int	load_posixinfo(const char *, state_t *);
4277c478bd9Sstevel@tonic-gate static int	load_zoneinfo(const char *, state_t *);
428d1419d5aSNobutomo Nakano static void	load_posix_transitions(state_t *, long, long, zone_rules_t);
429d1419d5aSNobutomo Nakano static void	adjust_posix_default(state_t *, long, long);
430d1419d5aSNobutomo Nakano static void	*ltzset_u(time_t);
4317c478bd9Sstevel@tonic-gate static struct tm *offtime_u(time_t, long, struct tm *);
4327c478bd9Sstevel@tonic-gate static int	posix_check_dst(long long, state_t *);
4337c478bd9Sstevel@tonic-gate static int	posix_daylight(long long *, int, posix_daylight_t *);
4347c478bd9Sstevel@tonic-gate static void	set_zone_context(time_t);
435d1419d5aSNobutomo Nakano static void	reload_counter(void);
436d1419d5aSNobutomo Nakano static void	purge_zone_cache(void);
437d1419d5aSNobutomo Nakano static void	set_tzname(const char **);
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate /*
4407c478bd9Sstevel@tonic-gate  * definition of difftime
4417c478bd9Sstevel@tonic-gate  *
4427c478bd9Sstevel@tonic-gate  * This code assumes time_t is type long.  Note the difference of two
4437c478bd9Sstevel@tonic-gate  * longs in absolute value is representable as an unsigned long.  So,
4447c478bd9Sstevel@tonic-gate  * compute the absolute value of the difference, cast the result to
4457c478bd9Sstevel@tonic-gate  * double and attach the sign back on.
4467c478bd9Sstevel@tonic-gate  *
4477c478bd9Sstevel@tonic-gate  * Note this code assumes 2's complement arithmetic.  The subtraction
4487c478bd9Sstevel@tonic-gate  * operation may overflow when using signed operands, but when the
4497c478bd9Sstevel@tonic-gate  * result is cast to unsigned long, it yields the desired value
4507c478bd9Sstevel@tonic-gate  * (ie, the absolute value of the difference).  The cast to unsigned
4517c478bd9Sstevel@tonic-gate  * long is done using pointers to avoid undefined behavior if casting
4527c478bd9Sstevel@tonic-gate  * a negative value to unsigned.
4537c478bd9Sstevel@tonic-gate  */
4547c478bd9Sstevel@tonic-gate double
difftime(time_t time1,time_t time0)4557c478bd9Sstevel@tonic-gate difftime(time_t time1, time_t time0)
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate 	if (time1 < time0) {
4587c478bd9Sstevel@tonic-gate 		time0 -= time1;
4597c478bd9Sstevel@tonic-gate 		return (-(double)*(unsigned long *) &time0);
4607c478bd9Sstevel@tonic-gate 	} else {
4617c478bd9Sstevel@tonic-gate 		time1 -= time0;
4627c478bd9Sstevel@tonic-gate 		return ((double)*(unsigned long *) &time1);
4637c478bd9Sstevel@tonic-gate 	}
4647c478bd9Sstevel@tonic-gate }
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate /*
4677c478bd9Sstevel@tonic-gate  * Accepts a time_t, returns a tm struct based on it, with
4687c478bd9Sstevel@tonic-gate  * no local timezone adjustment.
4697c478bd9Sstevel@tonic-gate  *
4707c478bd9Sstevel@tonic-gate  * This routine is the thread-safe variant of gmtime(), and
4717c478bd9Sstevel@tonic-gate  * requires that the call provide the address of their own tm
4727c478bd9Sstevel@tonic-gate  * struct.
4737c478bd9Sstevel@tonic-gate  *
4747c478bd9Sstevel@tonic-gate  * Locking is not done here because set_zone_context()
4757c478bd9Sstevel@tonic-gate  * is not called, thus timezone, altzone, and tzname[] are not
4767c478bd9Sstevel@tonic-gate  * accessed, no memory is allocated, and no common dynamic
4777c478bd9Sstevel@tonic-gate  * data is accessed.
4787c478bd9Sstevel@tonic-gate  *
4797c478bd9Sstevel@tonic-gate  * See ctime(3C)
4807c478bd9Sstevel@tonic-gate  */
4817c478bd9Sstevel@tonic-gate struct tm *
gmtime_r(const time_t * timep,struct tm * p_tm)4827257d1b4Sraf gmtime_r(const time_t *timep, struct tm *p_tm)
4837c478bd9Sstevel@tonic-gate {
4847c478bd9Sstevel@tonic-gate 	return (offtime_u((time_t)*timep, 0L, p_tm));
4857c478bd9Sstevel@tonic-gate }
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate /*
4887c478bd9Sstevel@tonic-gate  * Accepts a time_t, returns a tm struct based on it, with
4897c478bd9Sstevel@tonic-gate  * no local timezone adjustment.
4907c478bd9Sstevel@tonic-gate  *
4917c478bd9Sstevel@tonic-gate  * This function is explicitly NOT THREAD-SAFE.  The standards
4927c478bd9Sstevel@tonic-gate  * indicate it should provide its results in its own statically
4937c478bd9Sstevel@tonic-gate  * allocated tm struct that gets overwritten. The thread-safe
4947c478bd9Sstevel@tonic-gate  * variant is gmtime_r().  We make it mostly thread-safe by
4957c478bd9Sstevel@tonic-gate  * allocating its buffer in thread-specific data.
4967c478bd9Sstevel@tonic-gate  *
4977c478bd9Sstevel@tonic-gate  * See ctime(3C)
4987c478bd9Sstevel@tonic-gate  */
4997c478bd9Sstevel@tonic-gate struct tm *
gmtime(const time_t * timep)5007c478bd9Sstevel@tonic-gate gmtime(const time_t *timep)
5017c478bd9Sstevel@tonic-gate {
5027c478bd9Sstevel@tonic-gate 	struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	if (p_tm == NULL)	/* memory allocation failure */
5057c478bd9Sstevel@tonic-gate 		p_tm = &tm;	/* use static buffer and hope for the best */
5067257d1b4Sraf 	return (gmtime_r(timep, p_tm));
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate /*
5107c478bd9Sstevel@tonic-gate  * This is the hashing function, based on the input timezone name.
5117c478bd9Sstevel@tonic-gate  */
5127c478bd9Sstevel@tonic-gate static int
get_hashid(const char * id)5137c478bd9Sstevel@tonic-gate get_hashid(const char *id)
5147c478bd9Sstevel@tonic-gate {
5157c478bd9Sstevel@tonic-gate 	unsigned char	c;
5167c478bd9Sstevel@tonic-gate 	unsigned int	h;
5177c478bd9Sstevel@tonic-gate 
518d1419d5aSNobutomo Nakano 	h = *id++;
519d1419d5aSNobutomo Nakano 	while ((c = *id++) != '\0')
520d1419d5aSNobutomo Nakano 		h += c;
5217c478bd9Sstevel@tonic-gate 	return ((int)(h % HASHTABLE));
5227c478bd9Sstevel@tonic-gate }
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate /*
5257c478bd9Sstevel@tonic-gate  * find_zone() gets the hashid for zonename, then uses the hashid
5267c478bd9Sstevel@tonic-gate  * to search the hash table for the appropriate timezone entry.  If
5277c478bd9Sstevel@tonic-gate  * the entry for zonename is found in the hash table, return a pointer
528d1419d5aSNobutomo Nakano  * to the entry.
5297c478bd9Sstevel@tonic-gate  */
5307c478bd9Sstevel@tonic-gate static state_t *
find_zone(const char * zonename)531d1419d5aSNobutomo Nakano find_zone(const char *zonename)
5327c478bd9Sstevel@tonic-gate {
5337c478bd9Sstevel@tonic-gate 	int	hashid;
534d1419d5aSNobutomo Nakano 	state_t	*cur;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	hashid = get_hashid(zonename);
5377c478bd9Sstevel@tonic-gate 	cur = tzcache[hashid];
5387c478bd9Sstevel@tonic-gate 	while (cur) {
5397c478bd9Sstevel@tonic-gate 		int	res;
5407c478bd9Sstevel@tonic-gate 		res = strcmp(cur->zonename, zonename);
5417c478bd9Sstevel@tonic-gate 		if (res == 0) {
5427c478bd9Sstevel@tonic-gate 			return (cur);
5437c478bd9Sstevel@tonic-gate 		} else if (res > 0) {
5447c478bd9Sstevel@tonic-gate 			break;
5457c478bd9Sstevel@tonic-gate 		}
5467c478bd9Sstevel@tonic-gate 		cur = cur->next;
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate 	return (NULL);
5497c478bd9Sstevel@tonic-gate }
5507c478bd9Sstevel@tonic-gate 
551d1419d5aSNobutomo Nakano /*
552d1419d5aSNobutomo Nakano  * Register new state in the cache.
553d1419d5aSNobutomo Nakano  */
554d1419d5aSNobutomo Nakano static void
reg_zone(state_t * new)555d1419d5aSNobutomo Nakano reg_zone(state_t *new)
556d1419d5aSNobutomo Nakano {
557d1419d5aSNobutomo Nakano 	int	hashid, res;
558d1419d5aSNobutomo Nakano 	state_t	*cur, *prv;
559d1419d5aSNobutomo Nakano 
560d1419d5aSNobutomo Nakano 	hashid = get_hashid(new->zonename);
561d1419d5aSNobutomo Nakano 	cur = tzcache[hashid];
562d1419d5aSNobutomo Nakano 	prv = NULL;
563d1419d5aSNobutomo Nakano 	while (cur != NULL) {
564d1419d5aSNobutomo Nakano 		res = strcmp(cur->zonename, new->zonename);
565d1419d5aSNobutomo Nakano 		if (res == 0) {
566d1419d5aSNobutomo Nakano 			/* impossible, but just in case */
567d1419d5aSNobutomo Nakano 			return;
568d1419d5aSNobutomo Nakano 		} else if (res > 0) {
569d1419d5aSNobutomo Nakano 			break;
570d1419d5aSNobutomo Nakano 		}
571d1419d5aSNobutomo Nakano 		prv = cur;
572d1419d5aSNobutomo Nakano 		cur = cur->next;
573d1419d5aSNobutomo Nakano 	}
574d1419d5aSNobutomo Nakano 	if (prv != NULL) {
575d1419d5aSNobutomo Nakano 		new->next = prv->next;
576d1419d5aSNobutomo Nakano 		prv->next = new;
577d1419d5aSNobutomo Nakano 	} else {
578d1419d5aSNobutomo Nakano 		new->next = tzcache[hashid];
579d1419d5aSNobutomo Nakano 		tzcache[hashid] = new;
580d1419d5aSNobutomo Nakano 	}
581d1419d5aSNobutomo Nakano }
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * Returns tm struct based on input time_t argument, correcting
5857c478bd9Sstevel@tonic-gate  * for the local timezone, producing documented side-effects
5867c478bd9Sstevel@tonic-gate  * to extern global state, timezone, altzone, daylight and tzname[].
5877c478bd9Sstevel@tonic-gate  *
5887c478bd9Sstevel@tonic-gate  * localtime_r() is the thread-safe variant of localtime().
5897c478bd9Sstevel@tonic-gate  *
5907c478bd9Sstevel@tonic-gate  * IMPLEMENTATION NOTE:
5917c478bd9Sstevel@tonic-gate  *
5927c478bd9Sstevel@tonic-gate  *	Locking slows multithreaded access and is probably ultimately
5937c478bd9Sstevel@tonic-gate  *	unnecessary here. The POSIX specification is a bit vague
5947c478bd9Sstevel@tonic-gate  *	as to whether the extern variables set by tzset() need to
5957c478bd9Sstevel@tonic-gate  *	set as a result of a call to localtime_r()
5967c478bd9Sstevel@tonic-gate  *
5977c478bd9Sstevel@tonic-gate  *	Currently, the spec only mentions that tzname[] doesn't
5987c478bd9Sstevel@tonic-gate  *	need to be set.  As soon as it becomes unequivocal
5997c478bd9Sstevel@tonic-gate  *	that the external zone state doesn't need to be asserted
6007c478bd9Sstevel@tonic-gate  *	for this call, and it really doesn't make much sense
6017c478bd9Sstevel@tonic-gate  *	to set common state from multi-threaded calls made to this
6027c478bd9Sstevel@tonic-gate  *	function, locking can be dispensed with here.
6037c478bd9Sstevel@tonic-gate  *
6047c478bd9Sstevel@tonic-gate  *	local zone state would still need to be aquired for the
6057c478bd9Sstevel@tonic-gate  *	time in question in order for calculations elicited here
6067c478bd9Sstevel@tonic-gate  *	to be correct, but that state wouldn't need to be shared,
6077c478bd9Sstevel@tonic-gate  *	thus no multi-threaded synchronization would be required.
6087c478bd9Sstevel@tonic-gate  *
6097c478bd9Sstevel@tonic-gate  *	It would be nice if POSIX would approve an ltzset_r()
6107c478bd9Sstevel@tonic-gate  *	function, but if not, it wouldn't stop us from making one
6117c478bd9Sstevel@tonic-gate  *	privately.
6127c478bd9Sstevel@tonic-gate  *
6137c478bd9Sstevel@tonic-gate  *	localtime_r() can now return NULL if overflow is detected.
6147c478bd9Sstevel@tonic-gate  *	offtime_u() is the function that detects overflow, and sets
6157c478bd9Sstevel@tonic-gate  *	errno appropriately.  We unlock before the call to offtime_u(),
6167c478bd9Sstevel@tonic-gate  *	so that lmutex_unlock() does not reassign errno.  The function
6177c478bd9Sstevel@tonic-gate  *	offtime_u() is MT-safe and does not have to be locked.  Use
6187c478bd9Sstevel@tonic-gate  *	my_is_in_dst to reference local copy of is_in_dst outside locks.
6197c478bd9Sstevel@tonic-gate  *
6207c478bd9Sstevel@tonic-gate  * See ctime(3C)
6217c478bd9Sstevel@tonic-gate  */
6227c478bd9Sstevel@tonic-gate struct tm *
localtime_r(const time_t * timep,struct tm * p_tm)6237257d1b4Sraf localtime_r(const time_t *timep, struct tm *p_tm)
6247c478bd9Sstevel@tonic-gate {
6257c478bd9Sstevel@tonic-gate 	long	offset;
6267c478bd9Sstevel@tonic-gate 	struct tm *rt;
627d1419d5aSNobutomo Nakano 	void	*unused;
6287c478bd9Sstevel@tonic-gate 	int	my_is_in_dst;
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	lmutex_lock(&_time_lock);
631d1419d5aSNobutomo Nakano 	unused = ltzset_u(*timep);
6327c478bd9Sstevel@tonic-gate 	if (lclzonep == NULL) {
6337c478bd9Sstevel@tonic-gate 		lmutex_unlock(&_time_lock);
634d1419d5aSNobutomo Nakano 		if (unused != NULL)
635d1419d5aSNobutomo Nakano 			free(unused);
6367c478bd9Sstevel@tonic-gate 		return (offtime_u(*timep, 0L, p_tm));
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 	my_is_in_dst = is_in_dst;
6397c478bd9Sstevel@tonic-gate 	offset = (my_is_in_dst) ? -altzone : -timezone;
6407c478bd9Sstevel@tonic-gate 	lmutex_unlock(&_time_lock);
641d1419d5aSNobutomo Nakano 	if (unused != NULL)
642d1419d5aSNobutomo Nakano 		free(unused);
6437c478bd9Sstevel@tonic-gate 	rt = offtime_u(*timep, offset, p_tm);
6447c478bd9Sstevel@tonic-gate 	p_tm->tm_isdst = my_is_in_dst;
6457c478bd9Sstevel@tonic-gate 	return (rt);
6467c478bd9Sstevel@tonic-gate }
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate /*
6497c478bd9Sstevel@tonic-gate  * Accepts a time_t, returns a tm struct based on it, correcting
6507c478bd9Sstevel@tonic-gate  * for the local timezone.  Produces documented side-effects to
6517c478bd9Sstevel@tonic-gate  * extern global timezone state data.
6527c478bd9Sstevel@tonic-gate  *
6537c478bd9Sstevel@tonic-gate  * This function is explicitly NOT THREAD-SAFE.  The standards
6547c478bd9Sstevel@tonic-gate  * indicate it should provide its results in its own statically
6557c478bd9Sstevel@tonic-gate  * allocated tm struct that gets overwritten. The thread-safe
6567c478bd9Sstevel@tonic-gate  * variant is localtime_r().  We make it mostly thread-safe by
6577c478bd9Sstevel@tonic-gate  * allocating its buffer in thread-specific data.
6587c478bd9Sstevel@tonic-gate  *
6597c478bd9Sstevel@tonic-gate  * localtime() can now return NULL if overflow is detected.
6607c478bd9Sstevel@tonic-gate  * offtime_u() is the function that detects overflow, and sets
6617c478bd9Sstevel@tonic-gate  * errno appropriately.
6627c478bd9Sstevel@tonic-gate  *
6637c478bd9Sstevel@tonic-gate  * See ctime(3C)
6647c478bd9Sstevel@tonic-gate  */
6657c478bd9Sstevel@tonic-gate struct tm *
localtime(const time_t * timep)6667c478bd9Sstevel@tonic-gate localtime(const time_t *timep)
6677c478bd9Sstevel@tonic-gate {
6687c478bd9Sstevel@tonic-gate 	struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	if (p_tm == NULL)	/* memory allocation failure */
6717c478bd9Sstevel@tonic-gate 		p_tm = &tm;	/* use static buffer and hope for the best */
6727257d1b4Sraf 	return (localtime_r(timep, p_tm));
6737c478bd9Sstevel@tonic-gate }
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate /*
6767c478bd9Sstevel@tonic-gate  * This function takes a pointer to a tm struct and returns a
6777c478bd9Sstevel@tonic-gate  * normalized time_t, also inducing documented side-effects in
6787c478bd9Sstevel@tonic-gate  * extern global zone state variables.  (See mktime(3C)).
6797c478bd9Sstevel@tonic-gate  */
6806eaad1d3SGarrett D'Amore static time_t
mktime1(struct tm * tmptr,int usetz)6816eaad1d3SGarrett D'Amore mktime1(struct tm *tmptr, int usetz)
6827c478bd9Sstevel@tonic-gate {
6837c478bd9Sstevel@tonic-gate 	struct tm _tm;
6847c478bd9Sstevel@tonic-gate 	long long t;		/* must hold more than 32-bit time_t */
6857c478bd9Sstevel@tonic-gate 	int	temp;
6867c478bd9Sstevel@tonic-gate 	int	mketimerrno;
6877c478bd9Sstevel@tonic-gate 	int	overflow;
688*6b3dcabaSAlexander Stetsenko 	void	*unused = NULL;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	mketimerrno = errno;
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	/* mktime leaves errno unchanged if no error is encountered */
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	/* Calculate time_t from tm arg.  tm may need to be normalized. */
6957c478bd9Sstevel@tonic-gate 	t = tmptr->tm_sec + SECSPERMIN * tmptr->tm_min +
6967c478bd9Sstevel@tonic-gate 	    SECSPERHOUR * tmptr->tm_hour +
6977c478bd9Sstevel@tonic-gate 	    SECSPERDAY * (tmptr->tm_mday - 1);
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	if (tmptr->tm_mon >= 12) {
7007c478bd9Sstevel@tonic-gate 		tmptr->tm_year += tmptr->tm_mon / 12;
7017c478bd9Sstevel@tonic-gate 		tmptr->tm_mon %= 12;
7027c478bd9Sstevel@tonic-gate 	} else if (tmptr->tm_mon < 0) {
7037c478bd9Sstevel@tonic-gate 		temp = -tmptr->tm_mon;
7047c478bd9Sstevel@tonic-gate 		tmptr->tm_mon = 0;	/* If tm_mon divides by 12. */
7057c478bd9Sstevel@tonic-gate 		tmptr->tm_year -= (temp / 12);
7067c478bd9Sstevel@tonic-gate 		if (temp %= 12) {	/* Remainder... */
7077c478bd9Sstevel@tonic-gate 			tmptr->tm_year--;
7087c478bd9Sstevel@tonic-gate 			tmptr->tm_mon = 12 - temp;
7097c478bd9Sstevel@tonic-gate 		}
7107c478bd9Sstevel@tonic-gate 	}
7117c478bd9Sstevel@tonic-gate 
712d1419d5aSNobutomo Nakano 	lmutex_lock(&_time_lock);
713d1419d5aSNobutomo Nakano 
7147c478bd9Sstevel@tonic-gate 	/* Avoid numerous calculations embedded in macro if possible */
7157c478bd9Sstevel@tonic-gate 	if (!year_is_cached || (cached_year != tmptr->tm_year))	 {
7167c478bd9Sstevel@tonic-gate 		cached_year = tmptr->tm_year;
7177c478bd9Sstevel@tonic-gate 		year_is_cached = TRUE;
7187c478bd9Sstevel@tonic-gate 		/* For boundry values of tm_year, typecasting required */
7197c478bd9Sstevel@tonic-gate 		cached_secs_since_1970 =
7207c478bd9Sstevel@tonic-gate 		    (long long)SECSPERDAY * DAYS_SINCE_70(cached_year);
7217c478bd9Sstevel@tonic-gate 	}
7227c478bd9Sstevel@tonic-gate 	t += cached_secs_since_1970;
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	if (isleap(tmptr->tm_year + TM_YEAR_BASE))
7257c478bd9Sstevel@tonic-gate 		t += SECSPERDAY * __lyday_to_month[tmptr->tm_mon];
7267c478bd9Sstevel@tonic-gate 	else
7277c478bd9Sstevel@tonic-gate 		t += SECSPERDAY * __yday_to_month[tmptr->tm_mon];
7287c478bd9Sstevel@tonic-gate 
7296eaad1d3SGarrett D'Amore 
7306eaad1d3SGarrett D'Amore 	if (usetz) {
7316eaad1d3SGarrett D'Amore 		/*
7326eaad1d3SGarrett D'Amore 		 * If called from mktime(), then we need to do the TZ
7336eaad1d3SGarrett D'Amore 		 * related transformations.
7346eaad1d3SGarrett D'Amore 		 */
7356eaad1d3SGarrett D'Amore 
736d1419d5aSNobutomo Nakano 		unused = ltzset_u((time_t)t);
737d1419d5aSNobutomo Nakano 
7387c478bd9Sstevel@tonic-gate 		/* Attempt to convert time to GMT based on tm_isdst setting */
7397c478bd9Sstevel@tonic-gate 		t += (tmptr->tm_isdst > 0) ? altzone : timezone;
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate #ifdef _ILP32
7427c478bd9Sstevel@tonic-gate 		overflow = t > LONG_MAX || t < LONG_MIN ||
7437c478bd9Sstevel@tonic-gate 		    tmptr->tm_year < 1 || tmptr->tm_year > 138;
7447c478bd9Sstevel@tonic-gate #else
7457c478bd9Sstevel@tonic-gate 		overflow = t > LONG_MAX || t < LONG_MIN;
7467c478bd9Sstevel@tonic-gate #endif
7477c478bd9Sstevel@tonic-gate 		set_zone_context((time_t)t);
7487c478bd9Sstevel@tonic-gate 		if (tmptr->tm_isdst < 0) {
7497c478bd9Sstevel@tonic-gate 			long dst_delta = timezone - altzone;
7507c478bd9Sstevel@tonic-gate 			switch (curr_zonerules) {
7517c478bd9Sstevel@tonic-gate 			case ZONEINFO:
7527c478bd9Sstevel@tonic-gate 				if (is_in_dst) {
7537c478bd9Sstevel@tonic-gate 					t -= dst_delta;
7547c478bd9Sstevel@tonic-gate 					set_zone_context((time_t)t);
7557c478bd9Sstevel@tonic-gate 					if (is_in_dst) {
7567c478bd9Sstevel@tonic-gate 						(void) offtime_u((time_t)t,
7577c478bd9Sstevel@tonic-gate 						    -altzone, &_tm);
7587c478bd9Sstevel@tonic-gate 						_tm.tm_isdst = 1;
7597c478bd9Sstevel@tonic-gate 					} else {
7607c478bd9Sstevel@tonic-gate 						(void) offtime_u((time_t)t,
7617c478bd9Sstevel@tonic-gate 						    -timezone, &_tm);
7627c478bd9Sstevel@tonic-gate 					}
7637c478bd9Sstevel@tonic-gate 				} else {
7646eaad1d3SGarrett D'Amore 					(void) offtime_u((time_t)t, -timezone,
7656eaad1d3SGarrett D'Amore 					    &_tm);
7667c478bd9Sstevel@tonic-gate 				}
7677c478bd9Sstevel@tonic-gate 				break;
7687c478bd9Sstevel@tonic-gate 			case POSIX_USA:
7697c478bd9Sstevel@tonic-gate 			case POSIX:
7707c478bd9Sstevel@tonic-gate 				if (is_in_dst) {
7717c478bd9Sstevel@tonic-gate 					t -= dst_delta;
7727c478bd9Sstevel@tonic-gate 					set_zone_context((time_t)t);
7737c478bd9Sstevel@tonic-gate 					if (is_in_dst) {
7747c478bd9Sstevel@tonic-gate 						(void) offtime_u((time_t)t,
7757c478bd9Sstevel@tonic-gate 						    -altzone, &_tm);
7767c478bd9Sstevel@tonic-gate 						_tm.tm_isdst = 1;
7777c478bd9Sstevel@tonic-gate 					} else {
7787c478bd9Sstevel@tonic-gate 						(void) offtime_u((time_t)t,
7797c478bd9Sstevel@tonic-gate 						    -timezone, &_tm);
7807c478bd9Sstevel@tonic-gate 					}
7816eaad1d3SGarrett D'Amore 				} else {
7826eaad1d3SGarrett D'Amore 					/*
7836eaad1d3SGarrett D'Amore 					 * check for ambiguous
7846eaad1d3SGarrett D'Amore 					 * 'fallback' transition
7856eaad1d3SGarrett D'Amore 					 */
7867c478bd9Sstevel@tonic-gate 					set_zone_context((time_t)t - dst_delta);
7876eaad1d3SGarrett D'Amore 					if (is_in_dst) {
7886eaad1d3SGarrett D'Amore 						/* In fallback, force DST */
7897c478bd9Sstevel@tonic-gate 						t -= dst_delta;
7907c478bd9Sstevel@tonic-gate 						(void) offtime_u((time_t)t,
7917c478bd9Sstevel@tonic-gate 						    -altzone, &_tm);
7927c478bd9Sstevel@tonic-gate 						_tm.tm_isdst = 1;
7937c478bd9Sstevel@tonic-gate 					} else {
7947c478bd9Sstevel@tonic-gate 						(void) offtime_u((time_t)t,
7957c478bd9Sstevel@tonic-gate 						    -timezone, &_tm);
7967c478bd9Sstevel@tonic-gate 					}
7977c478bd9Sstevel@tonic-gate 				}
7987c478bd9Sstevel@tonic-gate 				break;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 			case ZONERULES_INVALID:
8017c478bd9Sstevel@tonic-gate 				(void) offtime_u((time_t)t, 0L, &_tm);
8027c478bd9Sstevel@tonic-gate 				break;
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 			}
8057c478bd9Sstevel@tonic-gate 		} else if (is_in_dst) {
8067c478bd9Sstevel@tonic-gate 			(void) offtime_u((time_t)t, -altzone, &_tm);
8077c478bd9Sstevel@tonic-gate 			_tm.tm_isdst = 1;
8087c478bd9Sstevel@tonic-gate 		} else {
8097c478bd9Sstevel@tonic-gate 			(void) offtime_u((time_t)t, -timezone, &_tm);
8107c478bd9Sstevel@tonic-gate 		}
8117c478bd9Sstevel@tonic-gate 
8126eaad1d3SGarrett D'Amore 	} else {	/* !usetz, i.e. using UTC */
8136eaad1d3SGarrett D'Amore 		overflow = 0;
8146eaad1d3SGarrett D'Amore 		/* Normalize the TM structure */
8156eaad1d3SGarrett D'Amore 		(void) offtime_u((time_t)t, 0, &_tm);
8166eaad1d3SGarrett D'Amore 	}
8176eaad1d3SGarrett D'Amore 
8187c478bd9Sstevel@tonic-gate 	if (overflow || t > LONG_MAX || t < LONG_MIN) {
8197c478bd9Sstevel@tonic-gate 		mketimerrno = EOVERFLOW;
8207c478bd9Sstevel@tonic-gate 		t = -1;
8217c478bd9Sstevel@tonic-gate 	} else {
8227c478bd9Sstevel@tonic-gate 		*tmptr = _tm;
8237c478bd9Sstevel@tonic-gate 	}
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 	lmutex_unlock(&_time_lock);
826d1419d5aSNobutomo Nakano 	if (unused != NULL)
827d1419d5aSNobutomo Nakano 		free(unused);
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	errno = mketimerrno;
8307c478bd9Sstevel@tonic-gate 	return ((time_t)t);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate 
8336eaad1d3SGarrett D'Amore time_t
mktime(struct tm * tmptr)8346eaad1d3SGarrett D'Amore mktime(struct tm *tmptr)
8356eaad1d3SGarrett D'Amore {
8366eaad1d3SGarrett D'Amore 	return (mktime1(tmptr, TRUE));
8376eaad1d3SGarrett D'Amore }
8386eaad1d3SGarrett D'Amore 
8396eaad1d3SGarrett D'Amore time_t
timegm(struct tm * tmptr)8406eaad1d3SGarrett D'Amore timegm(struct tm *tmptr)
8416eaad1d3SGarrett D'Amore {
8426eaad1d3SGarrett D'Amore 	return (mktime1(tmptr, FALSE));
8436eaad1d3SGarrett D'Amore }
8446eaad1d3SGarrett D'Amore 
8456eaad1d3SGarrett D'Amore 
8467c478bd9Sstevel@tonic-gate /*
8477c478bd9Sstevel@tonic-gate  * Sets extern global zone state variables based on the current
8487c478bd9Sstevel@tonic-gate  * time.  Specifically, tzname[], timezone, altzone, and daylight
8497c478bd9Sstevel@tonic-gate  * are updated.  See ctime(3C) manpage.
8507c478bd9Sstevel@tonic-gate  */
8517c478bd9Sstevel@tonic-gate void
tzset(void)8527257d1b4Sraf tzset(void)
8537c478bd9Sstevel@tonic-gate {
854d1419d5aSNobutomo Nakano 	void	*unused;
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 	lmutex_lock(&_time_lock);
857d1419d5aSNobutomo Nakano 	unused = ltzset_u(time(NULL));
8587c478bd9Sstevel@tonic-gate 	lmutex_unlock(&_time_lock);
859d1419d5aSNobutomo Nakano 	if (unused != NULL)
860d1419d5aSNobutomo Nakano 		free(unused);
8617c478bd9Sstevel@tonic-gate }
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate void
_ltzset(time_t tim)8647c478bd9Sstevel@tonic-gate _ltzset(time_t tim)
8657c478bd9Sstevel@tonic-gate {
866d1419d5aSNobutomo Nakano 	void	*unused;
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	lmutex_lock(&_time_lock);
869d1419d5aSNobutomo Nakano 	unused = ltzset_u(tim);
8707c478bd9Sstevel@tonic-gate 	lmutex_unlock(&_time_lock);
871d1419d5aSNobutomo Nakano 	if (unused != NULL)
872d1419d5aSNobutomo Nakano 		free(unused);
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate /*
8767c478bd9Sstevel@tonic-gate  * Loads local zone information if TZ changed since last time zone
8777c478bd9Sstevel@tonic-gate  * information was loaded, or if this is the first time thru.
8787c478bd9Sstevel@tonic-gate  * We already hold _time_lock; no further locking is required.
879d1419d5aSNobutomo Nakano  * Return a memory block which can be free'd at safe place.
8807c478bd9Sstevel@tonic-gate  */
881d1419d5aSNobutomo Nakano static void *
ltzset_u(time_t t)882d1419d5aSNobutomo Nakano ltzset_u(time_t t)
8837c478bd9Sstevel@tonic-gate {
884d1419d5aSNobutomo Nakano 	const char	*zonename;
885d1419d5aSNobutomo Nakano 	state_t		*entry, *new_entry;
886d1419d5aSNobutomo Nakano 	const char	*newtzname[2];
8877c478bd9Sstevel@tonic-gate 
888d1419d5aSNobutomo Nakano 	if (RELOAD_INFO()) {
889d1419d5aSNobutomo Nakano 		reload_counter();
890d1419d5aSNobutomo Nakano 		purge_zone_cache();
8917c478bd9Sstevel@tonic-gate 	}
8927c478bd9Sstevel@tonic-gate 
893d1419d5aSNobutomo Nakano 	if ((zonename = getsystemTZ()) == NULL || *zonename == '\0')
894d1419d5aSNobutomo Nakano 		zonename = _posix_gmt0;
895d1419d5aSNobutomo Nakano 
896d1419d5aSNobutomo Nakano 	if (namecache != NULL && strcmp(namecache, zonename) == 0) {
897d1419d5aSNobutomo Nakano 		set_zone_context(t);
898d1419d5aSNobutomo Nakano 		return (NULL);
899d1419d5aSNobutomo Nakano 	}
900d1419d5aSNobutomo Nakano 
901d1419d5aSNobutomo Nakano 	entry = find_zone(zonename);
9027c478bd9Sstevel@tonic-gate 	if (entry == NULL) {
9037c478bd9Sstevel@tonic-gate 		/*
904d1419d5aSNobutomo Nakano 		 * We need to release _time_lock to call out malloc().
905d1419d5aSNobutomo Nakano 		 * We can release _time_lock as far as global variables
906d1419d5aSNobutomo Nakano 		 * can remain consistent. Here, we haven't touch any
907d1419d5aSNobutomo Nakano 		 * variables, so it's okay to release lock.
908d1419d5aSNobutomo Nakano 		 */
909d1419d5aSNobutomo Nakano 		lmutex_unlock(&_time_lock);
910d1419d5aSNobutomo Nakano 		new_entry = malloc(sizeof (state_t));
911d1419d5aSNobutomo Nakano 		lmutex_lock(&_time_lock);
912d1419d5aSNobutomo Nakano 
913d1419d5aSNobutomo Nakano 		/*
914d1419d5aSNobutomo Nakano 		 * check it again, since zone may have been loaded while
915d1419d5aSNobutomo Nakano 		 * time_lock was unlocked.
916d1419d5aSNobutomo Nakano 		 */
917d1419d5aSNobutomo Nakano 		entry = find_zone(zonename);
918d1419d5aSNobutomo Nakano 	} else {
919d1419d5aSNobutomo Nakano 		new_entry = NULL;
920d1419d5aSNobutomo Nakano 		goto out;
921d1419d5aSNobutomo Nakano 	}
922d1419d5aSNobutomo Nakano 
923d1419d5aSNobutomo Nakano 	/*
924d1419d5aSNobutomo Nakano 	 * We are here because the 1st attemp failed.
925d1419d5aSNobutomo Nakano 	 * new_entry points newly allocated entry. If it was NULL, it
926d1419d5aSNobutomo Nakano 	 * indicates that the memory allocation also failed.
927d1419d5aSNobutomo Nakano 	 */
928d1419d5aSNobutomo Nakano 	if (entry == NULL) {
929d1419d5aSNobutomo Nakano 		/*
930d1419d5aSNobutomo Nakano 		 * 2nd attemp also failed.
9317c478bd9Sstevel@tonic-gate 		 * No timezone entry found in hash table, so load it,
9327c478bd9Sstevel@tonic-gate 		 * and create a new timezone entry.
9337c478bd9Sstevel@tonic-gate 		 */
9347c478bd9Sstevel@tonic-gate 		char	*newzonename, *charsbuf;
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 		newzonename = libc_strdup(zonename);
9377c478bd9Sstevel@tonic-gate 		daylight = 0;
938d1419d5aSNobutomo Nakano 		entry = new_entry;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 		if (entry == NULL || newzonename == NULL) {
9417c478bd9Sstevel@tonic-gate 			/* something wrong happened. */
942d1419d5aSNobutomo Nakano failed:
9437c478bd9Sstevel@tonic-gate 			if (newzonename != NULL)
9447c478bd9Sstevel@tonic-gate 				libc_free(newzonename);
945d1419d5aSNobutomo Nakano 
946d1419d5aSNobutomo Nakano 			/* Invalidate the current timezone */
947d1419d5aSNobutomo Nakano 			curr_zonerules = ZONERULES_INVALID;
948d1419d5aSNobutomo Nakano 			namecache = NULL;
949d1419d5aSNobutomo Nakano 
9507c478bd9Sstevel@tonic-gate 			timezone = altzone = 0;
9517c478bd9Sstevel@tonic-gate 			is_in_dst = 0;
952d1419d5aSNobutomo Nakano 			newtzname[0] = (char *)_tz_gmt;
953d1419d5aSNobutomo Nakano 			newtzname[1] = (char *)_tz_spaces;
954d1419d5aSNobutomo Nakano 			set_tzname(newtzname);
955d1419d5aSNobutomo Nakano 			return (entry);
9567c478bd9Sstevel@tonic-gate 		}
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 		/*
9597c478bd9Sstevel@tonic-gate 		 * Builds transition cache and sets up zone state data for zone
9607c478bd9Sstevel@tonic-gate 		 * specified in TZ, which can be specified as a POSIX zone or an
9617c478bd9Sstevel@tonic-gate 		 * Olson zoneinfo file reference.
9627c478bd9Sstevel@tonic-gate 		 *
9637c478bd9Sstevel@tonic-gate 		 * If local data cannot be parsed or loaded, the local zone
9647c478bd9Sstevel@tonic-gate 		 * tables are set up for GMT.
9657c478bd9Sstevel@tonic-gate 		 *
9667c478bd9Sstevel@tonic-gate 		 * Unless a leading ':' is prepended to TZ, TZ is initially
9677c478bd9Sstevel@tonic-gate 		 * parsed as a POSIX zone;  failing that, it reverts to
9687c478bd9Sstevel@tonic-gate 		 * a zoneinfo check.
9697c478bd9Sstevel@tonic-gate 		 * However, if a ':' is prepended, the zone will *only* be
9707c478bd9Sstevel@tonic-gate 		 * parsed as zoneinfo.  If any failure occurs parsing or
9717c478bd9Sstevel@tonic-gate 		 * loading a zoneinfo TZ, GMT data is loaded for the local zone.
9727c478bd9Sstevel@tonic-gate 		 *
9737c478bd9Sstevel@tonic-gate 		 * Example:  There is a zoneinfo file in the standard
9747c478bd9Sstevel@tonic-gate 		 * distribution called 'PST8PDT'.  The only way the user can
9757c478bd9Sstevel@tonic-gate 		 * specify that file under Solaris is to set TZ to ":PST8PDT".
9767c478bd9Sstevel@tonic-gate 		 * Otherwise the initial parse of PST8PDT as a POSIX zone will
9777c478bd9Sstevel@tonic-gate 		 * succeed and be used.
9787c478bd9Sstevel@tonic-gate 		 */
979d1419d5aSNobutomo Nakano 		if ((charsbuf = libc_malloc(TZ_MAX_CHARS)) == NULL)
980d1419d5aSNobutomo Nakano 			goto failed;
9817c478bd9Sstevel@tonic-gate 
982d1419d5aSNobutomo Nakano 		entry->zonerules = ZONERULES_INVALID;
9837c478bd9Sstevel@tonic-gate 		entry->charsbuf_size = TZ_MAX_CHARS;
9847c478bd9Sstevel@tonic-gate 		entry->chars = charsbuf;
9857c478bd9Sstevel@tonic-gate 		entry->default_tzname0 = _tz_gmt;
9867c478bd9Sstevel@tonic-gate 		entry->default_tzname1 = _tz_spaces;
9877c478bd9Sstevel@tonic-gate 		entry->zonename = newzonename;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		if (*zonename == ':') {
9907c478bd9Sstevel@tonic-gate 			if (load_zoneinfo(zonename + 1, entry) != 0) {
9917c478bd9Sstevel@tonic-gate 				(void) load_posixinfo(_posix_gmt0, entry);
9927c478bd9Sstevel@tonic-gate 			}
9937c478bd9Sstevel@tonic-gate 		} else if (load_posixinfo(zonename, entry) != 0) {
9947c478bd9Sstevel@tonic-gate 			if (load_zoneinfo(zonename, entry) != 0) {
9957c478bd9Sstevel@tonic-gate 				(void) load_posixinfo(_posix_gmt0, entry);
9967c478bd9Sstevel@tonic-gate 			}
9977c478bd9Sstevel@tonic-gate 		}
998d1419d5aSNobutomo Nakano 		entry->last_ats_idx = -1;
999d1419d5aSNobutomo Nakano 
10007c478bd9Sstevel@tonic-gate 		/*
10017c478bd9Sstevel@tonic-gate 		 * The pre-allocated buffer is used; reset the free flag
10027c478bd9Sstevel@tonic-gate 		 * so the buffer won't be freed.
10037c478bd9Sstevel@tonic-gate 		 */
1004d1419d5aSNobutomo Nakano 		reg_zone(entry);
1005d1419d5aSNobutomo Nakano 		new_entry = NULL;
10067c478bd9Sstevel@tonic-gate 	}
10077c478bd9Sstevel@tonic-gate 
1008d1419d5aSNobutomo Nakano out:
10097c478bd9Sstevel@tonic-gate 	curr_zonerules = entry->zonerules;
10107c478bd9Sstevel@tonic-gate 	namecache = entry->zonename;
10117c478bd9Sstevel@tonic-gate 	daylight = entry->daylight;
10127c478bd9Sstevel@tonic-gate 	lclzonep = entry;
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	set_zone_context(t);
1015d1419d5aSNobutomo Nakano 
1016d1419d5aSNobutomo Nakano 	/*
1017d1419d5aSNobutomo Nakano 	 * We shouldn't release lock beyond this point since lclzonep
1018d1419d5aSNobutomo Nakano 	 * can refer to invalid address if cache is invalidated.
1019d1419d5aSNobutomo Nakano 	 * We defer the call to free till it can be done safely.
1020d1419d5aSNobutomo Nakano 	 */
1021d1419d5aSNobutomo Nakano 	return (new_entry);
10227c478bd9Sstevel@tonic-gate }
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate /*
10257c478bd9Sstevel@tonic-gate  * Sets timezone, altzone, tzname[], extern globals, to represent
10267c478bd9Sstevel@tonic-gate  * disposition of t with respect to TZ; See ctime(3C). is_in_dst,
10277c478bd9Sstevel@tonic-gate  * internal global is also set.  daylight is set at zone load time.
10287c478bd9Sstevel@tonic-gate  *
10297c478bd9Sstevel@tonic-gate  * Issues:
10307c478bd9Sstevel@tonic-gate  *
10317c478bd9Sstevel@tonic-gate  *	In this function, any time_t not located in the cache is handled
10327c478bd9Sstevel@tonic-gate  *	as a miss.  To build/update transition cache, load_zoneinfo()
10337c478bd9Sstevel@tonic-gate  *	must be called prior to this routine.
10347c478bd9Sstevel@tonic-gate  *
10357c478bd9Sstevel@tonic-gate  *	If POSIX zone, cache miss penalty is slightly degraded
10367c478bd9Sstevel@tonic-gate  *	performance.  For zoneinfo, penalty is decreased is_in_dst
10377c478bd9Sstevel@tonic-gate  *	accuracy.
10387c478bd9Sstevel@tonic-gate  *
10397c478bd9Sstevel@tonic-gate  *	POSIX, despite its chicken/egg problem, ie. not knowing DST
10407c478bd9Sstevel@tonic-gate  *	until time known, and not knowing time until DST known, at
10417c478bd9Sstevel@tonic-gate  *	least uses the same algorithm for 64-bit time as 32-bit.
10427c478bd9Sstevel@tonic-gate  *
10437c478bd9Sstevel@tonic-gate  *	The fact that zoneinfo files only contain transistions for 32-bit
10447c478bd9Sstevel@tonic-gate  *	time space is a well known problem, as yet unresolved.
10457c478bd9Sstevel@tonic-gate  *	Without an official standard for coping with out-of-range
10467c478bd9Sstevel@tonic-gate  *	zoneinfo times,  assumptions must be made.  For now
10477c478bd9Sstevel@tonic-gate  *	the assumption is:   If t exceeds 32-bit boundries and local zone
10487c478bd9Sstevel@tonic-gate  *	is zoneinfo type, is_in_dst is set to to 0 for negative values
10497c478bd9Sstevel@tonic-gate  *	of t, and set to the same DST state as the highest ordered
10507c478bd9Sstevel@tonic-gate  * 	transition in cache for positive values of t.
10517c478bd9Sstevel@tonic-gate  */
10527c478bd9Sstevel@tonic-gate static void
set_zone_default_context(void)1053d1419d5aSNobutomo Nakano set_zone_default_context(void)
1054d1419d5aSNobutomo Nakano {
1055d1419d5aSNobutomo Nakano 	const char	*newtzname[2];
1056d1419d5aSNobutomo Nakano 
1057d1419d5aSNobutomo Nakano 	/* Retrieve suitable defaults for this zone */
1058d1419d5aSNobutomo Nakano 	altzone = lclzonep->default_altzone;
1059d1419d5aSNobutomo Nakano 	timezone = lclzonep->default_timezone;
1060d1419d5aSNobutomo Nakano 	newtzname[0] = (char *)lclzonep->default_tzname0;
1061d1419d5aSNobutomo Nakano 	newtzname[1] = (char *)lclzonep->default_tzname1;
1062d1419d5aSNobutomo Nakano 	is_in_dst = 0;
1063d1419d5aSNobutomo Nakano 
1064d1419d5aSNobutomo Nakano 	set_tzname(newtzname);
1065d1419d5aSNobutomo Nakano }
1066d1419d5aSNobutomo Nakano 
1067d1419d5aSNobutomo Nakano static void
set_zone_context(time_t t)10687c478bd9Sstevel@tonic-gate set_zone_context(time_t t)
10697c478bd9Sstevel@tonic-gate {
10707c478bd9Sstevel@tonic-gate 	prev_t		*prevp;
1071d1419d5aSNobutomo Nakano 	int    		lo, hi, tidx, lidx;
10727c478bd9Sstevel@tonic-gate 	ttinfo_t	*ttisp, *std, *alt;
1073d1419d5aSNobutomo Nakano 	const char	*newtzname[2];
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 	/* If state data not loaded or TZ busted, just use GMT */
10767c478bd9Sstevel@tonic-gate 	if (lclzonep == NULL || curr_zonerules == ZONERULES_INVALID) {
10777c478bd9Sstevel@tonic-gate 		timezone = altzone = 0;
10787c478bd9Sstevel@tonic-gate 		daylight = is_in_dst = 0;
1079d1419d5aSNobutomo Nakano 		newtzname[0] = (char *)_tz_gmt;
1080d1419d5aSNobutomo Nakano 		newtzname[1] = (char *)_tz_spaces;
1081d1419d5aSNobutomo Nakano 		set_tzname(newtzname);
10827c478bd9Sstevel@tonic-gate 		return;
10837c478bd9Sstevel@tonic-gate 	}
10847c478bd9Sstevel@tonic-gate 
1085d1419d5aSNobutomo Nakano 	if (lclzonep->timecnt <= 0 || lclzonep->typecnt < 2) {
10867c478bd9Sstevel@tonic-gate 		/* Loaded zone incapable of transitioning. */
1087d1419d5aSNobutomo Nakano 		set_zone_default_context();
10887c478bd9Sstevel@tonic-gate 		return;
1089d1419d5aSNobutomo Nakano 	}
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	/*
10927c478bd9Sstevel@tonic-gate 	 * At least one alt. zone and one transistion exist. Locate
10937c478bd9Sstevel@tonic-gate 	 * state for 't' quickly as possible.  Use defaults as necessary.
10947c478bd9Sstevel@tonic-gate 	 */
10957c478bd9Sstevel@tonic-gate 	lo = 0;
10967c478bd9Sstevel@tonic-gate 	hi = lclzonep->timecnt - 1;
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate 	if (t < lclzonep->ats[0] || t >= lclzonep->ats[hi]) {
1099d1419d5aSNobutomo Nakano 		/*
1100d1419d5aSNobutomo Nakano 		 * Date which is out of definition.
1101d1419d5aSNobutomo Nakano 		 * Calculate DST as best as possible
1102d1419d5aSNobutomo Nakano 		 */
11037c478bd9Sstevel@tonic-gate 		if (lclzonep->zonerules == POSIX_USA ||
11047c478bd9Sstevel@tonic-gate 		    lclzonep->zonerules == POSIX) {
1105d1419d5aSNobutomo Nakano 			/* Must invoke calculations to determine DST */
1106d1419d5aSNobutomo Nakano 			set_zone_default_context();
11077c478bd9Sstevel@tonic-gate 			is_in_dst = (daylight) ?
11087c478bd9Sstevel@tonic-gate 			    posix_check_dst(t, lclzonep) : 0;
11097c478bd9Sstevel@tonic-gate 			return;
11107c478bd9Sstevel@tonic-gate 		} else if (t < lclzonep->ats[0]) {   /* zoneinfo... */
11117c478bd9Sstevel@tonic-gate 			/* t precedes 1st transition.  Use defaults */
1112d1419d5aSNobutomo Nakano 			set_zone_default_context();
11137c478bd9Sstevel@tonic-gate 			return;
11147c478bd9Sstevel@tonic-gate 		} else	{    /* zoneinfo */
11157c478bd9Sstevel@tonic-gate 			/* t follows final transistion.  Use final */
11167c478bd9Sstevel@tonic-gate 			tidx = hi;
11177c478bd9Sstevel@tonic-gate 		}
11187c478bd9Sstevel@tonic-gate 	} else {
1119d1419d5aSNobutomo Nakano 		if ((lidx = lclzonep->last_ats_idx) != -1 &&
1120d1419d5aSNobutomo Nakano 		    lidx != hi &&
1121d1419d5aSNobutomo Nakano 		    t >= lclzonep->ats[lidx] &&
1122d1419d5aSNobutomo Nakano 		    t < lclzonep->ats[lidx + 1]) {
1123d1419d5aSNobutomo Nakano 			/* CACHE HIT. Nothing needs to be done */
1124d1419d5aSNobutomo Nakano 			tidx = lidx;
1125d1419d5aSNobutomo Nakano 		} else {
1126d1419d5aSNobutomo Nakano 			/*
1127d1419d5aSNobutomo Nakano 			 * CACHE MISS.  Locate transition using binary search.
1128d1419d5aSNobutomo Nakano 			 */
11297c478bd9Sstevel@tonic-gate 			while (lo <= hi) {
11307c478bd9Sstevel@tonic-gate 				tidx = (lo + hi) / 2;
11317c478bd9Sstevel@tonic-gate 				if (t == lclzonep->ats[tidx])
11327c478bd9Sstevel@tonic-gate 					break;
11337c478bd9Sstevel@tonic-gate 				else if (t < lclzonep->ats[tidx])
11347c478bd9Sstevel@tonic-gate 					hi = tidx - 1;
11357c478bd9Sstevel@tonic-gate 				else
11367c478bd9Sstevel@tonic-gate 					lo = tidx + 1;
11377c478bd9Sstevel@tonic-gate 			}
11387c478bd9Sstevel@tonic-gate 			if (lo > hi)
11397c478bd9Sstevel@tonic-gate 				tidx = hi;
11407c478bd9Sstevel@tonic-gate 		}
1141d1419d5aSNobutomo Nakano 	}
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	/*
11447c478bd9Sstevel@tonic-gate 	 * Set extern globals based on located transition and summary of
11457c478bd9Sstevel@tonic-gate 	 * its previous state, which were cached when zone was loaded
11467c478bd9Sstevel@tonic-gate 	 */
11477c478bd9Sstevel@tonic-gate 	ttisp = &lclzonep->ttis[lclzonep->types[tidx]];
11487c478bd9Sstevel@tonic-gate 	prevp = &lclzonep->prev[tidx];
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 	if ((is_in_dst = ttisp->tt_isdst) == 0) { /* std. time */
11517c478bd9Sstevel@tonic-gate 		timezone = -ttisp->tt_gmtoff;
1152d1419d5aSNobutomo Nakano 		newtzname[0] = &lclzonep->chars[ttisp->tt_abbrind];
11537c478bd9Sstevel@tonic-gate 		if ((alt = prevp->alt) != NULL) {
11547c478bd9Sstevel@tonic-gate 			altzone = -alt->tt_gmtoff;
1155d1419d5aSNobutomo Nakano 			newtzname[1] = &lclzonep->chars[alt->tt_abbrind];
1156d1419d5aSNobutomo Nakano 		} else {
1157d1419d5aSNobutomo Nakano 			altzone = lclzonep->default_altzone;
1158d1419d5aSNobutomo Nakano 			newtzname[1] = (char *)lclzonep->default_tzname1;
11597c478bd9Sstevel@tonic-gate 		}
11607c478bd9Sstevel@tonic-gate 	} else { /* alt. time */
11617c478bd9Sstevel@tonic-gate 		altzone = -ttisp->tt_gmtoff;
1162d1419d5aSNobutomo Nakano 		newtzname[1] = &lclzonep->chars[ttisp->tt_abbrind];
11637c478bd9Sstevel@tonic-gate 		if ((std = prevp->std) != NULL) {
11647c478bd9Sstevel@tonic-gate 			timezone = -std->tt_gmtoff;
1165d1419d5aSNobutomo Nakano 			newtzname[0] = &lclzonep->chars[std->tt_abbrind];
1166d1419d5aSNobutomo Nakano 		} else {
1167d1419d5aSNobutomo Nakano 			timezone = lclzonep->default_timezone;
1168d1419d5aSNobutomo Nakano 			newtzname[0] = (char *)lclzonep->default_tzname0;
11697c478bd9Sstevel@tonic-gate 		}
11707c478bd9Sstevel@tonic-gate 	}
1171d1419d5aSNobutomo Nakano 
1172d1419d5aSNobutomo Nakano 	lclzonep->last_ats_idx = tidx;
1173d1419d5aSNobutomo Nakano 	set_tzname(newtzname);
11747c478bd9Sstevel@tonic-gate }
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate /*
11777c478bd9Sstevel@tonic-gate  * This function takes a time_t and gmt offset and produces a
11787c478bd9Sstevel@tonic-gate  * tm struct based on specified time.
11797c478bd9Sstevel@tonic-gate  *
11807c478bd9Sstevel@tonic-gate  * The the following fields are calculated, based entirely
11817c478bd9Sstevel@tonic-gate  * on the offset-adjusted value of t:
11827c478bd9Sstevel@tonic-gate  *
11837c478bd9Sstevel@tonic-gate  * tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec
11847c478bd9Sstevel@tonic-gate  * tm_yday. tm_wday.  (tm_isdst is ALWAYS set to 0).
11857c478bd9Sstevel@tonic-gate  */
11867c478bd9Sstevel@tonic-gate 
11877c478bd9Sstevel@tonic-gate static struct tm *
offtime_u(time_t t,long offset,struct tm * tmptr)11887c478bd9Sstevel@tonic-gate offtime_u(time_t t, long offset, struct tm *tmptr)
11897c478bd9Sstevel@tonic-gate {
11907c478bd9Sstevel@tonic-gate 	long		days;
11917c478bd9Sstevel@tonic-gate 	long		rem;
11927c478bd9Sstevel@tonic-gate 	long		y;
11937c478bd9Sstevel@tonic-gate 	int		yleap;
11947c478bd9Sstevel@tonic-gate 	const int	*ip;
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 	days = t / SECSPERDAY;
11977c478bd9Sstevel@tonic-gate 	rem = t % SECSPERDAY;
11987c478bd9Sstevel@tonic-gate 	rem += offset;
11997c478bd9Sstevel@tonic-gate 	while (rem < 0) {
12007c478bd9Sstevel@tonic-gate 		rem += SECSPERDAY;
12017c478bd9Sstevel@tonic-gate 		--days;
12027c478bd9Sstevel@tonic-gate 	}
12037c478bd9Sstevel@tonic-gate 	while (rem >= SECSPERDAY) {
12047c478bd9Sstevel@tonic-gate 		rem -= SECSPERDAY;
12057c478bd9Sstevel@tonic-gate 		++days;
12067c478bd9Sstevel@tonic-gate 	}
12077c478bd9Sstevel@tonic-gate 	tmptr->tm_hour = (int)(rem / SECSPERHOUR);
12087c478bd9Sstevel@tonic-gate 	rem = rem % SECSPERHOUR;
12097c478bd9Sstevel@tonic-gate 	tmptr->tm_min = (int)(rem / SECSPERMIN);
12107c478bd9Sstevel@tonic-gate 	tmptr->tm_sec = (int)(rem % SECSPERMIN);
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	tmptr->tm_wday = (int)((EPOCH_WDAY + days) % DAYSPERWEEK);
12137c478bd9Sstevel@tonic-gate 	if (tmptr->tm_wday < 0)
12147c478bd9Sstevel@tonic-gate 		tmptr->tm_wday += DAYSPERWEEK;
12157c478bd9Sstevel@tonic-gate 	y = EPOCH_YEAR;
12167c478bd9Sstevel@tonic-gate 	while (days < 0 || days >= (long)__year_lengths[yleap = isleap(y)]) {
12177c478bd9Sstevel@tonic-gate 		long newy;
12187c478bd9Sstevel@tonic-gate 
12197c478bd9Sstevel@tonic-gate 		newy = y + days / DAYSPERNYEAR;
12207c478bd9Sstevel@tonic-gate 		if (days < 0)
12217c478bd9Sstevel@tonic-gate 			--newy;
12227c478bd9Sstevel@tonic-gate 		days -= ((long)newy - (long)y) * DAYSPERNYEAR +
12237c478bd9Sstevel@tonic-gate 		    LEAPS_THRU_END_OF(newy > 0 ? newy - 1L : newy) -
12247c478bd9Sstevel@tonic-gate 		    LEAPS_THRU_END_OF(y > 0 ? y - 1L : y);
12257c478bd9Sstevel@tonic-gate 		y = newy;
12267c478bd9Sstevel@tonic-gate 	}
12277c478bd9Sstevel@tonic-gate 	tmptr->tm_year = (int)(y - TM_YEAR_BASE);
12287c478bd9Sstevel@tonic-gate 	tmptr->tm_yday = (int)days;
12297c478bd9Sstevel@tonic-gate 	ip = __mon_lengths[yleap];
12307c478bd9Sstevel@tonic-gate 	for (tmptr->tm_mon = 0; days >=
1231d1419d5aSNobutomo Nakano 	    (long)ip[tmptr->tm_mon]; ++(tmptr->tm_mon)) {
12327c478bd9Sstevel@tonic-gate 		days = days - (long)ip[tmptr->tm_mon];
1233d1419d5aSNobutomo Nakano 	}
12347c478bd9Sstevel@tonic-gate 	tmptr->tm_mday = (int)(days + 1);
12357c478bd9Sstevel@tonic-gate 	tmptr->tm_isdst = 0;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate #ifdef _LP64
12387c478bd9Sstevel@tonic-gate 	/* do as much as possible before checking for error. */
12397c478bd9Sstevel@tonic-gate 	if ((y > (long)INT_MAX + TM_YEAR_BASE) ||
12407c478bd9Sstevel@tonic-gate 	    (y < (long)INT_MIN + TM_YEAR_BASE)) {
12417c478bd9Sstevel@tonic-gate 		errno = EOVERFLOW;
12427c478bd9Sstevel@tonic-gate 		return (NULL);
12437c478bd9Sstevel@tonic-gate 	}
12447c478bd9Sstevel@tonic-gate #endif
12457c478bd9Sstevel@tonic-gate 	return (tmptr);
12467c478bd9Sstevel@tonic-gate }
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate /*
12497c478bd9Sstevel@tonic-gate  * Check whether DST is set for time in question.  Only applies to
12507c478bd9Sstevel@tonic-gate  * POSIX timezones.  If explicit POSIX transition rules were provided
12517c478bd9Sstevel@tonic-gate  * for the current zone, use those, otherwise use default USA POSIX
12527c478bd9Sstevel@tonic-gate  * transitions.
12537c478bd9Sstevel@tonic-gate  */
12547c478bd9Sstevel@tonic-gate static int
posix_check_dst(long long t,state_t * sp)12557c478bd9Sstevel@tonic-gate posix_check_dst(long long t, state_t *sp)
12567c478bd9Sstevel@tonic-gate {
12577c478bd9Sstevel@tonic-gate 	struct tm	gmttm;
12587c478bd9Sstevel@tonic-gate 	long long	jan01;
12597c478bd9Sstevel@tonic-gate 	int		year, i, idx, ridx;
12607c478bd9Sstevel@tonic-gate 	posix_daylight_t	pdaylight;
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	(void) offtime_u(t, 0L, &gmttm);
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	year = gmttm.tm_year + 1900;
12657c478bd9Sstevel@tonic-gate 	jan01 = t - ((gmttm.tm_yday * SECSPERDAY) +
12667c478bd9Sstevel@tonic-gate 	    (gmttm.tm_hour * SECSPERHOUR) +
12677c478bd9Sstevel@tonic-gate 	    (gmttm.tm_min * SECSPERMIN) + gmttm.tm_sec);
12687c478bd9Sstevel@tonic-gate 	/*
12697c478bd9Sstevel@tonic-gate 	 * If transition rules were provided for this zone,
12707c478bd9Sstevel@tonic-gate 	 * use them, otherwise, default to USA daylight rules,
12717c478bd9Sstevel@tonic-gate 	 * which are historically correct for the continental USA,
12727c478bd9Sstevel@tonic-gate 	 * excluding local provisions.  (This logic may be replaced
12737c478bd9Sstevel@tonic-gate 	 * at some point in the future with "posixrules" to offer
12747c478bd9Sstevel@tonic-gate 	 * more flexibility to the system administrator).
12757c478bd9Sstevel@tonic-gate 	 */
12767c478bd9Sstevel@tonic-gate 	if (sp->zonerules == POSIX)	 {	/* POSIX rules */
12777c478bd9Sstevel@tonic-gate 		pdaylight.rules[0] = &sp->start_rule;
12787c478bd9Sstevel@tonic-gate 		pdaylight.rules[1] = &sp->end_rule;
12797c478bd9Sstevel@tonic-gate 	} else { 			/* POSIX_USA: USA */
12807c478bd9Sstevel@tonic-gate 		i = 0;
12817c478bd9Sstevel@tonic-gate 		while (year < __usa_rules[i].s_year && i < MAX_RULE_TABLE) {
12827c478bd9Sstevel@tonic-gate 			i++;
12837c478bd9Sstevel@tonic-gate 		}
12847c478bd9Sstevel@tonic-gate 		pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
12857c478bd9Sstevel@tonic-gate 		pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
12867c478bd9Sstevel@tonic-gate 	}
12877c478bd9Sstevel@tonic-gate 	pdaylight.offset[0] = timezone;
12887c478bd9Sstevel@tonic-gate 	pdaylight.offset[1] = altzone;
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 	idx = posix_daylight(&jan01, year, &pdaylight);
12917c478bd9Sstevel@tonic-gate 	ridx = !idx;
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 	/*
12947c478bd9Sstevel@tonic-gate 	 * Note:  t, rtime[0], and rtime[1] are all bounded within 'year'
12957c478bd9Sstevel@tonic-gate 	 * beginning on 'jan01'
12967c478bd9Sstevel@tonic-gate 	 */
12977c478bd9Sstevel@tonic-gate 	if (t >= pdaylight.rtime[idx] && t < pdaylight.rtime[ridx]) {
12987c478bd9Sstevel@tonic-gate 		return (ridx);
12997c478bd9Sstevel@tonic-gate 	} else {
13007c478bd9Sstevel@tonic-gate 		return (idx);
13017c478bd9Sstevel@tonic-gate 	}
13027c478bd9Sstevel@tonic-gate }
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate /*
13057c478bd9Sstevel@tonic-gate  * Given January 1, 00:00:00 GMT for a year as an Epoch-relative time,
13067c478bd9Sstevel@tonic-gate  * along with the integer year #, a posix_daylight_t that is composed
13077c478bd9Sstevel@tonic-gate  * of two rules, and two GMT offsets (timezone and altzone), calculate
13087c478bd9Sstevel@tonic-gate  * the two Epoch-relative times the two rules take effect, and return
13097c478bd9Sstevel@tonic-gate  * them in the two rtime fields of the posix_daylight_t structure.
13107c478bd9Sstevel@tonic-gate  * Also update janfirst by a year, by adding the appropriate number of
13117c478bd9Sstevel@tonic-gate  * seconds depending on whether the year is a leap year or not.  (We take
13127c478bd9Sstevel@tonic-gate  * advantage that this routine knows the leap year status.)
13137c478bd9Sstevel@tonic-gate  */
13147c478bd9Sstevel@tonic-gate static int
posix_daylight(long long * janfirst,int year,posix_daylight_t * pdaylightp)13157c478bd9Sstevel@tonic-gate posix_daylight(long long *janfirst, int year, posix_daylight_t *pdaylightp)
13167c478bd9Sstevel@tonic-gate {
13177c478bd9Sstevel@tonic-gate 	rule_t	*rulep;
13187c478bd9Sstevel@tonic-gate 	long	offset;
13197c478bd9Sstevel@tonic-gate 	int	idx;
13207c478bd9Sstevel@tonic-gate 	int	i, d, m1, yy0, yy1, yy2, dow;
13217c478bd9Sstevel@tonic-gate 	long	leapyear;
13227c478bd9Sstevel@tonic-gate 	long long	value;
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	static const int	__secs_year_lengths[2] = {
13257c478bd9Sstevel@tonic-gate 		DAYS_PER_NYEAR * SECSPERDAY,
13267c478bd9Sstevel@tonic-gate 		DAYS_PER_LYEAR * SECSPERDAY
13277c478bd9Sstevel@tonic-gate 	};
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	leapyear = isleap(year);
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 	for (idx = 0; idx < 2; idx++) {
13327c478bd9Sstevel@tonic-gate 		rulep = pdaylightp->rules[idx];
13337c478bd9Sstevel@tonic-gate 		offset = pdaylightp->offset[idx];
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate 		switch (rulep->r_type) {
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 		case MON_WEEK_DOW:
13387c478bd9Sstevel@tonic-gate 			/*
13397c478bd9Sstevel@tonic-gate 			 * Mm.n.d - nth "dth day" of month m.
13407c478bd9Sstevel@tonic-gate 			 */
13417c478bd9Sstevel@tonic-gate 			value = *janfirst;
13427c478bd9Sstevel@tonic-gate 			for (i = 0; i < rulep->r_mon - 1; ++i)
13437c478bd9Sstevel@tonic-gate 				value += __mon_lengths[leapyear][i] *
13447c478bd9Sstevel@tonic-gate 				    SECSPERDAY;
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 			/*
13477c478bd9Sstevel@tonic-gate 			 * Use Zeller's Congruence to get day-of-week of first
13487c478bd9Sstevel@tonic-gate 			 * day of month.
13497c478bd9Sstevel@tonic-gate 			 */
13507c478bd9Sstevel@tonic-gate 			m1 = (rulep->r_mon + 9) % 12 + 1;
13517c478bd9Sstevel@tonic-gate 			yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
13527c478bd9Sstevel@tonic-gate 			yy1 = yy0 / 100;
13537c478bd9Sstevel@tonic-gate 			yy2 = yy0 % 100;
13547c478bd9Sstevel@tonic-gate 			dow = ((26 * m1 - 2) / 10 +
13557c478bd9Sstevel@tonic-gate 			    1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate 			if (dow < 0)
13587c478bd9Sstevel@tonic-gate 				dow += DAYSPERWEEK;
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 			/*
13617c478bd9Sstevel@tonic-gate 			 * Following heuristic increases accuracy of USA rules
13627c478bd9Sstevel@tonic-gate 			 * for negative years.
13637c478bd9Sstevel@tonic-gate 			 */
13647c478bd9Sstevel@tonic-gate 			if (year < 1 && leapyear)
13657c478bd9Sstevel@tonic-gate 				++dow;
13667c478bd9Sstevel@tonic-gate 			/*
13677c478bd9Sstevel@tonic-gate 			 * "dow" is the day-of-week of the first day of the
13687c478bd9Sstevel@tonic-gate 			 * month.  Get the day-of-month, zero-origin, of the
13697c478bd9Sstevel@tonic-gate 			 * first "dow" day of the month.
13707c478bd9Sstevel@tonic-gate 			 */
13717c478bd9Sstevel@tonic-gate 			d = rulep->r_day - dow;
13727c478bd9Sstevel@tonic-gate 			if (d < 0)
13737c478bd9Sstevel@tonic-gate 				d += DAYSPERWEEK;
13747c478bd9Sstevel@tonic-gate 			for (i = 1; i < rulep->r_week; ++i) {
13757c478bd9Sstevel@tonic-gate 				if (d + DAYSPERWEEK >=
13767c478bd9Sstevel@tonic-gate 				    __mon_lengths[leapyear][rulep->r_mon - 1])
13777c478bd9Sstevel@tonic-gate 					break;
13787c478bd9Sstevel@tonic-gate 				d += DAYSPERWEEK;
13797c478bd9Sstevel@tonic-gate 			}
13807c478bd9Sstevel@tonic-gate 			/*
13817c478bd9Sstevel@tonic-gate 			 * "d" is the day-of-month, zero-origin, of the day
13827c478bd9Sstevel@tonic-gate 			 * we want.
13837c478bd9Sstevel@tonic-gate 			 */
13847c478bd9Sstevel@tonic-gate 			value += d * SECSPERDAY;
13857c478bd9Sstevel@tonic-gate 			break;
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate 		case JULIAN_DAY:
13887c478bd9Sstevel@tonic-gate 			/*
13897c478bd9Sstevel@tonic-gate 			 * Jn - Julian day, 1 == Jan 1, 60 == March 1 even
13907c478bd9Sstevel@tonic-gate 			 * in leap yrs.
13917c478bd9Sstevel@tonic-gate 			 */
13927c478bd9Sstevel@tonic-gate 			value = *janfirst + (rulep->r_day - 1) * SECSPERDAY;
13937c478bd9Sstevel@tonic-gate 			if (leapyear && rulep->r_day >= 60)
13947c478bd9Sstevel@tonic-gate 				value += SECSPERDAY;
13957c478bd9Sstevel@tonic-gate 			break;
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 		case DAY_OF_YEAR:
13987c478bd9Sstevel@tonic-gate 			/*
13997c478bd9Sstevel@tonic-gate 			 * n - day of year.
14007c478bd9Sstevel@tonic-gate 			 */
14017c478bd9Sstevel@tonic-gate 			value = *janfirst + rulep->r_day * SECSPERDAY;
14027c478bd9Sstevel@tonic-gate 			break;
14037c478bd9Sstevel@tonic-gate 		}
14047c478bd9Sstevel@tonic-gate 		pdaylightp->rtime[idx] = value + rulep->r_time + offset;
14057c478bd9Sstevel@tonic-gate 	}
14067c478bd9Sstevel@tonic-gate 	*janfirst += __secs_year_lengths[leapyear];
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 	return ((pdaylightp->rtime[0] > pdaylightp->rtime[1]) ? 1 : 0);
14097c478bd9Sstevel@tonic-gate }
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate /*
14127c478bd9Sstevel@tonic-gate  * Try to load zoneinfo file into internal transition tables using name
14137c478bd9Sstevel@tonic-gate  * indicated in TZ, and do validity checks.  The format of zic(1M)
14147c478bd9Sstevel@tonic-gate  * compiled zoneinfo files isdescribed in tzfile.h
14157c478bd9Sstevel@tonic-gate  */
14167c478bd9Sstevel@tonic-gate static int
load_zoneinfo(const char * name,state_t * sp)14177c478bd9Sstevel@tonic-gate load_zoneinfo(const char *name, state_t *sp)
14187c478bd9Sstevel@tonic-gate {
14197c478bd9Sstevel@tonic-gate 	char	*cp;
14207c478bd9Sstevel@tonic-gate 	char	*cp2;
14217c478bd9Sstevel@tonic-gate 	int	i;
14227c478bd9Sstevel@tonic-gate 	long	cnt;
14237c478bd9Sstevel@tonic-gate 	int	fid;
14247c478bd9Sstevel@tonic-gate 	int	ttisstdcnt;
14257c478bd9Sstevel@tonic-gate 	int	ttisgmtcnt;
14267c478bd9Sstevel@tonic-gate 	char	*fullname;
14277c478bd9Sstevel@tonic-gate 	size_t	namelen;
14287c478bd9Sstevel@tonic-gate 	char	*bufp;
14297c478bd9Sstevel@tonic-gate 	size_t	flen;
14307c478bd9Sstevel@tonic-gate 	prev_t	*prevp;
14317c478bd9Sstevel@tonic-gate /* LINTED */
14327c478bd9Sstevel@tonic-gate 	struct	tzhead *tzhp;
14337c478bd9Sstevel@tonic-gate 	struct	stat64	stbuf;
14347c478bd9Sstevel@tonic-gate 	ttinfo_t	*most_recent_alt = NULL;
14357c478bd9Sstevel@tonic-gate 	ttinfo_t	*most_recent_std = NULL;
14367c478bd9Sstevel@tonic-gate 	ttinfo_t	*ttisp;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	if (name == NULL && (name = TZDEFAULT) == NULL)
14407c478bd9Sstevel@tonic-gate 		return (-1);
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	if ((name[0] == '/') || strstr(name, "../"))
14437c478bd9Sstevel@tonic-gate 		return (-1);
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	/*
14467c478bd9Sstevel@tonic-gate 	 * We allocate fullname this way to avoid having
14477c478bd9Sstevel@tonic-gate 	 * a PATH_MAX size buffer in our stack frame.
14487c478bd9Sstevel@tonic-gate 	 */
14497c478bd9Sstevel@tonic-gate 	namelen = LEN_TZDIR + 1 + strlen(name) + 1;
14507c478bd9Sstevel@tonic-gate 	if ((fullname = lmalloc(namelen)) == NULL)
14517c478bd9Sstevel@tonic-gate 		return (-1);
14527c478bd9Sstevel@tonic-gate 	(void) strcpy(fullname, TZDIR "/");
14537c478bd9Sstevel@tonic-gate 	(void) strcpy(fullname + LEN_TZDIR + 1, name);
14547c478bd9Sstevel@tonic-gate 	if ((fid = open(fullname, O_RDONLY)) == -1) {
14557c478bd9Sstevel@tonic-gate 		lfree(fullname, namelen);
14567c478bd9Sstevel@tonic-gate 		return (-1);
14577c478bd9Sstevel@tonic-gate 	}
14587c478bd9Sstevel@tonic-gate 	lfree(fullname, namelen);
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	if (fstat64(fid, &stbuf) == -1) {
14617c478bd9Sstevel@tonic-gate 		(void) close(fid);
14627c478bd9Sstevel@tonic-gate 		return (-1);
14637c478bd9Sstevel@tonic-gate 	}
14647c478bd9Sstevel@tonic-gate 
14657c478bd9Sstevel@tonic-gate 	flen = (size_t)stbuf.st_size;
14667c478bd9Sstevel@tonic-gate 	if (flen < sizeof (struct tzhead)) {
14677c478bd9Sstevel@tonic-gate 		(void) close(fid);
14687c478bd9Sstevel@tonic-gate 		return (-1);
14697c478bd9Sstevel@tonic-gate 	}
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 	/*
14727c478bd9Sstevel@tonic-gate 	 * It would be nice to use alloca() to allocate bufp but,
14737c478bd9Sstevel@tonic-gate 	 * as above, we wish to avoid allocating a big buffer in
14747c478bd9Sstevel@tonic-gate 	 * our stack frame, and also because alloca() gives us no
14757c478bd9Sstevel@tonic-gate 	 * opportunity to fail gracefully on allocation failure.
14767c478bd9Sstevel@tonic-gate 	 */
14777c478bd9Sstevel@tonic-gate 	cp = bufp = lmalloc(flen);
14787c478bd9Sstevel@tonic-gate 	if (bufp == NULL) {
14797c478bd9Sstevel@tonic-gate 		(void) close(fid);
14807c478bd9Sstevel@tonic-gate 		return (-1);
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	if ((cnt = read(fid, bufp, flen)) != flen) {
14847c478bd9Sstevel@tonic-gate 		lfree(bufp, flen);
14857c478bd9Sstevel@tonic-gate 		(void) close(fid);
14867c478bd9Sstevel@tonic-gate 		return (-1);
14877c478bd9Sstevel@tonic-gate 	}
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 	if (close(fid) != 0) {
14907c478bd9Sstevel@tonic-gate 		lfree(bufp, flen);
14917c478bd9Sstevel@tonic-gate 		return (-1);
14927c478bd9Sstevel@tonic-gate 	}
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 	cp += (sizeof (tzhp->tzh_magic)) + (sizeof (tzhp->tzh_reserved));
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate /* LINTED: alignment */
14977c478bd9Sstevel@tonic-gate 	ttisstdcnt = CVTZCODE(cp);
14987c478bd9Sstevel@tonic-gate /* LINTED: alignment */
14997c478bd9Sstevel@tonic-gate 	ttisgmtcnt = CVTZCODE(cp);
15007c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15017c478bd9Sstevel@tonic-gate 	sp->leapcnt = CVTZCODE(cp);
15027c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15037c478bd9Sstevel@tonic-gate 	sp->timecnt = CVTZCODE(cp);
15047c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15057c478bd9Sstevel@tonic-gate 	sp->typecnt = CVTZCODE(cp);
15067c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15077c478bd9Sstevel@tonic-gate 	sp->charcnt = CVTZCODE(cp);
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate 	if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
15107c478bd9Sstevel@tonic-gate 	    sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
15117c478bd9Sstevel@tonic-gate 	    sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
15127c478bd9Sstevel@tonic-gate 	    sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
15137c478bd9Sstevel@tonic-gate 	    (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
15147c478bd9Sstevel@tonic-gate 	    (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) {
15157c478bd9Sstevel@tonic-gate 		lfree(bufp, flen);
15167c478bd9Sstevel@tonic-gate 		return (-1);
15177c478bd9Sstevel@tonic-gate 	}
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 	if (cnt - (cp - bufp) < (long)(sp->timecnt * 4 +	/* ats */
15207c478bd9Sstevel@tonic-gate 	    sp->timecnt +			/* types */
15217c478bd9Sstevel@tonic-gate 	    sp->typecnt * (4 + 2) +		/* ttinfos */
15227c478bd9Sstevel@tonic-gate 	    sp->charcnt +			/* chars */
15237c478bd9Sstevel@tonic-gate 	    sp->leapcnt * (4 + 4) +		/* lsinfos */
15247c478bd9Sstevel@tonic-gate 	    ttisstdcnt +			/* ttisstds */
15257c478bd9Sstevel@tonic-gate 	    ttisgmtcnt)) {			/* ttisgmts */
15267c478bd9Sstevel@tonic-gate 		lfree(bufp, flen);
15277c478bd9Sstevel@tonic-gate 		return (-1);
15287c478bd9Sstevel@tonic-gate 	}
15297c478bd9Sstevel@tonic-gate 
15307c478bd9Sstevel@tonic-gate 
15317c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->timecnt; ++i) {
15327c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15337c478bd9Sstevel@tonic-gate 		sp->ats[i] = CVTZCODE(cp);
15347c478bd9Sstevel@tonic-gate 	}
15357c478bd9Sstevel@tonic-gate 
15367c478bd9Sstevel@tonic-gate 	/*
15377c478bd9Sstevel@tonic-gate 	 * Skip over types[] for now and load ttis[] so that when
15387c478bd9Sstevel@tonic-gate 	 * types[] are loaded we can check for transitions to STD & DST.
15397c478bd9Sstevel@tonic-gate 	 * This allows us to shave cycles in ltzset_u(), including
15407c478bd9Sstevel@tonic-gate 	 * eliminating the need to check set 'daylight' later.
15417c478bd9Sstevel@tonic-gate 	 */
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 	cp2 = (char *)((uintptr_t)cp + sp->timecnt);
15447c478bd9Sstevel@tonic-gate 
15457c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->typecnt; ++i) {
15467c478bd9Sstevel@tonic-gate 		ttisp = &sp->ttis[i];
15477c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15487c478bd9Sstevel@tonic-gate 		ttisp->tt_gmtoff = CVTZCODE(cp2);
15497c478bd9Sstevel@tonic-gate 		ttisp->tt_isdst = (uchar_t)*cp2++;
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 		if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) {
15527c478bd9Sstevel@tonic-gate 			lfree(bufp, flen);
15537c478bd9Sstevel@tonic-gate 			return (-1);
15547c478bd9Sstevel@tonic-gate 		}
15557c478bd9Sstevel@tonic-gate 
15567c478bd9Sstevel@tonic-gate 		ttisp->tt_abbrind = (uchar_t)*cp2++;
15577c478bd9Sstevel@tonic-gate 		if (ttisp->tt_abbrind < 0 ||
15587c478bd9Sstevel@tonic-gate 		    ttisp->tt_abbrind > sp->charcnt) {
15597c478bd9Sstevel@tonic-gate 			lfree(bufp, flen);
15607c478bd9Sstevel@tonic-gate 			return (-1);
15617c478bd9Sstevel@tonic-gate 		}
15627c478bd9Sstevel@tonic-gate 	}
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	/*
15657c478bd9Sstevel@tonic-gate 	 * Since ttis were loaded ahead of types, it is possible to
15667c478bd9Sstevel@tonic-gate 	 * detect whether daylight is ever set for this zone now, and
15677c478bd9Sstevel@tonic-gate 	 * also preload other information to avoid repeated lookups later.
15687c478bd9Sstevel@tonic-gate 	 * This logic facilitates keeping a running tab on the state of
15697c478bd9Sstevel@tonic-gate 	 * std zone and alternate zone transitions such that timezone,
15707c478bd9Sstevel@tonic-gate 	 * altzone and tzname[] can be determined quickly via an
15717c478bd9Sstevel@tonic-gate 	 * index to any transition.
15727c478bd9Sstevel@tonic-gate 	 *
15737c478bd9Sstevel@tonic-gate 	 * For transition #0 there are no previous transitions,
15747c478bd9Sstevel@tonic-gate 	 * so prev->std and prev->alt will be null, but that's OK,
15757c478bd9Sstevel@tonic-gate 	 * because null prev->std/prev->alt effectively
15767c478bd9Sstevel@tonic-gate 	 * indicates none existed prior.
15777c478bd9Sstevel@tonic-gate 	 */
15787c478bd9Sstevel@tonic-gate 
15797c478bd9Sstevel@tonic-gate 	prevp = &sp->prev[0];
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->timecnt; ++i) {
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 		sp->types[i] = (uchar_t)*cp++;
15847c478bd9Sstevel@tonic-gate 		ttisp = &sp->ttis[sp->types[i]];
15857c478bd9Sstevel@tonic-gate 
15867c478bd9Sstevel@tonic-gate 		prevp->std = most_recent_std;
15877c478bd9Sstevel@tonic-gate 		prevp->alt = most_recent_alt;
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 		if (ttisp->tt_isdst == 1) {
15907c478bd9Sstevel@tonic-gate 			most_recent_alt = ttisp;
15917c478bd9Sstevel@tonic-gate 		} else {
15927c478bd9Sstevel@tonic-gate 			most_recent_std = ttisp;
15937c478bd9Sstevel@tonic-gate 		}
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate 		if ((int)sp->types[i] >= sp->typecnt) {
15967c478bd9Sstevel@tonic-gate 			lfree(bufp, flen);
15977c478bd9Sstevel@tonic-gate 			return (-1);
15987c478bd9Sstevel@tonic-gate 		}
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate 		++prevp;
16017c478bd9Sstevel@tonic-gate 	}
16027c478bd9Sstevel@tonic-gate 	if (most_recent_alt == NULL)
16037c478bd9Sstevel@tonic-gate 		sp->daylight = 0;
16047c478bd9Sstevel@tonic-gate 	else
16057c478bd9Sstevel@tonic-gate 		sp->daylight = 1;
16067c478bd9Sstevel@tonic-gate 
16077c478bd9Sstevel@tonic-gate 	/*
16087c478bd9Sstevel@tonic-gate 	 * Set pointer ahead to where it would have been if we
16097c478bd9Sstevel@tonic-gate 	 * had read types[] and ttis[] in the same order they
16107c478bd9Sstevel@tonic-gate 	 * occurred in the file.
16117c478bd9Sstevel@tonic-gate 	 */
16127c478bd9Sstevel@tonic-gate 	cp = cp2;
16137c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->charcnt; ++i)
16147c478bd9Sstevel@tonic-gate 		sp->chars[i] = *cp++;
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate 	sp->chars[i] = '\0';	/* ensure '\0' at end */
16177c478bd9Sstevel@tonic-gate 
16187c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->leapcnt; ++i) {
16197c478bd9Sstevel@tonic-gate 		struct lsinfo *lsisp;
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 		lsisp = &sp->lsis[i];
16227c478bd9Sstevel@tonic-gate /* LINTED: alignment */
16237c478bd9Sstevel@tonic-gate 		lsisp->ls_trans = CVTZCODE(cp);
16247c478bd9Sstevel@tonic-gate /* LINTED: alignment */
16257c478bd9Sstevel@tonic-gate 		lsisp->ls_corr = CVTZCODE(cp);
16267c478bd9Sstevel@tonic-gate 	}
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->typecnt; ++i) {
16297c478bd9Sstevel@tonic-gate 		ttisp = &sp->ttis[i];
16307c478bd9Sstevel@tonic-gate 		if (ttisstdcnt == 0) {
16317c478bd9Sstevel@tonic-gate 			ttisp->tt_ttisstd = FALSE;
16327c478bd9Sstevel@tonic-gate 		} else {
16337c478bd9Sstevel@tonic-gate 			ttisp->tt_ttisstd = *cp++;
16347c478bd9Sstevel@tonic-gate 			if (ttisp->tt_ttisstd != TRUE &&
16357c478bd9Sstevel@tonic-gate 			    ttisp->tt_ttisstd != FALSE) {
16367c478bd9Sstevel@tonic-gate 				lfree(bufp, flen);
16377c478bd9Sstevel@tonic-gate 				return (-1);
16387c478bd9Sstevel@tonic-gate 			}
16397c478bd9Sstevel@tonic-gate 		}
16407c478bd9Sstevel@tonic-gate 	}
16417c478bd9Sstevel@tonic-gate 
16427c478bd9Sstevel@tonic-gate 	for (i = 0; i < sp->typecnt; ++i) {
16437c478bd9Sstevel@tonic-gate 		ttisp = &sp->ttis[i];
16447c478bd9Sstevel@tonic-gate 		if (ttisgmtcnt == 0) {
16457c478bd9Sstevel@tonic-gate 			ttisp->tt_ttisgmt = FALSE;
16467c478bd9Sstevel@tonic-gate 		} else {
16477c478bd9Sstevel@tonic-gate 			ttisp->tt_ttisgmt = *cp++;
16487c478bd9Sstevel@tonic-gate 			if (ttisp->tt_ttisgmt != TRUE &&
16497c478bd9Sstevel@tonic-gate 			    ttisp->tt_ttisgmt != FALSE) {
16507c478bd9Sstevel@tonic-gate 				lfree(bufp, flen);
16517c478bd9Sstevel@tonic-gate 				return (-1);
16527c478bd9Sstevel@tonic-gate 			}
16537c478bd9Sstevel@tonic-gate 		}
16547c478bd9Sstevel@tonic-gate 	}
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 	/*
16577c478bd9Sstevel@tonic-gate 	 * Other defaults set at beginning of this routine
16587c478bd9Sstevel@tonic-gate 	 * to cover case where zoneinfo file cannot be loaded
16597c478bd9Sstevel@tonic-gate 	 */
16607c478bd9Sstevel@tonic-gate 	sp->default_timezone = -sp->ttis[0].tt_gmtoff;
16617c478bd9Sstevel@tonic-gate 	sp->default_altzone  = 0;
16627c478bd9Sstevel@tonic-gate 	sp->default_tzname0  = &sp->chars[0];
16637c478bd9Sstevel@tonic-gate 	sp->default_tzname1  = _tz_spaces;
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 	lfree(bufp, flen);
16667c478bd9Sstevel@tonic-gate 
16677c478bd9Sstevel@tonic-gate 	sp->zonerules = ZONEINFO;
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 	return (0);
16707c478bd9Sstevel@tonic-gate }
16717c478bd9Sstevel@tonic-gate 
1672d1419d5aSNobutomo Nakano #ifdef	_TZ_DEBUG
1673d1419d5aSNobutomo Nakano static void
print_state(state_t * sp)1674d1419d5aSNobutomo Nakano print_state(state_t *sp)
1675d1419d5aSNobutomo Nakano {
1676d1419d5aSNobutomo Nakano 	struct tm	tmp;
1677d1419d5aSNobutomo Nakano 	int	i, c;
1678d1419d5aSNobutomo Nakano 
1679d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "=========================================\n");
1680d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "zonename: \"%s\"\n", sp->zonename);
1681d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "next: 0x%p\n", (void *)sp->next);
1682d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "zonerules: %s\n",
1683d1419d5aSNobutomo Nakano 	    sp->zonerules == ZONERULES_INVALID ? "ZONERULES_INVALID" :
1684d1419d5aSNobutomo Nakano 	    sp->zonerules == POSIX ? "POSIX" :
1685d1419d5aSNobutomo Nakano 	    sp->zonerules == POSIX_USA ? "POSIX_USA" :
1686d1419d5aSNobutomo Nakano 	    sp->zonerules == ZONEINFO ? "ZONEINFO" : "UNKNOWN");
1687d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "daylight: %d\n", sp->daylight);
1688d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "default_timezone: %ld\n", sp->default_timezone);
1689d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "default_altzone: %ld\n", sp->default_altzone);
1690d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "default_tzname0: \"%s\"\n",
1691d1419d5aSNobutomo Nakano 	    sp->default_tzname0);
1692d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "default_tzname1: \"%s\"\n",
1693d1419d5aSNobutomo Nakano 	    sp->default_tzname1);
1694d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "leapcnt: %d\n", sp->leapcnt);
1695d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "timecnt: %d\n", sp->timecnt);
1696d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "typecnt: %d\n", sp->typecnt);
1697d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "charcnt: %d\n", sp->charcnt);
1698d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "chars: \"%s\"\n", sp->chars);
1699d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "charsbuf_size: %u\n", sp->charsbuf_size);
1700d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "prev: skipping...\n");
1701d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "ats = {\n");
1702d1419d5aSNobutomo Nakano 	for (c = 0, i = 0; i < sp->timecnt; i++) {
1703d1419d5aSNobutomo Nakano 		char	buf[64];
1704d1419d5aSNobutomo Nakano 		size_t	len;
1705d1419d5aSNobutomo Nakano 		if (c != 0) {
1706d1419d5aSNobutomo Nakano 			(void) fprintf(stderr, ", ");
1707d1419d5aSNobutomo Nakano 		}
1708d1419d5aSNobutomo Nakano 		(void) asctime_r(gmtime_r(&sp->ats[i], &tmp),
1709d1419d5aSNobutomo Nakano 		    buf, sizeof (buf));
1710d1419d5aSNobutomo Nakano 		len = strlen(buf);
1711d1419d5aSNobutomo Nakano 		buf[len-1] = '\0';
1712d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "%s", buf);
1713d1419d5aSNobutomo Nakano 		if (c == 1) {
1714d1419d5aSNobutomo Nakano 			(void) fprintf(stderr, "\n");
1715d1419d5aSNobutomo Nakano 			c = 0;
1716d1419d5aSNobutomo Nakano 		} else {
1717d1419d5aSNobutomo Nakano 			c++;
1718d1419d5aSNobutomo Nakano 		}
1719d1419d5aSNobutomo Nakano 	}
1720d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "}\n");
1721d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "types = {\n");
1722d1419d5aSNobutomo Nakano 	for (c = 0, i = 0; i < sp->timecnt; i++) {
1723d1419d5aSNobutomo Nakano 		if (c == 0) {
1724d1419d5aSNobutomo Nakano 			(void) fprintf(stderr, "\t");
1725d1419d5aSNobutomo Nakano 		} else {
1726d1419d5aSNobutomo Nakano 			(void) fprintf(stderr, ", ");
1727d1419d5aSNobutomo Nakano 		}
1728d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "%d", sp->types[i]);
1729d1419d5aSNobutomo Nakano 		if (c == 7) {
1730d1419d5aSNobutomo Nakano 			(void) fprintf(stderr, "\n");
1731d1419d5aSNobutomo Nakano 			c = 0;
1732d1419d5aSNobutomo Nakano 		} else {
1733d1419d5aSNobutomo Nakano 			c++;
1734d1419d5aSNobutomo Nakano 		}
1735d1419d5aSNobutomo Nakano 	}
1736d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "}\n");
1737d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "ttis = {\n");
1738d1419d5aSNobutomo Nakano 	for (i = 0; i < sp->typecnt; i++) {
1739d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t{\n");
1740d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t\ttt_gmtoff: %ld\n",
1741d1419d5aSNobutomo Nakano 		    sp->ttis[i].tt_gmtoff);
1742d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t\ttt_ttisdst: %d\n",
1743d1419d5aSNobutomo Nakano 		    sp->ttis[i].tt_isdst);
1744d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t\ttt_abbrind: %d\n",
1745d1419d5aSNobutomo Nakano 		    sp->ttis[i].tt_abbrind);
1746d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t\ttt_tt_isstd: %d\n",
1747d1419d5aSNobutomo Nakano 		    sp->ttis[i].tt_ttisstd);
1748d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t\ttt_ttisgmt: %d\n",
1749d1419d5aSNobutomo Nakano 		    sp->ttis[i].tt_ttisgmt);
1750d1419d5aSNobutomo Nakano 		(void) fprintf(stderr, "\t}\n");
1751d1419d5aSNobutomo Nakano 	}
1752d1419d5aSNobutomo Nakano 	(void) fprintf(stderr, "}\n");
1753d1419d5aSNobutomo Nakano }
1754d1419d5aSNobutomo Nakano #endif
1755d1419d5aSNobutomo Nakano 
17567c478bd9Sstevel@tonic-gate /*
17577c478bd9Sstevel@tonic-gate  * Given a POSIX section 8-style TZ string, fill in transition tables.
17587c478bd9Sstevel@tonic-gate  *
17597c478bd9Sstevel@tonic-gate  * Examples:
17607c478bd9Sstevel@tonic-gate  *
17617c478bd9Sstevel@tonic-gate  * TZ = PST8 or GMT0
17627c478bd9Sstevel@tonic-gate  *	Timecnt set to 0 and typecnt set to 1, reflecting std time only.
17637c478bd9Sstevel@tonic-gate  *
17647c478bd9Sstevel@tonic-gate  * TZ = PST8PDT or PST8PDT7
17657c478bd9Sstevel@tonic-gate  *	Create transition times by applying USA transitions from
17667c478bd9Sstevel@tonic-gate  *	Jan 1 of each year covering 1902-2038.  POSIX offsets
17677c478bd9Sstevel@tonic-gate  *	as specified in the TZ are used to calculate the tt_gmtoff
17687c478bd9Sstevel@tonic-gate  *	for each of the two zones.  If ommitted, DST defaults to
17697c478bd9Sstevel@tonic-gate  *	std. time minus one hour.
17707c478bd9Sstevel@tonic-gate  *
17717c478bd9Sstevel@tonic-gate  * TZ = <PST8>8PDT  or <PST8>8<PDT9>
17727c478bd9Sstevel@tonic-gate  *      Quoted transition.  The values in angled brackets are treated
17737c478bd9Sstevel@tonic-gate  *      as zone name text, not parsed as offsets.  The offsets
17747c478bd9Sstevel@tonic-gate  *      occuring following the zonename section.  In this way,
17757c478bd9Sstevel@tonic-gate  *      instead of PST being displayed for standard time, it could
17767c478bd9Sstevel@tonic-gate  *      be displayed as PST8 to give an indication of the offset
17777c478bd9Sstevel@tonic-gate  *      of that zone to GMT.
17787c478bd9Sstevel@tonic-gate  *
17797c478bd9Sstevel@tonic-gate  * TZ = GMT0BST, M3.5.0/1, M10.5.0/2   or  GMT0BST, J23953, J23989
17807c478bd9Sstevel@tonic-gate  *	Create transition times based on the application new-year
17817c478bd9Sstevel@tonic-gate  *	relative POSIX transitions, parsed from TZ, from Jan 1
17827c478bd9Sstevel@tonic-gate  *	for each year covering 1902-2038.  POSIX offsets specified
17837c478bd9Sstevel@tonic-gate  *	in TZ are used to calculate tt_gmtoff for each of the two
17847c478bd9Sstevel@tonic-gate  *	zones.
17857c478bd9Sstevel@tonic-gate  *
17867c478bd9Sstevel@tonic-gate  */
17877c478bd9Sstevel@tonic-gate static int
load_posixinfo(const char * name,state_t * sp)17887c478bd9Sstevel@tonic-gate load_posixinfo(const char *name, state_t *sp)
17897c478bd9Sstevel@tonic-gate {
17907c478bd9Sstevel@tonic-gate 	const char	*stdname;
17917c478bd9Sstevel@tonic-gate 	const char	*dstname = 0;
17927c478bd9Sstevel@tonic-gate 	size_t		stdlen;
17937c478bd9Sstevel@tonic-gate 	size_t		dstlen;
17947c478bd9Sstevel@tonic-gate 	long		stdoff = 0;
17957c478bd9Sstevel@tonic-gate 	long		dstoff = 0;
17967c478bd9Sstevel@tonic-gate 	char		*cp;
17977c478bd9Sstevel@tonic-gate 	int		i;
17987c478bd9Sstevel@tonic-gate 	ttinfo_t	*dst;
17997c478bd9Sstevel@tonic-gate 	ttinfo_t	*std;
18007c478bd9Sstevel@tonic-gate 	int		quoted;
18017c478bd9Sstevel@tonic-gate 	zone_rules_t	zonetype;
1802d1419d5aSNobutomo Nakano 
18037c478bd9Sstevel@tonic-gate 
18047c478bd9Sstevel@tonic-gate 	zonetype = POSIX_USA;
18057c478bd9Sstevel@tonic-gate 	stdname = name;
18067c478bd9Sstevel@tonic-gate 
18077c478bd9Sstevel@tonic-gate 	if ((quoted = (*stdname == '<')) != 0)
18087c478bd9Sstevel@tonic-gate 		++stdname;
18097c478bd9Sstevel@tonic-gate 
18107c478bd9Sstevel@tonic-gate 	/* Parse/extract STD zone name, len and GMT offset */
18117c478bd9Sstevel@tonic-gate 	if (*name != '\0') {
18127c478bd9Sstevel@tonic-gate 		if ((name = getzname(name, quoted)) == NULL)
18137c478bd9Sstevel@tonic-gate 			return (-1);
18147c478bd9Sstevel@tonic-gate 		stdlen = name - stdname;
18157c478bd9Sstevel@tonic-gate 		if (*name == '>')
18167c478bd9Sstevel@tonic-gate 			++name;
18177c478bd9Sstevel@tonic-gate 		if (*name == '\0' || stdlen < 1) {
18187c478bd9Sstevel@tonic-gate 			return (-1);
18197c478bd9Sstevel@tonic-gate 		} else {
18207c478bd9Sstevel@tonic-gate 			if ((name = getoffset(name, &stdoff)) == NULL)
18217c478bd9Sstevel@tonic-gate 				return (-1);
18227c478bd9Sstevel@tonic-gate 		}
18237c478bd9Sstevel@tonic-gate 	}
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 	/* If DST specified in TZ, extract DST zone details */
18267c478bd9Sstevel@tonic-gate 	if (*name != '\0') {
18277c478bd9Sstevel@tonic-gate 
18287c478bd9Sstevel@tonic-gate 		dstname = name;
18297c478bd9Sstevel@tonic-gate 		if ((quoted = (*dstname == '<')) != 0)
18307c478bd9Sstevel@tonic-gate 			++dstname;
18317c478bd9Sstevel@tonic-gate 		if ((name = getzname(name, quoted)) == NULL)
18327c478bd9Sstevel@tonic-gate 			return (-1);
18337c478bd9Sstevel@tonic-gate 		dstlen = name - dstname;
18347c478bd9Sstevel@tonic-gate 		if (dstlen < 1)
18357c478bd9Sstevel@tonic-gate 			return (-1);
18367c478bd9Sstevel@tonic-gate 		if (*name == '>')
18377c478bd9Sstevel@tonic-gate 			++name;
18387c478bd9Sstevel@tonic-gate 		if (*name != '\0' && *name != ',' && *name != ';') {
18397c478bd9Sstevel@tonic-gate 			if ((name = getoffset(name, &dstoff)) == NULL)
18407c478bd9Sstevel@tonic-gate 				return (-1);
18417c478bd9Sstevel@tonic-gate 		} else {
18427c478bd9Sstevel@tonic-gate 			dstoff = stdoff - SECSPERHOUR;
18437c478bd9Sstevel@tonic-gate 		}
18447c478bd9Sstevel@tonic-gate 
1845d1419d5aSNobutomo Nakano 		if (*name != ',' && *name != ';') {
1846d1419d5aSNobutomo Nakano 			/* no transtition specified; using default rule */
1847d1419d5aSNobutomo Nakano 			if (load_zoneinfo(TZDEFRULES, sp) == 0 &&
1848d1419d5aSNobutomo Nakano 			    sp->daylight == 1) {
1849d1419d5aSNobutomo Nakano 				/* loading TZDEFRULES zoneinfo succeeded */
1850d1419d5aSNobutomo Nakano 				adjust_posix_default(sp, stdoff, dstoff);
1851d1419d5aSNobutomo Nakano 			} else {
1852d1419d5aSNobutomo Nakano 				/* loading TZDEFRULES zoneinfo failed */
1853d1419d5aSNobutomo Nakano 				load_posix_transitions(sp, stdoff, dstoff,
1854d1419d5aSNobutomo Nakano 				    zonetype);
1855d1419d5aSNobutomo Nakano 			}
1856d1419d5aSNobutomo Nakano 		} else {
1857d1419d5aSNobutomo Nakano 			/* extract POSIX transitions from TZ */
18587c478bd9Sstevel@tonic-gate 			/* Backward compatibility using ';' separator */
18597c478bd9Sstevel@tonic-gate 			int	compat_flag = (*name == ';');
18607c478bd9Sstevel@tonic-gate 			++name;
18617c478bd9Sstevel@tonic-gate 			if ((name = getrule(name, &sp->start_rule, compat_flag))
18627c478bd9Sstevel@tonic-gate 			    == NULL)
18637c478bd9Sstevel@tonic-gate 				return (-1);
18647c478bd9Sstevel@tonic-gate 			if (*name++ != ',')
18657c478bd9Sstevel@tonic-gate 				return (-1);
18667c478bd9Sstevel@tonic-gate 			if ((name = getrule(name, &sp->end_rule, compat_flag))
18677c478bd9Sstevel@tonic-gate 			    == NULL)
18687c478bd9Sstevel@tonic-gate 				return (-1);
18697c478bd9Sstevel@tonic-gate 			if (*name != '\0')
18707c478bd9Sstevel@tonic-gate 				return (-1);
18717c478bd9Sstevel@tonic-gate 			zonetype = POSIX;
1872d1419d5aSNobutomo Nakano 			load_posix_transitions(sp, stdoff, dstoff, zonetype);
18737c478bd9Sstevel@tonic-gate 		}
18747c478bd9Sstevel@tonic-gate 		dst = &sp->ttis[0];
18757c478bd9Sstevel@tonic-gate 		std = &sp->ttis[1];
18767c478bd9Sstevel@tonic-gate 	} else {  /* DST wasn't specified in POSIX TZ */
18777c478bd9Sstevel@tonic-gate 
18787c478bd9Sstevel@tonic-gate 		/*  Since we only have STD time, there are no transitions */
18797c478bd9Sstevel@tonic-gate 		dstlen = 0;
18807c478bd9Sstevel@tonic-gate 		sp->daylight = 0;
18817c478bd9Sstevel@tonic-gate 		sp->typecnt = 1;
18827c478bd9Sstevel@tonic-gate 		sp->timecnt = 0;
18837c478bd9Sstevel@tonic-gate 		std = &sp->ttis[0];
18847c478bd9Sstevel@tonic-gate 		std->tt_gmtoff = -stdoff;
18857c478bd9Sstevel@tonic-gate 		std->tt_isdst = 0;
18867c478bd9Sstevel@tonic-gate 	}
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate 	/* Setup zone name character data for state table */
18897c478bd9Sstevel@tonic-gate 	sp->charcnt = (int)(stdlen + 1);
18907c478bd9Sstevel@tonic-gate 	if (dstlen != 0)
18917c478bd9Sstevel@tonic-gate 		sp->charcnt += dstlen + 1;
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	/* If bigger than zone name abbv. buffer, grow it */
18947c478bd9Sstevel@tonic-gate 	if ((size_t)sp->charcnt > sp->charsbuf_size) {
18957c478bd9Sstevel@tonic-gate 		if ((cp = libc_realloc(sp->chars, sp->charcnt)) == NULL)
18967c478bd9Sstevel@tonic-gate 			return (-1);
18977c478bd9Sstevel@tonic-gate 		sp->chars = cp;
18987c478bd9Sstevel@tonic-gate 		sp->charsbuf_size = sp->charcnt;
18997c478bd9Sstevel@tonic-gate 	}
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 	/*
19027c478bd9Sstevel@tonic-gate 	 * Copy zone name text null-terminatedly into state table.
19037c478bd9Sstevel@tonic-gate 	 * By doing the copy once during zone loading, setting
19047c478bd9Sstevel@tonic-gate 	 * tzname[] subsequently merely involves setting pointer
19057c478bd9Sstevel@tonic-gate 	 *
19067c478bd9Sstevel@tonic-gate 	 * If either or both std. or alt. zone name < 3 chars,
19077c478bd9Sstevel@tonic-gate 	 * space pad the deficient name(s) to right.
19087c478bd9Sstevel@tonic-gate 	 */
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	std->tt_abbrind = 0;
19117c478bd9Sstevel@tonic-gate 	cp = sp->chars;
19127c478bd9Sstevel@tonic-gate 	(void) strncpy(cp, stdname, stdlen);
19137c478bd9Sstevel@tonic-gate 	while (stdlen < 3)
19147c478bd9Sstevel@tonic-gate 		cp[stdlen++] = ' ';
19157c478bd9Sstevel@tonic-gate 	cp[stdlen] = '\0';
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	i = (int)(stdlen + 1);
19187c478bd9Sstevel@tonic-gate 	if (dstlen != 0) {
19197c478bd9Sstevel@tonic-gate 		dst->tt_abbrind = i;
19207c478bd9Sstevel@tonic-gate 		cp += i;
19217c478bd9Sstevel@tonic-gate 		(void) strncpy(cp, dstname, dstlen);
19227c478bd9Sstevel@tonic-gate 		while (dstlen < 3)
19237c478bd9Sstevel@tonic-gate 			cp[dstlen++] = ' ';
19247c478bd9Sstevel@tonic-gate 		cp[dstlen] = '\0';
19257c478bd9Sstevel@tonic-gate 	}
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 	/* Save default values */
19287c478bd9Sstevel@tonic-gate 	if (sp->typecnt == 1) {
19297c478bd9Sstevel@tonic-gate 		sp->default_timezone = stdoff;
19307c478bd9Sstevel@tonic-gate 		sp->default_altzone = stdoff;
19317c478bd9Sstevel@tonic-gate 		sp->default_tzname0 = &sp->chars[0];
19327c478bd9Sstevel@tonic-gate 		sp->default_tzname1 = _tz_spaces;
19337c478bd9Sstevel@tonic-gate 	} else {
19347c478bd9Sstevel@tonic-gate 		sp->default_timezone = -std->tt_gmtoff;
19357c478bd9Sstevel@tonic-gate 		sp->default_altzone = -dst->tt_gmtoff;
19367c478bd9Sstevel@tonic-gate 		sp->default_tzname0 = &sp->chars[std->tt_abbrind];
19377c478bd9Sstevel@tonic-gate 		sp->default_tzname1 = &sp->chars[dst->tt_abbrind];
19387c478bd9Sstevel@tonic-gate 	}
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 	sp->zonerules = zonetype;
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 	return (0);
19437c478bd9Sstevel@tonic-gate }
19447c478bd9Sstevel@tonic-gate 
1945d1419d5aSNobutomo Nakano /*
1946d1419d5aSNobutomo Nakano  * We loaded the TZDEFAULT which usually the one in US zones. We
1947d1419d5aSNobutomo Nakano  * adjust the GMT offset for the zone which has stdoff/dstoff
1948d1419d5aSNobutomo Nakano  * offset.
1949d1419d5aSNobutomo Nakano  */
1950d1419d5aSNobutomo Nakano static void
adjust_posix_default(state_t * sp,long stdoff,long dstoff)1951d1419d5aSNobutomo Nakano adjust_posix_default(state_t *sp, long stdoff, long dstoff)
1952d1419d5aSNobutomo Nakano {
1953d1419d5aSNobutomo Nakano 	long	zone_stdoff = 0;
1954d1419d5aSNobutomo Nakano 	long	zone_dstoff = 0;
1955d1419d5aSNobutomo Nakano 	int	zone_stdoff_flag = 0;
1956d1419d5aSNobutomo Nakano 	int	zone_dstoff_flag = 0;
1957d1419d5aSNobutomo Nakano 	int	isdst;
1958d1419d5aSNobutomo Nakano 	int	i;
1959d1419d5aSNobutomo Nakano 
1960d1419d5aSNobutomo Nakano 	/*
1961d1419d5aSNobutomo Nakano 	 * Initial values of zone_stdoff and zone_dstoff
1962d1419d5aSNobutomo Nakano 	 */
1963d1419d5aSNobutomo Nakano 	for (i = 0; (zone_stdoff_flag == 0 || zone_dstoff_flag == 0) &&
1964d1419d5aSNobutomo Nakano 	    i < sp->timecnt; i++) {
1965d1419d5aSNobutomo Nakano 		ttinfo_t	*zone;
1966d1419d5aSNobutomo Nakano 
1967d1419d5aSNobutomo Nakano 		zone = &sp->ttis[sp->types[i]];
1968d1419d5aSNobutomo Nakano 
1969d1419d5aSNobutomo Nakano 		if (zone_stdoff_flag == 0 && zone->tt_isdst == 0) {
1970d1419d5aSNobutomo Nakano 			zone_stdoff = -zone->tt_gmtoff;
1971d1419d5aSNobutomo Nakano 			zone_stdoff_flag = 1;
1972d1419d5aSNobutomo Nakano 		} else if (zone_dstoff_flag == 0 && zone->tt_isdst != 0) {
1973d1419d5aSNobutomo Nakano 			zone_dstoff = -zone->tt_gmtoff;
1974d1419d5aSNobutomo Nakano 			zone_dstoff_flag = 1;
1975d1419d5aSNobutomo Nakano 		}
1976d1419d5aSNobutomo Nakano 	}
1977d1419d5aSNobutomo Nakano 	if (zone_dstoff_flag == 0)
1978d1419d5aSNobutomo Nakano 		zone_dstoff = zone_stdoff;
1979d1419d5aSNobutomo Nakano 
1980d1419d5aSNobutomo Nakano 	/*
1981d1419d5aSNobutomo Nakano 	 * Initially we're assumed to be in standard time.
1982d1419d5aSNobutomo Nakano 	 */
1983d1419d5aSNobutomo Nakano 	isdst = 0;
1984d1419d5aSNobutomo Nakano 
1985d1419d5aSNobutomo Nakano 	for (i = 0; i < sp->timecnt; i++) {
1986d1419d5aSNobutomo Nakano 		ttinfo_t	*zone;
1987d1419d5aSNobutomo Nakano 		int	next_isdst;
1988d1419d5aSNobutomo Nakano 
1989d1419d5aSNobutomo Nakano 		zone = &sp->ttis[sp->types[i]];
1990d1419d5aSNobutomo Nakano 		next_isdst = zone->tt_isdst;
1991d1419d5aSNobutomo Nakano 
1992d1419d5aSNobutomo Nakano 		sp->types[i] = next_isdst ? 0 : 1;
1993d1419d5aSNobutomo Nakano 
1994d1419d5aSNobutomo Nakano 		if (zone->tt_ttisgmt == 0) {
1995d1419d5aSNobutomo Nakano 			/*
1996d1419d5aSNobutomo Nakano 			 * If summer time is in effect, and the transition time
1997d1419d5aSNobutomo Nakano 			 * was not specified as standard time, add the summer
1998d1419d5aSNobutomo Nakano 			 * time offset to the transition time;
1999d1419d5aSNobutomo Nakano 			 * otherwise, add the standard time offset to the
2000d1419d5aSNobutomo Nakano 			 * transition time.
2001d1419d5aSNobutomo Nakano 			 */
2002d1419d5aSNobutomo Nakano 			/*
2003d1419d5aSNobutomo Nakano 			 * Transitions from DST to DDST will effectively
2004d1419d5aSNobutomo Nakano 			 * disappear since POSIX provides for only one DST
2005d1419d5aSNobutomo Nakano 			 * offset.
2006d1419d5aSNobutomo Nakano 			 */
2007d1419d5aSNobutomo Nakano 			if (isdst != 0 && zone->tt_ttisstd == 0)
2008d1419d5aSNobutomo Nakano 				sp->ats[i] += dstoff - zone_dstoff;
2009d1419d5aSNobutomo Nakano 			else
2010d1419d5aSNobutomo Nakano 				sp->ats[i] += stdoff - zone_stdoff;
2011d1419d5aSNobutomo Nakano 		}
2012d1419d5aSNobutomo Nakano 		if (next_isdst != 0)
2013d1419d5aSNobutomo Nakano 			zone_dstoff = -zone->tt_gmtoff;
2014d1419d5aSNobutomo Nakano 		else
2015d1419d5aSNobutomo Nakano 			zone_stdoff = -zone->tt_gmtoff;
2016d1419d5aSNobutomo Nakano 		isdst = next_isdst;
2017d1419d5aSNobutomo Nakano 	}
2018d1419d5aSNobutomo Nakano 	/*
2019d1419d5aSNobutomo Nakano 	 * Finally, fill in ttis.
2020d1419d5aSNobutomo Nakano 	 * ttisstd and ttisgmt need not be handled.
2021d1419d5aSNobutomo Nakano 	 */
2022d1419d5aSNobutomo Nakano 	sp->ttis[0].tt_gmtoff = -dstoff;
2023d1419d5aSNobutomo Nakano 	sp->ttis[0].tt_isdst = 1;
2024d1419d5aSNobutomo Nakano 	sp->ttis[1].tt_gmtoff = -stdoff;
2025d1419d5aSNobutomo Nakano 	sp->ttis[1].tt_isdst = 0;
2026d1419d5aSNobutomo Nakano 	sp->typecnt = 2;
2027d1419d5aSNobutomo Nakano 	sp->daylight = 1;
2028d1419d5aSNobutomo Nakano }
2029d1419d5aSNobutomo Nakano 
2030d1419d5aSNobutomo Nakano /*
2031d1419d5aSNobutomo Nakano  *
2032d1419d5aSNobutomo Nakano  */
2033d1419d5aSNobutomo Nakano static void
load_posix_transitions(state_t * sp,long stdoff,long dstoff,zone_rules_t zonetype)2034d1419d5aSNobutomo Nakano load_posix_transitions(state_t *sp, long stdoff, long dstoff,
2035d1419d5aSNobutomo Nakano     zone_rules_t zonetype)
2036d1419d5aSNobutomo Nakano {
2037d1419d5aSNobutomo Nakano 	ttinfo_t	*std, *dst;
2038d1419d5aSNobutomo Nakano 	time_t	*tranp;
2039d1419d5aSNobutomo Nakano 	uchar_t	*typep;
2040d1419d5aSNobutomo Nakano 	prev_t	*prevp;
2041d1419d5aSNobutomo Nakano 	int	year;
2042d1419d5aSNobutomo Nakano 	int	i;
2043d1419d5aSNobutomo Nakano 	long long	janfirst;
2044d1419d5aSNobutomo Nakano 	posix_daylight_t	pdaylight;
2045d1419d5aSNobutomo Nakano 
2046d1419d5aSNobutomo Nakano 	/*
2047d1419d5aSNobutomo Nakano 	 * We know STD and DST zones are specified with this timezone
2048d1419d5aSNobutomo Nakano 	 * therefore the cache will be set up with 2 transitions per
2049d1419d5aSNobutomo Nakano 	 * year transitioning to their respective std and dst zones.
2050d1419d5aSNobutomo Nakano 	 */
2051d1419d5aSNobutomo Nakano 	sp->daylight = 1;
2052d1419d5aSNobutomo Nakano 	sp->typecnt = 2;
2053d1419d5aSNobutomo Nakano 	sp->timecnt = 272;
2054d1419d5aSNobutomo Nakano 
2055d1419d5aSNobutomo Nakano 	/*
2056d1419d5aSNobutomo Nakano 	 * Insert zone data from POSIX TZ into state table
2057d1419d5aSNobutomo Nakano 	 * The Olson public domain POSIX code sets up ttis[0] to be DST,
2058d1419d5aSNobutomo Nakano 	 * as we are doing here.  It seems to be the correct behavior.
2059d1419d5aSNobutomo Nakano 	 * The US/Pacific zoneinfo file also lists DST as first type.
2060d1419d5aSNobutomo Nakano 	 */
2061d1419d5aSNobutomo Nakano 
2062d1419d5aSNobutomo Nakano 	dst = &sp->ttis[0];
2063d1419d5aSNobutomo Nakano 	dst->tt_gmtoff = -dstoff;
2064d1419d5aSNobutomo Nakano 	dst->tt_isdst = 1;
2065d1419d5aSNobutomo Nakano 
2066d1419d5aSNobutomo Nakano 	std = &sp->ttis[1];
2067d1419d5aSNobutomo Nakano 	std->tt_gmtoff = -stdoff;
2068d1419d5aSNobutomo Nakano 	std->tt_isdst = 0;
2069d1419d5aSNobutomo Nakano 
2070d1419d5aSNobutomo Nakano 	sp->prev[0].std = NULL;
2071d1419d5aSNobutomo Nakano 	sp->prev[0].alt = NULL;
2072d1419d5aSNobutomo Nakano 
2073d1419d5aSNobutomo Nakano 	/* Create transition data based on POSIX TZ */
2074d1419d5aSNobutomo Nakano 	tranp = sp->ats;
2075d1419d5aSNobutomo Nakano 	prevp  = &sp->prev[1];
2076d1419d5aSNobutomo Nakano 	typep  = sp->types;
2077d1419d5aSNobutomo Nakano 
2078d1419d5aSNobutomo Nakano 	/*
2079d1419d5aSNobutomo Nakano 	 * We only cache from 1902 to 2037 to avoid transistions
2080d1419d5aSNobutomo Nakano 	 * that wrap at the 32-bit boundries, since 1901 and 2038
2081d1419d5aSNobutomo Nakano 	 * are not full years in 32-bit time.  The rough edges
2082d1419d5aSNobutomo Nakano 	 * will be handled as transition cache misses.
2083d1419d5aSNobutomo Nakano 	 */
2084d1419d5aSNobutomo Nakano 
2085d1419d5aSNobutomo Nakano 	janfirst = JAN_01_1902;
2086d1419d5aSNobutomo Nakano 
2087d1419d5aSNobutomo Nakano 	pdaylight.rules[0] = &sp->start_rule;
2088d1419d5aSNobutomo Nakano 	pdaylight.rules[1] = &sp->end_rule;
2089d1419d5aSNobutomo Nakano 	pdaylight.offset[0] = stdoff;
2090d1419d5aSNobutomo Nakano 	pdaylight.offset[1] = dstoff;
2091d1419d5aSNobutomo Nakano 
2092d1419d5aSNobutomo Nakano 	for (i = MAX_RULE_TABLE; i >= 0; i--) {
2093d1419d5aSNobutomo Nakano 		if (zonetype == POSIX_USA) {
2094d1419d5aSNobutomo Nakano 			pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
2095d1419d5aSNobutomo Nakano 			pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
2096d1419d5aSNobutomo Nakano 		}
2097d1419d5aSNobutomo Nakano 		for (year = __usa_rules[i].s_year;
2098d1419d5aSNobutomo Nakano 		    year <= __usa_rules[i].e_year; year++) {
2099d1419d5aSNobutomo Nakano 			int	idx, ridx;
2100d1419d5aSNobutomo Nakano 			idx = posix_daylight(&janfirst, year, &pdaylight);
2101d1419d5aSNobutomo Nakano 			ridx = !idx;
2102d1419d5aSNobutomo Nakano 
2103d1419d5aSNobutomo Nakano 			/*
2104d1419d5aSNobutomo Nakano 			 * Two transitions per year. Since there are
2105d1419d5aSNobutomo Nakano 			 * only two zone types for this POSIX zone,
2106d1419d5aSNobutomo Nakano 			 * previous std and alt are always set to
2107d1419d5aSNobutomo Nakano 			 * &ttis[0] and &ttis[1].
2108d1419d5aSNobutomo Nakano 			 */
2109d1419d5aSNobutomo Nakano 			*tranp++ = (time_t)pdaylight.rtime[idx];
2110d1419d5aSNobutomo Nakano 			*typep++ = idx;
2111d1419d5aSNobutomo Nakano 			prevp->std = std;
2112d1419d5aSNobutomo Nakano 			prevp->alt = dst;
2113d1419d5aSNobutomo Nakano 			++prevp;
2114d1419d5aSNobutomo Nakano 
2115d1419d5aSNobutomo Nakano 			*tranp++ = (time_t)pdaylight.rtime[ridx];
2116d1419d5aSNobutomo Nakano 			*typep++ = ridx;
2117d1419d5aSNobutomo Nakano 			prevp->std = std;
2118d1419d5aSNobutomo Nakano 			prevp->alt = dst;
2119d1419d5aSNobutomo Nakano 			++prevp;
2120d1419d5aSNobutomo Nakano 		}
2121d1419d5aSNobutomo Nakano 	}
2122d1419d5aSNobutomo Nakano }
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate /*
21257c478bd9Sstevel@tonic-gate  * Given a pointer into a time zone string, scan until a character that is not
21267c478bd9Sstevel@tonic-gate  * a valid character in a zone name is found.  Return ptr to that character.
21277c478bd9Sstevel@tonic-gate  * Return NULL if error (ie. non-printable character located in name)
21287c478bd9Sstevel@tonic-gate  */
21297c478bd9Sstevel@tonic-gate static const char *
getzname(const char * strp,int quoted)21307c478bd9Sstevel@tonic-gate getzname(const char *strp, int quoted)
21317c478bd9Sstevel@tonic-gate {
21327c478bd9Sstevel@tonic-gate 	char	c;
21337c478bd9Sstevel@tonic-gate 
21347c478bd9Sstevel@tonic-gate 	if (quoted) {
21357c478bd9Sstevel@tonic-gate 		while ((c = *strp) != '\0' && c != '>' &&
2136d1419d5aSNobutomo Nakano 		    isgraph((unsigned char)c)) {
21377c478bd9Sstevel@tonic-gate 			++strp;
2138d1419d5aSNobutomo Nakano 		}
21397c478bd9Sstevel@tonic-gate 	} else {
21407c478bd9Sstevel@tonic-gate 		while ((c = *strp) != '\0' && isgraph((unsigned char)c) &&
21417c478bd9Sstevel@tonic-gate 		    !isdigit((unsigned char)c) && c != ',' && c != '-' &&
2142d1419d5aSNobutomo Nakano 		    c != '+') {
21437c478bd9Sstevel@tonic-gate 			++strp;
21447c478bd9Sstevel@tonic-gate 		}
2145d1419d5aSNobutomo Nakano 	}
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 	/* Found an excessively invalid character.  Discredit whole name */
21487c478bd9Sstevel@tonic-gate 	if (c != '\0' && !isgraph((unsigned char)c))
21497c478bd9Sstevel@tonic-gate 		return (NULL);
21507c478bd9Sstevel@tonic-gate 
21517c478bd9Sstevel@tonic-gate 	return (strp);
21527c478bd9Sstevel@tonic-gate }
21537c478bd9Sstevel@tonic-gate 
21547c478bd9Sstevel@tonic-gate /*
21557c478bd9Sstevel@tonic-gate  * Given pointer into time zone string, extract first
21567c478bd9Sstevel@tonic-gate  * number pointed to.  Validate number within range specified,
21577c478bd9Sstevel@tonic-gate  * Return ptr to first char following valid numeric sequence.
21587c478bd9Sstevel@tonic-gate  */
21597c478bd9Sstevel@tonic-gate static const char *
getnum(const char * strp,int * nump,int min,int max)21607c478bd9Sstevel@tonic-gate getnum(const char *strp, int *nump, int min, int max)
21617c478bd9Sstevel@tonic-gate {
21627c478bd9Sstevel@tonic-gate 	char	c;
21637c478bd9Sstevel@tonic-gate 	int	num;
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 	if (strp == NULL || !isdigit((unsigned char)(c = *strp)))
21667c478bd9Sstevel@tonic-gate 		return (NULL);
21677c478bd9Sstevel@tonic-gate 	num = 0;
21687c478bd9Sstevel@tonic-gate 	do {
21697c478bd9Sstevel@tonic-gate 		num = num * 10 + (c - '0');
21707c478bd9Sstevel@tonic-gate 		if (num > max)
21717c478bd9Sstevel@tonic-gate 			return (NULL);	/* illegal value */
21727c478bd9Sstevel@tonic-gate 		c = *++strp;
21737c478bd9Sstevel@tonic-gate 	} while (isdigit((unsigned char)c));
21747c478bd9Sstevel@tonic-gate 	if (num < min)
21757c478bd9Sstevel@tonic-gate 		return (NULL);		/* illegal value */
21767c478bd9Sstevel@tonic-gate 	*nump = num;
21777c478bd9Sstevel@tonic-gate 	return (strp);
21787c478bd9Sstevel@tonic-gate }
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate /*
21817c478bd9Sstevel@tonic-gate  * Given a pointer into a time zone string, extract a number of seconds,
21827c478bd9Sstevel@tonic-gate  * in hh[:mm[:ss]] form, from the string.  If an error occurs, return NULL,
21837c478bd9Sstevel@tonic-gate  * otherwise, return a pointer to the first character not part of the number
21847c478bd9Sstevel@tonic-gate  * of seconds.
21857c478bd9Sstevel@tonic-gate  */
21867c478bd9Sstevel@tonic-gate static const char *
getsecs(const char * strp,long * secsp)21877c478bd9Sstevel@tonic-gate getsecs(const char *strp, long *secsp)
21887c478bd9Sstevel@tonic-gate {
21897c478bd9Sstevel@tonic-gate 	int	num;
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 	/*
21927c478bd9Sstevel@tonic-gate 	 * `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
21937c478bd9Sstevel@tonic-gate 	 * "M10.4.6/26", which does not conform to Posix,
21947c478bd9Sstevel@tonic-gate 	 * but which specifies the equivalent of
21957c478bd9Sstevel@tonic-gate 	 * ``02:00 on the first Sunday on or after 23 Oct''.
21967c478bd9Sstevel@tonic-gate 	 */
21977c478bd9Sstevel@tonic-gate 	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
21987c478bd9Sstevel@tonic-gate 	if (strp == NULL)
21997c478bd9Sstevel@tonic-gate 		return (NULL);
22007c478bd9Sstevel@tonic-gate 	*secsp = num * (long)SECSPERHOUR;
22017c478bd9Sstevel@tonic-gate 	if (*strp == ':') {
22027c478bd9Sstevel@tonic-gate 		++strp;
22037c478bd9Sstevel@tonic-gate 		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
22047c478bd9Sstevel@tonic-gate 		if (strp == NULL)
22057c478bd9Sstevel@tonic-gate 			return (NULL);
22067c478bd9Sstevel@tonic-gate 		*secsp += num * SECSPERMIN;
22077c478bd9Sstevel@tonic-gate 		if (*strp == ':') {
22087c478bd9Sstevel@tonic-gate 			++strp;
22097c478bd9Sstevel@tonic-gate 			/* `SECSPERMIN' allows for leap seconds.  */
22107c478bd9Sstevel@tonic-gate 			strp = getnum(strp, &num, 0, SECSPERMIN);
22117c478bd9Sstevel@tonic-gate 			if (strp == NULL)
22127c478bd9Sstevel@tonic-gate 				return (NULL);
22137c478bd9Sstevel@tonic-gate 			*secsp += num;
22147c478bd9Sstevel@tonic-gate 		}
22157c478bd9Sstevel@tonic-gate 	}
22167c478bd9Sstevel@tonic-gate 	return (strp);
22177c478bd9Sstevel@tonic-gate }
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate /*
22207c478bd9Sstevel@tonic-gate  * Given a pointer into a time zone string, extract an offset, in
22217c478bd9Sstevel@tonic-gate  * [+-]hh[:mm[:ss]] form, from the string.
22227c478bd9Sstevel@tonic-gate  * If any error occurs, return NULL.
22237c478bd9Sstevel@tonic-gate  * Otherwise, return a pointer to the first character not part of the time.
22247c478bd9Sstevel@tonic-gate  */
22257c478bd9Sstevel@tonic-gate static const char *
getoffset(const char * strp,long * offsetp)22267c478bd9Sstevel@tonic-gate getoffset(const char *strp, long *offsetp)
22277c478bd9Sstevel@tonic-gate {
22287c478bd9Sstevel@tonic-gate 	int	neg = 0;
22297c478bd9Sstevel@tonic-gate 
22307c478bd9Sstevel@tonic-gate 	if (*strp == '-') {
22317c478bd9Sstevel@tonic-gate 		neg = 1;
22327c478bd9Sstevel@tonic-gate 		++strp;
22337c478bd9Sstevel@tonic-gate 	} else if (*strp == '+') {
22347c478bd9Sstevel@tonic-gate 		++strp;
22357c478bd9Sstevel@tonic-gate 	}
22367c478bd9Sstevel@tonic-gate 	strp = getsecs(strp, offsetp);
22377c478bd9Sstevel@tonic-gate 	if (strp == NULL)
22387c478bd9Sstevel@tonic-gate 		return (NULL);		/* illegal time */
22397c478bd9Sstevel@tonic-gate 	if (neg)
22407c478bd9Sstevel@tonic-gate 		*offsetp = -*offsetp;
22417c478bd9Sstevel@tonic-gate 	return (strp);
22427c478bd9Sstevel@tonic-gate }
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate /*
22457c478bd9Sstevel@tonic-gate  * Given a pointer into a time zone string, extract a rule in the form
22467c478bd9Sstevel@tonic-gate  * date[/time].  See POSIX section 8 for the format of "date" and "time".
22477c478bd9Sstevel@tonic-gate  * If a valid rule is not found, return NULL.
22487c478bd9Sstevel@tonic-gate  * Otherwise, return a pointer to the first character not part of the rule.
22497c478bd9Sstevel@tonic-gate  *
22507c478bd9Sstevel@tonic-gate  * If compat_flag is set, support old 1-based day of year values.
22517c478bd9Sstevel@tonic-gate  */
22527c478bd9Sstevel@tonic-gate static const char *
getrule(const char * strp,rule_t * rulep,int compat_flag)22537c478bd9Sstevel@tonic-gate getrule(const char *strp, rule_t *rulep, int compat_flag)
22547c478bd9Sstevel@tonic-gate {
22557c478bd9Sstevel@tonic-gate 	if (compat_flag == 0 && *strp == 'M') {
22567c478bd9Sstevel@tonic-gate 		/*
22577c478bd9Sstevel@tonic-gate 		 * Month, week, day.
22587c478bd9Sstevel@tonic-gate 		 */
22597c478bd9Sstevel@tonic-gate 		rulep->r_type = MON_WEEK_DOW;
22607c478bd9Sstevel@tonic-gate 		++strp;
22617c478bd9Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
22627c478bd9Sstevel@tonic-gate 		if (strp == NULL)
22637c478bd9Sstevel@tonic-gate 			return (NULL);
22647c478bd9Sstevel@tonic-gate 		if (*strp++ != '.')
22657c478bd9Sstevel@tonic-gate 			return (NULL);
22667c478bd9Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_week, 1, 5);
22677c478bd9Sstevel@tonic-gate 		if (strp == NULL)
22687c478bd9Sstevel@tonic-gate 			return (NULL);
22697c478bd9Sstevel@tonic-gate 		if (*strp++ != '.')
22707c478bd9Sstevel@tonic-gate 			return (NULL);
22717c478bd9Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
22727c478bd9Sstevel@tonic-gate 	} else if (compat_flag == 0 && *strp == 'J') {
22737c478bd9Sstevel@tonic-gate 		/*
22747c478bd9Sstevel@tonic-gate 		 * Julian day.
22757c478bd9Sstevel@tonic-gate 		 */
22767c478bd9Sstevel@tonic-gate 		rulep->r_type = JULIAN_DAY;
22777c478bd9Sstevel@tonic-gate 		++strp;
22787c478bd9Sstevel@tonic-gate 		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
22797c478bd9Sstevel@tonic-gate 
22807c478bd9Sstevel@tonic-gate 	} else if (isdigit((unsigned char)*strp)) {
22817c478bd9Sstevel@tonic-gate 		/*
22827c478bd9Sstevel@tonic-gate 		 * Day of year.
22837c478bd9Sstevel@tonic-gate 		 */
22847c478bd9Sstevel@tonic-gate 		rulep->r_type = DAY_OF_YEAR;
22857c478bd9Sstevel@tonic-gate 		if (compat_flag == 0) {
22867c478bd9Sstevel@tonic-gate 			/* zero-based day of year */
22877c478bd9Sstevel@tonic-gate 			strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
22887c478bd9Sstevel@tonic-gate 		} else {
22897c478bd9Sstevel@tonic-gate 			/* one-based day of year */
22907c478bd9Sstevel@tonic-gate 			strp = getnum(strp, &rulep->r_day, 1, DAYSPERLYEAR);
22917c478bd9Sstevel@tonic-gate 			rulep->r_day--;
22927c478bd9Sstevel@tonic-gate 		}
22937c478bd9Sstevel@tonic-gate 	} else {
22947c478bd9Sstevel@tonic-gate 		return (NULL);		/* ZONERULES_INVALID format */
22957c478bd9Sstevel@tonic-gate 	}
22967c478bd9Sstevel@tonic-gate 	if (strp == NULL)
22977c478bd9Sstevel@tonic-gate 		return (NULL);
22987c478bd9Sstevel@tonic-gate 	if (*strp == '/') {
22997c478bd9Sstevel@tonic-gate 		/*
23007c478bd9Sstevel@tonic-gate 		 * Time specified.
23017c478bd9Sstevel@tonic-gate 		 */
23027c478bd9Sstevel@tonic-gate 		++strp;
23037c478bd9Sstevel@tonic-gate 		strp = getsecs(strp, &rulep->r_time);
23047c478bd9Sstevel@tonic-gate 	} else	{
23057c478bd9Sstevel@tonic-gate 		rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
23067c478bd9Sstevel@tonic-gate 	}
23077c478bd9Sstevel@tonic-gate 	return (strp);
23087c478bd9Sstevel@tonic-gate }
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate /*
23117c478bd9Sstevel@tonic-gate  * Returns default value for TZ as specified in /etc/default/init file, if
23127c478bd9Sstevel@tonic-gate  * a default value for TZ is provided there.
23137c478bd9Sstevel@tonic-gate  */
23147c478bd9Sstevel@tonic-gate static char *
get_default_tz(void)23157c478bd9Sstevel@tonic-gate get_default_tz(void)
23167c478bd9Sstevel@tonic-gate {
231706e1a714Sraf 	char	*tz = NULL;
231806e1a714Sraf 	uchar_t	*tzp, *tzq;
231906e1a714Sraf 	int	flags;
2320b9175c69SKenjiro Tsuji 	void	*defp;
23217c478bd9Sstevel@tonic-gate 
2322d1419d5aSNobutomo Nakano 	assert_no_libc_locks_held();
2323d1419d5aSNobutomo Nakano 
2324b9175c69SKenjiro Tsuji 	if ((defp = defopen_r(TIMEZONE)) != NULL) {
2325b9175c69SKenjiro Tsuji 		flags = defcntl_r(DC_GETFLAGS, 0, defp);
232606e1a714Sraf 		TURNON(flags, DC_STRIP_QUOTES);
2327b9175c69SKenjiro Tsuji 		(void) defcntl_r(DC_SETFLAGS, flags, defp);
23287c478bd9Sstevel@tonic-gate 
2329b9175c69SKenjiro Tsuji 		if ((tzp = (uchar_t *)defread_r(TZSTRING, defp)) != NULL) {
23307c478bd9Sstevel@tonic-gate 			while (isspace(*tzp))
23317c478bd9Sstevel@tonic-gate 				tzp++;
23327c478bd9Sstevel@tonic-gate 			tzq = tzp;
233306e1a714Sraf 			while (!isspace(*tzq) &&
23347c478bd9Sstevel@tonic-gate 			    *tzq != ';' &&
23357c478bd9Sstevel@tonic-gate 			    *tzq != '#' &&
233606e1a714Sraf 			    *tzq != '\0')
23377c478bd9Sstevel@tonic-gate 				tzq++;
23387c478bd9Sstevel@tonic-gate 			*tzq = '\0';
233906e1a714Sraf 			if (*tzp != '\0')
2340d1419d5aSNobutomo Nakano 				tz = libc_strdup((char *)tzp);
23417c478bd9Sstevel@tonic-gate 		}
234206e1a714Sraf 
2343b9175c69SKenjiro Tsuji 		defclose_r(defp);
23447c478bd9Sstevel@tonic-gate 	}
23457c478bd9Sstevel@tonic-gate 	return (tz);
23467c478bd9Sstevel@tonic-gate }
23477c478bd9Sstevel@tonic-gate 
2348d1419d5aSNobutomo Nakano /*
2349d1419d5aSNobutomo Nakano  * Purge all cache'd state_t
2350d1419d5aSNobutomo Nakano  */
2351d1419d5aSNobutomo Nakano static void
purge_zone_cache(void)2352d1419d5aSNobutomo Nakano purge_zone_cache(void)
23537c478bd9Sstevel@tonic-gate {
23547c478bd9Sstevel@tonic-gate 	int	hashid;
2355d1419d5aSNobutomo Nakano 	state_t	*p, *n, *r;
23567c478bd9Sstevel@tonic-gate 
2357d1419d5aSNobutomo Nakano 	/*
2358d1419d5aSNobutomo Nakano 	 * Create a single list of caches which are detached
2359d1419d5aSNobutomo Nakano 	 * from hash table.
2360d1419d5aSNobutomo Nakano 	 */
2361d1419d5aSNobutomo Nakano 	r = NULL;
2362d1419d5aSNobutomo Nakano 	for (hashid = 0; hashid < HASHTABLE; hashid++) {
2363d1419d5aSNobutomo Nakano 		for (p = tzcache[hashid]; p != NULL; p = n) {
2364d1419d5aSNobutomo Nakano 			n = p->next;
2365d1419d5aSNobutomo Nakano 			p->next = r;
2366d1419d5aSNobutomo Nakano 			r = p;
23677c478bd9Sstevel@tonic-gate 		}
2368d1419d5aSNobutomo Nakano 		tzcache[hashid] = NULL;
23697c478bd9Sstevel@tonic-gate 	}
2370d1419d5aSNobutomo Nakano 	namecache = NULL;
23717c478bd9Sstevel@tonic-gate 
2372d1419d5aSNobutomo Nakano 	/* last_tzname[] may point cache being freed */
2373d1419d5aSNobutomo Nakano 	last_tzname[0] = NULL;
2374d1419d5aSNobutomo Nakano 	last_tzname[1] = NULL;
2375d1419d5aSNobutomo Nakano 
2376d1419d5aSNobutomo Nakano 	/* We'll reload system TZ as well */
2377d1419d5aSNobutomo Nakano 	systemTZ = NULL;
2378d1419d5aSNobutomo Nakano 
2379d1419d5aSNobutomo Nakano 	/*
2380d1419d5aSNobutomo Nakano 	 * Hash table has been cleared, and all elements are detached from
2381d1419d5aSNobutomo Nakano 	 * the hash table. Now we are safe to release _time_lock.
2382d1419d5aSNobutomo Nakano 	 * We need to unlock _time_lock because we need to call out to
2383d1419d5aSNobutomo Nakano 	 * free().
2384d1419d5aSNobutomo Nakano 	 */
2385d1419d5aSNobutomo Nakano 	lmutex_unlock(&_time_lock);
2386d1419d5aSNobutomo Nakano 
2387d1419d5aSNobutomo Nakano 	assert_no_libc_locks_held();
2388d1419d5aSNobutomo Nakano 
2389d1419d5aSNobutomo Nakano 	while (r != NULL) {
2390d1419d5aSNobutomo Nakano 		n = r->next;
2391d1419d5aSNobutomo Nakano 		libc_free((char *)r->zonename);
2392d1419d5aSNobutomo Nakano 		libc_free((char *)r->chars);
2393d1419d5aSNobutomo Nakano 		free(r);
2394d1419d5aSNobutomo Nakano 		r = n;
2395d1419d5aSNobutomo Nakano 	}
2396d1419d5aSNobutomo Nakano 
2397d1419d5aSNobutomo Nakano 	lmutex_lock(&_time_lock);
2398d1419d5aSNobutomo Nakano }
2399d1419d5aSNobutomo Nakano 
2400d1419d5aSNobutomo Nakano /*
2401d1419d5aSNobutomo Nakano  * When called first time, open the counter device and load
2402d1419d5aSNobutomo Nakano  * the initial value. If counter is updated, copy value to
2403d1419d5aSNobutomo Nakano  * private memory.
2404d1419d5aSNobutomo Nakano  */
2405d1419d5aSNobutomo Nakano static void
reload_counter(void)2406d1419d5aSNobutomo Nakano reload_counter(void)
2407d1419d5aSNobutomo Nakano {
2408d1419d5aSNobutomo Nakano 	int	fd;
2409d1419d5aSNobutomo Nakano 	caddr_t	addr;
2410d1419d5aSNobutomo Nakano 
2411d1419d5aSNobutomo Nakano 	if (zoneinfo_seqadr != &zoneinfo_seqno_init) {
2412d1419d5aSNobutomo Nakano 		zoneinfo_seqno = *zoneinfo_seqadr;
2413d1419d5aSNobutomo Nakano 		return;
2414d1419d5aSNobutomo Nakano 	}
2415d1419d5aSNobutomo Nakano 
2416d1419d5aSNobutomo Nakano 	if ((fd = open(TZSYNC_FILE, O_RDONLY)) < 0)
2417d1419d5aSNobutomo Nakano 		return;
2418d1419d5aSNobutomo Nakano 
2419d1419d5aSNobutomo Nakano 	addr = mmap(0, sizeof (uint32_t), PROT_READ, MAP_SHARED, fd, 0);
2420d1419d5aSNobutomo Nakano 	(void) close(fd);
2421d1419d5aSNobutomo Nakano 
2422d1419d5aSNobutomo Nakano 	if (addr == MAP_FAILED)
2423d1419d5aSNobutomo Nakano 		return;
2424d1419d5aSNobutomo Nakano 	/*LINTED*/
2425d1419d5aSNobutomo Nakano 	zoneinfo_seqadr = (uint32_t *)addr;
2426d1419d5aSNobutomo Nakano 	zoneinfo_seqno = *zoneinfo_seqadr;
24277c478bd9Sstevel@tonic-gate }
24287c478bd9Sstevel@tonic-gate 
24297c478bd9Sstevel@tonic-gate /*
24307c478bd9Sstevel@tonic-gate  * getsystemTZ() returns the TZ value if it is set in the environment, or
2431d1419d5aSNobutomo Nakano  * it returns the system TZ;  if the systemTZ has not yet been set, or
2432d1419d5aSNobutomo Nakano  * cleared by tzreload, get_default_tz() is called to read the
2433d1419d5aSNobutomo Nakano  * /etc/default/init file to get the value.
24347c478bd9Sstevel@tonic-gate  */
2435d1419d5aSNobutomo Nakano static const char *
getsystemTZ()2436d1419d5aSNobutomo Nakano getsystemTZ()
24377c478bd9Sstevel@tonic-gate {
2438d1419d5aSNobutomo Nakano 	tznmlist_t *tzn;
24397c478bd9Sstevel@tonic-gate 	char	*tz;
24407c478bd9Sstevel@tonic-gate 
24417c478bd9Sstevel@tonic-gate 	tz = getenv("TZ");
2442d1419d5aSNobutomo Nakano 	if (tz != NULL && *tz != '\0')
2443d1419d5aSNobutomo Nakano 		return ((const char *)tz);
24447c478bd9Sstevel@tonic-gate 
2445d1419d5aSNobutomo Nakano 	if (systemTZ != NULL)
2446d1419d5aSNobutomo Nakano 		return (systemTZ);
24477c478bd9Sstevel@tonic-gate 
2448d1419d5aSNobutomo Nakano 	/*
2449d1419d5aSNobutomo Nakano 	 * get_default_tz calls out stdio functions via defread.
2450d1419d5aSNobutomo Nakano 	 */
2451d1419d5aSNobutomo Nakano 	lmutex_unlock(&_time_lock);
24527c478bd9Sstevel@tonic-gate 	tz = get_default_tz();
24537c478bd9Sstevel@tonic-gate 	lmutex_lock(&_time_lock);
2454d1419d5aSNobutomo Nakano 
2455d1419d5aSNobutomo Nakano 	if (tz == NULL) {
2456d1419d5aSNobutomo Nakano 		/* no TZ entry in the file */
2457d1419d5aSNobutomo Nakano 		systemTZ = _posix_gmt0;
2458d1419d5aSNobutomo Nakano 		return (systemTZ);
24597c478bd9Sstevel@tonic-gate 	}
24607c478bd9Sstevel@tonic-gate 
24617c478bd9Sstevel@tonic-gate 	/*
2462d1419d5aSNobutomo Nakano 	 * look up timezone used previously. We will not free the
2463d1419d5aSNobutomo Nakano 	 * old timezone name, because ltzset_u() can release _time_lock
2464d1419d5aSNobutomo Nakano 	 * while it has references to systemTZ (via zonename). If we
2465d1419d5aSNobutomo Nakano 	 * free the systemTZ, the reference via zonename can access
2466d1419d5aSNobutomo Nakano 	 * invalid memory when systemTZ is reset.
24677c478bd9Sstevel@tonic-gate 	 */
2468d1419d5aSNobutomo Nakano 	for (tzn = systemTZrec; tzn != NULL; tzn = tzn->link) {
2469d1419d5aSNobutomo Nakano 		if (strcmp(tz, tzn->name) == 0)
2470d1419d5aSNobutomo Nakano 			break;
2471d1419d5aSNobutomo Nakano 	}
2472d1419d5aSNobutomo Nakano 	if (tzn == NULL) {
2473d1419d5aSNobutomo Nakano 		/* This is new timezone name */
2474d1419d5aSNobutomo Nakano 		tzn = lmalloc(sizeof (tznmlist_t *) + strlen(tz) + 1);
2475d1419d5aSNobutomo Nakano 		(void) strcpy(tzn->name, tz);
2476d1419d5aSNobutomo Nakano 		tzn->link = systemTZrec;
2477d1419d5aSNobutomo Nakano 		systemTZrec = tzn;
2478d1419d5aSNobutomo Nakano 	}
2479d1419d5aSNobutomo Nakano 
2480d1419d5aSNobutomo Nakano 	libc_free(tz);
2481d1419d5aSNobutomo Nakano 
2482d1419d5aSNobutomo Nakano 	return (systemTZ = tzn->name);
2483d1419d5aSNobutomo Nakano }
2484d1419d5aSNobutomo Nakano 
24857c478bd9Sstevel@tonic-gate /*
2486d1419d5aSNobutomo Nakano  * tzname[] is the user visible string which applications may have
2487d1419d5aSNobutomo Nakano  * references. Even though TZ was changed, references to the old tzname
2488d1419d5aSNobutomo Nakano  * may continue to remain in the application, and those references need
2489d1419d5aSNobutomo Nakano  * to be valid. They were valid by our implementation because strings being
2490d1419d5aSNobutomo Nakano  * pointed by tzname were never be freed nor altered by the change of TZ.
2491d1419d5aSNobutomo Nakano  * However, this will no longer be the case.
2492d1419d5aSNobutomo Nakano  *
2493d1419d5aSNobutomo Nakano  * state_t is now freed when cache is purged. Therefore, reading string
2494d1419d5aSNobutomo Nakano  * from old tzname[] addr may end up with accessing a stale data(freed area).
2495d1419d5aSNobutomo Nakano  * To avoid this, we maintain a copy of all timezone name strings which will
2496d1419d5aSNobutomo Nakano  * never be freed, and tzname[] will point those copies.
2497d1419d5aSNobutomo Nakano  *
24987c478bd9Sstevel@tonic-gate  */
2499d1419d5aSNobutomo Nakano static int
set_one_tzname(const char * name,int idx)2500d1419d5aSNobutomo Nakano set_one_tzname(const char *name, int idx)
2501d1419d5aSNobutomo Nakano {
2502d1419d5aSNobutomo Nakano 	const unsigned char *nm;
2503d1419d5aSNobutomo Nakano 	int	hashid, i;
2504d1419d5aSNobutomo Nakano 	char	*s;
2505d1419d5aSNobutomo Nakano 	tznmlist_t *tzn;
2506d1419d5aSNobutomo Nakano 
2507d1419d5aSNobutomo Nakano 	if (name == _tz_gmt || name == _tz_spaces) {
2508d1419d5aSNobutomo Nakano 		tzname[idx] = (char *)name;
2509d1419d5aSNobutomo Nakano 		return (0);
2510d1419d5aSNobutomo Nakano 	}
2511d1419d5aSNobutomo Nakano 
2512d1419d5aSNobutomo Nakano 	nm = (const unsigned char *)name;
2513d1419d5aSNobutomo Nakano 	hashid = (nm[0] * 29 + nm[1] * 3) % TZNMC_SZ;
2514d1419d5aSNobutomo Nakano 	for (tzn = tznmhash[hashid]; tzn != NULL; tzn = tzn->link) {
2515d1419d5aSNobutomo Nakano 		s = tzn->name;
2516d1419d5aSNobutomo Nakano 		/* do the strcmp() */
2517d1419d5aSNobutomo Nakano 		for (i = 0; s[i] == name[i]; i++) {
2518d1419d5aSNobutomo Nakano 			if (s[i] == '\0') {
2519d1419d5aSNobutomo Nakano 				tzname[idx] = tzn->name;
2520d1419d5aSNobutomo Nakano 				return (0);
2521d1419d5aSNobutomo Nakano 			}
2522d1419d5aSNobutomo Nakano 		}
2523d1419d5aSNobutomo Nakano 	}
2524d1419d5aSNobutomo Nakano 	/*
2525d1419d5aSNobutomo Nakano 	 * allocate new entry. This entry is never freed, so use lmalloc
2526d1419d5aSNobutomo Nakano 	 */
2527d1419d5aSNobutomo Nakano 	tzn = lmalloc(sizeof (tznmlist_t *) + strlen(name) + 1);
2528d1419d5aSNobutomo Nakano 	if (tzn == NULL)
2529d1419d5aSNobutomo Nakano 		return (1);
2530d1419d5aSNobutomo Nakano 
2531d1419d5aSNobutomo Nakano 	(void) strcpy(tzn->name, name);
2532d1419d5aSNobutomo Nakano 
2533d1419d5aSNobutomo Nakano 	/* link it */
2534d1419d5aSNobutomo Nakano 	tzn->link = tznmhash[hashid];
2535d1419d5aSNobutomo Nakano 	tznmhash[hashid] = tzn;
2536d1419d5aSNobutomo Nakano 
2537d1419d5aSNobutomo Nakano 	tzname[idx] = tzn->name;
2538d1419d5aSNobutomo Nakano 	return (0);
2539d1419d5aSNobutomo Nakano }
2540d1419d5aSNobutomo Nakano 
2541d1419d5aSNobutomo Nakano /*
2542d1419d5aSNobutomo Nakano  * Set tzname[] after testing parameter to see if we are setting
2543d1419d5aSNobutomo Nakano  * same zone name. If we got same address, it should be same zone
2544d1419d5aSNobutomo Nakano  * name as tzname[], unless cache have been purged.
2545d1419d5aSNobutomo Nakano  * Note, purge_zone_cache() resets last_tzname[].
2546d1419d5aSNobutomo Nakano  */
2547d1419d5aSNobutomo Nakano static void
set_tzname(const char ** namep)2548d1419d5aSNobutomo Nakano set_tzname(const char **namep)
2549d1419d5aSNobutomo Nakano {
2550d1419d5aSNobutomo Nakano 	if (namep[0] != last_tzname[0]) {
2551d1419d5aSNobutomo Nakano 		if (set_one_tzname(namep[0], 0)) {
2552d1419d5aSNobutomo Nakano 			tzname[0] = (char *)_tz_gmt;
2553d1419d5aSNobutomo Nakano 			last_tzname[0] = NULL;
25547c478bd9Sstevel@tonic-gate 		} else {
2555d1419d5aSNobutomo Nakano 			last_tzname[0] = namep[0];
25567c478bd9Sstevel@tonic-gate 		}
2557d1419d5aSNobutomo Nakano 	}
2558d1419d5aSNobutomo Nakano 
2559d1419d5aSNobutomo Nakano 	if (namep[1] != last_tzname[1]) {
2560d1419d5aSNobutomo Nakano 		if (set_one_tzname(namep[1], 1)) {
2561d1419d5aSNobutomo Nakano 			tzname[1] = (char *)_tz_spaces;
2562d1419d5aSNobutomo Nakano 			last_tzname[1] = NULL;
2563d1419d5aSNobutomo Nakano 		} else {
2564d1419d5aSNobutomo Nakano 			last_tzname[1] = namep[1];
2565d1419d5aSNobutomo Nakano 		}
2566d1419d5aSNobutomo Nakano 	}
25677c478bd9Sstevel@tonic-gate }
2568