/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/*
* A part of this file comes from public domain source, so
* clarified as of June 5, 1996 by Arthur David Olson
* (arthur_david_olson@nih.gov).
*/
/*
*
* This file contains routines to convert struct tm to time_t and
* back as well as adjust time values based on their timezone, which
* is a local offset from GMT (Greenwich Mean Time).
*
* Many timezones actually consist of more than one offset from GMT.
* The GMT offset that is considered the normal offset is referred
* to as standard time. The other offset is referred to as alternate
* time, but is better known as daylight savings time or summer time.
*
* The current timezone for an application is derived from the TZ
* environment variable either as defined in the environment or in
* TZ variable can either be:
* :<characters>
* or
* <std><offset1>[<dst>[<offset2>]][,<start>[/<time>],<end>[/<time>]
*
* <characters> is an implementation-defined string that somehow describes
* a timezone. The implementation-defined description of a timezone used
* in Solaris is based on the public domain zoneinfo code available from
* elsie.nci.nih.gov and a timezone that is specified in this way is
*
* The precise definition of the second format can be found in POSIX,
* but, basically, <std> is the abbreviation for the timezone in standard
* (not daylight savings time), <offset1> is the standard offset from GMT,
* <dst> is the abbreviation for the timezone in daylight savings time and
* <offset2> is the daylight savings time offset from GMT. The remainder
* specifies when daylight savings time begins and ends. A timezone
* specified in this way is referred to as a POSIX timezone. An example
* of this is "PST7PDT".
*
* In Solaris, there is an extension to this. If the timezone is not
* preceded by a ":" and it does not parse as a POSIX timezone, then it
* will be treated as a zoneinfo timezone. Much usage of zoneinfo
* timezones in Solaris is done without the leading ":".
*
* A zoneinfo timezone is a reference to a file that contains a set of
* rules that describe the timezone. In Solaris, the file is in
* on zoneinfo rules "source" files. This is all described on the zic(1M)
* man page.
*/
/*
* Functions that are common to ctime(3C) and cftime(3C)
*/
#include "lint.h"
#include "libc.h"
#include "tsd.h"
#include <stdarg.h>
#include <mtlib.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <tzfile.h>
#include <thread.h>
#include <synch.h>
#include <fcntl.h>
#include <errno.h>
#include <deflt.h>
#include <sys/byteorder.h>
#include <paths.h>
/* JAN_01_1902 cast to (int) - negative number of seconds from 1970 */
/* Days since 1/1/70 to 12/31/(1900 + Y - 1) */
((1900L + (X)) * 365L + (1900L + (X)) / 4L - \
(1900L + (X)) / 100L + ((1900L + (X)) - 1600L) / 400L)
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#endif
#ifdef TIME64_TRANS
#else
#endif
extern mutex_t _time_lock;
extern const int __lyday_to_month[];
extern const int __yday_to_month[];
extern const int __year_lengths[2];
} ttinfo_t;
} lsinfo_t;
} prev_t;
typedef enum {
typedef struct {
} rule_t;
typedef struct {
/*
* Note: ZONERULES_INVALID used for global curr_zonerules variable, but not
* for zonerules field of state_t.
*/
typedef enum {
} zone_rules_t;
/*
* The following members are allocated from the libc-internal malloc:
*
* zonename
* chars
*/
typedef struct state {
} state_t;
typedef struct tznmlist {
} tznmlist_t;
static const char *systemTZ;
static const char *namecache;
enum wks {
};
enum dwk {
};
enum mth {
};
/*
* The following table defines standard USA DST transitions
* as they have been declared throughout history, disregarding
* the legally sanctioned local variants.
*
* Note: At some point, this table may be supplanted by
* more popular 'posixrules' logic.
*/
typedef struct {
int s_year;
int e_year;
{
2007, INT_MAX,
},
{
1987, 2006,
},
{
1976, 1986,
},
{
1975, 1975,
},
{
1974, 1974,
},
/*
* The entry below combines two previously separate entries for
* 1969-1973 and 1902-1968
*/
{
1902, 1973,
}
};
/*
* Prototypes for static functions.
*/
static const char *getsystemTZ(void);
static const char *getzname(const char *, int);
static const char *getnum(const char *, int *, int, int);
static const char *getsecs(const char *, long *);
static const char *getoffset(const char *, long *);
static int load_posixinfo(const char *, state_t *);
static int check_zoneinfo(state_t *);
static int load_zoneinfo(const char *, state_t *);
static void adjust_posix_default(state_t *, long, long);
static void set_zone_context(timeINT_t);
static void invalidate_cache(void);
static void reload_counter(void);
static void set_tzname(const char **);
static int32_t decode32_slow(const char *);
static int64_t decode64_slow(const char *);
#ifdef _TZ_DEBUG
static void print_state(state_t *);
#endif
/*
* definition of difftime
*
* This code assumes time_t is type long. Note the difference of two
* longs in absolute value is representable as an unsigned long. So,
* compute the absolute value of the difference, cast the result to
* double and attach the sign back on.
*
* Note this code assumes 2's complement arithmetic. The subtraction
* operation may overflow when using signed operands, but when the
* result is cast to unsigned long, it yields the desired value
* (ie, the absolute value of the difference). The cast to unsigned
* long is done using pointers to avoid undefined behavior if casting
* a negative value to unsigned.
*/
double
{
return (-(double)*(unsigned long *) &time0);
} else {
return ((double)*(unsigned long *) &time1);
}
}
/*
* Accepts a time_t, returns a tm struct based on it, with
* no local timezone adjustment.
*
* This routine is the thread-safe variant of gmtime(), and
* requires that the call provide the address of their own tm
* struct.
*
* Locking is not done here because set_zone_context()
* is not called, thus timezone, altzone, and tzname[] are not
* accessed, no memory is allocated, and no common dynamic
* data is accessed.
*
* See ctime(3C)
*/
struct tm *
{
}
/*
* Accepts a time_t, returns a tm struct based on it, with
* no local timezone adjustment.
*
* This function is explicitly NOT THREAD-SAFE. The standards
* indicate it should provide its results in its own statically
* allocated tm struct that gets overwritten. The thread-safe
* variant is gmtime_r(). We make it mostly thread-safe by
* allocating its buffer in thread-specific data.
*
* See ctime(3C)
*/
struct tm *
{
}
#ifdef TIME64_TRANS
struct tm *
{
}
#endif
/*
* This is the hashing function, based on the input timezone name.
*/
static int
{
unsigned char c;
unsigned int h;
h = *id++;
while ((c = *id++) != '\0')
h += c;
return ((int)(h % HASHTABLE));
}
/*
* find_zone() gets the hashid for zonename, then uses the hashid
* to search the hash table for the appropriate timezone entry. If
* the entry for zonename is found in the hash table, return a pointer
* to the entry.
*/
static state_t *
{
int hashid;
while (cur) {
int res;
if (res == 0) {
return (cur);
} else if (res > 0) {
break;
}
}
return (NULL);
}
/*
* Register new state in the cache.
*/
static void
{
if (res == 0) {
/* shouldn't occur */
return;
} else if (res > 0) {
break;
}
}
} else {
}
}
/*
* Remove one state in the cache.
*/
static void
{
int hashid;
break;
}
/* shouldn't occur */
return;
}
else
/* last_tzname[] may point cache being freed */
last_tzname[0] = NULL;
}
/*
* Returns tm struct based on input time_t argument, correcting
* for the local timezone, producing documented side-effects
* to extern global state, timezone, altzone, daylight and tzname[].
*
* localtime_r() is the thread-safe variant of localtime().
*
* IMPLEMENTATION NOTE:
*
* Locking slows multithreaded access and is probably ultimately
* unnecessary here. The POSIX specification is a bit vague
* as to whether the extern variables set by tzset() need to
* set as a result of a call to localtime_r()
*
* Currently, the spec only mentions that tzname[] doesn't
* need to be set. As soon as it becomes unequivocal
* that the external zone state doesn't need to be asserted
* for this call, and it really doesn't make much sense
* to set common state from multi-threaded calls made to this
* function, locking can be dispensed with here.
*
* local zone state would still need to be aquired for the
* time in question in order for calculations elicited here
* to be correct, but that state wouldn't need to be shared,
* thus no multi-threaded synchronization would be required.
*
* It would be nice if POSIX would approve an ltzset_r()
* function, but if not, it wouldn't stop us from making one
* privately.
*
* localtime_r() can now return NULL if overflow is detected.
* offtime_u() is the function that detects overflow, and sets
* errno appropriately. We unlock before the call to offtime_u(),
* so that lmutex_unlock() does not reassign errno. The function
* offtime_u() is MT-safe and does not have to be locked. Use
* my_is_in_dst to reference local copy of is_in_dst outside locks.
*
* See ctime(3C)
*/
static struct tm *
{
long offset;
void *unused;
int my_is_in_dst;
}
return (rt);
}
struct tm *
{
}
/*
* Accepts a time_t, returns a tm struct based on it, correcting
* for the local timezone. Produces documented side-effects to
* extern global timezone state data.
*
* This function is explicitly NOT THREAD-SAFE. The standards
* indicate it should provide its results in its own statically
* allocated tm struct that gets overwritten. The thread-safe
* variant is localtime_r(). We make it mostly thread-safe by
* allocating its buffer in thread-specific data.
*
* localtime() can now return NULL if overflow is detected.
* offtime_u() is the function that detects overflow, and sets
* errno appropriately.
*
* See ctime(3C)
*/
struct tm *
{
}
#ifdef TIME64_TRANS
/*
* 64bit time_t version of localtime: Consolidation private
*/
struct tm *
{
}
#endif
/*
* This function takes a pointer to a tm struct and returns a
* normalized time_t, also inducing documented side-effects in
* extern global zone state variables. (See mktime(3C)).
*/
{
int64_t t; /* must hold more than 32-bit time_t */
int temp;
int mketimerrno;
int overflow;
void *unused;
mketimerrno = errno;
/* mktime leaves errno unchanged if no error is encountered */
/* Calculate time_t from tm arg. tm may need to be normalized. */
}
}
/* Avoid numerous calculations embedded in macro if possible */
/* For boundry values of tm_year, typecasting required */
}
t += cached_secs_since_1970;
else
/* Attempt to convert time to GMT based on tm_isdst setting */
#ifdef _ILP32
#else
#endif
set_zone_context(t);
switch (curr_zonerules) {
case ZONEINFO:
if (is_in_dst) {
t -= dst_delta;
set_zone_context(t);
if (is_in_dst) {
} else {
}
} else {
}
break;
case POSIX_USA:
case POSIX:
if (is_in_dst) {
t -= dst_delta;
set_zone_context(t);
if (is_in_dst) {
} else {
}
} else { /* check for ambiguous 'fallback' transition */
set_zone_context(t - dst_delta);
if (is_in_dst) { /* In fallback, force DST */
t -= dst_delta;
} else {
}
}
break;
case ZONERULES_INVALID:
break;
}
} else if (is_in_dst) {
} else {
}
t = -1;
} else {
}
errno = mketimerrno;
return ((time_t)t);
}
/*
* Sets extern global zone state variables based on the current
* time. Specifically, tzname[], timezone, altzone, and daylight
* are updated. See ctime(3C) manpage.
*/
void
tzset(void)
{
void *unused;
}
void
{
void *unused;
}
/*
* Perform initial setup of state_t
*/
static int
{
return (-1);
return (-1);
}
return (0);
}
static void
{
/* last_tzname[] may point cache being freed */
last_tzname[0] = NULL;
}
/*
* Loads local zone information if TZ changed since last time zone
* information was loaded, or if this is the first time thru.
* We already hold _time_lock; no further locking is required.
* Return a memory block which can be free'd at safe place.
*/
static void *
{
const char *zonename;
int vers;
if (RELOAD_INFO()) {
}
#ifdef _ILP32
#else
#endif
#ifdef TIME64_TRANS
#else
#endif
{
set_zone_context(t);
return (NULL);
}
/*
* zoneinfo file has been updated.
*/
} else {
}
}
/*
* We need to release _time_lock to call out malloc().
* We can release _time_lock as far as global variables
* can remain consistent. Here, we haven't touch any
* variables, so it's okay to release lock.
*/
/*
* check it again, since zone may have been loaded while
* time_lock was unlocked.
*/
}
#ifdef TIME64_TRANS
/*
* We have zoeinfo data, but it's not suitable for date
* beyond 2038. So we'll load V2.
*/
/*
* If someone has allocated the entry while
* lock was released and if it's still lower
* version, discard it and use new one.
*/
} else {
}
}
}
}
#else
#endif
/*
* We are here because the 1st attemp failed.
* new_entry points newly allocated entry. If it was NULL, it
* indicates that the memory allocation also failed.
*/
/*
* 2nd attemp also failed.
* No timezone entry found in hash table, so load it,
* and create a new timezone entry.
*/
daylight = 0;
/*
* something wrong happened.
* Invalidate the current timezone
*/
is_in_dst = 0;
return (entry);
}
/*
* Builds transition cache and sets up zone state data for zone
* specified in TZ, which can be specified as a POSIX zone or an
* Olson zoneinfo file reference.
*
* If local data cannot be parsed or loaded, the local zone
* tables are set up for GMT.
*
* Unless a leading ':' is prepended to TZ, TZ is initially
* parsed as a POSIX zone; failing that, it reverts to
* a zoneinfo check.
* However, if a ':' is prepended, the zone will *only* be
* parsed as zoneinfo. If any failure occurs parsing or
* loading a zoneinfo TZ, GMT data is loaded for the local zone.
*
* Example: There is a zoneinfo file in the standard
* distribution called 'PST8PDT'. The only way the user can
* specify that file under Solaris is to set TZ to ":PST8PDT".
* Otherwise the initial parse of PST8PDT as a POSIX zone will
* succeed and be used.
*/
if (*zonename == ':') {
}
}
}
}
set_zone_context(t);
/*
* We shouldn't release lock beyond this point since lclzonep
* can refer to invalid address if cache is invalidated.
* We defer the call to free till it can be done safely.
*/
return (unused_entry);
}
/*
* Sets timezone, altzone, tzname[], extern globals, to represent
* disposition of t with respect to TZ; See ctime(3C). is_in_dst,
* internal global is also set. daylight is set at zone load time.
*
* Issues:
*
* In this function, any time_t not located in the cache is handled
* must be called prior to this routine.
*
* If POSIX zone, cache miss penalty is slightly degraded
* performance. For zoneinfo, penalty is decreased is_in_dst
* accuracy.
*
* until time known, and not knowing time until DST known, at
* least uses the same algorithm for 64-bit time as 32-bit.
*
* The fact that zoneinfo files only contain transistions for 32-bit
* time space is a well known problem, as yet unresolved.
* Without an official standard for coping with out-of-range
* zoneinfo times, assumptions must be made. For now
* the assumption is: If t exceeds 32-bit boundries and local zone
* is zoneinfo type, is_in_dst is set to to 0 for negative values
* of t, and set to the same DST state as the highest ordered
* transition in cache for positive values of t.
*/
static void
set_zone_default_context(void)
{
/* Retrieve suitable defaults for this zone */
is_in_dst = 0;
}
static void
{
/* If state data not loaded or TZ busted, just use GMT */
return;
}
/* Loaded zone incapable of transitioning. */
return;
}
/*
* At least one alt. zone and one transistion exist. Locate
* state for 't' quickly as possible. Use defaults as necessary.
*/
lo = 0;
/*
* Date which is out of definition.
* Calculate DST as best as possible
*/
/* Must invoke calculations to determine DST */
posix_check_dst(t, lclzonep) : 0;
return;
/* t precedes 1st transition. Use defaults */
return;
/*
* If we would use same POSIX rule beyond the last
* transition, it's a go. Otherwise, use the final
* transition.
*/
return;
}
} else {
/* t follows final transistion. Use final */
}
} else {
/* CACHE HIT. Nothing needs to be done */
} else {
/*
* CACHE MISS. Locate transition using binary search.
*/
break;
else
}
}
}
out:
/*
* Set extern globals based on located transition and summary of
* its previous state, which were cached when zone was loaded
*/
} else {
}
} else { /* alt. time */
} else {
}
}
}
/*
* This function takes a time_t and gmt offset and produces a
* tm struct based on specified time.
*
* The the following fields are calculated, based entirely
* on the offset-adjusted value of t:
*
* tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec
* tm_yday. tm_wday. (tm_isdst is ALWAYS set to 0).
*/
static struct tm *
{
long days;
long rem;
long y;
int yleap;
const int *ip;
days = t / SECSPERDAY;
rem = t % SECSPERDAY;
while (rem < 0) {
rem += SECSPERDAY;
--days;
}
while (rem >= SECSPERDAY) {
rem -= SECSPERDAY;
++days;
}
y = EPOCH_YEAR;
long newy;
if (days < 0)
--newy;
LEAPS_THRU_END_OF(y > 0 ? y - 1L : y);
y = newy;
}
}
#ifdef _LP64
/* do as much as possible before checking for error. */
if ((y > (long)INT_MAX + TM_YEAR_BASE) ||
(y < (long)INT_MIN + TM_YEAR_BASE)) {
return (NULL);
}
#endif
return (tmptr);
}
/*
* Check whether DST is set for time in question. Only applies to
* POSIX timezones. If explicit POSIX transition rules were provided
* for the current zone, use those, otherwise use default USA POSIX
* transitions.
*/
static int
{
/*
* If transition rules were provided for this zone,
* use them, otherwise, default to USA daylight rules,
* which are historically correct for the continental USA,
* excluding local provisions. (This logic may be replaced
* at some point in the future with "posixrules" to offer
* more flexibility to the system administrator).
*/
} else { /* POSIX_USA: USA */
i = 0;
i++;
}
}
/*
* Note: t, rtime[0], and rtime[1] are all bounded within 'year'
* beginning on 'jan01'
*/
return (ridx);
} else {
return (idx);
}
}
/*
* Given January 1, 00:00:00 GMT for a year as an Epoch-relative time,
* along with the integer year #, a posix_daylight_t that is composed
* of two rules, and two GMT offsets (timezone and altzone), calculate
* the two Epoch-relative times the two rules take effect, and return
* them in the two rtime fields of the posix_daylight_t structure.
* Also update janfirst by a year, by adding the appropriate number of
* seconds depending on whether the year is a leap year or not. (We take
* advantage that this routine knows the leap year status.)
*/
static int
{
long offset;
int idx;
long leapyear;
};
case MON_WEEK_DOW:
/*
* Mm.n.d - nth "dth day" of month m.
*/
/*
* Use Zeller's Congruence to get day-of-week of first
* day of month.
*/
if (dow < 0)
dow += DAYSPERWEEK;
/*
* Following heuristic increases accuracy of USA rules
* for negative years.
*/
++dow;
/*
* "dow" is the day-of-week of the first day of the
* month. Get the day-of-month, zero-origin, of the
* first "dow" day of the month.
*/
if (d < 0)
d += DAYSPERWEEK;
if (d + DAYSPERWEEK >=
break;
d += DAYSPERWEEK;
}
/*
* "d" is the day-of-month, zero-origin, of the day
* we want.
*/
value += d * SECSPERDAY;
break;
case JULIAN_DAY:
/*
* Jn - Julian day, 1 == Jan 1, 60 == March 1 even
* in leap yrs.
*/
value += SECSPERDAY;
break;
case DAY_OF_YEAR:
/*
* n - day of year.
*/
break;
}
}
}
static int
{
char *fullname;
return (-1);
return (-1);
return (-1);
if (stat_only) {
return (-1);
}
fd = 0;
} else {
return (-1);
}
return (-1);
}
}
return (fd);
}
/*
* Check to see if the zoneinfo database has been updated.
*/
static int
{
return (1);
return (1);
}
return (0);
}
static int
{
int tz_max_times;
/*LINTED align*/
/*LINTED align*/
/*LINTED align*/
/*LINTED align*/
/*LINTED align*/
/*LINTED align*/
return (-1);
}
ttisstdcnt + /* ttisstds */
ttisgmtcnt; /* ttisgmts */
*gmtcntp = ttisgmtcnt;
*stdcntp = ttisstdcnt;
return (dsz);
}
/*
* Try to load zoneinfo file into internal transition tables using name
* indicated in TZ, and do validity checks. The format of zic(1M)
* compiled zoneinfo files isdescribed in tzfile.h
*/
static int
{
int fid;
char *bufp;
return (-1);
return (-1);
}
/*
* It would be nice to use alloca() to allocate bufp but,
* as above, we wish to avoid allocating a big buffer in
* our stack frame, and also because alloca() gives us no
* opportunity to fail gracefully on allocation failure.
*/
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
/* check version */
/* read the first header */
&ttisgmtcnt, &ttisstdcnt)) < 0) {
return (-1);
}
/*
* If we are fine with V1 data, just read it. Otherwise, go ahead
* and read the V2 data.
*/
return (-1);
}
/*LINTED align*/
cp += 4;
}
} else {
&ttisgmtcnt, &ttisstdcnt)) < 0) {
return (-1);
}
return (-1);
}
/*LINTED align*/
cp += 8;
}
}
/*
* Skip over types[] for now and load ttis[] so that when
* types[] are loaded we can check for transitions to STD & DST.
* This allows us to shave cycles in ltzset_u(), including
* eliminating the need to check set 'daylight' later.
*/
/*LINTED align*/
cp2 += 4;
return (-1);
}
if (ttisp->tt_abbrind < 0 ||
return (-1);
}
}
/*
* Since ttis were loaded ahead of types, it is possible to
* detect whether daylight is ever set for this zone now, and
* also preload other information to avoid repeated lookups later.
* This logic facilitates keeping a running tab on the state of
* std zone and alternate zone transitions such that timezone,
* altzone and tzname[] can be determined quickly via an
* index to any transition.
*
* For transition #0 there are no previous transitions,
* so prev->std and prev->alt will be null, but that's OK,
* indicates none existed prior.
*/
else
return (-1);
}
++prevp;
}
/*
* Set pointer ahead to where it would have been if we
* had read types[] and ttis[] in the same order they
* occurred in the file.
*/
/*LINTED align*/
cp += 4;
} else {
/*LINTED align*/
cp += 8;
}
/*LINTED align*/
cp += 4;
}
if (ttisstdcnt == 0) {
} else {
return (-1);
}
}
}
if (ttisgmtcnt == 0) {
} else {
return (-1);
}
}
}
/*
* Handle V2 specific POSIX style transition rule
*/
cp++;
char *np;
fini_state(ts);
return (-1);
}
}
/* adjust index */
/* copy new chars */
/* append types */
/* find where we start from */
i = 0;
i++;
/* append trans */
else
i++;
prevp++;
}
}
fini_state(ts);
}
}
if (most_recent_alt == NULL)
else
break;
}
}
}
/*
* Other defaults set at beginning of this routine
* to cover case where zoneinfo file cannot be loaded
*/
sp->default_altzone = 0;
return (0);
}
static int
{
if (a == b)
return (TRUE);
return (TRUE);
}
return (FALSE);
}
#ifdef _TZ_DEBUG
static void
{
int i, c;
if (c != 0)
if (c == 2) {
c = 0;
} else {
c++;
}
}
if (c != 0) {
}
if (c == 1) {
c = 0;
} else {
c++;
}
}
if (c == 0) {
} else {
}
if (c == 7) {
c = 0;
} else {
c++;
}
}
}
}
#endif
/*
* Given a POSIX section 8-style TZ string, fill in transition tables.
*
* Examples:
*
* TZ = PST8 or GMT0
* Timecnt set to 0 and typecnt set to 1, reflecting std time only.
*
* TZ = PST8PDT or PST8PDT7
* Create transition times by applying USA transitions from
* Jan 1 of each year covering 1902-2038. POSIX offsets
* as specified in the TZ are used to calculate the tt_gmtoff
* for each of the two zones. If ommitted, DST defaults to
* std. time minus one hour.
*
* TZ = <PST8>8PDT or <PST8>8<PDT9>
* Quoted transition. The values in angled brackets are treated
* as zone name text, not parsed as offsets. The offsets
* occuring following the zonename section. In this way,
* instead of PST being displayed for standard time, it could
* be displayed as PST8 to give an indication of the offset
* of that zone to GMT.
*
* TZ = GMT0BST, M3.5.0/1, M10.5.0/2 or GMT0BST, J23953, J23989
* Create transition times based on the application new-year
* relative POSIX transitions, parsed from TZ, from Jan 1
* for each year covering 1902-2038. POSIX offsets specified
* in TZ are used to calculate tt_gmtoff for each of the two
* zones.
*
*/
static int
{
const char *stdname;
const char *dstname = 0;
long stdoff = 0;
long dstoff = 0;
char *cp;
int i;
int quoted;
++stdname;
if (*name != '\0') {
return (-1);
if (*name == '>')
++name;
return (-1);
} else {
return (-1);
}
}
/* If DST specified in TZ, extract DST zone details */
if (*name != '\0') {
++dstname;
return (-1);
if (dstlen < 1)
return (-1);
if (*name == '>')
++name;
return (-1);
} else {
}
/* no transtition specified; using default rule */
/* loading TZDEFRULES zoneinfo succeeded */
} else {
/*
* We either failed to load the default
* rule, or default rule doesn't have DST.
*/
/* loading TZDEFRULES zoneinfo failed */
zonetype);
}
} else {
/* extract POSIX transitions from TZ */
/* Backward compatibility using ';' separator */
++name;
== NULL)
return (-1);
if (*name++ != ',')
return (-1);
== NULL)
return (-1);
if (*name != '\0')
return (-1);
}
} else { /* DST wasn't specified in POSIX TZ */
/* Since we only have STD time, there are no transitions */
dstlen = 0;
}
/* Setup zone name character data for state table */
if (dstlen != 0)
/* If bigger than zone name abbv. buffer, grow it */
return (-1);
}
/*
* Copy zone name text null-terminatedly into state table.
* By doing the copy once during zone loading, setting
* tzname[] subsequently merely involves setting pointer
*
* If either or both std. or alt. zone name < 3 chars,
* space pad the deficient name(s) to right.
*/
std->tt_abbrind = 0;
while (stdlen < 3)
i = (int)(stdlen + 1);
if (dstlen != 0) {
dst->tt_abbrind = i;
cp += i;
while (dstlen < 3)
}
/* Save default values */
} else {
}
return (0);
}
/*
* We loaded the TZDEFAULT which usually the one in US zones. We
* offset.
*/
static void
{
long zone_stdoff = 0;
long zone_dstoff = 0;
int isdst;
int i;
/*
* Initial values of zone_stdoff and zone_dstoff
*/
break;
}
}
break;
}
}
/*
* Initially we're assumed to be in standard time.
*/
isdst = 0;
int next_isdst;
if (zone->tt_ttisgmt == 0) {
/*
* If summer time is in effect, and the transition time
* was not specified as standard time, add the summer
* time offset to the transition time;
* otherwise, add the standard time offset to the
* transition time.
*/
/*
* Transitions from DST to DDST will effectively
* disappear since POSIX provides for only one DST
* offset.
*/
else
}
if (next_isdst != 0) {
} else {
}
isdst = next_isdst;
prevp++;
}
/*
* Finally, fill in ttis.
* ttisstd and ttisgmt need not be handled.
*/
}
static void
{
/*
* We know STD and DST zones are specified with this timezone
* therefore the cache will be set up with 2 transitions per
* year transitioning to their respective std and dst zones.
*/
/*
* Insert zone data from POSIX TZ into state table
* The Olson public domain POSIX code sets up ttis[0] to be DST,
* as we are doing here. It seems to be the correct behavior.
*/
/* Create transition data based on POSIX TZ */
/*
* We only cache from 1902 to 2037 to avoid transistions
* that wrap at the 32-bit boundries, since 1901 and 2038
* are not full years in 32-bit time. The rough edges
* will be handled as transition cache misses.
*/
for (i = MAX_RULE_TABLE; i >= 0; i--) {
}
/*
* Two transitions per year. Since there are
* only two zone types for this POSIX zone,
* previous std and alt are always set to
* &ttis[0] and &ttis[1].
*/
++prevp;
++prevp;
/* Version 1 can only handle till 2038 */
if (year == 2038)
return;
}
}
}
}
/*
* Given a pointer into a time zone string, scan until a character that is not
* a valid character in a zone name is found. Return ptr to that character.
* Return NULL if error (ie. non-printable character located in name)
*/
static const char *
{
char c;
if (quoted) {
isgraph((unsigned char)c)) {
++strp;
}
} else {
c != '+') {
++strp;
}
}
/* Found an excessively invalid character. Discredit whole name */
if (c != '\0' && !isgraph((unsigned char)c))
return (NULL);
return (strp);
}
/*
* Given pointer into time zone string, extract first
* number pointed to. Validate number within range specified,
* Return ptr to first char following valid numeric sequence.
*/
static const char *
{
char c;
int num;
return (NULL);
num = 0;
do {
return (NULL); /* illegal value */
c = *++strp;
} while (isdigit((unsigned char)c));
return (NULL); /* illegal value */
return (strp);
}
/*
* Given a pointer into a time zone string, extract a number of seconds,
* in hh[:mm[:ss]] form, from the string. If an error occurs, return NULL,
* otherwise, return a pointer to the first character not part of the number
* of seconds.
*/
static const char *
{
int num;
/*
* `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
* "M10.4.6/26", which does not conform to Posix,
* but which specifies the equivalent of
* ``02:00 on the first Sunday on or after 23 Oct''.
*/
return (NULL);
if (*strp == ':') {
++strp;
return (NULL);
if (*strp == ':') {
++strp;
/* `SECSPERMIN' allows for leap seconds. */
return (NULL);
}
}
return (strp);
}
/*
* Given a pointer into a time zone string, extract an offset, in
* [+-]hh[:mm[:ss]] form, from the string.
* If any error occurs, return NULL.
* Otherwise, return a pointer to the first character not part of the time.
*/
static const char *
{
int neg = 0;
if (*strp == '-') {
neg = 1;
++strp;
} else if (*strp == '+') {
++strp;
}
return (NULL); /* illegal time */
if (neg)
return (strp);
}
/*
* Given a pointer into a time zone string, extract a rule in the form
* date[/time]. See POSIX section 8 for the format of "date" and "time".
* If a valid rule is not found, return NULL.
* Otherwise, return a pointer to the first character not part of the rule.
*
* If compat_flag is set, support old 1-based day of year values.
*/
static const char *
{
/*
* Month, week, day.
*/
++strp;
return (NULL);
if (*strp++ != '.')
return (NULL);
return (NULL);
if (*strp++ != '.')
return (NULL);
/*
* Julian day.
*/
++strp;
/*
* Day of year.
*/
if (compat_flag == 0) {
/* zero-based day of year */
} else {
/* one-based day of year */
}
} else {
return (NULL); /* ZONERULES_INVALID format */
}
return (NULL);
if (*strp == '/') {
/*
* Time specified.
*/
++strp;
} else {
}
return (strp);
}
/*
* a default value for TZ is provided there.
*/
static char *
get_default_tz(void)
{
int flags;
void *defp;
tzp++;
*tzq != ';' &&
*tzq != '#' &&
*tzq != '\0')
tzq++;
*tzq = '\0';
if (*tzp != '\0')
}
}
return (tz);
}
/*
* invalidate all cache'd state_t
*/
static void
invalidate_cache(void)
{
int hashid;
state_t *p;
/* mark all state invalid */
p->invalid = 1;
}
/* shouldn't go fast path */
/* last_tzname[] may point cache being freed */
last_tzname[0] = NULL;
/* We'll reload system TZ as well */
}
/*
* When called first time, open the counter device and load
* the initial value. If counter is updated, copy value to
* private memory.
*/
static void
reload_counter(void)
{
int fd;
if (TZSYNC_READY()) {
return;
}
return;
if (addr == MAP_FAILED)
return;
/*LINTED*/
}
/*
* getsystemTZ() returns the TZ value if it is set in the environment, or
* it returns the system TZ; if the systemTZ has not yet been set, or
* cleared by tzreload, get_default_tz() is called to read the
*/
static const char *
{
char *tz;
return ((const char *)tz);
return (systemTZ);
/*
* get_default_tz calls out stdio functions via defread.
*/
tz = get_default_tz();
/* no TZ entry in the file */
return (systemTZ);
}
/*
* look up timezone used previously. We will not free the
* old timezone name, because ltzset_u() can release _time_lock
* while it has references to systemTZ (via zonename). If we
* free the systemTZ, the reference via zonename can access
* invalid memory when systemTZ is reset.
*/
break;
}
/* This is new timezone name */
return (NULL);
}
systemTZrec = tzn;
}
}
/*
* tzname[] is the user visible string which applications may have
* references. Even though TZ was changed, references to the old tzname
* may continue to remain in the application, and those references need
* to be valid. They were valid by our implementation because strings being
* pointed by tzname were never be freed nor altered by the change of TZ.
* However, this will no longer be the case.
*
* state_t is now freed when cache is purged. Therefore, reading string
* from old tzname[] addr may end up with accessing a stale data(freed area).
* To avoid this, we maintain a copy of all timezone name strings which will
* never be freed, and tzname[] will point those copies.
*
*/
static int
{
const unsigned char *nm;
int hashid, i;
char *s;
return (0);
}
/* do the strcmp() */
for (i = 0; s[i] == name[i]; i++) {
if (s[i] == '\0') {
return (0);
}
}
}
/*
* allocate new entry. This entry is never freed, so use lmalloc
*/
return (1);
/* link it */
return (0);
}
/*
* Set tzname[] after testing parameter to see if we are setting
* same zone name. If we got same address, it should be same zone
* name as tzname[], unless cache have been purged.
* Note, fini_state() resets last_tzname[].
*/
static void
{
if (namep[0] != last_tzname[0]) {
if (set_one_tzname(namep[0], 0)) {
last_tzname[0] = NULL;
} else {
last_tzname[0] = namep[0];
}
}
} else {
}
}
}
static int32_t
decode32_slow(const char *p)
{
unsigned char *up = (unsigned char *)p;
int32_t r;
return (r);
}
static int64_t
decode64_slow(const char *p)
{
unsigned char *up;
int64_t r;
if (((uintptr_t)p % 4) == 0) {
/*LINTED*/
/*LINTED*/
return (r);
}
up = (unsigned char *)p;
return (r);
}