diff --git a/libc/Android.mk b/libc/Android.mk index bc6bbf306..b7278b7c1 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -688,11 +688,13 @@ LOCAL_SRC_FILES += upstream-openbsd/lib/libc/time/wcsftime.c LOCAL_CFLAGS := $(libc_common_cflags) \ -fvisibility=hidden \ + -Wno-unused-parameter \ # Don't use ridiculous amounts of stack. LOCAL_CFLAGS += -DALL_STATE # Include tzsetwall, timelocal, timegm, time2posix, and posix2time. LOCAL_CFLAGS += -DSTD_INSPIRED +LOCAL_CFLAGS += -DTHREAD_SAFE # The name of the tm_gmtoff field in our struct tm. LOCAL_CFLAGS += -DTM_GMTOFF=tm_gmtoff # Where we store our tzdata. diff --git a/libc/tzcode/asctime.c b/libc/tzcode/asctime.c index fea24e479..337a31375 100644 --- a/libc/tzcode/asctime.c +++ b/libc/tzcode/asctime.c @@ -55,7 +55,7 @@ ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n ** (two three-character abbreviations, five strings denoting integers, ** seven explicit spaces, two explicit colons, a newline, -** and a trailing ASCII nul). +** and a trailing NUL byte). ** The values above are for systems where an int is 32 bits and are provided ** as an example; the define below calculates the maximum for the system at ** hand. @@ -99,11 +99,11 @@ asctime_r(register const struct tm *timeptr, char *buf) ** Assume that strftime is unaffected by other out-of-range members ** (e.g., timeptr->tm_mday) when processing "%Y". */ - (void) strftime(year, sizeof year, "%Y", timeptr); + strftime(year, sizeof year, "%Y", timeptr); /* ** We avoid using snprintf since it's not available on all systems. */ - (void) snprintf(result, sizeof(result), /* Android change: use snprintf. */ + snprintf(result, sizeof(result), /* Android change: use snprintf. */ ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), wn, mn, timeptr->tm_mday, timeptr->tm_hour, @@ -112,11 +112,7 @@ asctime_r(register const struct tm *timeptr, char *buf) if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) return strcpy(buf, result); else { -#ifdef EOVERFLOW errno = EOVERFLOW; -#else /* !defined EOVERFLOW */ - errno = EINVAL; -#endif /* !defined EOVERFLOW */ return NULL; } } diff --git a/libc/tzcode/difftime.c b/libc/tzcode/difftime.c index 449cdf05d..ba2fd0377 100644 --- a/libc/tzcode/difftime.c +++ b/libc/tzcode/difftime.c @@ -7,42 +7,52 @@ #include "private.h" /* for time_t and TYPE_SIGNED */ +/* Return -X as a double. Using this avoids casting to 'double'. */ +static double +dminus(double x) +{ + return -x; +} + double ATTRIBUTE_CONST -difftime(const time_t time1, const time_t time0) +difftime(time_t time1, time_t time0) { /* - ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract + ** If double is large enough, simply convert and subtract ** (assuming that the larger type has more precision). */ - if (sizeof (double) > sizeof (time_t)) - return (double) time1 - (double) time0; - if (!TYPE_SIGNED(time_t)) { - /* - ** The difference of two unsigned values can't overflow - ** if the minuend is greater than or equal to the subtrahend. - */ - if (time1 >= time0) - return time1 - time0; - else return -(double) (time0 - time1); + if (sizeof (time_t) < sizeof (double)) { + double t1 = time1, t0 = time0; + return t1 - t0; } + + /* + ** The difference of two unsigned values can't overflow + ** if the minuend is greater than or equal to the subtrahend. + */ + if (!TYPE_SIGNED(time_t)) + return time0 <= time1 ? time1 - time0 : dminus(time0 - time1); + + /* Use uintmax_t if wide enough. */ + if (sizeof (time_t) <= sizeof (uintmax_t)) { + uintmax_t t1 = time1, t0 = time0; + return time0 <= time1 ? t1 - t0 : dminus(t0 - t1); + } + /* ** Handle cases where both time1 and time0 have the same sign ** (meaning that their difference cannot overflow). */ if ((time1 < 0) == (time0 < 0)) - return time1 - time0; + return time1 - time0; + /* - ** time1 and time0 have opposite signs. - ** Punt if uintmax_t is too narrow. + ** The values have opposite signs and uintmax_t is too narrow. ** This suffers from double rounding; attempt to lessen that ** by using long double temporaries. */ - if (sizeof (uintmax_t) < sizeof (time_t)) - return (long double) time1 - (long double) time0; - /* - ** Stay calm...decent optimizers will eliminate the complexity below. - */ - if (time1 >= 0 /* && time0 < 0 */) - return (uintmax_t) time1 + (uintmax_t) (-1 - time0) + 1; - return -(double) ((uintmax_t) time0 + (uintmax_t) (-1 - time1) + 1); + { + long double t1 = time1, t0 = time0; + return t1 - t0; + } } diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c index 3a8a3676e..79c4a9a59 100644 --- a/libc/tzcode/localtime.c +++ b/libc/tzcode/localtime.c @@ -10,10 +10,30 @@ /*LINTLIBRARY*/ +#define LOCALTIME_IMPLEMENTATION #include "private.h" + #include "tzfile.h" #include "fcntl.h" +#if THREAD_SAFE +# include +static pthread_mutex_t locallock = PTHREAD_MUTEX_INITIALIZER; +static int lock(void) { return pthread_mutex_lock(&locallock); } +static void unlock(void) { pthread_mutex_unlock(&locallock); } +#else +static int lock(void) { return 0; } +static void unlock(void) { } +#endif + +/* NETBSD_INSPIRED_EXTERN functions are exported to callers if + NETBSD_INSPIRED is defined, and are private otherwise. */ +#if NETBSD_INSPIRED +# define NETBSD_INSPIRED_EXTERN +#else +# define NETBSD_INSPIRED_EXTERN static +#endif + #ifndef TZ_ABBR_MAX_LEN #define TZ_ABBR_MAX_LEN 16 #endif /* !defined TZ_ABBR_MAX_LEN */ @@ -38,19 +58,6 @@ #define OPEN_MODE O_RDONLY #endif /* !defined O_BINARY */ -#if 0 -# define XLOG(xx) printf xx , fflush(stdout) -#else -# define XLOG(x) do{}while (0) -#endif - -/* BEGIN android-added: thread-safety. */ -#include -static pthread_mutex_t _tzMutex = PTHREAD_MUTEX_INITIALIZER; -static inline void _tzLock(void) { pthread_mutex_lock(&_tzMutex); } -static inline void _tzUnlock(void) { pthread_mutex_unlock(&_tzMutex); } -/* END android-added */ - #ifndef WILDABBR /* ** Someone might make incorrect use of a time zone abbreviation: @@ -91,10 +98,10 @@ static const char gmt[] = "GMT"; struct ttinfo { /* time type information */ int_fast32_t tt_gmtoff; /* UT offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ + bool tt_isdst; /* used to set tm_isdst */ int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is UT */ + bool tt_ttisstd; /* transition is std time */ + bool tt_ttisgmt; /* transition is UT */ }; struct lsinfo { /* leap second information */ @@ -102,6 +109,7 @@ struct lsinfo { /* leap second information */ int_fast64_t ls_corr; /* correction to apply */ }; +#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b)) #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) #ifdef TZNAME_MAX @@ -116,8 +124,8 @@ struct state { int timecnt; int typecnt; int charcnt; - int goback; - int goahead; + bool goback; + bool goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; @@ -127,74 +135,29 @@ struct state { int defaulttype; /* for early times or if no transitions */ }; +enum r_type { + JULIAN_DAY, /* Jn = Julian day */ + DAY_OF_YEAR, /* n = day of year */ + MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */ +}; + struct rule { - int r_type; /* type of rule; see below */ + enum r_type r_type; /* type of rule */ int r_day; /* day number of rule */ int r_week; /* week number of rule */ int r_mon; /* month number of rule */ int_fast32_t r_time; /* transition time of rule */ }; -#define JULIAN_DAY 0 /* Jn = Julian day */ -#define DAY_OF_YEAR 1 /* n = day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d = month, week, day of week */ - -/* -** Prototypes for static functions. -*/ - -/* NOTE: all internal functions assume that _tzLock() was already called */ - -static int __bionic_open_tzdata(const char*, int*); -static int_fast32_t detzcode(const char * codep); -static int_fast64_t detzcode64(const char * codep); -static int differ_by_repeat(time_t t1, time_t t0); -static const char * getzname(const char * strp) ATTRIBUTE_PURE; -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, int_fast32_t * secsp); -static const char * getoffset(const char * strp, int_fast32_t * offsetp); -static const char * getrule(const char * strp, struct rule * rulep); -static void gmtload(struct state * sp); -static struct tm * gmtsub(const time_t * timep, int_fast32_t offset, - struct tm * tmp, struct state * sp); // android-changed: added sp. -static struct tm * localsub(const time_t * timep, int_fast32_t offset, - struct tm * tmp, struct state * sp); // android-changed: added sp. -static int increment_overflow(int * number, int delta); -static int leaps_thru_end_of(int y) ATTRIBUTE_PURE; -static int increment_overflow32(int_fast32_t * number, int delta); -static int increment_overflow_time(time_t *t, int_fast32_t delta); -static int normalize_overflow32(int_fast32_t * tensptr, - int * unitsptr, int base); -static int normalize_overflow(int * tensptr, int * unitsptr, - int base); -static void settzname(void); -static time_t time1(struct tm * tmp, - struct tm * (*funcp)(const time_t *, - int_fast32_t, struct tm *, struct state *), // android-changed: added state*. - int_fast32_t, struct state * sp); // android-changed: added sp. -static time_t time2(struct tm * tmp, - struct tm * (*funcp)(const time_t *, - int_fast32_t, struct tm*, struct state *), // android-changed: added state*. - int_fast32_t offset, int * okayp, struct state * sp); // android-changed: added sp. -static time_t time2sub(struct tm *tmp, - struct tm * (*funcp) (const time_t *, - int_fast32_t, struct tm*, struct state *), // android-changed: added state*. - int_fast32_t offset, int * okayp, int do_norm_secs, struct state * sp); // android-change: added sp. -static struct tm * timesub(const time_t * timep, int_fast32_t offset, - const struct state * sp, struct tm * tmp); -static int tmcomp(const struct tm * atmp, - const struct tm * btmp); -static int_fast32_t transtime(int year, const struct rule * rulep, - int_fast32_t offset) - ATTRIBUTE_PURE; -static int typesequiv(const struct state * sp, int a, int b); -static int tzload(const char * name, struct state * sp, - int doextend); -static int tzparse(const char * name, struct state * sp, - int lastditch); +static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, + struct tm *); +static bool increment_overflow(int *, int); +static bool increment_overflow_time(time_t *, int_fast32_t); +static bool normalize_overflow32(int_fast32_t *, int *, int); +static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, + struct tm *); +static bool typesequiv(struct state const *, int, int); +static bool tzparse(char const *, struct state *, bool); #ifdef ALL_STATE static struct state * lclptr; @@ -214,7 +177,6 @@ static struct state gmtmem; static char lcl_TZname[TZ_STRLEN_MAX + 1]; static int lcl_is_set; -static int gmt_is_set; char * tzname[2] = { (char *) wildabbr, @@ -229,107 +191,150 @@ char * tzname[2] = { ** Thanks to Paul Eggert for noting this. */ -static struct tm tmGlobal; +static struct tm tm; #ifdef USG_COMPAT -long timezone = 0; -int daylight = 0; +long timezone; +int daylight; #endif /* defined USG_COMPAT */ #ifdef ALTZONE -long altzone = 0; +long altzone; #endif /* defined ALTZONE */ +/* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */ +static void +init_ttinfo(struct ttinfo *s, int_fast32_t gmtoff, bool isdst, int abbrind) +{ + s->tt_gmtoff = gmtoff; + s->tt_isdst = isdst; + s->tt_abbrind = abbrind; + s->tt_ttisstd = false; + s->tt_ttisgmt = false; +} + static int_fast32_t detzcode(const char *const codep) { - register int_fast32_t result; - register int i; + register int_fast32_t result; + register int i; + int_fast32_t one = 1; + int_fast32_t halfmaxval = one << (32 - 2); + int_fast32_t maxval = halfmaxval - 1 + halfmaxval; + int_fast32_t minval = -1 - maxval; - result = (codep[0] & 0x80) ? -1 : 0; - for (i = 0; i < 4; ++i) - result = (result << 8) | (codep[i] & 0xff); - return result; + result = codep[0] & 0x7f; + for (i = 1; i < 4; ++i) + result = (result << 8) | (codep[i] & 0xff); + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0; + result += minval; + } + return result; } static int_fast64_t detzcode64(const char *const codep) { - register int_fast64_t result; - register int i; + register uint_fast64_t result; + register int i; + int_fast64_t one = 1; + int_fast64_t halfmaxval = one << (64 - 2); + int_fast64_t maxval = halfmaxval - 1 + halfmaxval; + int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval; - result = (codep[0] & 0x80) ? -1 : 0; - for (i = 0; i < 8; ++i) - result = (result << 8) | (codep[i] & 0xff); - return result; + result = codep[0] & 0x7f; + for (i = 1; i < 8; ++i) + result = (result << 8) | (codep[i] & 0xff); + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0; + result += minval; + } + return result; +} + +static void +update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp) +{ + tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_abbrind]; +#ifdef USG_COMPAT + if (!ttisp->tt_isdst) + timezone = - ttisp->tt_gmtoff; +#endif +#ifdef ALTZONE + if (ttisp->tt_isdst) + altzone = - ttisp->tt_gmtoff; +#endif } static void settzname(void) { - register struct state * const sp = lclptr; - register int i; + register struct state * const sp = lclptr; + register int i; - tzname[0] = tzname[1] = (char *) wildabbr; + tzname[0] = tzname[1] = (char *) wildabbr; #ifdef USG_COMPAT - daylight = 0; - timezone = 0; + daylight = 0; + timezone = 0; #endif /* defined USG_COMPAT */ #ifdef ALTZONE - altzone = 0; + altzone = 0; #endif /* defined ALTZONE */ - if (sp == NULL) { - tzname[0] = tzname[1] = (char *) gmt; - return; - } - /* - ** And to get the latest zone names into tzname. . . - */ - for (i = 0; i < sp->typecnt; ++i) { - register const struct ttinfo * const ttisp = &sp->ttis[i]; - - tzname[ttisp->tt_isdst] = &sp->chars[ttisp->tt_abbrind]; - } - for (i = 0; i < sp->timecnt; ++i) { - register const struct ttinfo * const ttisp = - &sp->ttis[ - sp->types[i]]; - - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; + if (sp == NULL) { + tzname[0] = tzname[1] = (char *) gmt; + return; + } + /* + ** And to get the latest zone names into tzname. . . + */ + for (i = 0; i < sp->typecnt; ++i) { + register const struct ttinfo * const ttisp = &sp->ttis[i]; + update_tzname_etc(sp, ttisp); + } + for (i = 0; i < sp->timecnt; ++i) { + register const struct ttinfo * const ttisp = + &sp->ttis[ + sp->types[i]]; + update_tzname_etc(sp, ttisp); #ifdef USG_COMPAT - if (ttisp->tt_isdst) - daylight = 1; - if (!ttisp->tt_isdst) - timezone = -(ttisp->tt_gmtoff); + if (ttisp->tt_isdst) + daylight = 1; #endif /* defined USG_COMPAT */ -#ifdef ALTZONE - if (ttisp->tt_isdst) - altzone = -(ttisp->tt_gmtoff); -#endif /* defined ALTZONE */ - } - /* - ** Finally, scrub the abbreviations. - ** First, replace bogus characters. - */ - 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]; - - if (strlen(cp) > TZ_ABBR_MAX_LEN && - strcmp(cp, GRANDPARENTED) != 0) - *(cp + TZ_ABBR_MAX_LEN) = '\0'; - } + } } -static int -differ_by_repeat(const time_t t1 __unused, const time_t t0 __unused) +static void +scrub_abbrs(struct state *sp) +{ + int i; + /* + ** First, replace bogus characters. + */ + 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]; + + if (strlen(cp) > TZ_ABBR_MAX_LEN && + strcmp(cp, GRANDPARENTED) != 0) + *(cp + TZ_ABBR_MAX_LEN) = '\0'; + } +} + +static bool +differ_by_repeat(const time_t t1, const time_t t0) { if (TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) return 0; @@ -338,318 +343,387 @@ differ_by_repeat(const time_t t1 __unused, const time_t t0 __unused) #endif } -static int -tzload(register const char* name, register struct state* const sp, - register const int doextend) -{ - register const char * p; - register int i; - register int fid; - register int stored; - register int nread; - typedef union { - struct tzhead tzhead; - char buf[2 * sizeof(struct tzhead) + - 2 * sizeof *sp + - 4 * TZ_MAX_TIMES]; - } u_t; - union local_storage { - /* - ** Section 4.9.1 of the C standard says that - ** "FILENAME_MAX expands to an integral constant expression - ** that is the size needed for an array of char large enough - ** to hold the longest file name string that the implementation - ** guarantees can be opened." - */ - //char fullname[FILENAME_MAX + 1]; +/* Input buffer for data read from a compiled tz file. */ +union input_buffer { + /* The first part of the buffer, interpreted as a header. */ + struct tzhead tzhead; - /* The main part of the storage for this function. */ - struct { - u_t u; - struct state st; - } u; - }; - //register char *fullname; - register u_t *up; - register union local_storage *lsp; -#ifdef ALL_STATE - lsp = malloc(sizeof *lsp); - if (!lsp) - return -1; -#else /* !defined ALL_STATE */ - union local_storage ls; - lsp = &ls; -#endif /* !defined ALL_STATE */ - //fullname = lsp->fullname; - up = &lsp->u.u; - - sp->goback = sp->goahead = FALSE; - - if (! name) { - name = TZDEFAULT; - if (! name) - goto oops; - } - - int toread; - fid = __bionic_open_tzdata(name, &toread); - if (fid < 0) - goto oops; - - nread = read(fid, up->buf, sizeof up->buf); - if (close(fid) < 0 || nread <= 0) - goto oops; - for (stored = 4; stored <= 8; stored *= 2) { - int ttisstdcnt; - int ttisgmtcnt; - int timecnt; - - ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt); - p = up->tzhead.tzh_charcnt + sizeof up->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)) - goto oops; - if (nread - (p - up->buf) < - sp->timecnt * stored + /* ats */ - sp->timecnt + /* types */ - sp->typecnt * 6 + /* ttinfos */ - sp->charcnt + /* chars */ - sp->leapcnt * (stored + 4) + /* lsinfos */ - ttisstdcnt + /* ttisstds */ - ttisgmtcnt) /* ttisgmts */ - goto oops; - timecnt = 0; - for (i = 0; i < sp->timecnt; ++i) { - int_fast64_t at - = stored == 4 ? detzcode(p) : detzcode64(p); - sp->types[i] = ((TYPE_SIGNED(time_t) - ? time_t_min <= at - : 0 <= at) - && at <= time_t_max); - if (sp->types[i]) { - if (i && !timecnt && at != time_t_min) { - /* - ** Keep the earlier record, but tweak - ** it so that it starts with the - ** minimum time_t value. - */ - sp->types[i - 1] = 1; - sp->ats[timecnt++] = time_t_min; - } - sp->ats[timecnt++] = at; - } - p += stored; - } - timecnt = 0; - for (i = 0; i < sp->timecnt; ++i) { - unsigned char typ = *p++; - if (sp->typecnt <= typ) - goto oops; - if (sp->types[i]) - sp->types[timecnt++] = typ; - } - sp->timecnt = timecnt; - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - ttisp->tt_gmtoff = detzcode(p); - p += 4; - ttisp->tt_isdst = (unsigned char) *p++; - if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - goto oops; - ttisp->tt_abbrind = (unsigned char) *p++; - if (ttisp->tt_abbrind < 0 || - ttisp->tt_abbrind > sp->charcnt) - goto oops; - } - for (i = 0; i < sp->charcnt; ++i) - sp->chars[i] = *p++; - sp->chars[i] = '\0'; /* ensure '\0' at end */ - for (i = 0; i < sp->leapcnt; ++i) { - register struct lsinfo * lsisp; - - lsisp = &sp->lsis[i]; - lsisp->ls_trans = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - lsisp->ls_corr = detzcode(p); - p += 4; - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = FALSE; - else { - ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != TRUE && - ttisp->tt_ttisstd != FALSE) - goto oops; - } - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = FALSE; - else { - ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != TRUE && - ttisp->tt_ttisgmt != FALSE) - goto oops; - } - } - /* - ** If this is an old file, we're done. - */ - if (up->tzhead.tzh_version[0] == '\0') - break; - nread -= p - up->buf; - for (i = 0; i < nread; ++i) - up->buf[i] = p[i]; - /* - ** If this is a signed narrow time_t system, we're done. - */ - if (TYPE_SIGNED(time_t) && stored >= (int) sizeof(time_t)) - break; - } - if (doextend && nread > 2 && - up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && - sp->typecnt + 2 <= TZ_MAX_TYPES) { - struct state *ts = &lsp->u.st; - register int result; - - up->buf[nread - 1] = '\0'; - result = tzparse(&up->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]; - } - } - 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; - } - } - /* - ** If type 0 is is unused in transitions, - ** it's the type to use for early times. - */ - for (i = 0; i < sp->typecnt; ++i) - if (sp->types[i] == 0) - break; - i = (i >= sp->typecnt) ? 0 : -1; - /* - ** Absent the above, - ** if there are transition times - ** and the first transition is to a daylight time - ** find the standard type less than and closest to - ** the type of the first transition. - */ - if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) { - i = sp->types[0]; - while (--i >= 0) - if (!sp->ttis[i].tt_isdst) - break; - } - /* - ** If no result yet, find the first standard type. - ** If there is none, punt to type zero. - */ - if (i < 0) { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) { - i = 0; - break; - } - } - sp->defaulttype = i; -#ifdef ALL_STATE - free(up); -#endif /* defined ALL_STATE */ - return 0; -oops: -#ifdef ALL_STATE - free(up); -#endif /* defined ALL_STATE */ - return -1; -} - -static int -typesequiv(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] = { - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + /* The entire buffer. */ + char buf[2 * sizeof(struct tzhead) + 2 * sizeof (struct state) + + 4 * TZ_MAX_TIMES]; }; -static const int year_lengths[2] = { - DAYSPERNYEAR, DAYSPERLYEAR +/* Local storage needed for 'tzloadbody'. */ +union local_storage { + /* The file name to be opened. */ + char fullname[FILENAME_MAX + 1]; + + /* The results of analyzing the file's contents after it is opened. */ + struct { + /* The input buffer. */ + union input_buffer u; + + /* A temporary state used for parsing a TZ string in the file. */ + struct state st; + } u; +}; + +static int __bionic_open_tzdata(const char*); + +/* Load tz data from the file named NAME into *SP. Read extended + format if DOEXTEND. Use *LSP for temporary storage. Return 0 on + success, an errno value on failure. */ +static int +tzloadbody(char const *name, struct state *sp, bool doextend, + union local_storage *lsp) +{ + register int i; + register int fid; + register int stored; + register ssize_t nread; +#if !defined(__ANDROID__) + register bool doaccess; + register char *fullname = lsp->fullname; +#endif + register union input_buffer *up = &lsp->u.u; + register int tzheadsize = sizeof (struct tzhead); + + sp->goback = sp->goahead = false; + + if (! name) { + name = TZDEFAULT; + if (! name) + return EINVAL; + } + +#if defined(__ANDROID__) + fid = __bionic_open_tzdata(name); +#else + if (name[0] == ':') + ++name; + doaccess = name[0] == '/'; + if (!doaccess) { + char const *p = TZDIR; + if (! p) + return EINVAL; + if (sizeof lsp->fullname - 1 <= strlen(p) + strlen(name)) + return ENAMETOOLONG; + strcpy(fullname, p); + strcat(fullname, "/"); + strcat(fullname, name); + /* Set doaccess if '.' (as in "../") shows up in name. */ + if (strchr(name, '.')) + doaccess = true; + name = fullname; + } + if (doaccess && access(name, R_OK) != 0) + return errno; + fid = open(name, OPEN_MODE); +#endif + if (fid < 0) + return errno; + + nread = read(fid, up->buf, sizeof up->buf); + if (nread < tzheadsize) { + int err = nread < 0 ? errno : EINVAL; + close(fid); + return err; + } + if (close(fid) < 0) + return errno; + for (stored = 4; stored <= 8; stored *= 2) { + int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt); + int_fast32_t ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt); + int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt); + int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt); + int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt); + int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt); + char const *p = up->buf + tzheadsize; + if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS + && 0 < typecnt && typecnt < TZ_MAX_TYPES + && 0 <= timecnt && timecnt < TZ_MAX_TIMES + && 0 <= charcnt && charcnt < TZ_MAX_CHARS + && (ttisstdcnt == typecnt || ttisstdcnt == 0) + && (ttisgmtcnt == typecnt || ttisgmtcnt == 0))) + return EINVAL; + if (nread + < (tzheadsize /* struct tzhead */ + + timecnt * stored /* ats */ + + timecnt /* types */ + + typecnt * 6 /* ttinfos */ + + charcnt /* chars */ + + leapcnt * (stored + 4) /* lsinfos */ + + ttisstdcnt /* ttisstds */ + + ttisgmtcnt)) /* ttisgmts */ + return EINVAL; + sp->leapcnt = leapcnt; + sp->timecnt = timecnt; + sp->typecnt = typecnt; + sp->charcnt = charcnt; + + /* Read transitions, discarding those out of time_t range. + But pretend the last transition before time_t_min + occurred at time_t_min. */ + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + int_fast64_t at + = stored == 4 ? detzcode(p) : detzcode64(p); + sp->types[i] = at <= time_t_max; + if (sp->types[i]) { + time_t attime + = ((TYPE_SIGNED(time_t) ? at < time_t_min : at < 0) + ? time_t_min : at); + if (timecnt && attime <= sp->ats[timecnt - 1]) { + if (attime < sp->ats[timecnt - 1]) + return EINVAL; + sp->types[i - 1] = 0; + timecnt--; + } + sp->ats[timecnt++] = attime; + } + p += stored; + } + + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + unsigned char typ = *p++; + if (sp->typecnt <= typ) + return EINVAL; + if (sp->types[i]) + sp->types[timecnt++] = typ; + } + sp->timecnt = timecnt; + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + unsigned char isdst, abbrind; + + ttisp = &sp->ttis[i]; + ttisp->tt_gmtoff = detzcode(p); + p += 4; + isdst = *p++; + if (! (isdst < 2)) + return EINVAL; + ttisp->tt_isdst = isdst; + abbrind = *p++; + if (! (abbrind < sp->charcnt)) + return EINVAL; + ttisp->tt_abbrind = abbrind; + } + for (i = 0; i < sp->charcnt; ++i) + sp->chars[i] = *p++; + sp->chars[i] = '\0'; /* ensure '\0' at end */ + + /* Read leap seconds, discarding those out of time_t range. */ + leapcnt = 0; + for (i = 0; i < sp->leapcnt; ++i) { + int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p); + int_fast32_t corr = detzcode(p + stored); + p += stored + 4; + if (tr <= time_t_max) { + time_t trans + = ((TYPE_SIGNED(time_t) ? tr < time_t_min : tr < 0) + ? time_t_min : tr); + if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans) { + if (trans < sp->lsis[leapcnt - 1].ls_trans) + return EINVAL; + leapcnt--; + } + sp->lsis[leapcnt].ls_trans = trans; + sp->lsis[leapcnt].ls_corr = corr; + leapcnt++; + } + } + sp->leapcnt = leapcnt; + + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisstdcnt == 0) + ttisp->tt_ttisstd = false; + else { + if (*p != true && *p != false) + return EINVAL; + ttisp->tt_ttisstd = *p++; + } + } + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisgmtcnt == 0) + ttisp->tt_ttisgmt = false; + else { + if (*p != true && *p != false) + return EINVAL; + ttisp->tt_ttisgmt = *p++; + } + } + /* + ** If this is an old file, we're done. + */ + if (up->tzhead.tzh_version[0] == '\0') + break; + nread -= p - up->buf; + memmove(up->buf, p, nread); + } + if (doextend && nread > 2 && + up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) { + struct state *ts = &lsp->u.st; + + up->buf[nread - 1] = '\0'; + if (tzparse(&up->buf[1], ts, false) + && ts->typecnt == 2) { + + /* Attempt to reuse existing abbreviations. + Without this, America/Anchorage would stop + working after 2037 when TZ_MAX_CHARS is 50, as + sp->charcnt equals 42 (for LMT CAT CAWT CAPT AHST + AHDT YST AKDT AKST) and ts->charcnt equals 10 + (for AKST AKDT). Reusing means sp->charcnt can + stay 42 in this example. */ + int gotabbr = 0; + int charcnt = sp->charcnt; + for (i = 0; i < 2; i++) { + char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind; + int j; + for (j = 0; j < charcnt; j++) + if (strcmp(sp->chars + j, tsabbr) == 0) { + ts->ttis[i].tt_abbrind = j; + gotabbr++; + break; + } + if (! (j < charcnt)) { + int tsabbrlen = strlen(tsabbr); + if (j + tsabbrlen < TZ_MAX_CHARS) { + strcpy(sp->chars + j, tsabbr); + charcnt = j + tsabbrlen + 1; + ts->ttis[i].tt_abbrind = j; + gotabbr++; + } + } + } + if (gotabbr == 2) { + sp->charcnt = charcnt; + for (i = 0; i < ts->timecnt; i++) + if (sp->ats[sp->timecnt - 1] < ts->ats[i]) + break; + 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]; + } + } + } + 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; + } + } + /* + ** If type 0 is is unused in transitions, + ** it's the type to use for early times. + */ + for (i = 0; i < sp->timecnt; ++i) + if (sp->types[i] == 0) + break; + i = i < sp->timecnt ? -1 : 0; + /* + ** Absent the above, + ** if there are transition times + ** and the first transition is to a daylight time + ** find the standard type less than and closest to + ** the type of the first transition. + */ + if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) { + i = sp->types[0]; + while (--i >= 0) + if (!sp->ttis[i].tt_isdst) + break; + } + /* + ** If no result yet, find the first standard type. + ** If there is none, punt to type zero. + */ + if (i < 0) { + i = 0; + while (sp->ttis[i].tt_isdst) + if (++i >= sp->typecnt) { + i = 0; + break; + } + } + sp->defaulttype = i; + return 0; +} + +/* Load tz data from the file named NAME into *SP. Read extended + format if DOEXTEND. Return 0 on success, an errno value on failure. */ +static int +tzload(char const *name, struct state *sp, bool doextend) +{ +#ifdef ALL_STATE + union local_storage *lsp = malloc(sizeof *lsp); + if (!lsp) + return errno; + else { + int err = tzloadbody(name, sp, doextend, lsp); + free(lsp); + return err; + } +#else + union local_storage ls; + return tzloadbody(name, sp, doextend, &ls); +#endif +} + +static bool +typesequiv(const struct state *sp, int a, int b) +{ + register bool 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] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR }; /* @@ -658,15 +732,15 @@ static const int year_lengths[2] = { ** character. */ -static const char * -getzname(register const char * strp) +static const char * ATTRIBUTE_PURE +getzname(register const char *strp) { - register char c; + register char c; - while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && - c != '+') - ++strp; - return strp; + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && + c != '+') + ++strp; + return strp; } /* @@ -678,14 +752,14 @@ getzname(register const char * strp) ** We don't do any checking here; checking is done later in common-case code. */ -static const char * +static const char * ATTRIBUTE_PURE getqzname(register const char *strp, const int delim) { - register int c; + register int c; - while ((c = *strp) != '\0' && c != delim) - ++strp; - return strp; + while ((c = *strp) != '\0' && c != delim) + ++strp; + return strp; } /* @@ -696,24 +770,24 @@ getqzname(register const char *strp, const int delim) */ static const char * -getnum(register const char * strp, int * const nump, const int min, const int max) +getnum(register const char *strp, int *const nump, const int min, const int max) { - register char c; - register int num; + register char c; + register int num; - if (strp == NULL || !is_digit(c = *strp)) - return NULL; - num = 0; - do { - num = num * 10 + (c - '0'); - if (num > max) - return NULL; /* illegal value */ - c = *++strp; - } while (is_digit(c)); - if (num < min) - return NULL; /* illegal value */ - *nump = num; - return strp; + if (strp == NULL || !is_digit(c = *strp)) + return NULL; + num = 0; + do { + num = num * 10 + (c - '0'); + if (num > max) + return NULL; /* illegal value */ + c = *++strp; + } while (is_digit(c)); + if (num < min) + return NULL; /* illegal value */ + *nump = num; + return strp; } /* @@ -727,34 +801,34 @@ getnum(register const char * strp, int * const nump, const int min, const int ma static const char * getsecs(register const char *strp, int_fast32_t *const secsp) { - int num; + int num; - /* - ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like - ** "M10.4.6/26", which does not conform to Posix, - ** but which specifies the equivalent of - ** "02:00 on the first Sunday on or after 23 Oct". - */ - strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); - if (strp == NULL) - return NULL; - *secsp = num * (int_fast32_t) SECSPERHOUR; - if (*strp == ':') { - ++strp; - strp = getnum(strp, &num, 0, MINSPERHOUR - 1); - if (strp == NULL) - return NULL; - *secsp += num * SECSPERMIN; - if (*strp == ':') { - ++strp; - /* 'SECSPERMIN' allows for leap seconds. */ - strp = getnum(strp, &num, 0, SECSPERMIN); - if (strp == NULL) - return NULL; - *secsp += num; - } - } - return strp; + /* + ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like + ** "M10.4.6/26", which does not conform to Posix, + ** but which specifies the equivalent of + ** "02:00 on the first Sunday on or after 23 Oct". + */ + strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); + if (strp == NULL) + return NULL; + *secsp = num * (int_fast32_t) SECSPERHOUR; + if (*strp == ':') { + ++strp; + strp = getnum(strp, &num, 0, MINSPERHOUR - 1); + if (strp == NULL) + return NULL; + *secsp += num * SECSPERMIN; + if (*strp == ':') { + ++strp; + /* 'SECSPERMIN' allows for leap seconds. */ + strp = getnum(strp, &num, 0, SECSPERMIN); + if (strp == NULL) + return NULL; + *secsp += num; + } + } + return strp; } /* @@ -767,19 +841,19 @@ getsecs(register const char *strp, int_fast32_t *const secsp) static const char * getoffset(register const char *strp, int_fast32_t *const offsetp) { - register int neg = 0; + register bool neg = false; - if (*strp == '-') { - neg = 1; - ++strp; - } else if (*strp == '+') - ++strp; - strp = getsecs(strp, offsetp); - if (strp == NULL) - return NULL; /* illegal time */ - if (neg) - *offsetp = -*offsetp; - return strp; + if (*strp == '-') { + neg = true; + ++strp; + } else if (*strp == '+') + ++strp; + strp = getsecs(strp, offsetp); + if (strp == NULL) + return NULL; /* illegal time */ + if (neg) + *offsetp = -*offsetp; + return strp; } /* @@ -790,49 +864,49 @@ getoffset(register const char *strp, int_fast32_t *const offsetp) */ static const char * -getrule(const char * strp, register struct rule * const rulep) +getrule(const char *strp, register struct rule *const rulep) { - if (*strp == 'J') { - /* - ** Julian day. - */ - rulep->r_type = JULIAN_DAY; - ++strp; - strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); - } else if (*strp == 'M') { - /* - ** Month, week, day. - */ - rulep->r_type = MONTH_NTH_DAY_OF_WEEK; - ++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 (is_digit(*strp)) { - /* - ** Day of year. - */ - rulep->r_type = DAY_OF_YEAR; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); - } else return NULL; /* invalid format */ - if (strp == NULL) - return NULL; - if (*strp == '/') { - /* - ** Time specified. - */ - ++strp; - strp = getoffset(strp, &rulep->r_time); - } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ - return strp; + if (*strp == 'J') { + /* + ** Julian day. + */ + rulep->r_type = JULIAN_DAY; + ++strp; + strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); + } else if (*strp == 'M') { + /* + ** Month, week, day. + */ + rulep->r_type = MONTH_NTH_DAY_OF_WEEK; + ++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 (is_digit(*strp)) { + /* + ** Day of year. + */ + rulep->r_type = DAY_OF_YEAR; + strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); + } else return NULL; /* invalid format */ + if (strp == NULL) + return NULL; + if (*strp == '/') { + /* + ** Time specified. + */ + ++strp; + strp = getoffset(strp, &rulep->r_time); + } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ + return strp; } /* @@ -840,11 +914,11 @@ getrule(const char * strp, register struct rule * const rulep) ** effect, calculate the year-relative time that rule takes effect. */ -static int_fast32_t +static int_fast32_t ATTRIBUTE_PURE transtime(const int year, register const struct rule *const rulep, const int_fast32_t offset) { - register int leapyear; + register bool leapyear; register int_fast32_t value; register int i; int d, m1, yy0, yy1, yy2, dow; @@ -931,477 +1005,537 @@ transtime(const int year, register const struct rule *const rulep, ** appropriate. */ -static int -tzparse(const char * name, register struct state * const sp, - const int lastditch) +static bool +tzparse(const char *name, struct state *sp, bool lastditch) { - const char * stdname; - const char * dstname; - size_t stdlen; - size_t dstlen; - int_fast32_t stdoffset; - int_fast32_t dstoffset; - register char * cp; - register int load_result; - static struct ttinfo zttinfo; + const char * stdname; + const char * dstname; + size_t stdlen; + size_t dstlen; + size_t charcnt; + int_fast32_t stdoffset; + int_fast32_t dstoffset; + register char * cp; + register bool load_ok; - stdname = name; - if (lastditch) { - stdlen = strlen(name); /* length of standard zone name */ - name += stdlen; - if (stdlen >= sizeof sp->chars) - stdlen = (sizeof sp->chars) - 1; - stdoffset = 0; - } else { - if (*name == '<') { - name++; - stdname = name; - name = getqzname(name, '>'); - if (*name != '>') - return (-1); - stdlen = name - stdname; - name++; - } else { - name = getzname(name); - stdlen = name - stdname; - } - if (*name == '\0') - return -1; - name = getoffset(name, &stdoffset); - if (name == NULL) - return -1; - } - load_result = tzload(TZDEFRULES, sp, FALSE); - if (load_result != 0) - sp->leapcnt = 0; /* so, we're off a little */ - if (*name != '\0') { - if (*name == '<') { - dstname = ++name; - name = getqzname(name, '>'); - if (*name != '>') - return -1; - dstlen = name - dstname; - name++; - } else { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - } - if (*name != '\0' && *name != ',' && *name != ';') { - name = getoffset(name, &dstoffset); - if (name == NULL) - return -1; - } else dstoffset = stdoffset - SECSPERHOUR; - if (*name == '\0' && load_result != 0) - name = TZDEFRULESTRING; - if (*name == ',' || *name == ';') { - struct rule start; - struct rule end; - register int year; - register int yearlim; - register int timecnt; - time_t janfirst; + stdname = name; + if (lastditch) { + stdlen = sizeof gmt - 1; + name += stdlen; + stdoffset = 0; + } else { + if (*name == '<') { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') + return false; + stdlen = name - stdname; + name++; + } else { + name = getzname(name); + stdlen = name - stdname; + } + if (!stdlen) + return false; + name = getoffset(name, &stdoffset); + if (name == NULL) + return false; + } + charcnt = stdlen + 1; + if (sizeof sp->chars < charcnt) + return false; + load_ok = tzload(TZDEFRULES, sp, false) == 0; + if (!load_ok) + sp->leapcnt = 0; /* so, we're off a little */ + if (*name != '\0') { + if (*name == '<') { + dstname = ++name; + name = getqzname(name, '>'); + if (*name != '>') + return false; + dstlen = name - dstname; + name++; + } else { + dstname = name; + name = getzname(name); + dstlen = name - dstname; /* length of DST zone name */ + } + if (!dstlen) + return false; + charcnt += dstlen + 1; + if (sizeof sp->chars < charcnt) + return false; + if (*name != '\0' && *name != ',' && *name != ';') { + name = getoffset(name, &dstoffset); + if (name == NULL) + return false; + } else dstoffset = stdoffset - SECSPERHOUR; + if (*name == '\0' && !load_ok) + name = TZDEFRULESTRING; + if (*name == ',' || *name == ';') { + struct rule start; + struct rule end; + register int year; + register int yearlim; + register int timecnt; + time_t janfirst; - ++name; - if ((name = getrule(name, &start)) == NULL) - return -1; - if (*name++ != ',') - return -1; - if ((name = getrule(name, &end)) == NULL) - return -1; - if (*name != '\0') - return -1; - sp->typecnt = 2; /* standard time and DST */ - /* - ** Two transitions per year, from EPOCH_YEAR forward. - */ - sp->ttis[0] = sp->ttis[1] = zttinfo; - sp->ttis[0].tt_gmtoff = -dstoffset; - sp->ttis[0].tt_isdst = 1; - sp->ttis[0].tt_abbrind = stdlen + 1; - sp->ttis[1].tt_gmtoff = -stdoffset; - sp->ttis[1].tt_isdst = 0; - sp->ttis[1].tt_abbrind = 0; - sp->defaulttype = 0; - timecnt = 0; - janfirst = 0; - yearlim = EPOCH_YEAR + YEARSPERREPEAT; - for (year = EPOCH_YEAR; year < yearlim; year++) { - int_fast32_t - starttime = transtime(year, &start, stdoffset), - endtime = transtime(year, &end, dstoffset); - int_fast32_t - yearsecs = (year_lengths[isleap(year)] - * SECSPERDAY); - int reversed = endtime < starttime; - if (reversed) { - int_fast32_t swap = starttime; - starttime = endtime; - endtime = swap; - } - if (reversed - || (starttime < endtime - && (endtime - starttime - < (yearsecs - + (stdoffset - dstoffset))))) { - if (TZ_MAX_TIMES - 2 < timecnt) - break; - yearlim = year + YEARSPERREPEAT + 1; - sp->ats[timecnt] = janfirst; - if (increment_overflow_time - (&sp->ats[timecnt], starttime)) - break; - sp->types[timecnt++] = reversed; - sp->ats[timecnt] = janfirst; - if (increment_overflow_time - (&sp->ats[timecnt], endtime)) - break; - sp->types[timecnt++] = !reversed; - } - if (increment_overflow_time(&janfirst, yearsecs)) - break; - } - sp->timecnt = timecnt; - if (!timecnt) - sp->typecnt = 1; /* Perpetual DST. */ - } else { - register int_fast32_t theirstdoffset; - register int_fast32_t theirdstoffset; - register int_fast32_t theiroffset; - register int isdst; - register int i; - register int j; + ++name; + if ((name = getrule(name, &start)) == NULL) + return false; + if (*name++ != ',') + return false; + if ((name = getrule(name, &end)) == NULL) + return false; + if (*name != '\0') + return false; + sp->typecnt = 2; /* standard time and DST */ + /* + ** Two transitions per year, from EPOCH_YEAR forward. + */ + init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1); + init_ttinfo(&sp->ttis[1], -stdoffset, false, 0); + sp->defaulttype = 0; + timecnt = 0; + janfirst = 0; + yearlim = EPOCH_YEAR + YEARSPERREPEAT; + for (year = EPOCH_YEAR; year < yearlim; year++) { + int_fast32_t + starttime = transtime(year, &start, stdoffset), + endtime = transtime(year, &end, dstoffset); + int_fast32_t + yearsecs = (year_lengths[isleap(year)] + * SECSPERDAY); + bool reversed = endtime < starttime; + if (reversed) { + int_fast32_t swap = starttime; + starttime = endtime; + endtime = swap; + } + if (reversed + || (starttime < endtime + && (endtime - starttime + < (yearsecs + + (stdoffset - dstoffset))))) { + if (TZ_MAX_TIMES - 2 < timecnt) + break; + yearlim = year + YEARSPERREPEAT + 1; + sp->ats[timecnt] = janfirst; + if (increment_overflow_time + (&sp->ats[timecnt], starttime)) + break; + sp->types[timecnt++] = reversed; + sp->ats[timecnt] = janfirst; + if (increment_overflow_time + (&sp->ats[timecnt], endtime)) + break; + sp->types[timecnt++] = !reversed; + } + if (increment_overflow_time(&janfirst, yearsecs)) + break; + } + sp->timecnt = timecnt; + if (!timecnt) + sp->typecnt = 1; /* Perpetual DST. */ + } else { + register int_fast32_t theirstdoffset; + register int_fast32_t theirdstoffset; + register int_fast32_t theiroffset; + register bool isdst; + register int i; + register int j; - if (*name != '\0') - return -1; - /* - ** Initial values of theirstdoffset and theirdstoffset. - */ - theirstdoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (!sp->ttis[j].tt_isdst) { - theirstdoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - theirdstoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (sp->ttis[j].tt_isdst) { - theirdstoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - /* - ** Initially we're assumed to be in standard time. - */ - isdst = FALSE; - theiroffset = theirstdoffset; - /* - ** Now juggle transition times and types - ** tracking offsets as you do. - */ - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisgmt) { - /* No adjustment to transition time */ - } else { - /* - ** If summer time is in effect, and the - ** transition time was not specified as - ** standard time, add the summer time - ** offset to the transition time; - ** otherwise, add the standard time - ** offset to the transition time. - */ - /* - ** Transitions from DST to DDST - ** will effectively disappear since - ** POSIX provides for only one DST - ** offset. - */ - if (isdst && !sp->ttis[j].tt_ttisstd) { - sp->ats[i] += dstoffset - - theirdstoffset; - } else { - sp->ats[i] += stdoffset - - theirstdoffset; - } - } - theiroffset = -sp->ttis[j].tt_gmtoff; - if (sp->ttis[j].tt_isdst) - theirdstoffset = theiroffset; - else theirstdoffset = theiroffset; - } - /* - ** Finally, fill in ttis. - */ - sp->ttis[0] = sp->ttis[1] = zttinfo; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = FALSE; - sp->ttis[0].tt_abbrind = 0; - sp->ttis[1].tt_gmtoff = -dstoffset; - sp->ttis[1].tt_isdst = TRUE; - sp->ttis[1].tt_abbrind = stdlen + 1; - sp->typecnt = 2; - sp->defaulttype = 0; - } - } else { - dstlen = 0; - sp->typecnt = 1; /* only standard time */ - sp->timecnt = 0; - sp->ttis[0] = zttinfo; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_abbrind = 0; - sp->defaulttype = 0; - } - sp->charcnt = stdlen + 1; - if (dstlen != 0) - sp->charcnt += dstlen + 1; - if ((size_t) sp->charcnt > sizeof sp->chars) - return -1; - cp = sp->chars; - (void) strncpy(cp, stdname, stdlen); - cp += stdlen; - *cp++ = '\0'; - if (dstlen != 0) { - (void) strncpy(cp, dstname, dstlen); - *(cp + dstlen) = '\0'; - } - return 0; + if (*name != '\0') + return false; + /* + ** Initial values of theirstdoffset and theirdstoffset. + */ + theirstdoffset = 0; + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + if (!sp->ttis[j].tt_isdst) { + theirstdoffset = + -sp->ttis[j].tt_gmtoff; + break; + } + } + theirdstoffset = 0; + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + if (sp->ttis[j].tt_isdst) { + theirdstoffset = + -sp->ttis[j].tt_gmtoff; + break; + } + } + /* + ** Initially we're assumed to be in standard time. + */ + isdst = false; + theiroffset = theirstdoffset; + /* + ** Now juggle transition times and types + ** tracking offsets as you do. + */ + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + sp->types[i] = sp->ttis[j].tt_isdst; + if (sp->ttis[j].tt_ttisgmt) { + /* No adjustment to transition time */ + } else { + /* + ** If summer time is in effect, and the + ** transition time was not specified as + ** standard time, add the summer time + ** offset to the transition time; + ** otherwise, add the standard time + ** offset to the transition time. + */ + /* + ** Transitions from DST to DDST + ** will effectively disappear since + ** POSIX provides for only one DST + ** offset. + */ + if (isdst && !sp->ttis[j].tt_ttisstd) { + sp->ats[i] += dstoffset - + theirdstoffset; + } else { + sp->ats[i] += stdoffset - + theirstdoffset; + } + } + theiroffset = -sp->ttis[j].tt_gmtoff; + if (sp->ttis[j].tt_isdst) + theirdstoffset = theiroffset; + else theirstdoffset = theiroffset; + } + /* + ** Finally, fill in ttis. + */ + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); + sp->typecnt = 2; + sp->defaulttype = 0; + } + } else { + dstlen = 0; + sp->typecnt = 1; /* only standard time */ + sp->timecnt = 0; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + sp->defaulttype = 0; + } + sp->charcnt = charcnt; + cp = sp->chars; + memcpy(cp, stdname, stdlen); + cp += stdlen; + *cp++ = '\0'; + if (dstlen != 0) { + memcpy(cp, dstname, dstlen); + *(cp + dstlen) = '\0'; + } + return true; } static void -gmtload(struct state * const sp) +gmtload(struct state *const sp) { - if (tzload(gmt, sp, TRUE) != 0) - (void) tzparse(gmt, sp, TRUE); + if (tzload(gmt, sp, true) != 0) + tzparse(gmt, sp, true); } -#ifndef STD_INSPIRED -/* -** A non-static declaration of tzsetwall in a system header file -** may cause a warning about this upcoming static declaration... -*/ -static -#endif /* !defined STD_INSPIRED */ +/* Initialize *SP to a value appropriate for the TZ setting NAME. + Return 0 on success, an errno value on failure. */ +static int +zoneinit(struct state *sp, char const *name) +{ + if (name && ! name[0]) { + /* + ** User wants it fast rather than right. + */ + sp->leapcnt = 0; /* so, we're off a little */ + sp->timecnt = 0; + sp->typecnt = 0; + sp->charcnt = 0; + sp->goback = sp->goahead = false; + init_ttinfo(&sp->ttis[0], 0, false, 0); + strcpy(sp->chars, gmt); + sp->defaulttype = 0; + return 0; + } else { + int err = tzload(name, sp, true); + if (err != 0 && name && name[0] != ':' && tzparse(name, sp, false)) + err = 0; + if (err == 0) + scrub_abbrs(sp); + return err; + } +} + +static void +tzsetlcl(char const *name) +{ + struct state *sp = lclptr; + int lcl = name ? strlen(name) < sizeof lcl_TZname : -1; + if (lcl < 0 + ? lcl_is_set < 0 + : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0) + return; +#ifdef ALL_STATE + if (! sp) + lclptr = sp = malloc(sizeof *lclptr); +#endif /* defined ALL_STATE */ + if (sp) { + if (zoneinit(sp, name) != 0) + zoneinit(sp, ""); + if (0 < lcl) + strcpy(lcl_TZname, name); + } + settzname(); + lcl_is_set = lcl; +} + +#ifdef STD_INSPIRED void tzsetwall(void) { - if (lcl_is_set < 0) - return; - lcl_is_set = -1; - -#ifdef ALL_STATE - if (lclptr == NULL) { - lclptr = malloc(sizeof *lclptr); - if (lclptr == NULL) { - settzname(); /* all we can do */ - return; - } - } -#endif /* defined ALL_STATE */ - if (tzload(NULL, lclptr, TRUE) != 0) - gmtload(lclptr); - settzname(); + if (lock() != 0) + return; + tzsetlcl(NULL); + unlock(); } +#endif -#include +#if defined(__ANDROID__) #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include // For __system_property_serial. +#endif static void -tzset_locked(void) +tzset_unlocked(void) { - register const char * name; +#if defined(__ANDROID__) + const char * name = getenv("TZ"); - name = getenv("TZ"); + // Try the "persist.sys.timezone" system property. + if (name == NULL) { + static const prop_info *pi; - // try the "persist.sys.timezone" system property first - if (name == NULL) { - static const prop_info *pi; - - if (!pi) { - pi = __system_property_find("persist.sys.timezone"); - } - if (pi) { - static char buf[PROP_VALUE_MAX]; - static uint32_t s = -1; - static bool ok = false; - uint32_t serial; - - serial = __system_property_serial(pi); - if (serial != s) { - ok = __system_property_read(pi, 0, buf) > 0; - s = serial; - } - if (ok) { - name = buf; - } - } + if (!pi) { + pi = __system_property_find("persist.sys.timezone"); } - - if (name == NULL) { - tzsetwall(); - return; + if (pi) { + static char buf[PROP_VALUE_MAX]; + static uint32_t s = -1; + static bool ok = false; + uint32_t serial = __system_property_serial(pi); + if (serial != s) { + ok = __system_property_read(pi, 0, buf) > 0; + s = serial; + } + if (ok) { + name = buf; + } } + } - if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) - return; - lcl_is_set = strlen(name) < sizeof lcl_TZname; - if (lcl_is_set) - (void) strcpy(lcl_TZname, name); - -#ifdef ALL_STATE - if (lclptr == NULL) { - lclptr = malloc(sizeof *lclptr); - if (lclptr == NULL) { - settzname(); /* all we can do */ - return; - } - } -#endif /* defined ALL_STATE */ - if (*name == '\0') { - /* - ** User wants it fast rather than right. - */ - lclptr->leapcnt = 0; /* so, we're off a little */ - lclptr->timecnt = 0; - lclptr->typecnt = 0; - lclptr->ttis[0].tt_isdst = 0; - lclptr->ttis[0].tt_gmtoff = 0; - lclptr->ttis[0].tt_abbrind = 0; - (void) strcpy(lclptr->chars, gmt); - lclptr->defaulttype = 0; - } else if (tzload(name, lclptr, TRUE) != 0) - if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) - (void) gmtload(lclptr); - settzname(); + tzsetlcl(name); +#else + tzsetlcl(getenv("TZ")); +#endif } void tzset(void) { - _tzLock(); - tzset_locked(); - _tzUnlock(); + if (lock() != 0) + return; + tzset_unlocked(); + unlock(); +} + +static void +gmtcheck(void) +{ + static bool gmt_is_set; + if (lock() != 0) + return; + if (! gmt_is_set) { +#ifdef ALL_STATE + gmtptr = malloc(sizeof *gmtptr); +#endif + if (gmtptr) + gmtload(gmtptr); + gmt_is_set = true; + } + unlock(); +} + +#if NETBSD_INSPIRED + +timezone_t +tzalloc(char const *name) +{ + timezone_t sp = malloc(sizeof *sp); + if (sp) { + int err = zoneinit(sp, name); + if (err != 0) { + free(sp); + errno = err; + return NULL; + } + } + return sp; +} + +void +tzfree(timezone_t sp) +{ + free(sp); } +/* +** NetBSD 6.1.4 has ctime_rz, but omit it because POSIX says ctime and +** ctime_r are obsolescent and have potential security problems that +** ctime_rz would share. Callers can instead use localtime_rz + strftime. +** +** NetBSD 6.1.4 has tzgetname, but omit it because it doesn't work +** in zones with three or more time zone abbreviations. +** Callers can instead use localtime_rz + strftime. +*/ + +#endif + /* ** 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-- +** 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, ** but it *is* desirable.) ** -** The unused offset argument is for the benefit of mktime variants. +** If successful and SETNAME is nonzero, +** set the applicable parts of tzname, timezone and altzone; +** however, it's OK to omit this step if the time zone is POSIX-compatible, +** since in that case tzset should have already done this step correctly. +** SETNAME's type is intfast32_t for compatibility with gmtsub, +** but it is actually a boolean and its value should be 0 or 1. */ /*ARGSUSED*/ static struct tm * -localsub(const time_t * const timep, const int_fast32_t offset, - struct tm * const tmp, struct state * sp) // android-changed: added sp. +localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, + struct tm *const tmp) { - register const struct ttinfo * ttisp; - register int i; - register struct tm * result; - const time_t t = *timep; + register const struct ttinfo * ttisp; + register int i; + register struct tm * result; + const time_t t = *timep; - // BEGIN android-changed: support user-supplied sp. - if (sp == NULL) { - sp = lclptr; - } - // END android-changed - if (sp == NULL) - return gmtsub(timep, offset, tmp, sp); // android-changed: added sp. - 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 years; + if (sp == NULL) { + /* Don't bother to set tzname etc.; tzset has already done it. */ + return gmtsub(gmtptr, timep, 0, tmp); + } + 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 years; - if (t < sp->ats[0]) - seconds = sp->ats[0] - t; - else seconds = t - sp->ats[sp->timecnt - 1]; - --seconds; - years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT; - seconds = years * AVGSECSPERYEAR; - if (t < sp->ats[0]) - newt += seconds; - else newt -= seconds; - if (newt < sp->ats[0] || - newt > sp->ats[sp->timecnt - 1]) - return NULL; /* "cannot happen" */ - result = localsub(&newt, offset, tmp, sp); // android-changed: added sp. - if (result == tmp) { - register time_t newy; + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT; + seconds = years * AVGSECSPERYEAR; + if (t < sp->ats[0]) + newt += seconds; + else newt -= seconds; + if (newt < sp->ats[0] || + newt > sp->ats[sp->timecnt - 1]) + return NULL; /* "cannot happen" */ + result = localsub(sp, &newt, setname, tmp); + if (result) { + register int_fast64_t newy; - newy = tmp->tm_year; - if (t < sp->ats[0]) - newy -= years; - else newy += years; - tmp->tm_year = newy; - if (tmp->tm_year != newy) - return NULL; - } - return result; - } - if (sp->timecnt == 0 || t < sp->ats[0]) { - i = sp->defaulttype; - } else { - register int lo = 1; - register int hi = sp->timecnt; + newy = result->tm_year; + if (t < sp->ats[0]) + newy -= years; + else newy += years; + if (! (INT_MIN <= newy && newy <= INT_MAX)) + return NULL; + result->tm_year = newy; + } + return result; + } + if (sp->timecnt == 0 || t < sp->ats[0]) { + i = sp->defaulttype; + } else { + register int lo = 1; + register int hi = sp->timecnt; - while (lo < hi) { - register int mid = (lo + hi) >> 1; + 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]; - /* - ** To get (wrong) behavior that's compatible with System V Release 2.0 - ** you'd replace the statement below with - ** t += ttisp->tt_gmtoff; - ** timesub(&t, 0L, sp, tmp); - */ - result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); - tmp->tm_isdst = ttisp->tt_isdst; - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; + if (t < sp->ats[mid]) + hi = mid; + else lo = mid + 1; + } + i = (int) sp->types[lo - 1]; + } + ttisp = &sp->ttis[i]; + /* + ** To get (wrong) behavior that's compatible with System V Release 2.0 + ** you'd replace the statement below with + ** t += ttisp->tt_gmtoff; + ** timesub(&t, 0L, sp, tmp); + */ + result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); + if (result) { + result->tm_isdst = ttisp->tt_isdst; #ifdef TM_ZONE - tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; + result->TM_ZONE = (char *) &sp->chars[ttisp->tt_abbrind]; #endif /* defined TM_ZONE */ - return result; + if (setname) + update_tzname_etc(sp, ttisp); + } + return result; +} + +#if NETBSD_INSPIRED + +struct tm * +localtime_rz(struct state *sp, time_t const *timep, struct tm *tmp) +{ + return localsub(sp, timep, 0, tmp); +} + +#endif + +static struct tm * +localtime_tzset(time_t const *timep, struct tm *tmp, bool setname) +{ + int err = lock(); + if (err) { + errno = err; + return NULL; + } + if (setname || !lcl_is_set) + tzset_unlocked(); + tmp = localsub(lclptr, timep, setname, tmp); + unlock(); + return tmp; } struct tm * -localtime(const time_t * const timep) +localtime(const time_t *timep) { - return localtime_r(timep, &tmGlobal); + return localtime_tzset(timep, &tm, true); } -/* -** Re-entrant version of localtime. -*/ - struct tm * -localtime_r(const time_t * const timep, struct tm * tmp) +localtime_r(const time_t *timep, struct tm *tmp) { - struct tm* result; - - _tzLock(); - tzset_locked(); - result = localsub(timep, 0L, tmp, NULL); // android-changed: extra parameter. - _tzUnlock(); - - return result; + return localtime_tzset(timep, tmp, false); } /* @@ -1409,37 +1543,22 @@ localtime_r(const time_t * const timep, struct tm * tmp) */ static struct tm * -gmtsub(const time_t * const timep, const int_fast32_t offset, - struct tm *const tmp, struct state * sp __unused) // android-changed: added sp. +gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset, + struct tm *tmp) { - register struct tm * result; + register struct tm * result; - if (!gmt_is_set) { -#ifdef ALL_STATE - gmtptr = malloc(sizeof *gmtptr); - gmt_is_set = gmtptr != NULL; -#else - gmt_is_set = TRUE; -#endif /* defined ALL_STATE */ - if (gmt_is_set) - gmtload(gmtptr); - } - result = timesub(timep, offset, gmtptr, tmp); + result = timesub(timep, offset, gmtptr, tmp); #ifdef TM_ZONE - /* - ** Could get fancy here and deliver something such as - ** "UT+xxxx" or "UT-xxxx" if offset is non-zero, - ** but this is no time for a treasure hunt. - */ - tmp->TM_ZONE = offset ? wildabbr : gmtptr ? gmtptr->chars : gmt; + /* + ** Could get fancy here and deliver something such as + ** "UT+xxxx" or "UT-xxxx" if offset is non-zero, + ** but this is no time for a treasure hunt. + */ + tmp->TM_ZONE = ((char *) + (offset ? wildabbr : gmtptr ? gmtptr->chars : gmt)); #endif /* defined TM_ZONE */ - return result; -} - -struct tm * -gmtime(const time_t * const timep) -{ - return gmtime_r(timep, &tmGlobal); + return result; } /* @@ -1447,23 +1566,25 @@ gmtime(const time_t * const timep) */ struct tm * -gmtime_r(const time_t * const timep, struct tm * tmp) +gmtime_r(const time_t *timep, struct tm *tmp) { - struct tm* result; + gmtcheck(); + return gmtsub(gmtptr, timep, 0, tmp); +} - _tzLock(); - result = gmtsub(timep, 0L, tmp, NULL); // android-changed: extra parameter. - _tzUnlock(); - - return result; +struct tm * +gmtime(const time_t *timep) +{ + return gmtime_r(timep, &tm); } #ifdef STD_INSPIRED struct tm * -offtime(const time_t *const timep, const long offset) +offtime(const time_t *timep, long offset) { - return gmtsub(timep, offset, &tmGlobal, NULL); // android-changed: extra parameter. + gmtcheck(); + return gmtsub(gmtptr, timep, offset, &tm); } #endif /* defined STD_INSPIRED */ @@ -1473,597 +1594,610 @@ offtime(const time_t *const timep, const long offset) ** where, to make the math easy, the answer for year zero is defined as zero. */ -static int +static int ATTRIBUTE_PURE leaps_thru_end_of(register const int y) { - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); } static struct tm * -timesub(const time_t *const timep, const int_fast32_t offset, - register const struct state *const sp, - register struct tm *const tmp) +timesub(const time_t *timep, int_fast32_t offset, + const struct state *sp, struct tm *tmp) { - register const struct lsinfo * lp; - register time_t tdays; - register int idays; /* unsigned would be so 2003 */ - register int_fast64_t rem; - int y; - register const int * ip; - register int_fast64_t corr; - register int hit; - register int i; + register const struct lsinfo * lp; + register time_t tdays; + register int idays; /* unsigned would be so 2003 */ + register int_fast64_t rem; + int y; + register const int * ip; + register int_fast64_t corr; + register bool hit; + register int i; - corr = 0; - hit = 0; - i = (sp == NULL) ? 0 : sp->leapcnt; - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } - corr = lp->ls_corr; - break; - } - } - y = EPOCH_YEAR; - tdays = *timep / SECSPERDAY; - rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { - int newy; - register time_t tdelta; - register int idelta; - register int leapdays; + corr = 0; + hit = false; + i = (sp == NULL) ? 0 : sp->leapcnt; + while (--i >= 0) { + lp = &sp->lsis[i]; + if (*timep >= lp->ls_trans) { + if (*timep == lp->ls_trans) { + hit = ((i == 0 && lp->ls_corr > 0) || + lp->ls_corr > sp->lsis[i - 1].ls_corr); + if (hit) + while (i > 0 && + sp->lsis[i].ls_trans == + sp->lsis[i - 1].ls_trans + 1 && + sp->lsis[i].ls_corr == + sp->lsis[i - 1].ls_corr + 1) { + ++hit; + --i; + } + } + corr = lp->ls_corr; + break; + } + } + y = EPOCH_YEAR; + tdays = *timep / SECSPERDAY; + rem = *timep % SECSPERDAY; + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { + int newy; + register time_t tdelta; + register int idelta; + register int leapdays; - tdelta = tdays / DAYSPERLYEAR; - if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta) - && tdelta <= INT_MAX)) - return NULL; - idelta = tdelta; - if (idelta == 0) - idelta = (tdays < 0) ? -1 : 1; - newy = y; - if (increment_overflow(&newy, idelta)) - return NULL; - leapdays = leaps_thru_end_of(newy - 1) - - leaps_thru_end_of(y - 1); - tdays -= ((time_t) newy - y) * DAYSPERNYEAR; - tdays -= leapdays; - y = newy; - } - { - register int_fast32_t seconds; - - seconds = tdays * SECSPERDAY; - tdays = seconds / SECSPERDAY; - rem += seconds - tdays * SECSPERDAY; - } - /* - ** Given the range, we can now fearlessly cast... - */ - idays = tdays; - rem += offset - corr; - while (rem < 0) { - rem += SECSPERDAY; - --idays; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++idays; - } - while (idays < 0) { - if (increment_overflow(&y, -1)) - return NULL; - idays += year_lengths[isleap(y)]; - } - while (idays >= year_lengths[isleap(y)]) { - idays -= year_lengths[isleap(y)]; - if (increment_overflow(&y, 1)) - return NULL; - } - tmp->tm_year = y; - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; - tmp->tm_yday = idays; - /* - ** The "extra" mods below avoid overflow problems. - */ - tmp->tm_wday = EPOCH_WDAY + - ((y - EPOCH_YEAR) % DAYSPERWEEK) * - (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of(y - 1) - - leaps_thru_end_of(EPOCH_YEAR - 1) + - idays; - tmp->tm_wday %= DAYSPERWEEK; - if (tmp->tm_wday < 0) - tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem %= SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. - */ - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - ip = mon_lengths[isleap(y)]; - for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) - idays -= ip[tmp->tm_mon]; - tmp->tm_mday = (int) (idays + 1); - tmp->tm_isdst = 0; + tdelta = tdays / DAYSPERLYEAR; + if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta) + && tdelta <= INT_MAX)) + goto out_of_range; + idelta = tdelta; + if (idelta == 0) + idelta = (tdays < 0) ? -1 : 1; + newy = y; + if (increment_overflow(&newy, idelta)) + goto out_of_range; + leapdays = leaps_thru_end_of(newy - 1) - + leaps_thru_end_of(y - 1); + tdays -= ((time_t) newy - y) * DAYSPERNYEAR; + tdays -= leapdays; + y = newy; + } + /* + ** Given the range, we can now fearlessly cast... + */ + idays = tdays; + rem += offset - corr; + while (rem < 0) { + rem += SECSPERDAY; + --idays; + } + while (rem >= SECSPERDAY) { + rem -= SECSPERDAY; + ++idays; + } + while (idays < 0) { + if (increment_overflow(&y, -1)) + goto out_of_range; + idays += year_lengths[isleap(y)]; + } + while (idays >= year_lengths[isleap(y)]) { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + goto out_of_range; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + goto out_of_range; + tmp->tm_yday = idays; + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; + if (tmp->tm_wday < 0) + tmp->tm_wday += DAYSPERWEEK; + tmp->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + tmp->tm_min = (int) (rem / SECSPERMIN); + /* + ** A positive leap second requires a special + ** representation. This uses "... ??:59:60" et seq. + */ + tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = (int) (idays + 1); + tmp->tm_isdst = 0; #ifdef TM_GMTOFF - tmp->TM_GMTOFF = offset; + tmp->TM_GMTOFF = offset; #endif /* defined TM_GMTOFF */ - return tmp; + return tmp; + + out_of_range: + errno = EOVERFLOW; + return NULL; } char * -ctime(const time_t * const timep) +ctime(const time_t *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 -** asctime(localtime(timer)) +** The ctime function converts the calendar time pointed to by timer +** to local time in the form of a string. It is equivalent to +** asctime(localtime(timer)) */ - return asctime(localtime(timep)); + struct tm *tmp = localtime(timep); + return tmp ? asctime(tmp) : NULL; } char * -ctime_r(const time_t * const timep, char * buf) +ctime_r(const time_t *timep, char *buf) { - struct tm mytm; - - return asctime_r(localtime_r(timep, &mytm), buf); + struct tm mytm; + struct tm *tmp = localtime_r(timep, &mytm); + return tmp ? asctime_r(tmp, buf) : NULL; } /* ** 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. -** 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). +** 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. +** 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). */ #ifndef WRONG -#define WRONG (-1) +#define WRONG (-1) #endif /* !defined WRONG */ /* ** Normalize logic courtesy Paul Eggert. */ -static int -increment_overflow(int *const ip, int j) +static bool +increment_overflow(int *ip, int j) { - register int const i = *ip; + register int const i = *ip; - /* - ** If i >= 0 there can only be overflow if i + j > INT_MAX - ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. - ** If i < 0 there can only be overflow if i + j < INT_MIN - ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. - */ - if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) - return TRUE; - *ip += j; - return FALSE; + /* + ** If i >= 0 there can only be overflow if i + j > INT_MAX + ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + ** If i < 0 there can only be overflow if i + j < INT_MIN + ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) + return true; + *ip += j; + return false; } -static int +static bool increment_overflow32(int_fast32_t *const lp, int const m) { - register int_fast32_t const l = *lp; + register int_fast32_t const l = *lp; - if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) - return TRUE; - *lp += m; - return FALSE; + if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) + return true; + *lp += m; + return false; } -static int +static bool increment_overflow_time(time_t *tp, int_fast32_t j) { - /* - ** This is like - ** 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...', - ** except that it does the right thing even if *tp + j would overflow. - */ - if (! (j < 0 - ? (TYPE_SIGNED(time_t) ? time_t_min - j <= *tp : -1 - j < *tp) - : *tp <= time_t_max - j)) - return TRUE; - *tp += j; - return FALSE; + /* + ** This is like + ** 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...', + ** except that it does the right thing even if *tp + j would overflow. + */ + if (! (j < 0 + ? (TYPE_SIGNED(time_t) ? time_t_min - j <= *tp : -1 - j < *tp) + : *tp <= time_t_max - j)) + return true; + *tp += j; + return false; } -static int +static bool normalize_overflow(int *const tensptr, int *const unitsptr, const int base) { - register int tensdelta; + register int tensdelta; - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow(tensptr, tensdelta); +} + +static bool +normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base) +{ + register int tensdelta; + + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow32(tensptr, tensdelta); } static int -normalize_overflow32(int_fast32_t *const tensptr, int *const unitsptr, - const int base) +tmcomp(register const struct tm *const atmp, + register const struct tm *const btmp) { - register int tensdelta; + register int result; - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow32(tensptr, tensdelta); -} - -static int -tmcomp(register const struct tm * const atmp, - register const struct tm * const btmp) -{ - register int result; - - 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) - result = atmp->tm_sec - btmp->tm_sec; - return result; + 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) + result = atmp->tm_sec - btmp->tm_sec; + return result; } static time_t -time2sub(struct tm * const tmp, - struct tm *(*const funcp)(const time_t*, int_fast32_t, struct tm*, struct state*), - const int_fast32_t offset, - int * const okayp, - const int do_norm_secs, struct state * sp) // android-changed: added sp +time2sub(struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp, + bool do_norm_secs) { - register int dir; - register int i, j; - register int saved_seconds; - register int_fast32_t li; - register time_t lo; - register time_t hi; - int_fast32_t y; - time_t newt; - time_t t; - struct tm yourtm, mytm; + register int dir; + register int i, j; + register int saved_seconds; + register int_fast32_t li; + register time_t lo; + register time_t hi; + int_fast32_t y; + time_t newt; + time_t t; + struct tm yourtm, mytm; - *okayp = FALSE; - yourtm = *tmp; - if (do_norm_secs) { - if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, - SECSPERMIN)) - return WRONG; - } - if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; - if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - y = yourtm.tm_year; - if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; - /* - ** Turn y into an actual year number for now. - ** It is converted back to an offset from TM_YEAR_BASE later. - */ - if (increment_overflow32(&y, TM_YEAR_BASE)) - return WRONG; - while (yourtm.tm_mday <= 0) { - if (increment_overflow32(&y, -1)) - return WRONG; - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(li)]; - } - while (yourtm.tm_mday > DAYSPERLYEAR) { - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(li)]; - if (increment_overflow32(&y, 1)) - return WRONG; - } - for ( ; ; ) { - 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_overflow32(&y, 1)) - return WRONG; - } - } - if (increment_overflow32(&y, -TM_YEAR_BASE)) - return WRONG; - yourtm.tm_year = y; - if (yourtm.tm_year != y) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - 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. - ** Set tm_sec to 59 instead. - ** This assumes that the minimum representable time is - ** not in the same minute that a leap second was deleted from, - ** which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } else { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - /* - ** Do a binary search (this works whatever time_t's type is). - */ - if (!TYPE_SIGNED(time_t)) { - lo = 0; - hi = lo - 1; - } 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; - if ((*funcp)(&t, offset, &mytm, sp) == NULL) { // android-changed: added sp. - /* - ** 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); - if (dir != 0) { - if (t == lo) { - if (t == time_t_max) - return WRONG; - ++t; - ++lo; - } else if (t == hi) { - if (t == time_t_min) - return WRONG; - --t; - --hi; - } - if (lo > hi) - return WRONG; - if (dir > 0) - hi = t; - else lo = t; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - /* - ** Right time, wrong type. - ** Hunt for right time, right type. - ** It's okay to guess wrong since the guess - ** gets checked. - */ - // BEGIN android-changed: support user-supplied sp - if (sp == NULL) { - sp = (struct state *) - ((funcp == localsub) ? lclptr : gmtptr); - } - // END android-changed - if (sp == NULL) - return WRONG; - for (i = sp->typecnt - 1; i >= 0; --i) { - if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = sp->typecnt - 1; j >= 0; --j) { - if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) - continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - if ((*funcp)(&newt, offset, &mytm, sp) == NULL) // android-changed: added sp. - continue; - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - /* - ** We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } + *okayp = false; + yourtm = *tmp; + if (do_norm_secs) { + if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, + SECSPERMIN)) + return WRONG; + } + if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) + return WRONG; + if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) + return WRONG; + y = yourtm.tm_year; + if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) + return WRONG; + /* + ** Turn y into an actual year number for now. + ** It is converted back to an offset from TM_YEAR_BASE later. + */ + if (increment_overflow32(&y, TM_YEAR_BASE)) + return WRONG; + while (yourtm.tm_mday <= 0) { + if (increment_overflow32(&y, -1)) + return WRONG; + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday += year_lengths[isleap(li)]; + } + while (yourtm.tm_mday > DAYSPERLYEAR) { + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (increment_overflow32(&y, 1)) + return WRONG; + } + for ( ; ; ) { + 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_overflow32(&y, 1)) + return WRONG; + } + } + if (increment_overflow32(&y, -TM_YEAR_BASE)) + return WRONG; + if (! (INT_MIN <= y && y <= INT_MAX)) + return WRONG; + yourtm.tm_year = y; + if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) + saved_seconds = 0; + 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. + ** Set tm_sec to 59 instead. + ** This assumes that the minimum representable time is + ** not in the same minute that a leap second was deleted from, + ** which is a safer assumption than using 58 would be. + */ + if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) + return WRONG; + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = SECSPERMIN - 1; + } else { + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = 0; + } + /* + ** Do a binary search (this works whatever time_t's type is). + */ + lo = time_t_min; + hi = time_t_max; + for ( ; ; ) { + t = lo / 2 + hi / 2; + if (t < lo) + t = lo; + else if (t > hi) + t = hi; + if (! funcp(sp, &t, offset, &mytm)) { + /* + ** 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); + if (dir != 0) { + if (t == lo) { + if (t == time_t_max) + return WRONG; + ++t; + ++lo; + } else if (t == hi) { + if (t == time_t_min) + return WRONG; + --t; + --hi; + } + if (lo > hi) + return WRONG; + if (dir > 0) + hi = t; + else lo = t; + continue; + } +#if defined TM_GMTOFF && ! UNINIT_TRAP + if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF + && (yourtm.TM_GMTOFF < 0 + ? (-SECSPERDAY <= yourtm.TM_GMTOFF + && (mytm.TM_GMTOFF <= + (SMALLEST (INT_FAST32_MAX, LONG_MAX) + + yourtm.TM_GMTOFF))) + : (yourtm.TM_GMTOFF <= SECSPERDAY + && ((BIGGEST (INT_FAST32_MIN, LONG_MIN) + + yourtm.TM_GMTOFF) + <= mytm.TM_GMTOFF)))) { + /* MYTM matches YOURTM except with the wrong UTC offset. + YOURTM.TM_GMTOFF is plausible, so try it instead. + It's OK if YOURTM.TM_GMTOFF contains uninitialized data, + since the guess gets checked. */ + time_t altt = t; + int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF; + if (!increment_overflow_time(&altt, diff)) { + struct tm alttm; + if (funcp(sp, &altt, offset, &alttm) + && alttm.tm_isdst == mytm.tm_isdst + && alttm.TM_GMTOFF == yourtm.TM_GMTOFF + && tmcomp(&alttm, &yourtm) == 0) { + t = altt; + mytm = alttm; + } + } + } +#endif + if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) + break; + /* + ** Right time, wrong type. + ** Hunt for right time, right type. + ** It's okay to guess wrong since the guess + ** gets checked. + */ + if (sp == NULL) + return WRONG; + for (i = sp->typecnt - 1; i >= 0; --i) { + if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) + continue; + for (j = sp->typecnt - 1; j >= 0; --j) { + if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) + continue; + newt = t + sp->ttis[j].tt_gmtoff - + sp->ttis[i].tt_gmtoff; + if (! funcp(sp, &newt, offset, &mytm)) + continue; + if (tmcomp(&mytm, &yourtm) != 0) + continue; + if (mytm.tm_isdst != yourtm.tm_isdst) + continue; + /* + ** We have a match. + */ + t = newt; + goto label; + } + } + return WRONG; + } label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - if ((*funcp)(&t, offset, tmp, sp)) // android-changed: added sp. - *okayp = TRUE; - return t; + newt = t + saved_seconds; + if ((newt < t) != (saved_seconds < 0)) + return WRONG; + t = newt; + if (funcp(sp, &t, offset, tmp)) + *okayp = true; + return t; } static time_t -time2(struct tm * const tmp, - struct tm * (*const funcp)(const time_t *, int_fast32_t, struct tm *, struct state *), // android-changed: added sp. +time2(struct tm * const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, const int_fast32_t offset, - int *const okayp, struct state* sp) // android-changed: added sp. + bool *okayp) { - time_t t; + time_t t; - /* - ** First try without normalization of seconds - ** (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, sp); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp); + /* + ** First try without normalization of seconds + ** (in case tm_sec contains a value associated with a leap second). + ** If that fails, try with normalization of seconds. + */ + t = time2sub(tmp, funcp, sp, offset, okayp, false); + return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true); } static time_t -time1(struct tm * const tmp, - struct tm * (* const funcp) (const time_t *, int_fast32_t, struct tm *, struct state *), // android-changed: added sp. - const int_fast32_t offset, struct state * sp) // android-changed: added sp. +time1(struct tm *const tmp, + struct tm *(*funcp) (struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset) { - register time_t t; - register int samei, otheri; - register int sameind, otherind; - register int i; - register int nseen; - char seen[TZ_MAX_TYPES]; - unsigned char types[TZ_MAX_TYPES]; - int okay; + register time_t t; + register int samei, otheri; + register int sameind, otherind; + register int i; + register int nseen; + char seen[TZ_MAX_TYPES]; + unsigned char types[TZ_MAX_TYPES]; + bool okay; - if (tmp == NULL) { - errno = EINVAL; - return WRONG; - } - if (tmp->tm_isdst > 1) - tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay, sp); // android-changed: added sp. - if (okay) - return t; - if (tmp->tm_isdst < 0) + if (tmp == NULL) { + errno = EINVAL; + return WRONG; + } + if (tmp->tm_isdst > 1) + tmp->tm_isdst = 1; + t = time2(tmp, funcp, sp, offset, &okay); + if (okay) + return t; + if (tmp->tm_isdst < 0) #ifdef PCTS - /* - ** POSIX Conformance Test Suite code courtesy Grant Sullivan. - */ - tmp->tm_isdst = 0; /* reset to std and try again */ + /* + ** POSIX Conformance Test Suite code courtesy Grant Sullivan. + */ + tmp->tm_isdst = 0; /* reset to std and try again */ #else - return t; + return t; #endif /* !defined PCTS */ - /* - ** We're supposed to assume that somebody took a time of one type - ** and did some math on it that yielded a "struct tm" that's bad. - ** We try to divine the type they started from and adjust to the - ** type they need. - */ - // BEGIN android-changed: support user-supplied sp. - if (sp == NULL) { - sp = (struct state *) ((funcp == localsub) ? lclptr : gmtptr); - } - // BEGIN android-changed - if (sp == NULL) - return WRONG; - for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; - nseen = 0; - for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) { - seen[sp->types[i]] = TRUE; - types[nseen++] = sp->types[i]; - } - for (sameind = 0; sameind < nseen; ++sameind) { - samei = types[sameind]; - if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) - continue; - for (otherind = 0; otherind < nseen; ++otherind) { - otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - 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, sp); // android-changed: added sp. - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; + /* + ** We're supposed to assume that somebody took a time of one type + ** and did some math on it that yielded a "struct tm" that's bad. + ** We try to divine the type they started from and adjust to the + ** type they need. + */ + if (sp == NULL) + return WRONG; + for (i = 0; i < sp->typecnt; ++i) + seen[i] = false; + nseen = 0; + for (i = sp->timecnt - 1; i >= 0; --i) + if (!seen[sp->types[i]]) { + seen[sp->types[i]] = true; + types[nseen++] = sp->types[i]; + } + for (sameind = 0; sameind < nseen; ++sameind) { + samei = types[sameind]; + if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) + continue; + for (otherind = 0; otherind < nseen; ++otherind) { + otheri = types[otherind]; + if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) + continue; + tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - + sp->ttis[samei].tt_gmtoff; + tmp->tm_isdst = !tmp->tm_isdst; + t = time2(tmp, funcp, sp, offset, &okay); + if (okay) + return t; + tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - + sp->ttis[samei].tt_gmtoff; + tmp->tm_isdst = !tmp->tm_isdst; + } + } + return WRONG; } +static time_t +mktime_tzname(struct state *sp, struct tm *tmp, bool setname) +{ + if (sp) + return time1(tmp, localsub, sp, setname); + else { + gmtcheck(); + return time1(tmp, gmtsub, gmtptr, 0); + } +} + +#if NETBSD_INSPIRED + time_t -mktime(struct tm * const tmp) +mktime_z(struct state *sp, struct tm *tmp) { - _tzLock(); - tzset_locked(); - time_t result = time1(tmp, localsub, 0L, NULL); // android-changed: extra parameter. - _tzUnlock(); - return result; + return mktime_tzname(sp, tmp, false); +} + +#endif + +time_t +mktime(struct tm *tmp) +{ + time_t t; + int err = lock(); + if (err) { + errno = err; + return -1; + } + tzset_unlocked(); + t = mktime_tzname(lclptr, tmp, true); + unlock(); + return t; } #ifdef STD_INSPIRED time_t -timelocal(struct tm * const tmp) +timelocal(struct tm *tmp) { - if (tmp != NULL) - tmp->tm_isdst = -1; /* in case it wasn't initialized */ - return mktime(tmp); + if (tmp != NULL) + tmp->tm_isdst = -1; /* in case it wasn't initialized */ + return mktime(tmp); } time_t -timegm(struct tm * const tmp) +timegm(struct tm *tmp) { - time_t result; - - if (tmp != NULL) - tmp->tm_isdst = 0; - _tzLock(); - result = time1(tmp, gmtsub, 0L, NULL); // android-changed: extra parameter. - _tzUnlock(); - - return result; + return timeoff(tmp, 0); } time_t -timeoff(struct tm *const tmp, const long offset) +timeoff(struct tm *tmp, long offset) { - if (tmp != NULL) - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset, NULL); // android-changed: extra parameter. + if (tmp) + tmp->tm_isdst = 0; + gmtcheck(); + return time1(tmp, gmtsub, gmtptr, offset); } #endif /* defined STD_INSPIRED */ -#ifdef CMUCS - -/* -** The following is supplied for compatibility with -** previous versions of the CMUCS runtime library. -*/ - -long -gtime(struct tm * const tmp) -{ - const time_t t = mktime(tmp); - - if (t == WRONG) - return -1; - return t; -} - -#endif /* defined CMUCS */ - /* ** XXX--is the below the right way to conditionalize?? */ @@ -2079,64 +2213,105 @@ gtime(struct tm * const tmp) */ static int_fast64_t -leapcorr(time_t * timep) +leapcorr(struct state const *sp, time_t t) { - register struct state * sp; - register struct lsinfo * lp; - register int i; + register struct lsinfo const * lp; + register int i; - sp = lclptr; - i = sp->leapcnt; - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) - return lp->ls_corr; - } - return 0; + i = sp->leapcnt; + while (--i >= 0) { + lp = &sp->lsis[i]; + if (t >= lp->ls_trans) + return lp->ls_corr; + } + return 0; +} + +NETBSD_INSPIRED_EXTERN time_t ATTRIBUTE_PURE +time2posix_z(struct state *sp, time_t t) +{ + return t - leapcorr(sp, t); } time_t time2posix(time_t t) { - tzset(); - return t - leapcorr(&t); + int err = lock(); + if (err) { + errno = err; + return -1; + } + if (!lcl_is_set) + tzset_unlocked(); + if (lclptr) + t = time2posix_z(lclptr, t); + unlock(); + return t; +} + +NETBSD_INSPIRED_EXTERN time_t ATTRIBUTE_PURE +posix2time_z(struct state *sp, time_t t) +{ + time_t x; + time_t y; + /* + ** For a positive leap second hit, the result + ** is not unique. For a negative leap second + ** hit, the corresponding time doesn't exist, + ** so we return an adjacent second. + */ + x = t + leapcorr(sp, t); + y = x - leapcorr(sp, x); + if (y < t) { + do { + x++; + y = x - leapcorr(sp, x); + } while (y < t); + x -= y != t; + } else if (y > t) { + do { + --x; + y = x - leapcorr(sp, x); + } while (y > t); + x += y != t; + } + return x; } time_t posix2time(time_t t) { - time_t x; - time_t y; - - tzset(); - /* - ** For a positive leap second hit, the result - ** is not unique. For a negative leap second - ** hit, the corresponding time doesn't exist, - ** so we return an adjacent second. - */ - x = t + leapcorr(&t); - y = x - leapcorr(&x); - if (y < t) { - do { - x++; - y = x - leapcorr(&x); - } while (y < t); - if (t != y) - return x - 1; - } else if (y > t) { - do { - --x; - y = x - leapcorr(&x); - } while (y > t); - if (t != y) - return x + 1; - } - return x; + int err = lock(); + if (err) { + errno = err; + return -1; + } + if (!lcl_is_set) + tzset_unlocked(); + if (lclptr) + t = posix2time_z(lclptr, t); + unlock(); + return t; } #endif /* defined STD_INSPIRED */ +#ifdef time_tz + +/* Convert from the underlying system's time_t to the ersatz time_tz, + which is called 'time_t' in this file. */ + +time_t +time(time_t *p) +{ + time_t r = sys_time(0); + if (p) + *p = r; + return r; +} + +#endif + // BEGIN android-added #include @@ -2144,7 +2319,7 @@ posix2time(time_t t) #include // For ntohl(3). static int __bionic_open_tzdata_path(const char* path_prefix_variable, const char* path_suffix, - const char* olson_id, int* data_size) { + const char* olson_id) { const char* path_prefix = getenv(path_prefix_variable); if (path_prefix == NULL) { fprintf(stderr, "%s: %s not set!\n", __FUNCTION__, path_prefix_variable); @@ -2159,7 +2334,6 @@ static int __bionic_open_tzdata_path(const char* path_prefix_variable, const cha snprintf(path, path_length, "%s/%s", path_prefix, path_suffix); int fd = TEMP_FAILURE_RETRY(open(path, OPEN_MODE)); if (fd == -1) { - XLOG(("%s: could not open \"%s\": %s\n", __FUNCTION__, path, strerror(errno))); free(path); return -2; // Distinguish failure to find any data from failure to find a specific id. } @@ -2243,7 +2417,6 @@ static int __bionic_open_tzdata_path(const char* path_prefix_variable, const cha if (strcmp(this_id, olson_id) == 0) { specific_zone_offset = ntohl(entry->start) + ntohl(header.data_offset); - *data_size = ntohl(entry->length); break; } @@ -2252,7 +2425,6 @@ static int __bionic_open_tzdata_path(const char* path_prefix_variable, const cha free(index); if (specific_zone_offset == -1) { - XLOG(("%s: couldn't find zone \"%s\"\n", __FUNCTION__, olson_id)); free(path); close(fd); return -1; @@ -2272,10 +2444,10 @@ static int __bionic_open_tzdata_path(const char* path_prefix_variable, const cha return fd; } -static int __bionic_open_tzdata(const char* olson_id, int* data_size) { - int fd = __bionic_open_tzdata_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata", olson_id, data_size); +static int __bionic_open_tzdata(const char* olson_id) { + int fd = __bionic_open_tzdata_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata", olson_id); if (fd < 0) { - fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata", olson_id, data_size); + fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata", olson_id); if (fd == -2) { // The first thing that 'recovery' does is try to format the current time. It doesn't have // any tzdata available, so we must not abort here --- doing so breaks the recovery image! @@ -2287,7 +2459,7 @@ static int __bionic_open_tzdata(const char* olson_id, int* data_size) { // Caches the most recent timezone (http://b/8270865). static int __bionic_tzload_cached(const char* name, struct state* const sp, const int doextend) { - _tzLock(); + lock(); // Our single-item cache. static char* g_cached_time_zone_name; @@ -2296,7 +2468,7 @@ static int __bionic_tzload_cached(const char* name, struct state* const sp, cons // Do we already have this timezone cached? if (g_cached_time_zone_name != NULL && strcmp(name, g_cached_time_zone_name) == 0) { *sp = g_cached_time_zone; - _tzUnlock(); + unlock(); return 0; } @@ -2309,7 +2481,7 @@ static int __bionic_tzload_cached(const char* name, struct state* const sp, cons g_cached_time_zone = *sp; } - _tzUnlock(); + unlock(); return rc; } @@ -2321,12 +2493,12 @@ __attribute__((visibility("default"))) time_t mktime_tz(struct tm* const tmp, co if (st == NULL) return 0; - if (__bionic_tzload_cached(tz, st, TRUE) != 0) { + if (__bionic_tzload_cached(tz, st, true) != 0) { // TODO: not sure what's best here, but for now, we fall back to gmt. gmtload(st); } - return_value = time1(tmp, localsub, 0L, st); + return_value = time1(tmp, localsub, st, 0L); free(st); return return_value; } diff --git a/libc/tzcode/private.h b/libc/tzcode/private.h index 494e14249..1c176e62b 100644 --- a/libc/tzcode/private.h +++ b/libc/tzcode/private.h @@ -19,13 +19,9 @@ /* ** Defaults for preprocessor symbols. -** You can override these in your C compiler options, e.g. '-DHAVE_ADJTIME=0'. +** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'. */ -#ifndef HAVE_ADJTIME -#define HAVE_ADJTIME 1 -#endif /* !defined HAVE_ADJTIME */ - #ifndef HAVE_GETTEXT #define HAVE_GETTEXT 0 #endif /* !defined HAVE_GETTEXT */ @@ -38,9 +34,9 @@ #define HAVE_LINK 1 #endif /* !defined HAVE_LINK */ -#ifndef HAVE_SETTIMEOFDAY -#define HAVE_SETTIMEOFDAY 3 -#endif /* !defined HAVE_SETTIMEOFDAY */ +#ifndef HAVE_STRDUP +#define HAVE_STRDUP 1 +#endif #ifndef HAVE_SYMLINK #define HAVE_SYMLINK 1 @@ -59,32 +55,61 @@ #endif /* !defined HAVE_UNISTD_H */ #ifndef HAVE_UTMPX_H -#define HAVE_UTMPX_H 0 +#define HAVE_UTMPX_H 1 #endif /* !defined HAVE_UTMPX_H */ -#if !defined(__ANDROID__) -#ifndef LOCALE_HOME -#define LOCALE_HOME "/usr/lib/locale" -#endif /* !defined LOCALE_HOME */ -#endif // __ANDROID__ +#ifndef NETBSD_INSPIRED +# define NETBSD_INSPIRED 1 +#endif #if HAVE_INCOMPATIBLE_CTIME_R #define asctime_r _incompatible_asctime_r #define ctime_r _incompatible_ctime_r #endif /* HAVE_INCOMPATIBLE_CTIME_R */ +/* Enable tm_gmtoff and tm_zone on GNUish systems. */ +#define _GNU_SOURCE 1 +/* Fix asctime_r on Solaris 10. */ +#define _POSIX_PTHREAD_SEMANTICS 1 +/* Enable strtoimax on Solaris 10. */ +#define __EXTENSIONS__ 1 + /* ** Nested includes */ +/* Avoid clashes with NetBSD by renaming NetBSD's declarations. */ +#define localtime_rz sys_localtime_rz +#define mktime_z sys_mktime_z +#define posix2time_z sys_posix2time_z +#define time2posix_z sys_time2posix_z +#define timezone_t sys_timezone_t +#define tzalloc sys_tzalloc +#define tzfree sys_tzfree +#include +#undef localtime_rz +#undef mktime_z +#undef posix2time_z +#undef time2posix_z +#undef timezone_t +#undef tzalloc +#undef tzfree + #include "sys/types.h" /* for time_t */ #include "stdio.h" -#include "errno.h" #include "string.h" #include "limits.h" /* for CHAR_BIT et al. */ -#include "time.h" #include "stdlib.h" +#include "errno.h" + +#ifndef ENAMETOOLONG +# define ENAMETOOLONG EINVAL +#endif +#ifndef EOVERFLOW +# define EOVERFLOW EINVAL +#endif + #if HAVE_GETTEXT #include "libintl.h" #endif /* HAVE_GETTEXT */ @@ -104,6 +129,14 @@ #include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */ #endif /* HAVE_UNISTD_H */ +#ifndef HAVE_STRFTIME_L +# if _POSIX_VERSION < 200809 +# define HAVE_STRFTIME_L 0 +# else +# define HAVE_STRFTIME_L 1 +# endif +#endif + #ifndef F_OK #define F_OK 0 #endif /* !defined F_OK */ @@ -138,65 +171,98 @@ # include #endif -#ifndef INT_FAST64_MAX /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ -#if defined LLONG_MAX || defined __LONG_LONG_MAX__ -typedef long long int_fast64_t; +#ifdef __LONG_LONG_MAX__ +# ifndef LLONG_MAX +# define LLONG_MAX __LONG_LONG_MAX__ +# endif +# ifndef LLONG_MIN +# define LLONG_MIN (-1 - LLONG_MAX) +# endif +#endif + +#ifndef INT_FAST64_MAX # ifdef LLONG_MAX +typedef long long int_fast64_t; # define INT_FAST64_MIN LLONG_MIN # define INT_FAST64_MAX LLONG_MAX # else -# define INT_FAST64_MIN __LONG_LONG_MIN__ -# define INT_FAST64_MAX __LONG_LONG_MAX__ -# endif -# define SCNdFAST64 "lld" -#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ -#if (LONG_MAX >> 31) < 0xffffffff +# if LONG_MAX >> 31 < 0xffffffff Please use a compiler that supports a 64-bit integer type (or wider); you may need to compile with "-DHAVE_STDINT_H". -#endif /* (LONG_MAX >> 31) < 0xffffffff */ +# endif typedef long int_fast64_t; -# define INT_FAST64_MIN LONG_MIN -# define INT_FAST64_MAX LONG_MAX -# define SCNdFAST64 "ld" -#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ -#endif /* !defined INT_FAST64_MAX */ +# define INT_FAST64_MIN LONG_MIN +# define INT_FAST64_MAX LONG_MAX +# endif +#endif + +#ifndef SCNdFAST64 +# if INT_FAST64_MAX == LLONG_MAX +# define SCNdFAST64 "lld" +# else +# define SCNdFAST64 "ld" +# endif +#endif #ifndef INT_FAST32_MAX # if INT_MAX >> 31 == 0 typedef long int_fast32_t; +# define INT_FAST32_MAX LONG_MAX +# define INT_FAST32_MIN LONG_MIN # else typedef int int_fast32_t; +# define INT_FAST32_MAX INT_MAX +# define INT_FAST32_MIN INT_MIN # endif #endif #ifndef INTMAX_MAX -# if defined LLONG_MAX || defined __LONG_LONG_MAX__ +# ifdef LLONG_MAX typedef long long intmax_t; # define strtoimax strtoll -# define PRIdMAX "lld" -# ifdef LLONG_MAX -# define INTMAX_MAX LLONG_MAX -# define INTMAX_MIN LLONG_MIN -# else -# define INTMAX_MAX __LONG_LONG_MAX__ -# define INTMAX_MIN __LONG_LONG_MIN__ -# endif +# define INTMAX_MAX LLONG_MAX +# define INTMAX_MIN LLONG_MIN # else typedef long intmax_t; # define strtoimax strtol -# define PRIdMAX "ld" # define INTMAX_MAX LONG_MAX # define INTMAX_MIN LONG_MIN # endif #endif +#ifndef PRIdMAX +# if INTMAX_MAX == LLONG_MAX +# define PRIdMAX "lld" +# else +# define PRIdMAX "ld" +# endif +#endif + +#ifndef UINT_FAST64_MAX +# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ +typedef unsigned long long uint_fast64_t; +# else +# if ULONG_MAX >> 31 >> 1 < 0xffffffff +Please use a compiler that supports a 64-bit integer type (or wider); +you may need to compile with "-DHAVE_STDINT_H". +# endif +typedef unsigned long uint_fast64_t; +# endif +#endif + #ifndef UINTMAX_MAX # if defined ULLONG_MAX || defined __LONG_LONG_MAX__ typedef unsigned long long uintmax_t; -# define PRIuMAX "llu" # else typedef unsigned long uintmax_t; +# endif +#endif + +#ifndef PRIuMAX +# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ +# define PRIuMAX "llu" +# else # define PRIuMAX "lu" # endif #endif @@ -238,16 +304,6 @@ typedef unsigned long uintmax_t; ** Workarounds for compilers/systems. */ -/* -** Some time.h implementations don't declare asctime_r. -** Others might define it as a macro. -** Fix the former without affecting the latter. -*/ - -#ifndef asctime_r -extern char * asctime_r(struct tm const *, char *); -#endif - /* ** Compile with -Dtime_tz=T to build the tz package with a private ** time_t type equivalent to T rather than the system-supplied time_t. @@ -256,7 +312,11 @@ extern char * asctime_r(struct tm const *, char *); ** typical platforms. */ #ifdef time_tz +# ifdef LOCALTIME_IMPLEMENTATION static time_t sys_time(time_t *x) { return time(x); } +# endif + +typedef time_tz tz_time_t; # undef ctime # define ctime tz_ctime @@ -272,14 +332,40 @@ static time_t sys_time(time_t *x) { return time(x); } # define localtime tz_localtime # undef localtime_r # define localtime_r tz_localtime_r +# undef localtime_rz +# define localtime_rz tz_localtime_rz # undef mktime # define mktime tz_mktime +# undef mktime_z +# define mktime_z tz_mktime_z +# undef offtime +# define offtime tz_offtime +# undef posix2time +# define posix2time tz_posix2time +# undef posix2time_z +# define posix2time_z tz_posix2time_z # undef time # define time tz_time +# undef time2posix +# define time2posix tz_time2posix +# undef time2posix_z +# define time2posix_z tz_time2posix_z # undef time_t # define time_t tz_time_t - -typedef time_tz time_t; +# undef timegm +# define timegm tz_timegm +# undef timelocal +# define timelocal tz_timelocal +# undef timeoff +# define timeoff tz_timeoff +# undef tzalloc +# define tzalloc tz_tzalloc +# undef tzfree +# define tzfree tz_tzfree +# undef tzset +# define tzset tz_tzset +# undef tzsetwall +# define tzsetwall tz_tzsetwall char *ctime(time_t const *); char *ctime_r(time_t const *, char *); @@ -289,36 +375,111 @@ struct tm *gmtime_r(time_t const *restrict, struct tm *restrict); struct tm *localtime(time_t const *); struct tm *localtime_r(time_t const *restrict, struct tm *restrict); time_t mktime(struct tm *); - -static time_t -time(time_t *p) -{ - time_t r = sys_time(0); - if (p) - *p = r; - return r; -} +time_t time(time_t *); +void tzset(void); #endif /* -** Private function declarations. +** Some time.h implementations don't declare asctime_r. +** Others might define it as a macro. +** Fix the former without affecting the latter. +** Similarly for timezone, daylight, and altzone. */ -char * icatalloc(char * old, const char * new); -char * icpyalloc(const char * string); -const char * scheck(const char * string, const char * format); +#ifndef asctime_r +extern char * asctime_r(struct tm const *restrict, char *restrict); +#endif + +#ifdef USG_COMPAT +# ifndef timezone +extern long timezone; +# endif +# ifndef daylight +extern int daylight; +# endif +#endif +#if defined ALTZONE && !defined altzone +extern long altzone; +#endif + +/* +** The STD_INSPIRED functions are similar, but most also need +** declarations if time_tz is defined. +*/ + +#ifdef STD_INSPIRED +# if !defined tzsetwall || defined time_tz +void tzsetwall(void); +# endif +# if !defined offtime || defined time_tz +struct tm *offtime(time_t const *, long); +# endif +# if !defined timegm || defined time_tz +time_t timegm(struct tm *); +# endif +# if !defined timelocal || defined time_tz +time_t timelocal(struct tm *); +# endif +# if !defined timeoff || defined time_tz +time_t timeoff(struct tm *, long); +# endif +# if !defined time2posix || defined time_tz +time_t time2posix(time_t); +# endif +# if !defined posix2time || defined time_tz +time_t posix2time(time_t); +# endif +#endif + +/* Infer TM_ZONE on systems where this information is known, but suppress + guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ +#if (defined __GLIBC__ \ + || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__)) +# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF +# define TM_GMTOFF tm_gmtoff +# endif +# if !defined TM_ZONE && !defined NO_TM_ZONE +# define TM_ZONE tm_zone +# endif +#endif + +/* +** Define functions that are ABI compatible with NetBSD but have +** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t +** and labors under the misconception that 'const timezone_t' is a +** pointer to a constant. This use of 'const' is ineffective, so it +** is not done here. What we call 'struct state' NetBSD calls +** 'struct __state', but this is a private name so it doesn't matter. +*/ +#if NETBSD_INSPIRED +typedef struct state *timezone_t; +struct tm *localtime_rz(timezone_t restrict, time_t const *restrict, + struct tm *restrict); +time_t mktime_z(timezone_t restrict, struct tm *restrict); +timezone_t tzalloc(char const *); +void tzfree(timezone_t); +# ifdef STD_INSPIRED +# if !defined posix2time_z || defined time_tz +time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE; +# endif +# if !defined time2posix_z || defined time_tz +time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE; +# endif +# endif +#endif /* ** Finally, some convenience items. */ -#ifndef TRUE -#define TRUE 1 -#endif /* !defined TRUE */ - -#ifndef FALSE -#define FALSE 0 -#endif /* !defined FALSE */ +#if __STDC_VERSION__ < 199901 +# define true 1 +# define false 0 +# define bool int +#else +# include +#endif #ifndef TYPE_BIT #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) @@ -330,14 +491,14 @@ const char * scheck(const char * string, const char * format); #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) - /* Max and min values of the integer type T, of which only the bottom - * B bits are used, and where the highest-order used bit is considered - * to be a sign bit if T is signed. */ -#define MAXVAL(t, b) \ - ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \ - - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t))))) -#define MINVAL(t, b) \ - ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) +/* Max and min values of the integer type T, of which only the bottom + B bits are used, and where the highest-order used bit is considered + to be a sign bit if T is signed. */ +#define MAXVAL(t, b) \ + ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \ + - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t))))) +#define MINVAL(t, b) \ + ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) /* The minimum and maximum finite time values. This assumes no padding. */ static time_t const time_t_min = MINVAL(time_t, TYPE_BIT(time_t)); @@ -365,6 +526,10 @@ static time_t const time_t_max = MAXVAL(time_t, TYPE_BIT(time_t)); # define INITIALIZE(x) #endif +#ifndef UNINIT_TRAP +# define UNINIT_TRAP 0 +#endif + /* ** For the benefit of GNU folk... ** '_(MSGID)' uses the current locale's message library string for MSGID. @@ -379,7 +544,7 @@ static time_t const time_t_max = MAXVAL(time_t, TYPE_BIT(time_t)); #endif /* !HAVE_GETTEXT */ #endif /* !defined _ */ -#if !defined TZ_DOMAIN && defined TZ_DOMAINDIR +#if !defined TZ_DOMAIN && defined HAVE_GETTEXT # define TZ_DOMAIN "tz" #endif @@ -410,8 +575,4 @@ char *ctime_r(time_t const *, char *); #define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ #endif /* !defined SECSPERREPEAT_BITS */ -/* -** UNIX was a registered trademark of The Open Group in 2003. -*/ - #endif /* !defined PRIVATE_H */ diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c index 4328b4c53..10dfb4b6c 100644 --- a/libc/tzcode/strftime.c +++ b/libc/tzcode/strftime.c @@ -55,15 +55,7 @@ struct lc_time_T { const char * date_fmt; }; -#ifdef LOCALE_HOME -#include "sys/stat.h" -static struct lc_time_T localebuf; -static struct lc_time_T * _loc(void); -#define Locale _loc() -#endif /* defined LOCALE_HOME */ -#ifndef LOCALE_HOME #define Locale (&C_time_locale) -#endif /* !defined LOCALE_HOME */ static const struct lc_time_T C_time_locale = { { @@ -115,7 +107,7 @@ static char * _add(const char *, char *, const char *, int); static char * _conv(int, const char *, char *, const char *); static char * _fmt(const char *, const struct tm *, char *, const char *, int *); -static char * _yconv(int, int, int, int, char *, const char *, int); +static char * _yconv(int, int, bool, bool, char *, const char *, int); static char * getformat(int, char *, char *, char *, char *); extern char * tzname[]; @@ -132,32 +124,28 @@ extern char * tzname[]; #define FORCE_LOWER_CASE 0x100 size_t -strftime(char * const s, const size_t maxsize, const char *const format, - const struct tm *const t) +strftime(char *s, size_t maxsize, const char *format, const struct tm *t) { char * p; int warn; tzset(); -#ifdef LOCALE_HOME - localebuf.mon[0] = 0; -#endif /* defined LOCALE_HOME */ warn = IN_NONE; p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn); #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { - (void) fprintf(stderr, "\n"); + fprintf(stderr, "\n"); if (format == NULL) - (void) fprintf(stderr, "NULL strftime format "); - else (void) fprintf(stderr, "strftime format \"%s\" ", + fprintf(stderr, "NULL strftime format "); + else fprintf(stderr, "strftime format \"%s\" ", format); - (void) fprintf(stderr, "yields only two digits of years in "); + fprintf(stderr, "yields only two digits of years in "); if (warn == IN_SOME) - (void) fprintf(stderr, "some locales"); + fprintf(stderr, "some locales"); else if (warn == IN_THIS) - (void) fprintf(stderr, "the current locale"); - else (void) fprintf(stderr, "all locales"); - (void) fprintf(stderr, "\n"); + fprintf(stderr, "the current locale"); + else fprintf(stderr, "all locales"); + fprintf(stderr, "\n"); } #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ if (p == s + maxsize) @@ -171,20 +159,17 @@ static char *getformat(int modifier, char *normal, char *underscore, switch (modifier) { case '_': return underscore; - case '-': return dash; - case '0': return zero; } - return normal; } static char * -_fmt(const char *format, const struct tm *const t, char * pt, - const char *const ptlim, int *warnp) +_fmt(const char *format, const struct tm *t, char *pt, + const char *ptlim, int *warnp) { for ( ; *format; ++format) { if (*format == '%') { @@ -227,8 +212,8 @@ label: ** something completely different. ** (ado, 1993-05-24) */ - pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, - pt, ptlim, modifier); + pt = _yconv(t->tm_year, TM_YEAR_BASE, + true, false, pt, ptlim, modifier); continue; case 'c': { @@ -245,10 +230,7 @@ label: pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); continue; case 'd': - pt = _conv(t->tm_mday, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_mday, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'E': case 'O': @@ -270,31 +252,21 @@ label: modifier = *format; goto label; case 'e': - pt = _conv(t->tm_mday, - getformat(modifier, "%2d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_mday, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'F': pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); continue; case 'H': - pt = _conv(t->tm_hour, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_hour, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'I': pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'j': - pt = _conv(t->tm_yday + 1, - getformat(modifier, "%03d", "%3d", "%d", "%03d"), - pt, ptlim); + pt = _conv(t->tm_yday + 1, getformat(modifier, "%03d", "%3d", "%d", "%03d"), pt, ptlim); continue; case 'k': /* @@ -307,10 +279,7 @@ label: ** "%l" have been swapped. ** (ado, 1993-05-24) */ - pt = _conv(t->tm_hour, - getformat(modifier, "%2d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_hour, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim); continue; #ifdef KITCHEN_SINK case 'K': @@ -332,36 +301,23 @@ label: */ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, - getformat(modifier, "%2d", - "%2d", "%d", "%02d"), - pt, ptlim); + getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'M': - pt = _conv(t->tm_min, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_min, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'm': - pt = _conv(t->tm_mon + 1, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_mon + 1, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'n': pt = _add("\n", pt, ptlim, modifier); continue; + case 'P': case 'p': pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? Locale->pm : Locale->am, - pt, ptlim, modifier); - continue; - case 'P': - pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? - Locale->pm : - Locale->am, - pt, ptlim, FORCE_LOWER_CASE); + pt, ptlim, (*format == 'P') ? FORCE_LOWER_CASE : modifier); continue; case 'R': pt = _fmt("%H:%M", t, pt, ptlim, warnp); @@ -370,10 +326,7 @@ label: pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); continue; case 'S': - pt = _conv(t->tm_sec, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + pt = _conv(t->tm_sec, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 's': { @@ -385,10 +338,10 @@ label: tm = *t; mkt = mktime64(&tm); if (TYPE_SIGNED(time64_t)) - (void) snprintf(buf, sizeof(buf), "%lld", - (long long) mkt); - else (void) snprintf(buf, sizeof(buf), "%llu", - (unsigned long long) mkt); + snprintf(buf, sizeof(buf), "%"PRIdMAX, + (intmax_t) mkt); + else snprintf(buf, sizeof(buf), "%"PRIuMAX, + (uintmax_t) mkt); pt = _add(buf, pt, ptlim, modifier); } continue; @@ -401,9 +354,7 @@ label: case 'U': pt = _conv((t->tm_yday + DAYSPERWEEK - t->tm_wday) / DAYSPERWEEK, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'u': /* @@ -413,7 +364,8 @@ label: ** (ado, 1993-05-24) */ pt = _conv((t->tm_wday == 0) ? - DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); + DAYSPERWEEK : t->tm_wday, + "%d", pt, ptlim); continue; case 'V': /* ISO 8601 week number */ case 'G': /* ISO 8601 year (four digits) */ @@ -493,14 +445,15 @@ label: w = 53; #endif /* defined XPG4_1994_04_09 */ if (*format == 'V') - pt = _conv(w, - getformat(modifier, "%02d", "%2d", "%d", "%02d"), + pt = _conv(w, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); else if (*format == 'g') { *warnp = IN_ALL; - pt = _yconv(year, base, 0, 1, + pt = _yconv(year, base, + false, true, pt, ptlim, modifier); - } else pt = _yconv(year, base, 1, 1, + } else pt = _yconv(year, base, + true, true, pt, ptlim, modifier); } continue; @@ -517,9 +470,7 @@ label: (t->tm_wday ? (t->tm_wday - 1) : (DAYSPERWEEK - 1))) / DAYSPERWEEK, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); + getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); continue; case 'w': pt = _conv(t->tm_wday, "%d", pt, ptlim); @@ -540,23 +491,23 @@ label: continue; case 'y': *warnp = IN_ALL; - pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, + pt = _yconv(t->tm_year, TM_YEAR_BASE, + false, true, pt, ptlim, modifier); continue; case 'Y': - pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, + pt = _yconv(t->tm_year, TM_YEAR_BASE, + true, true, pt, ptlim, modifier); continue; case 'Z': #ifdef TM_ZONE - if (t->TM_ZONE != NULL) - pt = _add(t->TM_ZONE, pt, ptlim, - modifier); - else -#endif /* defined TM_ZONE */ + pt = _add(t->TM_ZONE, pt, ptlim, modifier); +#else if (t->tm_isdst >= 0) pt = _add(tzname[t->tm_isdst != 0], - pt, ptlim, modifier); + pt, ptlim); +#endif /* ** C99 says that %Z must be replaced by the ** empty string if the time zone is not @@ -613,10 +564,7 @@ label: diff /= SECSPERMIN; diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); - pt = _conv(diff, - getformat(modifier, "%04d", - "%4d", "%d", "%04d"), - pt, ptlim); + pt = _conv(diff, getformat(modifier, "%04d", "%4d", "%d", "%04d"), pt, ptlim); } continue; case '+': @@ -641,13 +589,12 @@ label: } static char * -_conv(const int n, const char *const format, char *const pt, - const char *const ptlim) +_conv(int n, const char *format, char *pt, const char *ptlim) { - char buf[INT_STRLEN_MAXIMUM(int) + 1]; + char buf[INT_STRLEN_MAXIMUM(int) + 1]; - (void) snprintf(buf, sizeof(buf), format, n); - return _add(buf, pt, ptlim, 0); + snprintf(buf, sizeof(buf), format, n); + return _add(buf, pt, ptlim, 0); } static char * @@ -699,8 +646,8 @@ _add(const char *str, char *pt, const char *const ptlim, int modifier) */ static char * -_yconv(const int a, const int b, const int convert_top, const int convert_yy, - char *pt, const char *const ptlim, int modifier) +_yconv(int a, int b, bool convert_top, bool convert_yy, + char *pt, const char *ptlim, int modifier) { register int lead; register int trail; diff --git a/libc/tzcode/tzfile.h b/libc/tzcode/tzfile.h index 529650dd8..ebecd6832 100644 --- a/libc/tzcode/tzfile.h +++ b/libc/tzcode/tzfile.h @@ -40,7 +40,7 @@ struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ - char tzh_reserved[15]; /* reserved--must be zero */ + char tzh_reserved[15]; /* reserved; must be zero */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ @@ -62,13 +62,13 @@ struct tzhead { ** tzh_leapcnt repetitions of ** one (char [4]) coded leap second transition times ** one (char [4]) total correction after above -** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition -** time is standard time, if FALSE, +** tzh_ttisstdcnt (char)s indexed by type; if 1, transition +** time is standard time, if 0, ** transition time is wall clock time ** if absent, transition times are ** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition -** time is UT, if FALSE, +** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition +** time is UT, if 0, ** transition time is local time ** if absent, transition times are ** assumed to be local time @@ -97,7 +97,7 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 1200 +#define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES