#pragma weak _tzset = tzset
#include "lint.h"
#include "libc.h"
#include "tsd.h"
#include <stdarg.h>
#include <mtlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#include <sys/param.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/stat.h>
#include <sys/mman.h>
#include <stdbool.h>
#define JAN_01_1902 (int)0x8017E880
#define LEN_TZDIR (sizeof (TZDIR) - 1)
#define TIMEZONE "/etc/default/init"
#define TZSTRING "TZ="
#define HASHTABLE 31
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
#define DAYS_SINCE_70(Y) (YR((Y)-1L) - YR(70-1))
#define YR(X) \
((1900L + (X)) * 365L + (1900L + (X)) / 4L - \
(1900L + (X)) / 100L + ((1900L + (X)) - 1600L) / 400L)
#if defined(__sparc) || defined(__sparcv9)
#define GET_LONG(p) \
*(uint_t *)(p)
#define GET_SHORTS(p) \
*(ushort_t *)(p) << 16 |\
*(ushort_t *)((p) + 2)
#define GET_CHARS(p) \
*(uchar_t *)(p) << 24 |\
*(uchar_t *)((p) + 1) << 16 |\
*(uchar_t *)((p) + 2) << 8 |\
*(uchar_t *)((p) + 3)
#else
#define GET_BYTE(x) \
((uchar_t)(x) & 0xff)
#define SWAP_BYTES(x) ((\
GET_BYTE(x) << 8) |\
GET_BYTE((x) >> 8))
#define SWAP_WORDS(x) ((\
SWAP_BYTES(x) << 16) |\
SWAP_BYTES((x) >> 16))
#define GET_LONG(p) \
SWAP_WORDS(*(uint_t *)(p))
#define GET_SHORTS(p) \
SWAP_BYTES(*(ushort_t *)(p)) << 16 |\
SWAP_BYTES(*(ushort_t *)((p) + 2))
#define GET_CHARS(p) \
GET_BYTE(*(uchar_t *)(p)) << 24 |\
GET_BYTE(*(uchar_t *)((p) + 1)) << 16 |\
GET_BYTE(*(uchar_t *)((p) + 2)) << 8 |\
GET_BYTE(*(uchar_t *)((p) + 3))
#endif
#define IF_ALIGNED(ptr, byte_alignment) \
!((uintptr_t)(ptr) & (byte_alignment - 1))
#define CVTZCODE(p) (int)(\
IF_ALIGNED(p, 4) ? GET_LONG(p) :\
IF_ALIGNED(p, 2) ? GET_SHORTS(p) : GET_CHARS(p));\
p += 4;
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (1)
#endif
extern mutex_t _time_lock;
extern const int __lyday_to_month[];
extern const int __yday_to_month[];
extern const int __mon_lengths[2][MONSPERYEAR];
extern const int __year_lengths[2];
const char _tz_gmt[4] = "GMT";
const char _tz_spaces[4] = " ";
static const char _posix_gmt0[5] = "GMT0";
typedef struct ttinfo {
long tt_gmtoff;
int tt_isdst;
int tt_abbrind;
int tt_ttisstd;
int tt_ttisgmt;
} ttinfo_t;
typedef struct lsinfo {
time_t ls_trans;
long ls_corr;
} lsinfo_t;
typedef struct previnfo {
ttinfo_t *std;
ttinfo_t *alt;
} prev_t;
typedef enum {
MON_WEEK_DOW,
JULIAN_DAY,
DAY_OF_YEAR
} posrule_type_t;
typedef struct {
posrule_type_t r_type;
int r_day;
int r_week;
int r_mon;
long r_time;
} rule_t;
typedef struct {
rule_t *rules[2];
long offset[2];
long long rtime[2];
} posix_daylight_t;
typedef enum {
ZONERULES_INVALID, POSIX, POSIX_USA, ZONEINFO
} zone_rules_t;
typedef struct state {
const char *zonename;
struct state *next;
zone_rules_t zonerules;
int daylight;
long default_timezone;
long default_altzone;
const char *default_tzname0;
const char *default_tzname1;
int leapcnt;
int timecnt;
int typecnt;
int charcnt;
char *chars;
size_t charsbuf_size;
prev_t prev[TZ_MAX_TIMES];
time_t ats[TZ_MAX_TIMES];
uchar_t types[TZ_MAX_TIMES];
ttinfo_t ttis[TZ_MAX_TYPES];
lsinfo_t lsis[TZ_MAX_LEAPS];
int last_ats_idx;
rule_t start_rule;
rule_t end_rule;
} state_t;
typedef struct tznmlist {
struct tznmlist *link;
char name[1];
} tznmlist_t;
static const char *systemTZ;
static tznmlist_t *systemTZrec;
static const char *namecache;
static state_t *tzcache[HASHTABLE];
#define TZNMC_SZ 43
static tznmlist_t *tznmhash[TZNMC_SZ];
static const char *last_tzname[2];
static state_t *lclzonep;
static struct tm tm;
static int is_in_dst;
static zone_rules_t curr_zonerules = ZONERULES_INVALID;
static int cached_year;
static long long cached_secs_since_1970;
static int year_is_cached = FALSE;
#define TZSYNC_FILE "/var/run/tzsync"
static uint32_t zoneinfo_seqno;
static uint32_t zoneinfo_seqno_init = 1;
static uint32_t *zoneinfo_seqadr = &zoneinfo_seqno_init;
#define RELOAD_INFO() (zoneinfo_seqno != *zoneinfo_seqadr)
#define _2AM (2 * SECSPERHOUR)
#define FIRSTWEEK 1
#define LASTWEEK 5
enum wks {
_1st_week = 1,
_2nd_week,
_3rd_week,
_4th_week,
_Last_week
};
enum dwk {
Sun,
Mon,
Tue,
Wed,
Thu,
Fri,
Sat
};
enum mth {
Jan = 1,
Feb,
Mar,
Apr,
May,
Jun,
Jul,
Aug,
Sep,
Oct,
Nov,
Dec
};
typedef struct {
int s_year;
int e_year;
rule_t start;
rule_t end;
} __usa_rules_t;
static const __usa_rules_t __usa_rules[] = {
{
2007, 2037,
{ MON_WEEK_DOW, Sun, _2nd_week, Mar, _2AM },
{ MON_WEEK_DOW, Sun, _1st_week, Nov, _2AM },
},
{
1987, 2006,
{ MON_WEEK_DOW, Sun, _1st_week, Apr, _2AM },
{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
},
{
1976, 1986,
{ MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
},
{
1975, 1975,
{ MON_WEEK_DOW, Sun, _Last_week, Feb, _2AM },
{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
},
{
1974, 1974,
{ MON_WEEK_DOW, Sun, _1st_week, Jan, _2AM },
{ MON_WEEK_DOW, Sun, _Last_week, Nov, _2AM },
},
{
1902, 1973,
{ MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
{ MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
}
};
#define MAX_RULE_TABLE (sizeof (__usa_rules) / sizeof (__usa_rules_t) - 1)
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 const char *getrule(const char *, rule_t *, int);
static int load_posixinfo(const char *, state_t *);
static int load_zoneinfo(const char *, state_t *);
static void load_posix_transitions(state_t *, long, long, zone_rules_t);
static void adjust_posix_default(state_t *, long, long);
static void *ltzset_u(time_t);
static struct tm *offtime_u(time_t, long, struct tm *);
static int posix_check_dst(long long, state_t *);
static int posix_daylight(long long *, int, posix_daylight_t *);
static void set_zone_context(time_t);
static void reload_counter(void);
static void purge_zone_cache(void);
static void set_tzname(const char **);
double
difftime(time_t time1, time_t time0)
{
if (time1 < time0) {
time0 -= time1;
return (-(double)*(unsigned long *) &time0);
} else {
time1 -= time0;
return ((double)*(unsigned long *) &time1);
}
}
struct tm *
gmtime_r(const time_t *timep, struct tm *p_tm)
{
return (offtime_u((time_t)*timep, 0L, p_tm));
}
struct tm *
gmtime(const time_t *timep)
{
struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
if (p_tm == NULL)
p_tm = &tm;
return (gmtime_r(timep, p_tm));
}
static int
get_hashid(const char *id)
{
unsigned char c;
unsigned int h;
h = *id++;
while ((c = *id++) != '\0')
h += c;
return ((int)(h % HASHTABLE));
}
static state_t *
find_zone(const char *zonename)
{
int hashid;
state_t *cur;
hashid = get_hashid(zonename);
cur = tzcache[hashid];
while (cur) {
int res;
res = strcmp(cur->zonename, zonename);
if (res == 0) {
return (cur);
} else if (res > 0) {
break;
}
cur = cur->next;
}
return (NULL);
}
static void
reg_zone(state_t *new)
{
int hashid, res;
state_t *cur, *prv;
hashid = get_hashid(new->zonename);
cur = tzcache[hashid];
prv = NULL;
while (cur != NULL) {
res = strcmp(cur->zonename, new->zonename);
if (res == 0) {
return;
} else if (res > 0) {
break;
}
prv = cur;
cur = cur->next;
}
if (prv != NULL) {
new->next = prv->next;
prv->next = new;
} else {
new->next = tzcache[hashid];
tzcache[hashid] = new;
}
}
struct tm *
localtime_r(const time_t *timep, struct tm *p_tm)
{
long offset;
struct tm *rt;
void *unused;
int my_is_in_dst;
lmutex_lock(&_time_lock);
unused = ltzset_u(*timep);
if (lclzonep == NULL) {
lmutex_unlock(&_time_lock);
if (unused != NULL)
free(unused);
return (offtime_u(*timep, 0L, p_tm));
}
my_is_in_dst = is_in_dst;
offset = (my_is_in_dst) ? -altzone : -timezone;
lmutex_unlock(&_time_lock);
if (unused != NULL)
free(unused);
rt = offtime_u(*timep, offset, p_tm);
p_tm->tm_isdst = my_is_in_dst;
return (rt);
}
struct tm *
localtime(const time_t *timep)
{
struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
if (p_tm == NULL)
p_tm = &tm;
return (localtime_r(timep, p_tm));
}
static time_t
mktime1(struct tm *tmptr, int usetz)
{
struct tm _tm;
long long t;
int temp;
int mketimerrno;
int overflow;
void *unused = NULL;
mketimerrno = errno;
t = tmptr->tm_sec + SECSPERMIN * tmptr->tm_min +
SECSPERHOUR * tmptr->tm_hour +
SECSPERDAY * (tmptr->tm_mday - 1);
if (tmptr->tm_mon >= 12) {
tmptr->tm_year += tmptr->tm_mon / 12;
tmptr->tm_mon %= 12;
} else if (tmptr->tm_mon < 0) {
temp = -tmptr->tm_mon;
tmptr->tm_mon = 0;
tmptr->tm_year -= (temp / 12);
if (temp %= 12) {
tmptr->tm_year--;
tmptr->tm_mon = 12 - temp;
}
}
lmutex_lock(&_time_lock);
if (!year_is_cached || (cached_year != tmptr->tm_year)) {
cached_year = tmptr->tm_year;
year_is_cached = TRUE;
cached_secs_since_1970 =
(long long)SECSPERDAY * DAYS_SINCE_70(cached_year);
}
t += cached_secs_since_1970;
if (isleap(tmptr->tm_year + TM_YEAR_BASE))
t += SECSPERDAY * __lyday_to_month[tmptr->tm_mon];
else
t += SECSPERDAY * __yday_to_month[tmptr->tm_mon];
if (usetz) {
unused = ltzset_u((time_t)t);
t += (tmptr->tm_isdst > 0) ? altzone : timezone;
#ifdef _ILP32
overflow = t > LONG_MAX || t < LONG_MIN ||
tmptr->tm_year < 1 || tmptr->tm_year > 138;
#else
overflow = t > LONG_MAX || t < LONG_MIN;
#endif
set_zone_context((time_t)t);
if (tmptr->tm_isdst < 0) {
long dst_delta = timezone - altzone;
switch (curr_zonerules) {
case ZONEINFO:
if (is_in_dst) {
t -= dst_delta;
set_zone_context((time_t)t);
if (is_in_dst) {
(void) offtime_u((time_t)t,
-altzone, &_tm);
_tm.tm_isdst = 1;
} else {
(void) offtime_u((time_t)t,
-timezone, &_tm);
}
} else {
(void) offtime_u((time_t)t, -timezone,
&_tm);
}
break;
case POSIX_USA:
case POSIX:
if (is_in_dst) {
t -= dst_delta;
set_zone_context((time_t)t);
if (is_in_dst) {
(void) offtime_u((time_t)t,
-altzone, &_tm);
_tm.tm_isdst = 1;
} else {
(void) offtime_u((time_t)t,
-timezone, &_tm);
}
} else {
set_zone_context((time_t)t - dst_delta);
if (is_in_dst) {
t -= dst_delta;
(void) offtime_u((time_t)t,
-altzone, &_tm);
_tm.tm_isdst = 1;
} else {
(void) offtime_u((time_t)t,
-timezone, &_tm);
}
}
break;
case ZONERULES_INVALID:
(void) offtime_u((time_t)t, 0L, &_tm);
break;
}
} else if (is_in_dst) {
(void) offtime_u((time_t)t, -altzone, &_tm);
_tm.tm_isdst = 1;
} else {
(void) offtime_u((time_t)t, -timezone, &_tm);
}
} else {
overflow = 0;
(void) offtime_u((time_t)t, 0, &_tm);
}
if (overflow || t > LONG_MAX || t < LONG_MIN) {
mketimerrno = EOVERFLOW;
t = -1;
} else {
*tmptr = _tm;
}
lmutex_unlock(&_time_lock);
if (unused != NULL)
free(unused);
errno = mketimerrno;
return ((time_t)t);
}
time_t
mktime(struct tm *tmptr)
{
return (mktime1(tmptr, TRUE));
}
time_t
timegm(struct tm *tmptr)
{
return (mktime1(tmptr, FALSE));
}
void
tzset(void)
{
void *unused;
lmutex_lock(&_time_lock);
unused = ltzset_u(time(NULL));
lmutex_unlock(&_time_lock);
if (unused != NULL)
free(unused);
}
void
_ltzset(time_t tim)
{
void *unused;
lmutex_lock(&_time_lock);
unused = ltzset_u(tim);
lmutex_unlock(&_time_lock);
if (unused != NULL)
free(unused);
}
static void *
ltzset_u(time_t t)
{
const char *zonename;
state_t *entry, *new_entry;
const char *newtzname[2];
if (RELOAD_INFO()) {
reload_counter();
purge_zone_cache();
}
if ((zonename = getsystemTZ()) == NULL || *zonename == '\0')
zonename = _posix_gmt0;
if (namecache != NULL && strcmp(namecache, zonename) == 0) {
set_zone_context(t);
return (NULL);
}
entry = find_zone(zonename);
if (entry == NULL) {
lmutex_unlock(&_time_lock);
new_entry = malloc(sizeof (state_t));
lmutex_lock(&_time_lock);
entry = find_zone(zonename);
} else {
new_entry = NULL;
goto out;
}
if (entry == NULL) {
char *newzonename, *charsbuf;
newzonename = libc_strdup(zonename);
daylight = 0;
entry = new_entry;
if (entry == NULL || newzonename == NULL) {
failed:
if (newzonename != NULL)
libc_free(newzonename);
curr_zonerules = ZONERULES_INVALID;
namecache = NULL;
timezone = altzone = 0;
is_in_dst = 0;
newtzname[0] = (char *)_tz_gmt;
newtzname[1] = (char *)_tz_spaces;
set_tzname(newtzname);
return (entry);
}
if ((charsbuf = libc_malloc(TZ_MAX_CHARS)) == NULL)
goto failed;
entry->zonerules = ZONERULES_INVALID;
entry->charsbuf_size = TZ_MAX_CHARS;
entry->chars = charsbuf;
entry->default_tzname0 = _tz_gmt;
entry->default_tzname1 = _tz_spaces;
entry->zonename = newzonename;
if (*zonename == ':') {
if (load_zoneinfo(zonename + 1, entry) != 0) {
(void) load_posixinfo(_posix_gmt0, entry);
}
} else if (load_posixinfo(zonename, entry) != 0) {
if (load_zoneinfo(zonename, entry) != 0) {
(void) load_posixinfo(_posix_gmt0, entry);
}
}
entry->last_ats_idx = -1;
reg_zone(entry);
new_entry = NULL;
}
out:
curr_zonerules = entry->zonerules;
namecache = entry->zonename;
daylight = entry->daylight;
lclzonep = entry;
set_zone_context(t);
return (new_entry);
}
static void
set_zone_default_context(void)
{
const char *newtzname[2];
altzone = lclzonep->default_altzone;
timezone = lclzonep->default_timezone;
newtzname[0] = (char *)lclzonep->default_tzname0;
newtzname[1] = (char *)lclzonep->default_tzname1;
is_in_dst = 0;
set_tzname(newtzname);
}
static bool
state_is_posix(const state_t *state)
{
return (state->zonerules == POSIX || state->zonerules == POSIX_USA);
}
static void
set_zone_context(time_t t)
{
prev_t *prevp;
int lo, hi, tidx, lidx;
ttinfo_t *ttisp, *std, *alt;
const char *newtzname[2];
if (lclzonep == NULL || curr_zonerules == ZONERULES_INVALID) {
timezone = altzone = 0;
daylight = is_in_dst = 0;
newtzname[0] = (char *)_tz_gmt;
newtzname[1] = (char *)_tz_spaces;
set_tzname(newtzname);
return;
}
if (lclzonep->timecnt <= 0 || lclzonep->typecnt < 2) {
set_zone_default_context();
return;
}
lo = 0;
hi = lclzonep->timecnt - 1;
if (t < lclzonep->ats[0] || t >= lclzonep->ats[hi]) {
if (lclzonep->zonerules == POSIX_USA ||
lclzonep->zonerules == POSIX) {
set_zone_default_context();
is_in_dst = (daylight) ?
posix_check_dst(t, lclzonep) : 0;
return;
} else if (t < lclzonep->ats[0]) {
set_zone_default_context();
return;
} else {
tidx = hi;
}
} else {
if ((lidx = lclzonep->last_ats_idx) != -1 &&
lidx != hi &&
t >= lclzonep->ats[lidx] &&
t < lclzonep->ats[lidx + 1]) {
tidx = lidx;
} else {
while (lo <= hi) {
tidx = (lo + hi) / 2;
if (t == lclzonep->ats[tidx])
break;
else if (t < lclzonep->ats[tidx])
hi = tidx - 1;
else
lo = tidx + 1;
}
if (lo > hi)
tidx = hi;
}
}
ttisp = &lclzonep->ttis[lclzonep->types[tidx]];
prevp = &lclzonep->prev[tidx];
bool posix = state_is_posix(lclzonep);
if ((is_in_dst = ttisp->tt_isdst) == 0) {
timezone = -ttisp->tt_gmtoff;
newtzname[0] = &lclzonep->chars[ttisp->tt_abbrind];
if (!posix && (alt = prevp->alt) != NULL) {
altzone = -alt->tt_gmtoff;
newtzname[1] = &lclzonep->chars[alt->tt_abbrind];
} else {
altzone = lclzonep->default_altzone;
newtzname[1] = (char *)lclzonep->default_tzname1;
}
} else {
altzone = -ttisp->tt_gmtoff;
newtzname[1] = &lclzonep->chars[ttisp->tt_abbrind];
if (!posix && (std = prevp->std) != NULL) {
timezone = -std->tt_gmtoff;
newtzname[0] = &lclzonep->chars[std->tt_abbrind];
} else {
timezone = lclzonep->default_timezone;
newtzname[0] = (char *)lclzonep->default_tzname0;
}
}
lclzonep->last_ats_idx = tidx;
set_tzname(newtzname);
}
static struct tm *
offtime_u(time_t t, long offset, struct tm *tmptr)
{
long days;
long rem;
long y;
int yleap;
const int *ip;
days = t / SECSPERDAY;
rem = t % SECSPERDAY;
rem += offset;
while (rem < 0) {
rem += SECSPERDAY;
--days;
}
while (rem >= SECSPERDAY) {
rem -= SECSPERDAY;
++days;
}
tmptr->tm_hour = (int)(rem / SECSPERHOUR);
rem = rem % SECSPERHOUR;
tmptr->tm_min = (int)(rem / SECSPERMIN);
tmptr->tm_sec = (int)(rem % SECSPERMIN);
tmptr->tm_wday = (int)((EPOCH_WDAY + days) % DAYSPERWEEK);
if (tmptr->tm_wday < 0)
tmptr->tm_wday += DAYSPERWEEK;
y = EPOCH_YEAR;
while (days < 0 || days >= (long)__year_lengths[yleap = isleap(y)]) {
long newy;
newy = y + days / DAYSPERNYEAR;
if (days < 0)
--newy;
days -= ((long)newy - (long)y) * DAYSPERNYEAR +
LEAPS_THRU_END_OF(newy > 0 ? newy - 1L : newy) -
LEAPS_THRU_END_OF(y > 0 ? y - 1L : y);
y = newy;
}
tmptr->tm_year = (int)(y - TM_YEAR_BASE);
tmptr->tm_yday = (int)days;
ip = __mon_lengths[yleap];
for (tmptr->tm_mon = 0; days >=
(long)ip[tmptr->tm_mon]; ++(tmptr->tm_mon)) {
days = days - (long)ip[tmptr->tm_mon];
}
tmptr->tm_mday = (int)(days + 1);
tmptr->tm_isdst = 0;
#ifdef _LP64
if ((y > (long)INT_MAX + TM_YEAR_BASE) ||
(y < (long)INT_MIN + TM_YEAR_BASE)) {
errno = EOVERFLOW;
return (NULL);
}
#endif
return (tmptr);
}
static int
posix_check_dst(long long t, state_t *sp)
{
struct tm gmttm;
long long jan01;
int year, i, idx, ridx;
posix_daylight_t pdaylight;
(void) offtime_u(t, 0L, &gmttm);
year = gmttm.tm_year + 1900;
jan01 = t - ((gmttm.tm_yday * SECSPERDAY) +
(gmttm.tm_hour * SECSPERHOUR) +
(gmttm.tm_min * SECSPERMIN) + gmttm.tm_sec);
if (sp->zonerules == POSIX) {
pdaylight.rules[0] = &sp->start_rule;
pdaylight.rules[1] = &sp->end_rule;
} else {
i = 0;
while (year < __usa_rules[i].s_year && i < MAX_RULE_TABLE) {
i++;
}
pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
}
pdaylight.offset[0] = timezone;
pdaylight.offset[1] = altzone;
idx = posix_daylight(&jan01, year, &pdaylight);
ridx = !idx;
if (t >= pdaylight.rtime[idx] && t < pdaylight.rtime[ridx]) {
return (ridx);
} else {
return (idx);
}
}
static int
posix_daylight(long long *janfirst, int year, posix_daylight_t *pdaylightp)
{
rule_t *rulep;
long offset;
int idx;
int i, d, m1, yy0, yy1, yy2, dow;
long leapyear;
long long value;
static const int __secs_year_lengths[2] = {
DAYSPERNYEAR * SECSPERDAY,
DAYSPERLYEAR * SECSPERDAY
};
leapyear = isleap(year);
for (idx = 0; idx < 2; idx++) {
rulep = pdaylightp->rules[idx];
offset = pdaylightp->offset[idx];
switch (rulep->r_type) {
case MON_WEEK_DOW:
value = *janfirst;
for (i = 0; i < rulep->r_mon - 1; ++i)
value += __mon_lengths[leapyear][i] *
SECSPERDAY;
m1 = (rulep->r_mon + 9) % 12 + 1;
yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
yy1 = yy0 / 100;
yy2 = yy0 % 100;
dow = ((26 * m1 - 2) / 10 +
1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
if (dow < 0)
dow += DAYSPERWEEK;
if (year < 1 && leapyear)
++dow;
d = rulep->r_day - dow;
if (d < 0)
d += DAYSPERWEEK;
for (i = 1; i < rulep->r_week; ++i) {
if (d + DAYSPERWEEK >=
__mon_lengths[leapyear][rulep->r_mon - 1])
break;
d += DAYSPERWEEK;
}
value += d * SECSPERDAY;
break;
case JULIAN_DAY:
value = *janfirst + (rulep->r_day - 1) * SECSPERDAY;
if (leapyear && rulep->r_day >= 60)
value += SECSPERDAY;
break;
case DAY_OF_YEAR:
value = *janfirst + rulep->r_day * SECSPERDAY;
break;
}
pdaylightp->rtime[idx] = value + rulep->r_time + offset;
}
*janfirst += __secs_year_lengths[leapyear];
return ((pdaylightp->rtime[0] > pdaylightp->rtime[1]) ? 1 : 0);
}
static int
load_zoneinfo(const char *name, state_t *sp)
{
char *cp;
char *cp2;
int i;
long cnt;
int fid;
int ttisstdcnt;
int ttisgmtcnt;
char *fullname;
size_t namelen;
char *bufp;
size_t flen;
prev_t *prevp;
struct tzhead *tzhp;
struct stat64 stbuf;
ttinfo_t *most_recent_alt = NULL;
ttinfo_t *most_recent_std = NULL;
ttinfo_t *ttisp;
if (name == NULL) {
name = TZDEFAULT;
if (name == NULL)
return (-1);
}
if ((name[0] == '/') || strstr(name, "../"))
return (-1);
namelen = LEN_TZDIR + 1 + strlen(name) + 1;
if ((fullname = lmalloc(namelen)) == NULL)
return (-1);
(void) strcpy(fullname, TZDIR "/");
(void) strcpy(fullname + LEN_TZDIR + 1, name);
if ((fid = open(fullname, O_RDONLY)) == -1) {
lfree(fullname, namelen);
return (-1);
}
lfree(fullname, namelen);
if (fstat64(fid, &stbuf) == -1) {
(void) close(fid);
return (-1);
}
flen = (size_t)stbuf.st_size;
if (flen < sizeof (struct tzhead)) {
(void) close(fid);
return (-1);
}
cp = bufp = lmalloc(flen);
if (bufp == NULL) {
(void) close(fid);
return (-1);
}
if ((cnt = read(fid, bufp, flen)) != flen) {
lfree(bufp, flen);
(void) close(fid);
return (-1);
}
if (close(fid) != 0) {
lfree(bufp, flen);
return (-1);
}
cp += offsetof(struct tzhead, tzh_ttisutcnt);
ttisstdcnt = CVTZCODE(cp);
ttisgmtcnt = CVTZCODE(cp);
sp->leapcnt = CVTZCODE(cp);
sp->timecnt = CVTZCODE(cp);
sp->typecnt = CVTZCODE(cp);
sp->charcnt = CVTZCODE(cp);
if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) {
lfree(bufp, flen);
return (-1);
}
if (cnt - (cp - bufp) < (long)(sp->timecnt * 4 +
sp->timecnt +
sp->typecnt * (4 + 2) +
sp->charcnt +
sp->leapcnt * (4 + 4) +
ttisstdcnt +
ttisgmtcnt)) {
lfree(bufp, flen);
return (-1);
}
for (i = 0; i < sp->timecnt; ++i) {
sp->ats[i] = CVTZCODE(cp);
}
cp2 = (char *)((uintptr_t)cp + sp->timecnt);
for (i = 0; i < sp->typecnt; ++i) {
ttisp = &sp->ttis[i];
ttisp->tt_gmtoff = CVTZCODE(cp2);
ttisp->tt_isdst = (uchar_t)*cp2++;
if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) {
lfree(bufp, flen);
return (-1);
}
ttisp->tt_abbrind = (uchar_t)*cp2++;
if (ttisp->tt_abbrind < 0 ||
ttisp->tt_abbrind > sp->charcnt) {
lfree(bufp, flen);
return (-1);
}
}
prevp = &sp->prev[0];
for (i = 0; i < sp->timecnt; ++i) {
sp->types[i] = (uchar_t)*cp++;
ttisp = &sp->ttis[sp->types[i]];
prevp->std = most_recent_std;
prevp->alt = most_recent_alt;
if (ttisp->tt_isdst == 1) {
most_recent_alt = ttisp;
} else {
most_recent_std = ttisp;
}
if ((int)sp->types[i] >= sp->typecnt) {
lfree(bufp, flen);
return (-1);
}
++prevp;
}
if (most_recent_alt == NULL)
sp->daylight = 0;
else
sp->daylight = 1;
cp = cp2;
for (i = 0; i < sp->charcnt; ++i)
sp->chars[i] = *cp++;
sp->chars[i] = '\0';
for (i = 0; i < sp->leapcnt; ++i) {
struct lsinfo *lsisp;
lsisp = &sp->lsis[i];
lsisp->ls_trans = CVTZCODE(cp);
lsisp->ls_corr = CVTZCODE(cp);
}
for (i = 0; i < sp->typecnt; ++i) {
ttisp = &sp->ttis[i];
if (ttisstdcnt == 0) {
ttisp->tt_ttisstd = FALSE;
} else {
ttisp->tt_ttisstd = *cp++;
if (ttisp->tt_ttisstd != TRUE &&
ttisp->tt_ttisstd != FALSE) {
lfree(bufp, flen);
return (-1);
}
}
}
for (i = 0; i < sp->typecnt; ++i) {
ttisp = &sp->ttis[i];
if (ttisgmtcnt == 0) {
ttisp->tt_ttisgmt = FALSE;
} else {
ttisp->tt_ttisgmt = *cp++;
if (ttisp->tt_ttisgmt != TRUE &&
ttisp->tt_ttisgmt != FALSE) {
lfree(bufp, flen);
return (-1);
}
}
}
sp->default_timezone = -sp->ttis[0].tt_gmtoff;
sp->default_altzone = 0;
sp->default_tzname0 = &sp->chars[0];
sp->default_tzname1 = _tz_spaces;
lfree(bufp, flen);
sp->zonerules = ZONEINFO;
return (0);
}
#ifdef _TZ_DEBUG
static void
print_state(state_t *sp)
{
struct tm tmp;
int i, c;
(void) fprintf(stderr, "=========================================\n");
(void) fprintf(stderr, "zonename: \"%s\"\n", sp->zonename);
(void) fprintf(stderr, "next: 0x%p\n", (void *)sp->next);
(void) fprintf(stderr, "zonerules: %s\n",
sp->zonerules == ZONERULES_INVALID ? "ZONERULES_INVALID" :
sp->zonerules == POSIX ? "POSIX" :
sp->zonerules == POSIX_USA ? "POSIX_USA" :
sp->zonerules == ZONEINFO ? "ZONEINFO" : "UNKNOWN");
(void) fprintf(stderr, "daylight: %d\n", sp->daylight);
(void) fprintf(stderr, "default_timezone: %ld\n", sp->default_timezone);
(void) fprintf(stderr, "default_altzone: %ld\n", sp->default_altzone);
(void) fprintf(stderr, "default_tzname0: \"%s\"\n",
sp->default_tzname0);
(void) fprintf(stderr, "default_tzname1: \"%s\"\n",
sp->default_tzname1);
(void) fprintf(stderr, "leapcnt: %d\n", sp->leapcnt);
(void) fprintf(stderr, "timecnt: %d\n", sp->timecnt);
(void) fprintf(stderr, "typecnt: %d\n", sp->typecnt);
(void) fprintf(stderr, "charcnt: %d\n", sp->charcnt);
(void) fprintf(stderr, "chars: \"%s\"\n", sp->chars);
(void) fprintf(stderr, "charsbuf_size: %u\n", sp->charsbuf_size);
(void) fprintf(stderr, "prev: skipping...\n");
(void) fprintf(stderr, "ats = {\n");
for (c = 0, i = 0; i < sp->timecnt; i++) {
char buf[64];
size_t len;
if (c != 0) {
(void) fprintf(stderr, ", ");
}
(void) asctime_r(gmtime_r(&sp->ats[i], &tmp),
buf, sizeof (buf));
len = strlen(buf);
buf[len-1] = '\0';
(void) fprintf(stderr, "%s", buf);
if (c == 1) {
(void) fprintf(stderr, "\n");
c = 0;
} else {
c++;
}
}
(void) fprintf(stderr, "}\n");
(void) fprintf(stderr, "types = {\n");
for (c = 0, i = 0; i < sp->timecnt; i++) {
if (c == 0) {
(void) fprintf(stderr, "\t");
} else {
(void) fprintf(stderr, ", ");
}
(void) fprintf(stderr, "%d", sp->types[i]);
if (c == 7) {
(void) fprintf(stderr, "\n");
c = 0;
} else {
c++;
}
}
(void) fprintf(stderr, "}\n");
(void) fprintf(stderr, "ttis = {\n");
for (i = 0; i < sp->typecnt; i++) {
(void) fprintf(stderr, "\t{\n");
(void) fprintf(stderr, "\t\ttt_gmtoff: %ld\n",
sp->ttis[i].tt_gmtoff);
(void) fprintf(stderr, "\t\ttt_ttisdst: %d\n",
sp->ttis[i].tt_isdst);
(void) fprintf(stderr, "\t\ttt_abbrind: %d\n",
sp->ttis[i].tt_abbrind);
(void) fprintf(stderr, "\t\ttt_tt_isstd: %d\n",
sp->ttis[i].tt_ttisstd);
(void) fprintf(stderr, "\t\ttt_ttisgmt: %d\n",
sp->ttis[i].tt_ttisgmt);
(void) fprintf(stderr, "\t}\n");
}
(void) fprintf(stderr, "}\n");
}
#endif
static int
load_posixinfo(const char *name, state_t *sp)
{
const char *stdname;
const char *dstname = 0;
size_t stdlen;
size_t dstlen;
long stdoff = 0;
long dstoff = 0;
char *cp;
int i;
ttinfo_t *dst;
ttinfo_t *std;
int quoted;
zone_rules_t zonetype;
zonetype = POSIX_USA;
stdname = name;
if ((quoted = (*stdname == '<')) != 0)
++stdname;
if (*name != '\0') {
if ((name = getzname(name, quoted)) == NULL)
return (-1);
stdlen = name - stdname;
if (*name == '>')
++name;
if (*name == '\0' || stdlen < 1) {
return (-1);
} else {
if ((name = getoffset(name, &stdoff)) == NULL)
return (-1);
}
}
if (*name != '\0') {
dstname = name;
if ((quoted = (*dstname == '<')) != 0)
++dstname;
if ((name = getzname(name, quoted)) == NULL)
return (-1);
dstlen = name - dstname;
if (dstlen < 1)
return (-1);
if (*name == '>')
++name;
if (*name != '\0' && *name != ',' && *name != ';') {
if ((name = getoffset(name, &dstoff)) == NULL)
return (-1);
} else {
dstoff = stdoff - SECSPERHOUR;
}
if (*name != ',' && *name != ';') {
if (load_zoneinfo(TZDEFRULES, sp) == 0 &&
sp->daylight == 1) {
adjust_posix_default(sp, stdoff, dstoff);
} else {
load_posix_transitions(sp, stdoff, dstoff,
zonetype);
}
} else {
int compat_flag = (*name == ';');
++name;
if ((name = getrule(name, &sp->start_rule, compat_flag))
== NULL)
return (-1);
if (*name++ != ',')
return (-1);
if ((name = getrule(name, &sp->end_rule, compat_flag))
== NULL)
return (-1);
if (*name != '\0')
return (-1);
zonetype = POSIX;
load_posix_transitions(sp, stdoff, dstoff, zonetype);
}
dst = &sp->ttis[0];
std = &sp->ttis[1];
} else {
dstlen = 0;
sp->daylight = 0;
sp->typecnt = 1;
sp->timecnt = 0;
std = &sp->ttis[0];
std->tt_gmtoff = -stdoff;
std->tt_isdst = 0;
}
sp->charcnt = (int)(stdlen + 1);
if (dstlen != 0)
sp->charcnt += dstlen + 1;
if ((size_t)sp->charcnt > sp->charsbuf_size) {
if ((cp = libc_realloc(sp->chars, sp->charcnt)) == NULL)
return (-1);
sp->chars = cp;
sp->charsbuf_size = sp->charcnt;
}
std->tt_abbrind = 0;
cp = sp->chars;
(void) strncpy(cp, stdname, stdlen);
while (stdlen < 3)
cp[stdlen++] = ' ';
cp[stdlen] = '\0';
i = (int)(stdlen + 1);
if (dstlen != 0) {
dst->tt_abbrind = i;
cp += i;
(void) strncpy(cp, dstname, dstlen);
while (dstlen < 3)
cp[dstlen++] = ' ';
cp[dstlen] = '\0';
}
if (sp->typecnt == 1) {
sp->default_timezone = stdoff;
sp->default_altzone = stdoff;
sp->default_tzname0 = &sp->chars[0];
sp->default_tzname1 = _tz_spaces;
} else {
sp->default_timezone = -std->tt_gmtoff;
sp->default_altzone = -dst->tt_gmtoff;
sp->default_tzname0 = &sp->chars[std->tt_abbrind];
sp->default_tzname1 = &sp->chars[dst->tt_abbrind];
}
sp->zonerules = zonetype;
return (0);
}
static void
adjust_posix_default(state_t *sp, long stdoff, long dstoff)
{
long zone_stdoff = 0;
long zone_dstoff = 0;
int zone_stdoff_flag = 0;
int zone_dstoff_flag = 0;
int isdst;
int i;
for (i = 0; (zone_stdoff_flag == 0 || zone_dstoff_flag == 0) &&
i < sp->timecnt; i++) {
ttinfo_t *zone;
zone = &sp->ttis[sp->types[i]];
if (zone_stdoff_flag == 0 && zone->tt_isdst == 0) {
zone_stdoff = -zone->tt_gmtoff;
zone_stdoff_flag = 1;
} else if (zone_dstoff_flag == 0 && zone->tt_isdst != 0) {
zone_dstoff = -zone->tt_gmtoff;
zone_dstoff_flag = 1;
}
}
if (zone_dstoff_flag == 0)
zone_dstoff = zone_stdoff;
isdst = 0;
for (i = 0; i < sp->timecnt; i++) {
ttinfo_t *zone;
int next_isdst;
zone = &sp->ttis[sp->types[i]];
next_isdst = zone->tt_isdst;
sp->types[i] = next_isdst ? 0 : 1;
if (zone->tt_ttisgmt == 0) {
if (isdst != 0 && zone->tt_ttisstd == 0)
sp->ats[i] += dstoff - zone_dstoff;
else
sp->ats[i] += stdoff - zone_stdoff;
}
if (next_isdst != 0)
zone_dstoff = -zone->tt_gmtoff;
else
zone_stdoff = -zone->tt_gmtoff;
isdst = next_isdst;
}
sp->ttis[0].tt_gmtoff = -dstoff;
sp->ttis[0].tt_isdst = 1;
sp->ttis[1].tt_gmtoff = -stdoff;
sp->ttis[1].tt_isdst = 0;
sp->typecnt = 2;
sp->daylight = 1;
}
static void
load_posix_transitions(state_t *sp, long stdoff, long dstoff,
zone_rules_t zonetype)
{
ttinfo_t *std, *dst;
time_t *tranp;
uchar_t *typep;
prev_t *prevp;
int year;
int i;
long long janfirst;
posix_daylight_t pdaylight;
sp->daylight = 1;
sp->typecnt = 2;
sp->timecnt = 272;
dst = &sp->ttis[0];
dst->tt_gmtoff = -dstoff;
dst->tt_isdst = 1;
std = &sp->ttis[1];
std->tt_gmtoff = -stdoff;
std->tt_isdst = 0;
sp->prev[0].std = NULL;
sp->prev[0].alt = NULL;
tranp = sp->ats;
prevp = &sp->prev[1];
typep = sp->types;
janfirst = JAN_01_1902;
pdaylight.rules[0] = &sp->start_rule;
pdaylight.rules[1] = &sp->end_rule;
pdaylight.offset[0] = stdoff;
pdaylight.offset[1] = dstoff;
for (i = MAX_RULE_TABLE; i >= 0; i--) {
if (zonetype == POSIX_USA) {
pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
}
for (year = __usa_rules[i].s_year;
year <= __usa_rules[i].e_year; year++) {
int idx, ridx;
idx = posix_daylight(&janfirst, year, &pdaylight);
ridx = !idx;
*tranp++ = (time_t)pdaylight.rtime[idx];
*typep++ = idx;
prevp->std = std;
prevp->alt = dst;
++prevp;
*tranp++ = (time_t)pdaylight.rtime[ridx];
*typep++ = ridx;
prevp->std = std;
prevp->alt = dst;
++prevp;
}
}
}
static const char *
getzname(const char *strp, int quoted)
{
char c;
if (quoted) {
while ((c = *strp) != '\0' && c != '>' &&
isgraph((unsigned char)c)) {
++strp;
}
} else {
while ((c = *strp) != '\0' && isgraph((unsigned char)c) &&
!isdigit((unsigned char)c) && c != ',' && c != '-' &&
c != '+') {
++strp;
}
}
if (c != '\0' && !isgraph((unsigned char)c))
return (NULL);
return (strp);
}
static const char *
getnum(const char *strp, int *nump, int min, int max)
{
char c;
int num;
if (strp == NULL || !isdigit((unsigned char)(c = *strp)))
return (NULL);
num = 0;
do {
num = num * 10 + (c - '0');
if (num > max)
return (NULL);
c = *++strp;
} while (isdigit((unsigned char)c));
if (num < min)
return (NULL);
*nump = num;
return (strp);
}
static const char *
getsecs(const char *strp, long *secsp)
{
int num;
strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
if (strp == NULL)
return (NULL);
*secsp = num * (long)SECSPERHOUR;
if (*strp == ':') {
++strp;
strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
if (strp == NULL)
return (NULL);
*secsp += num * SECSPERMIN;
if (*strp == ':') {
++strp;
strp = getnum(strp, &num, 0, SECSPERMIN);
if (strp == NULL)
return (NULL);
*secsp += num;
}
}
return (strp);
}
static const char *
getoffset(const char *strp, long *offsetp)
{
int neg = 0;
if (*strp == '-') {
neg = 1;
++strp;
} else if (*strp == '+') {
++strp;
}
strp = getsecs(strp, offsetp);
if (strp == NULL)
return (NULL);
if (neg)
*offsetp = -*offsetp;
return (strp);
}
static const char *
getrule(const char *strp, rule_t *rulep, int compat_flag)
{
if (compat_flag == 0 && *strp == 'M') {
rulep->r_type = MON_WEEK_DOW;
++strp;
strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
if (strp == NULL)
return (NULL);
if (*strp++ != '.')
return (NULL);
strp = getnum(strp, &rulep->r_week, 1, 5);
if (strp == NULL)
return (NULL);
if (*strp++ != '.')
return (NULL);
strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
} else if (compat_flag == 0 && *strp == 'J') {
rulep->r_type = JULIAN_DAY;
++strp;
strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
} else if (isdigit((unsigned char)*strp)) {
rulep->r_type = DAY_OF_YEAR;
if (compat_flag == 0) {
strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
} else {
strp = getnum(strp, &rulep->r_day, 1, DAYSPERLYEAR);
rulep->r_day--;
}
} else {
return (NULL);
}
if (strp == NULL)
return (NULL);
if (*strp == '/') {
++strp;
strp = getsecs(strp, &rulep->r_time);
} else {
rulep->r_time = 2 * SECSPERHOUR;
}
return (strp);
}
static char *
get_default_tz(void)
{
char *tz = NULL;
uchar_t *tzp, *tzq;
int flags;
void *defp;
assert_no_libc_locks_held();
if ((defp = defopen_r(TIMEZONE)) != NULL) {
flags = defcntl_r(DC_GETFLAGS, 0, defp);
TURNON(flags, DC_STRIP_QUOTES);
(void) defcntl_r(DC_SETFLAGS, flags, defp);
if ((tzp = (uchar_t *)defread_r(TZSTRING, defp)) != NULL) {
while (isspace(*tzp))
tzp++;
tzq = tzp;
while (!isspace(*tzq) &&
*tzq != ';' &&
*tzq != '#' &&
*tzq != '\0')
tzq++;
*tzq = '\0';
if (*tzp != '\0')
tz = libc_strdup((char *)tzp);
}
defclose_r(defp);
}
return (tz);
}
static void
purge_zone_cache(void)
{
int hashid;
state_t *p, *n, *r;
r = NULL;
for (hashid = 0; hashid < HASHTABLE; hashid++) {
for (p = tzcache[hashid]; p != NULL; p = n) {
n = p->next;
p->next = r;
r = p;
}
tzcache[hashid] = NULL;
}
namecache = NULL;
last_tzname[0] = NULL;
last_tzname[1] = NULL;
systemTZ = NULL;
lmutex_unlock(&_time_lock);
assert_no_libc_locks_held();
while (r != NULL) {
n = r->next;
libc_free((char *)r->zonename);
libc_free((char *)r->chars);
free(r);
r = n;
}
lmutex_lock(&_time_lock);
}
static void
reload_counter(void)
{
int fd;
caddr_t addr;
if (zoneinfo_seqadr != &zoneinfo_seqno_init) {
zoneinfo_seqno = *zoneinfo_seqadr;
return;
}
if ((fd = open(TZSYNC_FILE, O_RDONLY)) < 0)
return;
addr = mmap(0, sizeof (uint32_t), PROT_READ, MAP_SHARED, fd, 0);
(void) close(fd);
if (addr == MAP_FAILED)
return;
zoneinfo_seqadr = (uint32_t *)addr;
zoneinfo_seqno = *zoneinfo_seqadr;
}
static const char *
getsystemTZ()
{
tznmlist_t *tzn;
char *tz;
tz = getenv("TZ");
if (tz != NULL && *tz != '\0')
return ((const char *)tz);
if (systemTZ != NULL)
return (systemTZ);
lmutex_unlock(&_time_lock);
tz = get_default_tz();
lmutex_lock(&_time_lock);
if (tz == NULL) {
systemTZ = _posix_gmt0;
return (systemTZ);
}
for (tzn = systemTZrec; tzn != NULL; tzn = tzn->link) {
if (strcmp(tz, tzn->name) == 0)
break;
}
if (tzn == NULL) {
size_t tzl = strlen(tz) + 1;
tzn = lmalloc(sizeof (tznmlist_t *) + tzl);
(void) memcpy(tzn->name, tz, tzl);
tzn->link = systemTZrec;
systemTZrec = tzn;
}
libc_free(tz);
return (systemTZ = tzn->name);
}
static int
set_one_tzname(const char *name, int idx)
{
const unsigned char *nm;
int hashid, i;
char *s;
tznmlist_t *tzn;
size_t tznl;
if (name == _tz_gmt || name == _tz_spaces) {
tzname[idx] = (char *)name;
return (0);
}
nm = (const unsigned char *)name;
hashid = (nm[0] * 29 + nm[1] * 3) % TZNMC_SZ;
for (tzn = tznmhash[hashid]; tzn != NULL; tzn = tzn->link) {
s = tzn->name;
for (i = 0; s[i] == name[i]; i++) {
if (s[i] == '\0') {
tzname[idx] = tzn->name;
return (0);
}
}
}
tznl = strlen(name) + 1;
tzn = lmalloc(sizeof (tznmlist_t *) + tznl);
if (tzn == NULL)
return (1);
(void) memcpy(tzn->name, name, tznl);
tzn->link = tznmhash[hashid];
tznmhash[hashid] = tzn;
tzname[idx] = tzn->name;
return (0);
}
static void
set_tzname(const char **namep)
{
if (namep[0] != last_tzname[0]) {
if (set_one_tzname(namep[0], 0)) {
tzname[0] = (char *)_tz_gmt;
last_tzname[0] = NULL;
} else {
last_tzname[0] = namep[0];
}
}
if (namep[1] != last_tzname[1]) {
if (set_one_tzname(namep[1], 1)) {
tzname[1] = (char *)_tz_spaces;
last_tzname[1] = NULL;
} else {
last_tzname[1] = namep[1];
}
}
}