diff options
Diffstat (limited to 'elsie.nci.nih.gov/src/localtime.c')
-rw-r--r-- | elsie.nci.nih.gov/src/localtime.c | 389 |
1 files changed, 224 insertions, 165 deletions
diff --git a/elsie.nci.nih.gov/src/localtime.c b/elsie.nci.nih.gov/src/localtime.c index 622f7d4..63a1816 100644 --- a/elsie.nci.nih.gov/src/localtime.c +++ b/elsie.nci.nih.gov/src/localtime.c @@ -18,12 +18,10 @@ #if THREAD_SAFE # include <pthread.h> -# define VOLATILE volatile 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 -# define VOLATILE static int lock(void) { return 0; } static void unlock(void) { } #endif @@ -176,7 +174,7 @@ static struct state gmtmem; #endif /* !defined TZ_STRLEN_MAX */ static char lcl_TZname[TZ_STRLEN_MAX + 1]; -static int VOLATILE lcl_is_set; +static int lcl_is_set; char * tzname[2] = { (char *) wildabbr, @@ -202,6 +200,17 @@ int daylight = 0; long altzone = 0; #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) { @@ -298,67 +307,64 @@ differ_by_repeat(const time_t t1, const time_t t0) return t1 - t0 == SECSPERREPEAT; } -static bool -tzload(register const char *name, register struct state *const sp, - bool doextend) +/* 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 entire buffer. */ + char buf[2 * sizeof(struct tzhead) + 2 * sizeof (struct state) + + 4 * TZ_MAX_TIMES]; +}; + +/* 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; +}; + +/* 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 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]; - - /* 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 ssize_t nread; register bool doaccess; - register union local_storage *lsp; -#ifdef ALL_STATE - lsp = malloc(sizeof *lsp); - if (!lsp) - return false; -#else /* !defined ALL_STATE */ - union local_storage ls; - lsp = &ls; -#endif /* !defined ALL_STATE */ - fullname = lsp->fullname; - up = &lsp->u.u; + register char *fullname = lsp->fullname; + 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) - goto oops; + return EINVAL; } if (name[0] == ':') ++name; doaccess = name[0] == '/'; if (!doaccess) { - p = TZDIR; - if (! p || sizeof lsp->fullname - 1 <= strlen(p) + strlen(name)) - goto oops; + 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); @@ -368,14 +374,19 @@ tzload(register const char *name, register struct state *const sp, name = fullname; } if (doaccess && access(name, R_OK) != 0) - goto oops; + return errno; fid = open(name, OPEN_MODE); if (fid < 0) - goto oops; + return errno; nread = read(fid, up->buf, sizeof up->buf); - if (close(fid) < 0 || nread <= 0) - goto oops; + 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); @@ -383,23 +394,24 @@ tzload(register const char *name, register struct state *const sp, 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); - p = up->tzhead.tzh_charcnt + sizeof 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))) - goto oops; - if (nread - (p - up->buf) - < (timecnt * stored /* ats */ + 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 */ - goto oops; + return EINVAL; sp->leapcnt = leapcnt; sp->timecnt = timecnt; sp->typecnt = typecnt; @@ -419,7 +431,7 @@ tzload(register const char *name, register struct state *const sp, ? time_t_min : at); if (timecnt && attime <= sp->ats[timecnt - 1]) { if (attime < sp->ats[timecnt - 1]) - goto oops; + return EINVAL; sp->types[i - 1] = 0; timecnt--; } @@ -432,7 +444,7 @@ tzload(register const char *name, register struct state *const sp, for (i = 0; i < sp->timecnt; ++i) { unsigned char typ = *p++; if (sp->typecnt <= typ) - goto oops; + return EINVAL; if (sp->types[i]) sp->types[timecnt++] = typ; } @@ -446,11 +458,11 @@ tzload(register const char *name, register struct state *const sp, p += 4; isdst = *p++; if (! (isdst < 2)) - goto oops; + return EINVAL; ttisp->tt_isdst = isdst; abbrind = *p++; if (! (abbrind < sp->charcnt)) - goto oops; + return EINVAL; ttisp->tt_abbrind = abbrind; } for (i = 0; i < sp->charcnt; ++i) @@ -469,7 +481,7 @@ tzload(register const char *name, register struct state *const sp, ? time_t_min : tr); if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans) { if (trans < sp->lsis[leapcnt - 1].ls_trans) - goto oops; + return EINVAL; leapcnt--; } sp->lsis[leapcnt].ls_trans = trans; @@ -487,7 +499,7 @@ tzload(register const char *name, register struct state *const sp, ttisp->tt_ttisstd = false; else { if (*p != true && *p != false) - goto oops; + return EINVAL; ttisp->tt_ttisstd = *p++; } } @@ -499,7 +511,7 @@ tzload(register const char *name, register struct state *const sp, ttisp->tt_ttisgmt = false; else { if (*p != true && *p != false) - goto oops; + return EINVAL; ttisp->tt_ttisgmt = *p++; } } @@ -509,8 +521,7 @@ tzload(register const char *name, register struct state *const sp, if (up->tzhead.tzh_version[0] == '\0') break; nread -= p - up->buf; - for (i = 0; i < nread; ++i) - up->buf[i] = p[i]; + memmove(up->buf, p, nread); /* ** If this is a signed narrow time_t system, we're done. */ @@ -571,10 +582,10 @@ tzload(register const char *name, register struct state *const sp, ** If type 0 is is unused in transitions, ** it's the type to use for early times. */ - for (i = 0; i < sp->typecnt; ++i) + for (i = 0; i < sp->timecnt; ++i) if (sp->types[i] == 0) break; - i = (i >= sp->typecnt) ? 0 : -1; + i = i < sp->timecnt ? -1 : 0; /* ** Absent the above, ** if there are transition times @@ -601,15 +612,27 @@ tzload(register const char *name, register struct state *const sp, } } 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 - free(up); -#endif /* defined ALL_STATE */ - return true; -oops: -#ifdef ALL_STATE - free(up); -#endif /* defined ALL_STATE */ - return false; + 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 @@ -934,7 +957,6 @@ tzparse(const char *name, register struct state *const sp, int_fast32_t dstoffset; register char * cp; register bool load_ok; - static struct ttinfo zttinfo; stdname = name; if (lastditch) { @@ -962,7 +984,7 @@ tzparse(const char *name, register struct state *const sp, if (name == NULL) return false; } - load_ok = tzload(TZDEFRULES, sp, false); + load_ok = tzload(TZDEFRULES, sp, false) == 0; if (!load_ok) sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { @@ -1006,13 +1028,8 @@ tzparse(const char *name, register struct state *const sp, /* ** 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 = true; - sp->ttis[0].tt_abbrind = stdlen + 1; - sp->ttis[1].tt_gmtoff = -stdoffset; - sp->ttis[1].tt_isdst = false; - sp->ttis[1].tt_abbrind = 0; + init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1); + init_ttinfo(&sp->ttis[1], -stdoffset, false, 0); sp->defaulttype = 0; timecnt = 0; janfirst = 0; @@ -1131,13 +1148,8 @@ tzparse(const char *name, register struct state *const sp, /* ** 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; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); sp->typecnt = 2; sp->defaulttype = 0; } @@ -1145,10 +1157,7 @@ tzparse(const char *name, register struct state *const sp, 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 = false; - sp->ttis[0].tt_abbrind = 0; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); sp->defaulttype = 0; } sp->charcnt = stdlen + 1; @@ -1170,47 +1179,55 @@ tzparse(const char *name, register struct state *const sp, static void gmtload(struct state *const sp) { - if (! tzload(gmt, sp, true)) + if (tzload(gmt, sp, true) != 0) tzparse(gmt, sp, true); } -static struct state * +/* 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 (sp) { - 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->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_gmtoff = 0; - sp->ttis[0].tt_abbrind = 0; - strcpy(sp->chars, gmt); - } else if (! (tzload(name, sp, true) - || (name && name[0] != ':' && tzparse(name, sp, false)))) - return NULL; + 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)) + return 0; + return err; } - return sp; } 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; - if (0 < lcl) - strcpy(lcl_TZname, name); #ifdef ALL_STATE - if (! lclptr) - lclptr = malloc(sizeof *lclptr); + if (! sp) + lclptr = sp = malloc(sizeof *lclptr); #endif /* defined ALL_STATE */ - zoneinit(lclptr, name); + if (sp) { + if (zoneinit(sp, name) != 0) + zoneinit(sp, ""); + if (0 < lcl) + strcpy(lcl_TZname, name); + } settzname(); lcl_is_set = lcl; } @@ -1244,17 +1261,17 @@ tzset(void) static void gmtcheck(void) { - static bool VOLATILE gmt_is_set; - if (gmt_is_set) - return; + static bool gmt_is_set; if (lock() != 0) return; + if (! gmt_is_set) { #ifdef ALL_STATE - gmtptr = malloc(sizeof *gmtptr); + gmtptr = malloc(sizeof *gmtptr); #endif - if (gmtptr) - gmtload(gmtptr); - gmt_is_set = true; + if (gmtptr) + gmtload(gmtptr); + gmt_is_set = true; + } unlock(); } @@ -1264,10 +1281,15 @@ timezone_t tzalloc(char const *name) { timezone_t sp = malloc(sizeof *sp); - timezone_t tp = sp ? zoneinit(sp, name) : sp; - if (!tp) - free(sp); - return tp; + if (sp) { + int err = zoneinit(sp, name); + if (err != 0) { + free(sp); + errno = err; + return NULL; + } + } + return sp; } void @@ -1294,7 +1316,11 @@ tzfree(timezone_t sp) ** 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 OFFSET 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. +** OFFSET's type is intfast32_t for compatibility with gmtsub. */ /*ARGSUSED*/ @@ -1307,8 +1333,10 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t offset, register struct tm * result; const time_t t = *timep; - if (sp == NULL) - return gmtsub(gmtptr, timep, offset, tmp); + 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; @@ -1328,16 +1356,16 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t offset, newt > sp->ats[sp->timecnt - 1]) return NULL; /* "cannot happen" */ result = localsub(sp, &newt, offset, tmp); - if (result == tmp) { + if (result) { register int_fast64_t newy; - newy = tmp->tm_year; + newy = result->tm_year; if (t < sp->ats[0]) newy -= years; else newy += years; if (! (INT_MIN <= newy && newy <= INT_MAX)) return NULL; - tmp->tm_year = newy; + result->tm_year = newy; } return result; } @@ -1364,30 +1392,52 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t offset, ** timesub(&t, 0L, sp, tmp); */ result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); - tmp->tm_isdst = ttisp->tt_isdst; + if (result) { + bool tm_isdst = ttisp->tt_isdst; + char *tm_zone = (char *) &sp->chars[ttisp->tt_abbrind]; + result->tm_isdst = tm_isdst; #ifdef TM_ZONE - tmp->TM_ZONE = (char *) &sp->chars[ttisp->tt_abbrind]; + result->TM_ZONE = tm_zone; #endif /* defined TM_ZONE */ + if (offset) { + /* Always set the tzname etc. vars whose values can easily + be determined, as it's too much trouble to tell whether + tzset has already done it correctly. */ + tzname[tm_isdst] = tm_zone; +#ifdef USG_COMPAT + if (!tm_isdst) + timezone = - ttisp->tt_gmtoff; +#endif +#ifdef ALTZONE + if (tm_isdst) + altzone = - ttisp->tt_gmtoff; +#endif + } + } return result; } -NETBSD_INSPIRED_EXTERN struct tm * +#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 skip_tzset) +localtime_tzset(time_t const *timep, struct tm *tmp, bool setname) { int err = lock(); if (err) { errno = err; return NULL; } - if (!skip_tzset) + if (setname || !lcl_is_set) tzset_unlocked(); - tmp = localtime_rz(lclptr, timep, tmp); + tmp = localsub(lclptr, timep, setname, tmp); unlock(); return tmp; } @@ -1395,13 +1445,13 @@ localtime_tzset(time_t const *timep, struct tm *tmp, bool skip_tzset) struct tm * localtime(const time_t *const timep) { - return localtime_tzset(timep, &tm, 0); + return localtime_tzset(timep, &tm, true); } struct tm * localtime_r(const time_t *const timep, struct tm *tmp) { - return localtime_tzset(timep, tmp, lcl_is_set != 0); + return localtime_tzset(timep, tmp, false); } /* @@ -1441,8 +1491,7 @@ struct tm * gmtime_r(const time_t *const timep, struct tm *tmp) { gmtcheck(); - tmp = gmtsub(gmtptr, timep, 0, tmp); - return tmp; + return gmtsub(gmtptr, timep, 0, tmp); } #ifdef STD_INSPIRED @@ -1450,10 +1499,8 @@ gmtime_r(const time_t *const timep, struct tm *tmp) struct tm * offtime(const time_t *const timep, const long offset) { - struct tm *tmp; gmtcheck(); - tmp = gmtsub(gmtptr, timep, offset, &tm); - return tmp; + return gmtsub(gmtptr, timep, offset, &tm); } #endif /* defined STD_INSPIRED */ @@ -1520,13 +1567,13 @@ timesub(const time_t *const timep, const int_fast32_t offset, tdelta = tdays / DAYSPERLYEAR; if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta) && tdelta <= INT_MAX)) - return NULL; + goto out_of_range; idelta = tdelta; if (idelta == 0) idelta = (tdays < 0) ? -1 : 1; newy = y; if (increment_overflow(&newy, idelta)) - return NULL; + goto out_of_range; leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); tdays -= ((time_t) newy - y) * DAYSPERNYEAR; @@ -1555,17 +1602,17 @@ timesub(const time_t *const timep, const int_fast32_t offset, } while (idays < 0) { if (increment_overflow(&y, -1)) - return NULL; + 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)) - return NULL; + goto out_of_range; } tmp->tm_year = y; if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; + goto out_of_range; tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. @@ -1596,6 +1643,10 @@ timesub(const time_t *const timep, const int_fast32_t offset, tmp->TM_GMTOFF = offset; #endif /* defined TM_GMTOFF */ return tmp; + + out_of_range: + errno = EOVERFLOW; + return NULL; } char * @@ -1607,15 +1658,16 @@ ctime(const time_t *const timep) ** 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) { - 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; } /* @@ -2015,17 +2067,27 @@ time1(struct tm *const tmp, return WRONG; } -NETBSD_INSPIRED_EXTERN time_t -mktime_z(struct state *sp, struct tm *tmp) +static time_t +mktime_tzname(struct state *sp, struct tm *tmp, bool setname) { if (sp) - return time1(tmp, localsub, sp, 0); + return time1(tmp, localsub, sp, setname); else { gmtcheck(); return time1(tmp, gmtsub, gmtptr, 0); } } +#if NETBSD_INSPIRED + +time_t +mktime_z(struct state *sp, struct tm *tmp) +{ + return mktime_tzname(sp, tmp, false); +} + +#endif + time_t mktime(struct tm *const tmp) { @@ -2036,7 +2098,7 @@ mktime(struct tm *const tmp) return -1; } tzset_unlocked(); - t = mktime_z(lclptr, tmp); + t = mktime_tzname(lclptr, tmp, true); unlock(); return t; } @@ -2060,12 +2122,10 @@ timegm(struct tm *const tmp) time_t timeoff(struct tm *const tmp, const long offset) { - time_t t; if (tmp) tmp->tm_isdst = 0; gmtcheck(); - t = time1(tmp, gmtsub, gmtptr, offset); - return t; + return time1(tmp, gmtsub, gmtptr, offset); } #endif /* defined STD_INSPIRED */ @@ -2090,7 +2150,6 @@ leapcorr(struct state const *sp, time_t t) register struct lsinfo const * lp; register int i; - sp = lclptr; i = sp->leapcnt; while (--i >= 0) { lp = &sp->lsis[i]; |