X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/1f2f436a38f7ae2d39a943ad2898d8fed4ed2e58..fc56b708803d28b949a9181528bb0da4d25b3b7b:/stdtime/FreeBSD/localtime.c diff --git a/stdtime/FreeBSD/localtime.c b/stdtime/FreeBSD/localtime.c index 97b022c..ab37c66 100644 --- a/stdtime/FreeBSD/localtime.c +++ b/stdtime/FreeBSD/localtime.c @@ -1,20 +1,22 @@ /* ** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +** 1996-06-05 by Arthur David Olson. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" + #include #ifndef lint #ifndef NOID -static char elsieid[] __unused = "@(#)localtime.c 7.78"; +static char elsieid[] __unused = "@(#)localtime.c 8.14"; #endif /* !defined NOID */ #endif /* !defined lint */ -__FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11 davidxu Exp $"); +__FBSDID("$FreeBSD: head/contrib/tzcode/stdtime/localtime.c 289027 2015-10-08 11:42:15Z rodrigc $"); /* -** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). -** POSIX-style TZ environment variable handling from Guy Harris -** (guy@auspex.com). +** Leap second handling from Bradley White. +** POSIX-style TZ environment variable handling from Guy Harris. */ /*LINTLIBRARY*/ @@ -22,12 +24,41 @@ __FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11 #include "namespace.h" #include #include +#include +#include #include #include +#ifdef NOTIFY_TZ +//#define NOTIFY_TZ_DEBUG +//#define NOTIFY_TZ_DEBUG_FILE "/var/log/localtime.debug" +//#define NOTIFY_TZ_LOG "/var/log/localtime.log" +/* force ALL_STATE if NOTIFY_TZ is set */ +#ifndef ALL_STATE +#define ALL_STATE +#endif /* ALL_STATE */ +#include +#include +#include +#endif /* NOTIFY_TZ */ #include "private.h" #include "un-namespace.h" #include "tzfile.h" +#include "float.h" /* for FLT_MAX and DBL_MAX */ + +#ifndef TZ_ABBR_MAX_LEN +/* UNIX03 requires this to be the same as sysconf(_SC_TZNAME_MAX) */ +#define TZ_ABBR_MAX_LEN 255 +#endif /* !defined TZ_ABBR_MAX_LEN */ + +#ifndef TZ_ABBR_CHAR_SET +#define TZ_ABBR_CHAR_SET \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" +#endif /* !defined TZ_ABBR_CHAR_SET */ + +#ifndef TZ_ABBR_ERR_CHAR +#define TZ_ABBR_ERR_CHAR '_' +#endif /* !defined TZ_ABBR_ERR_CHAR */ #include "libc_private.h" @@ -74,23 +105,23 @@ __FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11 ** 5. They might reference tm.TM_ZONE after calling offtime. ** What's best to do in the above cases is open to debate; ** for now, we just set things up so that in any of the five cases -** WILDABBR is used. Another possibility: initialize tzname[0] to the +** WILDABBR is used. Another possibility: initialize tzname[0] to the ** string "tzname[0] used before set", and similarly for the other cases. -** And another: initialize tzname[0] to "ERA", with an explanation in the +** And another: initialize tzname[0] to "ERA", with an explanation in the ** manual page of what this "time zone abbreviation" means (doing this so ** that tzname[0] has the "normal" length of three characters). */ #define WILDABBR " " #endif /* !defined WILDABBR */ -static char wildabbr[] = "WILDABBR"; +__used static const char wildabbr[] = WILDABBR; /* * In June 2004 it was decided UTC was a more appropriate default time * zone than GMT. */ -static const char gmt[] = "UTC"; +__used static const char gmt[] = "UTC"; /* ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. @@ -130,6 +161,8 @@ struct state { int timecnt; int typecnt; int charcnt; + int goback; + int goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; @@ -150,45 +183,117 @@ struct rule { #define DAY_OF_YEAR 1 /* n - day of year */ #define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ +#ifdef NOTIFY_TZ +typedef struct { + int token; + int is_set; + int null_bootstrap; +} notify_tz_t; + +#define NOTIFY_TZ_NAME "com.apple.system.timezone" +#endif /* NOTIFY_TZ */ + /* ** Prototypes for static functions. */ +#define localsub _st_localsub +#define time1 _st_time1 +#define tzset_basic _st_tzset_basic +__private_extern__ +#ifdef __LP64__ +struct tm * localsub(const time_t * timep, long offset, + struct tm * tmp); +#else /* !__LP64__ */ +void localsub(const time_t * timep, long offset, + struct tm * tmp); +#endif /* __LP64__ */ +__private_extern__ +time_t time1(struct tm * tmp, +#ifdef __LP64__ + struct tm *(*funcp) (const time_t *, + long, struct tm *), +#else /* !__LP64__ */ + void(*funcp) (const time_t *, + long, struct tm *), +#endif /* __LP64__ */ + long offset, + int unix03); +__private_extern__ +void tzset_basic(int); +#if !BUILDING_VARIANT static long detzcode(const char * codep); -static const char * getzname(const char * strp); +static time_t detzcode64(const char * codep); +static int differ_by_repeat(time_t t1, time_t t0); +static const char * getzname(const char * strp, char **name, size_t *len); +static const char * getqzname(const char * strp, const int delim) + ATTRIBUTE_PURE; static const char * getnum(const char * strp, int * nump, int min, int max); static const char * getsecs(const char * strp, long * secsp); static const char * getoffset(const char * strp, long * offsetp); static const char * getrule(const char * strp, struct rule * rulep); +#ifdef NOTIFY_TZ +static void gmtload(struct state * sp, char *path); +#else /* ! NOTIFY_TZ */ static void gmtload(struct state * sp); -static void gmtsub(const time_t * timep, long offset, +#endif /* NOTIFY_TZ */ +#ifdef __LP64__ +static struct tm * gmtsub(const time_t * timep, long offset, struct tm * tmp); -static void localsub(const time_t * timep, long offset, +#else /* !__LP64__ */ +static void gmtsub(const time_t * timep, long offset, struct tm * tmp); +#endif /* __LP64__ */ static int increment_overflow(int * number, int delta); +static int leaps_thru_end_of(int y) ATTRIBUTE_PURE; +static int long_increment_overflow(long * number, int delta); +static int long_normalize_overflow(long * tensptr, + int * unitsptr, int base); static int normalize_overflow(int * tensptr, int * unitsptr, int base); +#ifdef NOTIFY_TZ +static void notify_check_tz(notify_tz_t *p); +static void notify_register_tz(char *file, notify_tz_t *p); +#endif /* NOTIFY_TZ */ static void settzname(void); -static time_t time1(struct tm * tmp, - void(*funcp) (const time_t *, - long, struct tm *), - long offset); static time_t time2(struct tm *tmp, +#ifdef __LP64__ + struct tm *(*funcp) (const time_t *, + long, struct tm*), +#else /* !__LP64__ */ void(*funcp) (const time_t *, long, struct tm*), - long offset, int * okayp); +#endif /* __LP64__ */ + long offset, int * okayp, int unix03); static time_t time2sub(struct tm *tmp, +#ifdef __LP64__ + struct tm *(*funcp) (const time_t *, + long, struct tm*), +#else /* !__LP64__ */ void(*funcp) (const time_t *, long, struct tm*), - long offset, int * okayp, int do_norm_secs); +#endif /* __LP64__ */ + long offset, int * okayp, int do_norm_secs, + int unix03); +#ifdef __LP64__ +static struct tm * timesub(const time_t * timep, long offset, + const struct state * sp, struct tm * tmp); +#else /* !__LP64__ */ static void timesub(const time_t * timep, long offset, const struct state * sp, struct tm * tmp); +#endif /* __LP64__ */ static int tmcomp(const struct tm * atmp, const struct tm * btmp); static time_t transtime(time_t janfirst, int year, - const struct rule * rulep, long offset); -static int tzload(const char * name, struct state * sp); + const struct rule * rulep, long offset) + ATTRIBUTE_PURE; +static int typesequiv(const struct state * sp, int a, int b); +#ifdef NOTIFY_TZ +static int tzload(const char * name, struct state * sp, char *path, int doextend); +#else /* ! NOTIFY_TZ */ +static int tzload(const char * name, struct state * sp, int doextend); +#endif /* NOTIFY_TZ */ static int tzparse(const char * name, struct state * sp, int lastditch); @@ -209,14 +314,24 @@ static struct state gmtmem; #endif /* !defined TZ_STRLEN_MAX */ static char lcl_TZname[TZ_STRLEN_MAX + 1]; +#ifdef NOTIFY_TZ +#define lcl_is_set (lcl_notify.is_set) +#define gmt_is_set (gmt_notify.is_set) +#else /* ! NOTIFY_TZ */ static int lcl_is_set; -static int gmt_is_set; -static pthread_rwlock_t lcl_rwlock = PTHREAD_RWLOCK_INITIALIZER; -static pthread_mutex_t gmt_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif /* NOTIFY_TZ */ +static pthread_once_t gmt_once = PTHREAD_ONCE_INIT; +__private_extern__ pthread_rwlock_t lcl_rwlock = PTHREAD_RWLOCK_INITIALIZER; +static pthread_once_t gmtime_once = PTHREAD_ONCE_INIT; +static pthread_key_t gmtime_key; +static int gmtime_key_error; +static pthread_once_t localtime_once = PTHREAD_ONCE_INIT; +static pthread_key_t localtime_key; +static int localtime_key_error; char * tzname[2] = { - wildabbr, - wildabbr + (char *)wildabbr, + (char *)wildabbr }; /* @@ -224,97 +339,370 @@ char * tzname[2] = { ** Except for the strftime function, these functions [asctime, ** ctime, gmtime, localtime] return values in one of two static ** objects: a broken-down time structure and an array of char. -** Thanks to Paul Eggert (eggert@twinsun.com) for noting this. +** Thanks to Paul Eggert for noting this. */ static struct tm tm; +#define USG_COMPAT +#define ALTZONE #ifdef USG_COMPAT -time_t timezone = 0; int daylight = 0; +__private_extern__ void _st_set_timezone(long); #endif /* defined USG_COMPAT */ #ifdef ALTZONE -time_t altzone = 0; +__private_extern__ long __darwin_altzone = 0; +#define altzone __darwin_altzone #endif /* defined ALTZONE */ +#ifdef NOTIFY_TZ +#ifdef NOTIFY_TZ_DEBUG +#ifdef NOTIFY_TZ_DEBUG_FILE +#define NOTIFY_TZ_PRINTF(fmt, args...) \ +{ \ + FILE *_notify_tz_fp_; \ + if((_notify_tz_fp_ = fopen(NOTIFY_TZ_DEBUG_FILE, "a")) != NULL) { \ + fprintf(_notify_tz_fp_, "%d: " fmt, getpid(), ## args); \ + fclose(_notify_tz_fp_); \ + } \ +} +#else /* ! NOTIFY_TZ_DEBUG_FILE */ +#define NOTIFY_TZ_PRINTF(args...) fprintf(stdout, ## args) +#endif /* NOTIFY_TZ_DEBUG_FILE */ +#endif /* NOTIFY_TZ_DEBUG */ +#ifdef NOTIFY_TZ_LOG +#define NOTIFY_LOG(fmt, args...) \ +{ \ + FILE *_notify_log_fp_; \ + if((_notify_log_fp_ = fopen(NOTIFY_TZ_LOG, "a")) != NULL) { \ + fprintf(_notify_log_fp_, "%d: " fmt, getpid(), ## args); \ + fclose(_notify_log_fp_); \ + } \ +} +#endif /* NOTIFY_TZ_LOG */ + +static notify_tz_t gmt_notify = {-1, 0, 0}; +static notify_tz_t lcl_notify = {-1, 0, 0}; +static const char notify_tz_name[] = NOTIFY_TZ_NAME; +#endif /* NOTIFY_TZ */ + static long -detzcode(codep) -const char * const codep; +detzcode(const char *const codep) { long result; int i; - result = (codep[0] & 0x80) ? ~0L : 0L; + result = (codep[0] & 0x80) ? ~0L : 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } +static time_t +detzcode64(const char *const codep) +{ + register time_t result; + register int i; + + result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0; + for (i = 0; i < 8; ++i) + result = result * 256 + (codep[i] & 0xff); + return result; +} + static void settzname(void) { struct state * sp = lclptr; - int i; - - tzname[0] = wildabbr; - tzname[1] = wildabbr; + int i, need; + unsigned char * types; +#define NEED_STD 1 +#define NEED_DST 2 +#define NEED_DAYLIGHT 4 +#define NEED_ALL (NEED_STD | NEED_DST | NEED_DAYLIGHT) + + tzname[0] = (char *)wildabbr; + tzname[1] = (char *)wildabbr; #ifdef USG_COMPAT daylight = 0; - timezone = 0; + _st_set_timezone(0); #endif /* defined USG_COMPAT */ #ifdef ALTZONE altzone = 0; #endif /* defined ALTZONE */ #ifdef ALL_STATE if (sp == NULL) { - tzname[0] = tzname[1] = gmt; + tzname[0] = tzname[1] = (char *)gmt; return; } #endif /* defined ALL_STATE */ - for (i = 0; i < sp->typecnt; ++i) { - const struct ttinfo * const ttisp = &sp->ttis[i]; + /* + * PR-3765457: The original settzname went sequentially through the ttis + * array, rather than correctly indexing via the types array, to get + * the real order of the timezone changes. In addition, as a speed up, + * we start at the end of the changes, and work back, so that most of + * the time, we don't have to look through the entire array. + */ + if (sp->timecnt == 0 && sp->typecnt == 1) { + /* + * Unfortunately, there is an edge case when typecnt == 1 and + * timecnt == 0, which would cause the loop to never run. So + * in that case, we fudge things up so that it is as if + * timecnt == 1. + */ + i = 0; + types = (unsigned char *)""; /* we use the null as index */ + } else { + /* the usual case */ + i = sp->timecnt - 1; + types = sp->types; + } + need = NEED_ALL; + for (; i >= 0 && need; --i) { + const struct ttinfo * const ttisp = &sp->ttis[types[i]]; - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; #ifdef USG_COMPAT - if (ttisp->tt_isdst) + if ((need & NEED_DAYLIGHT) && ttisp->tt_isdst) { + need &= ~NEED_DAYLIGHT; daylight = 1; - if (i == 0 || !ttisp->tt_isdst) - timezone = -(ttisp->tt_gmtoff); + } #endif /* defined USG_COMPAT */ + if (ttisp->tt_isdst) { + if (need & NEED_DST) { + need &= ~NEED_DST; + tzname[1] = &sp->chars[ttisp->tt_abbrind]; #ifdef ALTZONE - if (i == 0 || ttisp->tt_isdst) - altzone = -(ttisp->tt_gmtoff); + altzone = -(ttisp->tt_gmtoff); #endif /* defined ALTZONE */ + } + } else if (need & NEED_STD) { + need &= ~NEED_STD; + tzname[0] = &sp->chars[ttisp->tt_abbrind]; +#ifdef USG_COMPAT + _st_set_timezone(-(ttisp->tt_gmtoff)); +#endif /* defined USG_COMPAT */ + } +#if defined(ALTZONE) || defined(USG_COMPAT) + if (i == 0) { +#endif /* defined(ALTZONE) || defined(USG_COMPAT) */ +#ifdef ALTZONE + if (need & NEED_DST) + altzone = -(ttisp->tt_gmtoff); +#endif /* defined ALTZONE */ +#ifdef USG_COMPAT + if (need & NEED_STD) + _st_set_timezone(-(ttisp->tt_gmtoff)); +#endif /* defined USG_COMPAT */ +#if defined(ALTZONE) || defined(USG_COMPAT) + } +#endif /* defined(ALTZONE) || defined(USG_COMPAT) */ } /* - ** And to get the latest zone names into tzname. . . + ** Finally, scrub the abbreviations. + ** First, replace bogus characters. */ - for (i = 0; i < sp->timecnt; ++i) { - const struct ttinfo * const ttisp = - &sp->ttis[ - sp->types[i]]; + for (i = 0; i < sp->charcnt; ++i) + if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL) + sp->chars[i] = TZ_ABBR_ERR_CHAR; + /* + ** Second, truncate long abbreviations. + */ + for (i = 0; i < sp->typecnt; ++i) { + register const struct ttinfo * const ttisp = &sp->ttis[i]; + register char * cp = &sp->chars[ttisp->tt_abbrind]; - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; + if (strlen(cp) > TZ_ABBR_MAX_LEN && + strcmp(cp, GRANDPARENTED) != 0) + *(cp + TZ_ABBR_MAX_LEN) = '\0'; } } +#ifdef NOTIFY_TZ static int -tzload(name, sp) +do_null_bootstrap_check(notify_tz_t *p) +{ + /* + * If we're running in a null bootstrap context (e.g. the bootstrap server), + * we will not be able to contact the notify server. In this case we want to + * avoid opening /etc/localtime every time the process does a asctime_r(3) + * or similar. But we have to do this once to get the right time zone. + * + * So first time through, we set a bit to indicate that we're in the null + * bootstrap context. The second time through, we force the "set" bit in the + * notify_tz_t structure to -1 and avoid the path where it can be set to + * zero (which would trigger opening and reloading the timezone file). + */ + if (bootstrap_port != MACH_PORT_NULL) { + return -1; + } + + if (!p->null_bootstrap) { + p->null_bootstrap = 1; + p->is_set = 0; + return -1; + } + + p->is_set = -1; + return 0; +} + +static void +notify_check_tz(notify_tz_t *p) +{ + unsigned int nstat; + int ncheck; + + if (p->token < 0) + return; + if (do_null_bootstrap_check(p) == 0) { + return; + } + nstat = notify_check(p->token, &ncheck); + if (nstat || ncheck) { + p->is_set = 0; +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("notify_check_tz: %s changed\n", (p == &lcl_notify) ? "lcl" : "gmt"); +#endif /* NOTIFY_TZ_DEBUG */ + } +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("notify_check_tz: %s unchanged\n", (p == &lcl_notify) ? "lcl" : "gmt"); +#endif /* NOTIFY_TZ_DEBUG */ +} + +extern uint32_t notify_monitor_file(int token, char *path, int flags); + +static void +notify_register_tz(char *file, notify_tz_t *p) +{ + char *name; + unsigned int nstat; + int ncheck; + + if (do_null_bootstrap_check(p) == 0) { + return; + } + + /*---------------------------------------------------------------- + * Since we don't record the last time zone filename, just cancel + * (which should remove the file monitor) and setup from scratch + *----------------------------------------------------------------*/ + if (p->token >= 0) + notify_cancel(p->token); + if (!file || *file == 0) { + /* no time zone file to monitor */ + p->token = -1; + return; + } + /*---------------------------------------------------------------- + * Just use com.apple.system.timezone if the path is /etc/localtime. + * Otherwise use com.apple.system.timezone. + *----------------------------------------------------------------*/ + if (TZDEFAULT && strcmp(file, TZDEFAULT) == 0) + name = (char *)notify_tz_name; + else { + name = alloca(sizeof(notify_tz_name) + strlen(file) + 1); + if (name == NULL) { + p->token = -1; + return; + } + strcpy(name, notify_tz_name); + strcat(name, "."); + strcat(name, file); + } +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("notify_register_tz: file=%s name=%s\n", file, name); +#endif /* NOTIFY_TZ_DEBUG */ + nstat = notify_register_check(name, &p->token); + if (nstat != 0) { + p->token = -1; + p->is_set = 0; +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("***notify_register_tz: notify_register_check failed: %u\n", nstat); +#endif /* NOTIFY_TZ_DEBUG */ +#ifdef NOTIFY_TZ_LOG + NOTIFY_LOG("notify_register_check(%s) failed: %u\n", name, nstat); +#endif /* NOTIFY_TZ_LOG */ + return; + } + /* don't need to request monitoring /etc/localtime */ + if (name != notify_tz_name) { +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("notify_register_tz: monitor %s\n", file); +#endif /* NOTIFY_TZ_DEBUG */ + nstat = notify_monitor_file(p->token, file, 0); + if (nstat != 0) { + notify_cancel(p->token); + p->token = -1; + p->is_set = 0; +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("***notify_register_tz: notify_monitor_file failed: %u\n", nstat); +#endif /* NOTIFY_TZ_DEBUG */ +#ifdef NOTIFY_TZ_LOG + NOTIFY_LOG("notify_monitor_file(%s) failed: %u\n", file, nstat); +#endif /* NOTIFY_TZ_LOG */ + return; + } + } + notify_check(p->token, &ncheck); /* this always returns true */ +} +#endif /* NOTIFY_TZ */ + +static int +differ_by_repeat(const time_t t1, const time_t t0) +{ + int_fast64_t _t0 = t0; + int_fast64_t _t1 = t1; + + if (TYPE_INTEGRAL(time_t) && + TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) + return 0; + //turn ((int_fast64_t)(t1 - t0) == SECSPERREPEAT); + return _t1 - _t0 == SECSPERREPEAT; +} + +static int +#ifdef NOTIFY_TZ +tzload(name, sp, path, doextend) +#else /* ! NOTIFY_TZ */ +tzload(name, sp, doextend) +#endif /* NOTIFY_TZ */ const char * name; struct state * const sp; +#ifdef NOTIFY_TZ +char * path; /* copy full path if non-NULL */ +#endif /* NOTIFY_TZ */ +register const int doextend; { const char * p; int i; int fid; - + int stored; + int nread; + int res; + union { + struct tzhead tzhead; + char buf[2 * sizeof(struct tzhead) + + 2 * sizeof *sp + + 4 * TZ_MAX_TIMES]; + } *u; + + u = NULL; + res = -1; + sp->goback = sp->goahead = FALSE; + +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzload: name=%s\n", name); +#endif /* NOTIFY_TZ_DEBUG */ /* XXX The following is from OpenBSD, and I'm not sure it is correct */ if (name != NULL && issetugid() != 0) if ((name[0] == ':' && name[1] == '/') || name[0] == '/' || strchr(name, '.')) name = NULL; +#ifdef NOTIFY_TZ + if (path) + *path = 0; /* default to empty string on error */ +#endif /* NOTIFY_TZ */ if (name == NULL && (name = TZDEFAULT) == NULL) return -1; { @@ -327,16 +715,24 @@ struct state * const sp; ** to hold the longest file name string that the implementation ** guarantees can be opened." */ - char fullname[FILENAME_MAX + 1]; + char *fullname; + + fullname = malloc(FILENAME_MAX + 1); + if (fullname == NULL) + goto out; if (name[0] == ':') ++name; doaccess = name[0] == '/'; if (!doaccess) { - if ((p = TZDIR) == NULL) + if ((p = TZDIR) == NULL) { + free(fullname); return -1; - if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname) + } + if (strlen(p) + 1 + strlen(name) >= FILENAME_MAX) { + free(fullname); return -1; + } (void) strcpy(fullname, p); (void) strcat(fullname, "/"); (void) strcat(fullname, name); @@ -347,57 +743,73 @@ struct state * const sp; doaccess = TRUE; name = fullname; } - if (doaccess && access(name, R_OK) != 0) +#ifdef NOTIFY_TZ + if (path) { + if (strlen(name) > FILENAME_MAX) + return -1; + strcpy(path, name); + } +#endif /* NOTIFY_TZ */ + if (doaccess && access(name, R_OK) != 0) { + free(fullname); return -1; - if ((fid = _open(name, OPEN_MODE)) == -1) + } + if ((fid = _open(name, OPEN_MODE)) == -1) { + free(fullname); return -1; + } if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) { + free(fullname); _close(fid); return -1; } + free(fullname); } - { - struct tzhead * tzhp; - union { - struct tzhead tzhead; - char buf[sizeof *sp + sizeof *tzhp]; - } u; + u = malloc(sizeof(*u)); + if (u == NULL) + goto out; +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzload: reading %s\n", name); +#endif /* NOTIFY_TZ_DEBUG */ + nread = _read(fid, u->buf, sizeof u->buf); + if (_close(fid) < 0 || nread <= 0) + goto out; + for (stored = 4; stored <= 8; stored *= 2) { int ttisstdcnt; int ttisgmtcnt; - i = _read(fid, u.buf, sizeof u.buf); - if (_close(fid) != 0) - return -1; - ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); - p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; + ttisstdcnt = (int) detzcode(u->tzhead.tzh_ttisstdcnt); + ttisgmtcnt = (int) detzcode(u->tzhead.tzh_ttisgmtcnt); + sp->leapcnt = (int) detzcode(u->tzhead.tzh_leapcnt); + sp->timecnt = (int) detzcode(u->tzhead.tzh_timecnt); + sp->typecnt = (int) detzcode(u->tzhead.tzh_typecnt); + sp->charcnt = (int) detzcode(u->tzhead.tzh_charcnt); + p = u->tzhead.tzh_charcnt + sizeof u->tzhead.tzh_charcnt; 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)) - return -1; - if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ + goto out; + if (nread - (p - u->buf) < + sp->timecnt * stored + /* ats */ sp->timecnt + /* types */ - sp->typecnt * (4 + 2) + /* ttinfos */ + sp->typecnt * 6 + /* ttinfos */ sp->charcnt + /* chars */ - sp->leapcnt * (4 + 4) + /* lsinfos */ + sp->leapcnt * (stored + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ - return -1; + goto out; for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = detzcode(p); - p += 4; + sp->ats[i] = (stored == 4) ? + detzcode(p) : detzcode64(p); + p += stored; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; if (sp->types[i] >= sp->typecnt) - return -1; + goto out; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; @@ -407,11 +819,11 @@ struct state * const sp; p += 4; ttisp->tt_isdst = (unsigned char) *p++; if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - return -1; + goto out; ttisp->tt_abbrind = (unsigned char) *p++; if (ttisp->tt_abbrind < 0 || ttisp->tt_abbrind > sp->charcnt) - return -1; + goto out; } for (i = 0; i < sp->charcnt; ++i) sp->chars[i] = *p++; @@ -420,8 +832,9 @@ struct state * const sp; struct lsinfo * lsisp; lsisp = &sp->lsis[i]; - lsisp->ls_trans = detzcode(p); - p += 4; + lsisp->ls_trans = (stored == 4) ? + detzcode(p) : detzcode64(p); + p += stored; lsisp->ls_corr = detzcode(p); p += 4; } @@ -435,7 +848,7 @@ struct state * const sp; ttisp->tt_ttisstd = *p++; if (ttisp->tt_ttisstd != TRUE && ttisp->tt_ttisstd != FALSE) - return -1; + goto out; } } for (i = 0; i < sp->typecnt; ++i) { @@ -448,11 +861,134 @@ struct state * const sp; ttisp->tt_ttisgmt = *p++; if (ttisp->tt_ttisgmt != TRUE && ttisp->tt_ttisgmt != FALSE) - return -1; + goto out; + } + } + /* + ** Out-of-sort ats should mean we're running on a + ** signed time_t system but using a data file with + ** unsigned values (or vice versa). + */ + for (i = 0; i < sp->timecnt - 2; ++i) + if (sp->ats[i] > sp->ats[i + 1]) { + ++i; + if (TYPE_SIGNED(time_t)) { + /* + ** Ignore the end (easy). + */ + sp->timecnt = i; + } else { + /* + ** Ignore the beginning (harder). + */ + register int j; + + for (j = 0; j + i < sp->timecnt; ++j) { + sp->ats[j] = sp->ats[j + i]; + sp->types[j] = sp->types[j + i]; + } + sp->timecnt = j; + } + break; + } + /* + ** If this is an old file, we're done. + */ + if (u->tzhead.tzh_version[0] == '\0') + break; + nread -= p - u->buf; + for (i = 0; i < nread; ++i) + u->buf[i] = p[i]; + /* + ** If this is a narrow integer time_t system, we're done. + */ + if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t)) + break; + } + if (doextend && nread > 2 && + u->buf[0] == '\n' && u->buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) { + struct state *ts; + register int result; + + ts = malloc(sizeof(*ts)); + if (ts == NULL) + goto out; + u->buf[nread - 1] = '\0'; + result = tzparse(&u->buf[1], ts, FALSE); + if (result == 0 && ts->typecnt == 2 && + sp->charcnt + ts->charcnt <= TZ_MAX_CHARS) { + for (i = 0; i < 2; ++i) + ts->ttis[i].tt_abbrind += + sp->charcnt; + for (i = 0; i < ts->charcnt; ++i) + sp->chars[sp->charcnt++] = + ts->chars[i]; + i = 0; + while (i < ts->timecnt && + ts->ats[i] <= + sp->ats[sp->timecnt - 1]) + ++i; + while (i < ts->timecnt && + sp->timecnt < TZ_MAX_TIMES) { + sp->ats[sp->timecnt] = + ts->ats[i]; + sp->types[sp->timecnt] = + sp->typecnt + + ts->types[i]; + ++sp->timecnt; + ++i; + } + sp->ttis[sp->typecnt++] = ts->ttis[0]; + sp->ttis[sp->typecnt++] = ts->ttis[1]; } + free(ts); + } + if (sp->timecnt > 1) { + for (i = 1; i < sp->timecnt; ++i) + if (typesequiv(sp, sp->types[i], sp->types[0]) && + differ_by_repeat(sp->ats[i], sp->ats[0])) { + sp->goback = TRUE; + break; + } + for (i = sp->timecnt - 2; i >= 0; --i) + if (typesequiv(sp, sp->types[sp->timecnt - 1], + sp->types[i]) && + differ_by_repeat(sp->ats[sp->timecnt - 1], + sp->ats[i])) { + sp->goahead = TRUE; + break; } } - return 0; + res = 0; +out: + free(u); + return (res); +} + +static int +typesequiv(sp, a, b) +const struct state * const sp; +const int a; +const int b; +{ + register int result; + + if (sp == NULL || + a < 0 || a >= sp->typecnt || + b < 0 || b >= sp->typecnt) + result = FALSE; + else { + register const struct ttinfo * ap = &sp->ttis[a]; + register const struct ttinfo * bp = &sp->ttis[b]; + result = ap->tt_gmtoff == bp->tt_gmtoff && + ap->tt_isdst == bp->tt_isdst && + ap->tt_ttisstd == bp->tt_ttisstd && + ap->tt_ttisgmt == bp->tt_ttisgmt && + strcmp(&sp->chars[ap->tt_abbrind], + &sp->chars[bp->tt_abbrind]) == 0; + } + return result; } static const int mon_lengths[2][MONSPERYEAR] = { @@ -466,19 +1002,48 @@ static const int year_lengths[2] = { /* ** 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 a pointer to that +** a valid character in a zone name is found. Return a pointer to that ** character. */ static const char * -getzname(strp) +getzname(strp, name, len) const char * strp; +char ** name; +size_t * len; { char c; + char * ket; + if (*strp == '<' && (ket = strchr(strp, '>')) != NULL) { + *name = (char *)(strp + 1); + *len = ket - strp - 1; + return ket + 1; + } + *name = (char *)strp; while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') ++strp; + *len = strp - *name; + return strp; +} + +/* +** Given a pointer into an extended time zone string, scan until the ending +** delimiter of the zone name is located. Return a pointer to the delimiter. +** +** As with getzname above, the legal character set is actually quite +** restricted, with other characters producing undefined results. +** We don't do any checking here; checking is done later in common-case code. +*/ + +static const char * +getqzname(register const char *strp, const int delim) +{ + register int c; + + while ((c = *strp) != '\0' && c != delim) + ++strp; return strp; } @@ -547,7 +1112,7 @@ long * const secsp; *secsp += num * SECSPERMIN; if (*strp == ':') { ++strp; - /* `SECSPERMIN' allows for leap seconds. */ + /* `SECSPERMIN' allows for leap seconds. */ strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) return NULL; @@ -586,7 +1151,7 @@ long * const offsetp; /* ** 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". +** 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. */ @@ -705,7 +1270,7 @@ const long offset; dow += DAYSPERWEEK; /* - ** "dow" is the day-of-week of the first day of the month. Get + ** "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. */ @@ -728,7 +1293,7 @@ const long offset; /* ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in - ** question. To get the Epoch-relative time of the specified local + ** question. To get the Epoch-relative time of the specified local ** time on that day, add the transition time and the current offset ** from UTC. */ @@ -758,18 +1323,15 @@ const int lastditch; int load_result; INITIALIZE(dstname); - stdname = name; if (lastditch) { + stdname = name; stdlen = strlen(name); /* length of standard zone name */ name += stdlen; if (stdlen >= sizeof sp->chars) stdlen = (sizeof sp->chars) - 1; stdoffset = 0; } else { - name = getzname(name); - stdlen = name - stdname; - if (stdlen < 3) - return -1; + name = getzname(name, (char **)&stdname, &stdlen); if (*name == '\0') return -1; /* was "stdoffset = 0;" */ else { @@ -778,15 +1340,25 @@ const int lastditch; return -1; } } - load_result = tzload(TZDEFRULES, sp); +#ifdef NOTIFY_TZ + load_result = tzload(TZDEFRULES, sp, NULL, FALSE); +#else /* !NOTIFY_TZ */ + load_result = tzload(TZDEFRULES, sp, FALSE); +#endif /* NOTIFY_TZ */ if (load_result != 0) sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - if (dstlen < 3) - return -1; + if (*name == '<') { + dstname = ++name; + name = getqzname(name, '>'); + if (*name != '>') + return -1; + dstlen = name - dstname; + name++; + } else { + dstname = name; + name = getzname(name, (char **)&dstname, &dstlen); + } if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); if (name == NULL) @@ -813,11 +1385,8 @@ const int lastditch; return -1; sp->typecnt = 2; /* standard time and DST */ /* - ** Two transitions per year, from EPOCH_YEAR to 2037. + ** Two transitions per year, from EPOCH_YEAR forward. */ - sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); - if (sp->timecnt > TZ_MAX_TIMES) - return -1; sp->ttis[0].tt_gmtoff = -dstoffset; sp->ttis[0].tt_isdst = 1; sp->ttis[0].tt_abbrind = stdlen + 1; @@ -827,7 +1396,12 @@ const int lastditch; atp = sp->ats; typep = sp->types; janfirst = 0; - for (year = EPOCH_YEAR; year <= 2037; ++year) { + sp->timecnt = 0; + for (year = EPOCH_YEAR; + sp->timecnt + 2 <= TZ_MAX_TIMES; + ++year) { + time_t newfirst; + starttime = transtime(janfirst, year, &start, stdoffset); endtime = transtime(janfirst, year, &end, @@ -843,8 +1417,13 @@ const int lastditch; *atp++ = endtime; *typep++ = 1; /* DST ends */ } - janfirst += year_lengths[isleap(year)] * + sp->timecnt += 2; + newfirst = janfirst; + newfirst += year_lengths[isleap(year)] * SECSPERDAY; + if (newfirst <= janfirst) + break; + janfirst = newfirst; } } else { long theirstdoffset; @@ -956,23 +1535,58 @@ const int lastditch; } static void +#ifdef NOTIFY_TZ +gmtload(sp, path) +#else /* ! NOTIFY_TZ */ gmtload(sp) +#endif /* NOTIFY_TZ */ struct state * const sp; +#ifdef NOTIFY_TZ +char *path; +#endif /* NOTIFY_TZ */ { - if (tzload(gmt, sp) != 0) +#ifdef NOTIFY_TZ + if (tzload(gmt, sp, path, TRUE) != 0) +#else /* ! NOTIFY_TZ */ + if (tzload(gmt, sp, TRUE) != 0) +#endif /* NOTIFY_TZ */ (void) tzparse(gmt, sp, TRUE); } static void tzsetwall_basic(int rdlocked) { +#ifdef NOTIFY_TZ + notify_check_tz(&lcl_notify); +#else + if (TZDEFAULT) { + static struct timespec last_mtimespec = {0, 0}; + struct stat statbuf; + + if (lstat(TZDEFAULT, &statbuf) == 0) { + if (statbuf.st_mtimespec.tv_sec > last_mtimespec.tv_sec || + (statbuf.st_mtimespec.tv_sec == last_mtimespec.tv_sec && + statbuf.st_mtimespec.tv_nsec > last_mtimespec.tv_nsec)) { + /* Trigger resetting the local TZ */ + lcl_is_set = 0; + } + last_mtimespec = statbuf.st_mtimespec; + } + } +#endif /* NOTIFY_TZ */ if (!rdlocked) _RWLOCK_RDLOCK(&lcl_rwlock); if (lcl_is_set < 0) { +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzsetwall_basic lcl_is_set < 0\n"); +#endif if (!rdlocked) _RWLOCK_UNLOCK(&lcl_rwlock); return; } +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzsetwall_basic not set\n"); +#endif _RWLOCK_UNLOCK(&lcl_rwlock); _RWLOCK_WRLOCK(&lcl_rwlock); @@ -980,7 +1594,7 @@ tzsetwall_basic(int rdlocked) #ifdef ALL_STATE if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); + lclptr = calloc(1, sizeof *lclptr); if (lclptr == NULL) { settzname(); /* all we can do */ _RWLOCK_UNLOCK(&lcl_rwlock); @@ -990,8 +1604,21 @@ tzsetwall_basic(int rdlocked) } } #endif /* defined ALL_STATE */ - if (tzload((char *) NULL, lclptr) != 0) +#ifdef NOTIFY_TZ + { + char fullname[FILENAME_MAX + 1]; + if (tzload((char *) NULL, lclptr, fullname, TRUE) != 0) + /* + * If fullname is empty (an error occurred) then + * default to the UTC path + */ + gmtload(lclptr, *fullname ? NULL : fullname); + notify_register_tz(fullname, &lcl_notify); + } +#else /* ! NOTIFY_TZ */ + if (tzload((char *) NULL, lclptr, TRUE) != 0) gmtload(lclptr); +#endif /* NOTIFY_TZ */ settzname(); _RWLOCK_UNLOCK(&lcl_rwlock); @@ -1002,10 +1629,13 @@ tzsetwall_basic(int rdlocked) void tzsetwall(void) { +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzsetwall called\n"); +#endif /* NOTIFY_TZ_DEBUG */ tzsetwall_basic(0); } -static void +__private_extern__ void tzset_basic(int rdlocked) { const char * name; @@ -1016,11 +1646,17 @@ tzset_basic(int rdlocked) return; } +#ifdef NOTIFY_TZ + notify_check_tz(&lcl_notify); +#endif /* NOTIFY_TZ */ if (!rdlocked) _RWLOCK_RDLOCK(&lcl_rwlock); if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) { if (!rdlocked) _RWLOCK_UNLOCK(&lcl_rwlock); +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzset_basic matched %s\n", lcl_TZname); +#endif return; } _RWLOCK_UNLOCK(&lcl_rwlock); @@ -1032,7 +1668,7 @@ tzset_basic(int rdlocked) #ifdef ALL_STATE if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); + lclptr = (struct state *) calloc(1, sizeof *lclptr); if (lclptr == NULL) { settzname(); /* all we can do */ _RWLOCK_UNLOCK(&lcl_rwlock); @@ -1053,9 +1689,34 @@ tzset_basic(int rdlocked) lclptr->ttis[0].tt_gmtoff = 0; lclptr->ttis[0].tt_abbrind = 0; (void) strcpy(lclptr->chars, gmt); - } else if (tzload(name, lclptr) != 0) +#ifdef NOTIFY_TZ + notify_register_tz(NULL, &lcl_notify); +#endif /* NOTIFY_TZ */ + } else +#ifdef NOTIFY_TZ + { + char fullname[FILENAME_MAX + 1]; + /* + * parsedOK indicates whether tzparse() was called and + * succeeded. This means that TZ is a time conversion + * specification, so we don't need to register for + * notifications. + */ + int parsedOK = FALSE; + if (tzload(name, lclptr, fullname, TRUE) != 0) + if (name[0] == ':' || !(parsedOK = tzparse(name, lclptr, FALSE) == 0)) + /* + * If fullname is empty (an error occurred) then + * default to the UTC path + */ + (void) gmtload(lclptr, *fullname ? NULL : fullname); + notify_register_tz(parsedOK ? NULL : fullname, &lcl_notify); + } +#else /* ! NOTIFY_TZ */ + if (tzload(name, lclptr, TRUE) != 0) if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) (void) gmtload(lclptr); +#endif /* NOTIFY_TZ */ settzname(); _RWLOCK_UNLOCK(&lcl_rwlock); @@ -1066,37 +1727,107 @@ tzset_basic(int rdlocked) void tzset(void) { +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("tzset called TZ=%s\n", getenv("TZ")); +#endif /* NOTIFY_TZ_DEBUG */ tzset_basic(0); } /* ** The easy way to behave "as if no library function calls" localtime ** is to not call it--so we drop its guts into "localsub", which can be -** freely called. (And no, the PANS doesn't require the above behavior-- +** freely called. (And no, the PANS doesn't require the above behavior-- ** but it *is* desirable.) ** ** The unused offset argument is for the benefit of mktime variants. */ /*ARGSUSED*/ -static void -localsub(timep, offset, tmp) -const time_t * const timep; -const long offset; -struct tm * const tmp; +#ifdef __LP64__ +__private_extern__ struct tm * +#else /* !__LP64__ */ +__private_extern__ void +#endif /* __LP64__ */ +localsub(const time_t *const timep, const long offset, struct tm *const tmp) { struct state * sp; const struct ttinfo * ttisp; int i; - const time_t t = *timep; - +#ifdef __LP64__ + struct tm * result; +#endif /* __LP64__ */ + const time_t t = *timep; + +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("localsub called\n"); +#endif /* NOTIFY_TZ_DEBUG */ sp = lclptr; #ifdef ALL_STATE if (sp == NULL) { +#ifdef __LP64__ + return gmtsub(timep, offset, tmp); +#else /* !__LP64__ */ gmtsub(timep, offset, tmp); return; +#endif /* __LP64__ */ } #endif /* defined ALL_STATE */ + if ((sp->goback && t < sp->ats[0]) || + (sp->goahead && t > sp->ats[sp->timecnt - 1])) { + time_t newt = t; + register time_t seconds; + register time_t tcycles; + register int_fast64_t icycles; + + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; + ++tcycles; + icycles = tcycles; + if (tcycles - icycles >= 1 || icycles - tcycles >= 1) { +#ifdef __LP64__ + return NULL; +#else /* !__LP64__ */ + return; +#endif /* __LP64__ */ + } + seconds = icycles; + seconds *= YEARSPERREPEAT; + seconds *= AVGSECSPERYEAR; + if (t < sp->ats[0]) + newt += seconds; + else newt -= seconds; + if (newt < sp->ats[0] || + newt > sp->ats[sp->timecnt - 1]) +#ifdef __LP64__ + return NULL; /* "cannot happen" */ + result = localsub(&newt, offset, tmp); + if (result == tmp) { +#else /* !__LP64__ */ + return; + localsub(&newt, offset, tmp); + { +#endif /* __LP64__ */ + register time_t newy; + + newy = tmp->tm_year; + if (t < sp->ats[0]) + newy -= icycles * YEARSPERREPEAT; + else newy += icycles * YEARSPERREPEAT; + tmp->tm_year = newy; + if (tmp->tm_year != newy) +#ifdef __LP64__ + return NULL; + } + return result; +#else /* !__LP64__ */ + return; + } + return; +#endif /* __LP64__ */ + } if (sp->timecnt == 0 || t < sp->ats[0]) { i = 0; while (sp->ttis[i].tt_isdst) @@ -1105,10 +1836,17 @@ struct tm * const tmp; break; } } else { - for (i = 1; i < sp->timecnt; ++i) - if (t < sp->ats[i]) - break; - i = sp->types[i - 1]; + register int lo = 1; + register int hi = sp->timecnt; + + while (lo < hi) { + register int mid = (lo + hi) >> 1; + + if (t < sp->ats[mid]) + hi = mid; + else lo = mid + 1; + } + i = (int) sp->types[lo - 1]; } ttisp = &sp->ttis[i]; /* @@ -1117,32 +1855,41 @@ struct tm * const tmp; ** t += ttisp->tt_gmtoff; ** timesub(&t, 0L, sp, tmp); */ +#ifdef __LP64__ + result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); + if (result == NULL) + return NULL; +#else /* !__LP64__ */ timesub(&t, ttisp->tt_gmtoff, sp, tmp); +#endif /* __LP64__ */ tmp->tm_isdst = ttisp->tt_isdst; tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; #ifdef TM_ZONE tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; #endif /* defined TM_ZONE */ +#ifdef __LP64__ + return result; +#endif /* __LP64__ */ +} + +static void +localtime_key_init(void) +{ + + localtime_key = __LIBC_PTHREAD_KEY_LOCALTIME; + localtime_key_error = pthread_key_init_np(localtime_key, free); } struct tm * -localtime(timep) -const time_t * const timep; +localtime(const time_t *const timep) { - static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER; - static pthread_key_t localtime_key = -1; struct tm *p_tm; if (__isthreaded != 0) { - if (localtime_key < 0) { - _pthread_mutex_lock(&localtime_mutex); - if (localtime_key < 0) { - if (_pthread_key_create(&localtime_key, free) < 0) { - _pthread_mutex_unlock(&localtime_mutex); - return(NULL); - } - } - _pthread_mutex_unlock(&localtime_mutex); + _pthread_once(&localtime_once, localtime_key_init); + if (localtime_key_error != 0) { + errno = localtime_key_error; + return(NULL); } p_tm = _pthread_getspecific(localtime_key); if (p_tm == NULL) { @@ -1153,13 +1900,21 @@ const time_t * const timep; } _RWLOCK_RDLOCK(&lcl_rwlock); tzset_basic(1); +#ifdef __LP64__ + p_tm = localsub(timep, 0L, p_tm); +#else /* !__LP64__ */ localsub(timep, 0L, p_tm); +#endif /* __LP64__ */ _RWLOCK_UNLOCK(&lcl_rwlock); return(p_tm); } else { tzset_basic(0); +#ifdef __LP64__ + return localsub(timep, 0L, &tm); +#else /* !__LP64__ */ localsub(timep, 0L, &tm); return(&tm); +#endif /* __LP64__ */ } } @@ -1168,40 +1923,73 @@ const time_t * const timep; */ struct tm * -localtime_r(timep, tm) -const time_t * const timep; -struct tm * tm; +localtime_r(const time_t *const __restrict timep, struct tm * __restrict tmp) { _RWLOCK_RDLOCK(&lcl_rwlock); tzset_basic(1); - localsub(timep, 0L, tm); +#ifdef __LP64__ + tmp = localsub(timep, 0L, tmp); +#else /* !__LP64__ */ + localsub(timep, 0L, tmp); +#endif /* __LP64__ */ _RWLOCK_UNLOCK(&lcl_rwlock); - return tm; + return tmp; +} + +static void +gmt_init(void) +{ + +#ifdef ALL_STATE +#ifdef NOTIFY_TZ + if (gmtptr == NULL) +#endif /* NOTIFY_TZ */ + gmtptr = (struct state *) calloc(1, sizeof *gmtptr); + if (gmtptr != NULL) +#endif /* defined ALL_STATE */ +#ifdef NOTIFY_TZ + { + char fullname[FILENAME_MAX + 1]; + gmtload(gmtptr, fullname); + notify_register_tz(fullname, &gmt_notify); + } +#else /* ! NOTIFY_TZ */ + gmtload(gmtptr); +#endif /* NOTIFY_TZ */ } /* ** gmtsub is to gmtime as localsub is to localtime. */ +#ifdef __LP64__ +static struct tm * +#else /* !__LP64__ */ static void +#endif /* __LP64__ */ gmtsub(timep, offset, tmp) const time_t * const timep; const long offset; struct tm * const tmp; { - if (!gmt_is_set) { - _MUTEX_LOCK(&gmt_mutex); - if (!gmt_is_set) { -#ifdef ALL_STATE - gmtptr = (struct state *) malloc(sizeof *gmtptr); - if (gmtptr != NULL) -#endif /* defined ALL_STATE */ - gmtload(gmtptr); - gmt_is_set = TRUE; - } - _MUTEX_UNLOCK(&gmt_mutex); - } +#ifdef __LP64__ + register struct tm * result; +#endif /* __LP64__ */ + +#ifdef NOTIFY_TZ_DEBUG + NOTIFY_TZ_PRINTF("gmtsub called\n"); +#endif /* NOTIFY_TZ_DEBUG */ +#ifdef NOTIFY_TZ + notify_check_tz(&gmt_notify); +#endif /* NOTIFY_TZ */ + pthread_once(&gmt_once, gmt_init); +#ifdef __LP64__ + result = timesub(timep, offset, gmtptr, tmp); + if (result == NULL) + return NULL; +#else /* !__LP64__ */ timesub(timep, offset, gmtptr, tmp); +#endif /* __LP64__ */ #ifdef TM_ZONE /* ** Could get fancy here and deliver something such as @@ -1209,11 +1997,11 @@ struct tm * const tmp; ** but this is no time for a treasure hunt. */ if (offset != 0) - tmp->TM_ZONE = wildabbr; + tmp->TM_ZONE = (char*)wildabbr; else { #ifdef ALL_STATE if (gmtptr == NULL) - tmp->TM_ZONE = gmt; + tmp->TM_ZONE = (char *)gmt; else tmp->TM_ZONE = gmtptr->chars; #endif /* defined ALL_STATE */ #ifndef ALL_STATE @@ -1221,26 +2009,29 @@ struct tm * const tmp; #endif /* State Farm */ } #endif /* defined TM_ZONE */ +#ifdef __LP64__ + return result; +#endif /* __LP64__ */ +} + +static void +gmtime_key_init(void) +{ + + gmtime_key = __LIBC_PTHREAD_KEY_GMTIME; + gmtime_key_error = pthread_key_init_np(gmtime_key, free); } struct tm * -gmtime(timep) -const time_t * const timep; +gmtime(const time_t *const timep) { - static pthread_mutex_t gmtime_mutex = PTHREAD_MUTEX_INITIALIZER; - static pthread_key_t gmtime_key = -1; struct tm *p_tm; if (__isthreaded != 0) { - if (gmtime_key < 0) { - _pthread_mutex_lock(&gmtime_mutex); - if (gmtime_key < 0) { - if (_pthread_key_create(&gmtime_key, free) < 0) { - _pthread_mutex_unlock(&gmtime_mutex); - return(NULL); - } - } - _pthread_mutex_unlock(&gmtime_mutex); + _pthread_once(&gmtime_once, gmtime_key_init); + if (gmtime_key_error != 0) { + errno = gmtime_key_error; + return(NULL); } /* * Changed to follow POSIX.1 threads standard, which @@ -1253,12 +2044,20 @@ const time_t * const timep; } _pthread_setspecific(gmtime_key, p_tm); } +#ifdef __LP64__ + return gmtsub(timep, 0L, p_tm); +#else /* !__LP64__ */ gmtsub(timep, 0L, p_tm); return(p_tm); +#endif /* __LP64__ */ } else { +#ifdef __LP64__ + return gmtsub(timep, 0L, &tm); +#else /* !__LP64__ */ gmtsub(timep, 0L, &tm); return(&tm); +#endif /* __LP64__ */ } } @@ -1267,28 +2066,54 @@ const time_t * const timep; */ struct tm * -gmtime_r(timep, tm) -const time_t * const timep; -struct tm * tm; +gmtime_r(const time_t *const timep, struct tm *tmp) { - gmtsub(timep, 0L, tm); - return tm; + +#ifdef __LP64__ + return gmtsub(timep, 0L, tmp); +#else /* !__LP64__ */ + gmtsub(timep, 0L, tmp); + return tmp; +#endif /* __LP64__ */ } #ifdef STD_INSPIRED struct tm * -offtime(timep, offset) -const time_t * const timep; -const long offset; +offtime(const time_t *const timep, const long offset) { +#ifdef __LP64__ + return gmtsub(timep, offset, &tm); +#else /* !__LP64__ */ gmtsub(timep, offset, &tm); return &tm; +#endif /* __LP64__ */ } #endif /* defined STD_INSPIRED */ +/* +** Return the number of leap years through the end of the given year +** where, to make the math easy, the answer for year zero is defined as zero. +*/ + +__unused static int +leaps_thru_end_of(y) +register const int y; +{ +#ifdef __LP64__ + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); +#else /* !__LP64__ */ + return (y / 4 - y / 100 + y / 400); +#endif /* __LP64__ */ +} + +#ifdef __LP64__ +static struct tm * +#else /* !__LP64__ */ static void +#endif /* __LP64__ */ timesub(timep, offset, sp, tmp) const time_t * const timep; const long offset; @@ -1365,7 +2190,12 @@ struct tm * const tmp; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; y = EPOCH_YEAR; -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) +#define _LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) +#ifdef __LP64__ +#define LEAPS_THRU_END_OF(y) ((y) >= 0 ? _LEAPS_THRU_END_OF(y) : _LEAPS_THRU_END_OF((y) + 1) - 1) +#else /* !__LP64__ */ +#define LEAPS_THRU_END_OF(y) _LEAPS_THRU_END_OF(y) +#endif /* __LP64__ */ while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { long newy; @@ -1377,7 +2207,16 @@ struct tm * const tmp; LEAPS_THRU_END_OF(y - 1); y = newy; } +#ifdef __LP64__ + y -= TM_YEAR_BASE; + if (y < INT_MIN || y > INT_MAX) { + errno = EOVERFLOW; + return NULL; + } + tmp->tm_year = y; +#else /* !__LP64__ */ tmp->tm_year = y - TM_YEAR_BASE; +#endif /* __LP64__ */ tmp->tm_yday = (int) days; ip = mon_lengths[yleap]; for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) @@ -1387,37 +2226,60 @@ struct tm * const tmp; #ifdef TM_GMTOFF tmp->TM_GMTOFF = offset; #endif /* defined TM_GMTOFF */ +#ifdef __LP64__ + return tmp; +#endif /* __LP64__ */ } char * -ctime(timep) -const time_t * const timep; +ctime(const time_t *const timep) { /* ** Section 4.12.3.2 of X3.159-1989 requires that ** The ctime function converts the calendar time pointed to by timer -** to local time in the form of a string. It is equivalent to +** to local time in the form of a string. It is equivalent to ** asctime(localtime(timer)) */ +#ifdef __LP64__ + /* + * In 64-bit, the timep value may produce a time value with a year + * that exceeds 32-bits in size (won't fit in struct tm), so localtime + * will return NULL. + */ + struct tm *tm = localtime(timep); + + if (tm == NULL) + return NULL; + return asctime(tm); +#else /* !__LP64__ */ return asctime(localtime(timep)); +#endif /* __LP64__ */ } char * -ctime_r(timep, buf) -const time_t * const timep; -char * buf; +ctime_r(const time_t *const timep, char *buf) { - struct tm tm; + struct tm mytm; - return asctime_r(localtime_r(timep, &tm), buf); +#ifdef __LP64__ + /* + * In 64-bit, the timep value may produce a time value with a year + * that exceeds 32-bits in size (won't fit in struct tm), so localtime_r + * will return NULL. + */ + if (localtime_r(timep, &mytm) == NULL) + return NULL; + return asctime_r(&mytm, buf); +#else /* !__LP64__ */ + return asctime_r(localtime_r(timep, &mytm), buf); +#endif /* __LP64__ */ } /* ** Adapted from code provided by Robert Elz, who writes: ** The "best" way to do mktime I think is based on an idea of Bob ** Kridle's (so its said...) from a long time ago. -** [kridle@xinet.com as of 1996-01-16.] -** It does a binary search of the time_t space. Since time_t's are +** It does a binary search of the time_t space. Since time_t's are ** just 32 bits, its a max of 32 iterations (even at 64 bits it ** would still be very reasonable). */ @@ -1427,7 +2289,7 @@ char * buf; #endif /* !defined WRONG */ /* -** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). +** Simplified normalize logic courtesy Paul Eggert. */ static int @@ -1443,10 +2305,19 @@ int delta; } static int -normalize_overflow(tensptr, unitsptr, base) -int * const tensptr; -int * const unitsptr; -const int base; +long_increment_overflow(number, delta) +long * number; +int delta; +{ + long number0; + + number0 = *number; + *number += delta; + return (*number < number0) != (delta < 0); +} + +static int +normalize_overflow(int *const tensptr, int *const unitsptr, const int base) { int tensdelta; @@ -1457,6 +2328,18 @@ const int base; return increment_overflow(tensptr, tensdelta); } +static int +long_normalize_overflow(long *const tensptr, int *const unitsptr, const int base) +{ + register int tensdelta; + + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return long_increment_overflow(tensptr, tensdelta); +} + static int tmcomp(atmp, btmp) const struct tm * const atmp; @@ -1464,8 +2347,14 @@ const struct tm * const btmp; { int result; - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && + /* + * Assume that atmp and btmp point to normalized tm strutures. + * So only arithmetic with tm_year could overflow in 64-bit. + */ + if (atmp->tm_year != btmp->tm_year) { + return (atmp->tm_year > btmp->tm_year ? 1 : -1); + } + if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && (result = (atmp->tm_min - btmp->tm_min)) == 0) @@ -1474,21 +2363,28 @@ const struct tm * const btmp; } static time_t -time2sub(tmp, funcp, offset, okayp, do_norm_secs) -struct tm * const tmp; -void (* const funcp)(const time_t*, long, struct tm*); -const long offset; -int * const okayp; -const int do_norm_secs; +time2sub(struct tm *const tmp, +#ifdef __LP64__ + struct tm *(*const funcp)(const time_t *, long, struct tm *), +#else /* !__LP64__ */ + void(*funcp) (const time_t *, long, struct tm*), +#endif /* __LP64__ */ + const long offset, + int *const okayp, + const int do_norm_secs, + int unix03) { const struct state * sp; int dir; - int bits; - int i, j ; + int i, j; int saved_seconds; - time_t newt; - time_t t; - struct tm yourtm, mytm; + long li; + time_t lo; + time_t hi; + long y; + time_t newt; + time_t t; + struct tm yourtm, mytm; *okayp = FALSE; yourtm = *tmp; @@ -1501,45 +2397,49 @@ const int do_norm_secs; return WRONG; if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) return WRONG; - if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) + y = yourtm.tm_year; + if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) return WRONG; /* - ** Turn yourtm.tm_year into an actual year number for now. + ** Turn y into an actual year number for now. ** It is converted back to an offset from TM_YEAR_BASE later. */ - if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) + if (long_increment_overflow(&y, TM_YEAR_BASE)) return WRONG; while (yourtm.tm_mday <= 0) { - if (increment_overflow(&yourtm.tm_year, -1)) + if (long_increment_overflow(&y, -1)) return WRONG; - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(i)]; + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday += year_lengths[isleap(li)]; } while (yourtm.tm_mday > DAYSPERLYEAR) { - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(i)]; - if (increment_overflow(&yourtm.tm_year, 1)) + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (long_increment_overflow(&y, 1)) return WRONG; } for ( ; ; ) { - i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; + i = mon_lengths[isleap(y)][yourtm.tm_mon]; if (yourtm.tm_mday <= i) break; yourtm.tm_mday -= i; if (++yourtm.tm_mon >= MONSPERYEAR) { yourtm.tm_mon = 0; - if (increment_overflow(&yourtm.tm_year, 1)) + if (long_increment_overflow(&y, 1)) return WRONG; } } - if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) + if (long_increment_overflow(&y, -TM_YEAR_BASE)) + return WRONG; + yourtm.tm_year = y; + if (yourtm.tm_year != y) return WRONG; /* Don't go below 1900 for POLA */ if (yourtm.tm_year < 0) return WRONG; if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) saved_seconds = 0; - else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { + else if (y + TM_YEAR_BASE < EPOCH_YEAR) { /* ** We can't set tm_sec to 0, because that might push the ** time below the minimum representable time. @@ -1557,35 +2457,66 @@ const int do_norm_secs; yourtm.tm_sec = 0; } /* - ** Divide the search space in half - ** (this works whether time_t is signed or unsigned). + ** Do a binary search (this works whatever time_t's type is). */ - bits = TYPE_BIT(time_t) - 1; - /* - ** If we have more than this, we will overflow tm_year for tmcomp(). - ** We should really return an error if we cannot represent it. - */ - if (bits > 48) - bits = 48; - /* - ** If time_t is signed, then 0 is just above the median, - ** assuming two's complement arithmetic. - ** If time_t is unsigned, then (1 << bits) is just above the median. - */ - t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); + if (!TYPE_SIGNED(time_t)) { + lo = 0; + hi = lo - 1; + } else if (!TYPE_INTEGRAL(time_t)) { + if (sizeof(time_t) > sizeof(float)) + hi = (time_t) DBL_MAX; + else hi = (time_t) FLT_MAX; + lo = -hi; + } else { + lo = 1; + for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) + lo *= 2; + hi = -(lo + 1); + } for ( ; ; ) { + t = lo / 2 + hi / 2; + if (t < lo) + t = lo; + else if (t > hi) + t = hi; +#ifdef __LP64__ + if ((*funcp)(&t, offset, &mytm) == NULL) { + /* + ** Assume that t is too extreme to be represented in + ** a struct tm; arrange things so that it is less + ** extreme on the next pass. + */ + dir = (t > 0) ? 1 : -1; + } else dir = tmcomp(&mytm, &yourtm); +#else /* !__LP64__ */ (*funcp)(&t, offset, &mytm); dir = tmcomp(&mytm, &yourtm); + // If we have searched the entire space without a match, exit + if (dir != 0 && t == lo && t == hi) + return WRONG; +#endif /* __LP64__ */ if (dir != 0) { - if (bits-- < 0) + if (t == lo) { + ++t; + if (t <= lo) + return WRONG; + ++lo; + } else if (t == hi) { + --t; + if (t >= hi) + return WRONG; + --hi; + } + if (lo > hi) return WRONG; - if (bits < 0) - --t; /* may be needed if new t is minimal */ - else if (dir > 0) - t -= ((time_t) 1) << bits; - else t += ((time_t) 1) << bits; + if (dir > 0) + hi = t; + else lo = t; continue; } + sp = (funcp == localsub) ? lclptr : gmtptr; + if (unix03 && sp->typecnt == 1 && yourtm.tm_isdst > 0) + yourtm.tm_isdst = 0; /* alternative time does not apply */ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) break; /* @@ -1594,7 +2525,6 @@ const int do_norm_secs; ** It's okay to guess wrong since the guess ** gets checked. */ - sp = (funcp == localsub) ? lclptr : gmtptr; #ifdef ALL_STATE if (sp == NULL) return WRONG; @@ -1607,7 +2537,12 @@ const int do_norm_secs; continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; +#ifdef __LP64__ + if ((*funcp)(&newt, offset, &mytm) == NULL) + continue; +#else /* !__LP64__ */ (*funcp)(&newt, offset, &mytm); +#endif /* __LP64__ */ if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) @@ -1626,17 +2561,26 @@ label: if ((newt < t) != (saved_seconds < 0)) return WRONG; t = newt; +#ifdef __LP64__ + if ((*funcp)(&t, offset, tmp) == NULL) + return WRONG; +#else /* !__LP64__ */ (*funcp)(&t, offset, tmp); - *okayp = TRUE; +#endif /* __LP64__ */ + *okayp = TRUE; return t; } static time_t -time2(tmp, funcp, offset, okayp) -struct tm * const tmp; -void (* const funcp)(const time_t*, long, struct tm*); -const long offset; -int * const okayp; +time2(struct tm * const tmp, +#ifdef __LP64__ + struct tm * (*const funcp)(const time_t *, long, struct tm *), +#else /* !__LP64__ */ + void (*const funcp)(const time_t *, long, struct tm *), +#endif /* __LP64__ */ + const long offset, + int *const okayp, + int unix03) { time_t t; @@ -1645,15 +2589,20 @@ int * const okayp; ** (in case tm_sec contains a value associated with a leap second). ** If that fails, try with normalization of seconds. */ - t = time2sub(tmp, funcp, offset, okayp, FALSE); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); + t = time2sub(tmp, funcp, offset, okayp, FALSE, unix03); + return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, unix03); } -static time_t -time1(tmp, funcp, offset) +__private_extern__ time_t +time1(tmp, funcp, offset, unix03) struct tm * const tmp; -void (* const funcp)(const time_t *, long, struct tm *); +#ifdef __LP64__ +struct tm * (* const funcp)(const time_t *, long, struct tm *); +#else /* !__LP64__ */ +void (* const funcp)(const time_t *, long, struct tm *); +#endif /* __LP64__ */ const long offset; +int unix03; { time_t t; const struct state * sp; @@ -1665,12 +2614,17 @@ const long offset; int types[TZ_MAX_TYPES]; int okay; + if (tmp == NULL) { + errno = EINVAL; + return WRONG; + } + if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); + t = time2(tmp, funcp, offset, &okay, unix03); #ifdef PCTS /* - ** PCTS code courtesy Grant Sullivan (grant@osf.org). + ** PCTS code courtesy Grant Sullivan. */ if (okay) return t; @@ -1687,7 +2641,7 @@ const long offset; ** We try to divine the type they started from and adjust to the ** type they need. */ - sp = (funcp == localsub) ? lclptr : gmtptr; + sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr); #ifdef ALL_STATE if (sp == NULL) return WRONG; @@ -1711,7 +2665,7 @@ const long offset; tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - sp->ttis[samei].tt_gmtoff; tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); + t = time2(tmp, funcp, offset, &okay, unix03); if (okay) return t; tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - @@ -1721,44 +2675,48 @@ const long offset; } return WRONG; } +#else /* BUILDING_VARIANT */ +extern pthread_rwlock_t lcl_rwlock; +#endif /* BUILDING_VARIANT */ time_t -mktime(tmp) -struct tm * const tmp; +mktime(struct tm *const tmp) { time_t mktime_return_value; + int serrno = errno; _RWLOCK_RDLOCK(&lcl_rwlock); tzset_basic(1); - mktime_return_value = time1(tmp, localsub, 0L); + mktime_return_value = time1(tmp, localsub, 0L, __DARWIN_UNIX03); _RWLOCK_UNLOCK(&lcl_rwlock); + errno = serrno; return(mktime_return_value); } +#if !BUILDING_VARIANT #ifdef STD_INSPIRED time_t -timelocal(tmp) -struct tm * const tmp; +timelocal(struct tm *const tmp) { - tmp->tm_isdst = -1; /* in case it wasn't initialized */ + if (tmp != NULL) + tmp->tm_isdst = -1; /* in case it wasn't initialized */ return mktime(tmp); } time_t -timegm(tmp) -struct tm * const tmp; +timegm(struct tm *const tmp) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); + if (tmp != NULL) + tmp->tm_isdst = 0; + return time1(tmp, gmtsub, 0L, __DARWIN_UNIX03); } time_t -timeoff(tmp, offset) -struct tm * const tmp; -const long offset; +timeoff(struct tm *const tmp, const long offset) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset); + if (tmp != NULL) + tmp->tm_isdst = 0; + return time1(tmp, gmtsub, offset, __DARWIN_UNIX03); } #endif /* defined STD_INSPIRED */ @@ -1771,8 +2729,7 @@ const long offset; */ long -gtime(tmp) -struct tm * const tmp; +gtime(struct tm *const tmp) { const time_t t = mktime(tmp); @@ -1798,8 +2755,7 @@ struct tm * const tmp; */ static long -leapcorr(timep) -time_t * timep; +leapcorr(time_t *timep) { struct state * sp; struct lsinfo * lp; @@ -1816,16 +2772,14 @@ time_t * timep; } time_t -time2posix(t) -time_t t; +time2posix(time_t t) { tzset(); return t - leapcorr(&t); } time_t -posix2time(t) -time_t t; +posix2time(time_t t) { time_t x; time_t y; @@ -1833,7 +2787,7 @@ time_t t; tzset(); /* ** For a positive leap second hit, the result - ** is not unique. For a negative leap second + ** is not unique. For a negative leap second ** hit, the corresponding time doesn't exist, ** so we return an adjacent second. */ @@ -1858,3 +2812,5 @@ time_t t; } #endif /* defined STD_INSPIRED */ +#endif /* !BUILDING_VARIANT */ +#pragma clang diagnostic pop