aboutsummaryrefslogtreecommitdiff
path: root/contrib/tzcode/localtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tzcode/localtime.c')
-rw-r--r--contrib/tzcode/localtime.c594
1 files changed, 379 insertions, 215 deletions
diff --git a/contrib/tzcode/localtime.c b/contrib/tzcode/localtime.c
index 1a01db931cab..15afeeecb6d0 100644
--- a/contrib/tzcode/localtime.c
+++ b/contrib/tzcode/localtime.c
@@ -34,6 +34,14 @@ int __tz_change_interval = DETECT_TZ_CHANGES_INTERVAL;
#include "un-namespace.h"
#endif /* __FreeBSD__ */
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if !defined S_ISREG && defined S_IFREG
+/* Ancient UNIX or recent MS-Windows. */
+# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+
#if defined THREAD_SAFE && THREAD_SAFE
# include <pthread.h>
#ifdef __FreeBSD__
@@ -48,6 +56,73 @@ static int lock(void) { return 0; }
static void unlock(void) { }
#endif
+/* Unless intptr_t is missing, pacify gcc -Wcast-qual on char const * exprs.
+ Use this carefully, as the casts disable type checking.
+ This is a macro so that it can be used in static initializers. */
+#ifdef INTPTR_MAX
+# define UNCONST(a) ((char *) (intptr_t) (a))
+#else
+# define UNCONST(a) ((char *) (a))
+#endif
+
+/* A signed type wider than int, so that we can add 1900 + tm_mon/12 to tm_year
+ without overflow. The static_assert checks that it is indeed wider
+ than int; if this fails on your platform please let us know. */
+#if INT_MAX < LONG_MAX
+typedef long iinntt;
+# define IINNTT_MIN LONG_MIN
+# define IINNTT_MAX LONG_MAX
+#elif INT_MAX < LLONG_MAX
+typedef long long iinntt;
+# define IINNTT_MIN LLONG_MIN
+# define IINNTT_MAX LLONG_MAX
+#else
+typedef intmax_t iinntt;
+# define IINNTT_MIN INTMAX_MIN
+# define IINNTT_MAX INTMAX_MAX
+#endif
+static_assert(IINNTT_MIN < INT_MIN && INT_MAX < IINNTT_MAX);
+
+/* On platforms where offtime or mktime might overflow,
+ strftime.c defines USE_TIMEX_T to be true and includes us.
+ This tells us to #define time_t to an internal type timex_t that is
+ wide enough so that strftime %s never suffers from integer overflow,
+ and to #define offtime (if TM_GMTOFF is defined) or mktime (otherwise)
+ to a static function that returns the redefined time_t.
+ It also tells us to define only data and code needed
+ to support the offtime or mktime variant. */
+#ifndef USE_TIMEX_T
+# define USE_TIMEX_T false
+#endif
+#if USE_TIMEX_T
+# undef TIME_T_MIN
+# undef TIME_T_MAX
+# undef time_t
+# define time_t timex_t
+# if MKTIME_FITS_IN(LONG_MIN, LONG_MAX)
+typedef long timex_t;
+# define TIME_T_MIN LONG_MIN
+# define TIME_T_MAX LONG_MAX
+# elif MKTIME_FITS_IN(LLONG_MIN, LLONG_MAX)
+typedef long long timex_t;
+# define TIME_T_MIN LLONG_MIN
+# define TIME_T_MAX LLONG_MAX
+# else
+typedef intmax_t timex_t;
+# define TIME_T_MIN INTMAX_MIN
+# define TIME_T_MAX INTMAX_MAX
+# endif
+
+# ifdef TM_GMTOFF
+# undef timeoff
+# define timeoff timex_timeoff
+# undef EXTERN_TIMEOFF
+# else
+# undef mktime
+# define mktime timex_mktime
+# endif
+#endif
+
#ifndef TZ_ABBR_CHAR_SET
# define TZ_ABBR_CHAR_SET \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
@@ -57,12 +132,23 @@ static void unlock(void) { }
# define TZ_ABBR_ERR_CHAR '_'
#endif /* !defined TZ_ABBR_ERR_CHAR */
-/*
-** Support non-POSIX platforms that distinguish between text and binary files.
-*/
+/* Port to platforms that lack some O_* flags. Unless otherwise
+ specified, the flags are standardized by POSIX. */
#ifndef O_BINARY
-# define O_BINARY 0
+# define O_BINARY 0 /* MS-Windows */
+#endif
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+#ifndef O_CLOFORK
+# define O_CLOFORK 0
+#endif
+#ifndef O_IGNORE_CTTY
+# define O_IGNORE_CTTY 0 /* GNU/Hurd */
+#endif
+#ifndef O_NOCTTY
+# define O_NOCTTY 0
#endif
#ifndef WILDABBR
@@ -91,7 +177,10 @@ static void unlock(void) { }
static const char wildabbr[] = WILDABBR;
static char const etc_utc[] = "Etc/UTC";
+
+#if !USE_TIMEX_T || defined TM_ZONE || !defined TM_GMTOFF
static char const *utc = etc_utc + sizeof "Etc/" - 1;
+#endif
/*
** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
@@ -103,10 +192,31 @@ static char const *utc = etc_utc + sizeof "Etc/" - 1;
# define TZDEFRULESTRING ",M3.2.0,M11.1.0"
#endif
+/* Limit to time zone abbreviation length in proleptic TZ strings.
+ This is distinct from TZ_MAX_CHARS, which limits TZif file contents.
+ It defaults to 254, not 255, so that desigidx_type can be an unsigned char.
+ unsigned char suffices for TZif files, so the only reason to increase
+ TZNAME_MAXIMUM is to support TZ strings specifying abbreviations
+ longer than 254 bytes. There is little reason to do that, though,
+ as strings that long are hardly "abbreviations". */
+#ifndef TZNAME_MAXIMUM
+# define TZNAME_MAXIMUM 254
+#endif
+
+#if TZNAME_MAXIMUM < UCHAR_MAX
+typedef unsigned char desigidx_type;
+#elif TZNAME_MAXIMUM < INT_MAX
+typedef int desigidx_type;
+#elif TZNAME_MAXIMUM < PTRDIFF_MAX
+typedef ptrdiff_t desigidx_type;
+#else
+# error "TZNAME_MAXIMUM too large"
+#endif
+
struct ttinfo { /* time type information */
- int_fast32_t tt_utoff; /* UT offset in seconds */
+ int_least32_t tt_utoff; /* UT offset in seconds */
+ desigidx_type tt_desigidx; /* abbreviation list index */
bool tt_isdst; /* used to set tm_isdst */
- int tt_desigidx; /* abbreviation list index */
bool tt_ttisstd; /* transition is std time */
bool tt_ttisut; /* transition is UT */
};
@@ -125,12 +235,6 @@ static char const UNSPEC[] = "-00";
for ttunspecified to work without crashing. */
enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
-/* Limit to time zone abbreviation length in proleptic TZ strings.
- This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */
-#ifndef TZNAME_MAXIMUM
-# define TZNAME_MAXIMUM 255
-#endif
-
/* A representation of the contents of a TZif file. Ideally this
would have no size limits; the following sizes should suffice for
practical use. This struct should not be too large, as instances
@@ -173,7 +277,6 @@ static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t,
static bool increment_overflow(int *, int);
static bool increment_overflow_time(time_t *, int_fast32_t);
static int_fast32_t leapcorr(struct state const *, time_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 tzparse(char const *, struct state *, struct state const *);
@@ -194,8 +297,10 @@ static struct state *const gmtptr = &gmtmem;
# define TZ_STRLEN_MAX 255
#endif /* !defined TZ_STRLEN_MAX */
+#if !USE_TIMEX_T || !defined TM_GMTOFF
static char lcl_TZname[TZ_STRLEN_MAX + 1];
static int lcl_is_set;
+#endif
#ifdef __FreeBSD__
static pthread_once_t gmt_once = PTHREAD_ONCE_INIT;
static pthread_once_t gmtime_once = PTHREAD_ONCE_INIT;
@@ -221,27 +326,29 @@ static int localtime_key_error;
** trigger latent bugs in programs.
*/
-#if SUPPORT_C89
+#if !USE_TIMEX_T
+
+# if SUPPORT_C89
static struct tm tm;
#endif
-#if 2 <= HAVE_TZNAME + TZ_TIME_T
-char * tzname[2] = {
- (char *) wildabbr,
- (char *) wildabbr
-};
-#endif
-#if 2 <= USG_COMPAT + TZ_TIME_T
+# if 2 <= HAVE_TZNAME + TZ_TIME_T
+char *tzname[2] = { UNCONST(wildabbr), UNCONST(wildabbr) };
+# endif
+# if 2 <= USG_COMPAT + TZ_TIME_T
long timezone;
int daylight;
-#endif
-#if 2 <= ALTZONE + TZ_TIME_T
+# endif
+# if 2 <= ALTZONE + TZ_TIME_T
long altzone;
+# endif
+
#endif
/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */
static void
-init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
+init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst,
+ desigidx_type desigidx)
{
s->tt_utoff = utoff;
s->tt_isdst = isdst;
@@ -305,20 +412,22 @@ detzcode64(const char *const codep)
return result;
}
+#if !USE_TIMEX_T || !defined TM_GMTOFF
+
static void
update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp)
{
-#if HAVE_TZNAME
- tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx];
-#endif
-#if USG_COMPAT
+# if HAVE_TZNAME
+ tzname[ttisp->tt_isdst] = UNCONST(&sp->chars[ttisp->tt_desigidx]);
+# endif
+# if USG_COMPAT
if (!ttisp->tt_isdst)
timezone = - ttisp->tt_utoff;
-#endif
-#if ALTZONE
+# endif
+# if ALTZONE
if (ttisp->tt_isdst)
altzone = - ttisp->tt_utoff;
-#endif
+# endif
}
/* If STDDST_MASK indicates that SP's TYPE provides useful info,
@@ -349,18 +458,18 @@ settzname(void)
When STDDST_MASK becomes zero we can stop looking. */
int stddst_mask = 0;
-#if HAVE_TZNAME
- tzname[0] = tzname[1] = (char *) (sp ? wildabbr : utc);
+# if HAVE_TZNAME
+ tzname[0] = tzname[1] = UNCONST(sp ? wildabbr : utc);
stddst_mask = 3;
-#endif
-#if USG_COMPAT
+# endif
+# if USG_COMPAT
timezone = 0;
stddst_mask = 3;
-#endif
-#if ALTZONE
+# endif
+# if ALTZONE
altzone = 0;
stddst_mask |= 2;
-#endif
+# endif
/*
** And to get the latest time zone abbreviations into tzname. . .
*/
@@ -370,9 +479,9 @@ settzname(void)
for (i = sp->typecnt - 1; stddst_mask && 0 <= i; i--)
stddst_mask = may_update_tzname_etc(stddst_mask, sp, i);
}
-#if USG_COMPAT
+# if USG_COMPAT
daylight = stddst_mask >> 1 ^ 1;
-#endif
+# endif
}
/* Replace bogus characters in time zone abbreviations.
@@ -399,6 +508,8 @@ scrub_abbrs(struct state *sp)
return 0;
}
+#endif
+
#ifdef DETECT_TZ_CHANGES
/*
* Check whether either the time zone name or the file it refers to has
@@ -473,11 +584,15 @@ union local_storage {
#endif /* !__FreeBSD__ */
};
-/* Load tz data from the file named NAME into *SP. Read extended
- format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
+/* These tzload flags can be ORed together, and fit into 'char'. */
+enum { TZLOAD_FROMENV = 1 }; /* The TZ string came from the environment. */
+enum { TZLOAD_TZSTRING = 2 }; /* Read any newline-surrounded TZ string. */
+
+/* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS.
+ 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,
+tzloadbody(char const *name, struct state *sp, char tzloadflags,
union local_storage *lsp)
{
register int i;
@@ -485,7 +600,9 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
register int stored;
register ssize_t nread;
#ifdef __FreeBSD__
- int serrno;
+ struct stat sb;
+ const char *relname;
+ int dd, serrno;
#else /* !__FreeBSD__ */
register bool doaccess;
#endif /* !__FreeBSD__ */
@@ -498,6 +615,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
name = TZDEFAULT;
if (! name)
return EINVAL;
+ tzloadflags &= ~TZLOAD_FROMENV;
}
if (name[0] == ':')
@@ -533,44 +651,77 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
name = lsp->fullname;
}
- if (doaccess && access(name, R_OK) != 0)
- return errno;
-#else /* __FreeBSD__ */
- if (issetugid()) {
- const char *relname = name;
- if (strncmp(relname, TZDIR "/", strlen(TZDIR) + 1) == 0)
- relname += strlen(TZDIR) + 1;
- int dd = _open(TZDIR, O_DIRECTORY | O_RDONLY);
- if (dd < 0)
- return errno;
- fid = _openat(dd, relname, O_RDONLY | O_BINARY, AT_RESOLVE_BENEATH);
- serrno = errno;
- _close(dd);
- errno = serrno;
- } else
+ if (doaccess && (tzloadflags & TZLOAD_FROMENV)) {
+ /* Check for security violations and for devices whose mere
+ opening could have unwanted side effects. Although these
+ checks are racy, they're better than nothing and there is
+ no portable way to fix the races. */
+ if (access(name, R_OK) < 0)
+ return errno;
+#ifdef S_ISREG
+ {
+ struct stat st;
+ if (stat(name, &st) < 0)
+ return errno;
+ if (!S_ISREG(st.st_mode))
+ return EINVAL;
+ }
#endif
- fid = _open(name, O_RDONLY | O_BINARY);
+ }
+ fid = _open(name, (O_RDONLY | O_BINARY | O_CLOEXEC | O_CLOFORK
+ | O_IGNORE_CTTY | O_NOCTTY));
+#else /* __FreeBSD__ */
+ if ((tzloadflags & TZLOAD_FROMENV) && strcmp(name, TZDEFAULT) == 0)
+ tzloadflags &= ~TZLOAD_FROMENV;
+ relname = name;
+ if (strncmp(relname, TZDIR "/", strlen(TZDIR) + 1) == 0)
+ relname += strlen(TZDIR) + 1;
+ dd = _open(TZDIR, O_DIRECTORY | O_RDONLY);
+ if ((tzloadflags & TZLOAD_FROMENV) && issetugid()) {
+ if (dd < 0)
+ return errno;
+ if (fstatat(dd, name, &sb, AT_RESOLVE_BENEATH) < 0) {
+ fid = -1;
+ } else if (!S_ISREG(sb.st_mode)) {
+ fid = -1;
+ errno = EINVAL;
+ } else {
+ fid = _openat(dd, relname, O_RDONLY | O_BINARY, AT_RESOLVE_BENEATH);
+ }
+ } else {
+ if (dd < 0) {
+ relname = name;
+ dd = AT_FDCWD;
+ }
+ fid = _openat(dd, relname, O_RDONLY | O_BINARY, 0);
+ }
+ if (dd != AT_FDCWD && dd >= 0) {
+ serrno = errno;
+ _close(dd);
+ errno = serrno;
+ }
+#endif /* __FreeBSD__ */
if (fid < 0)
return errno;
#ifdef DETECT_TZ_CHANGES
- if (doextend) {
- /*
- * Detect if the timezone file has changed. Check 'doextend' to
- * ignore TZDEFRULES; the tzfile_changed() function can only
- * keep state for a single file.
- */
- switch (tzfile_changed(name, fid)) {
- case -1:
- serrno = errno;
- _close(fid);
- return serrno;
- case 0:
- _close(fid);
- return 0;
- case 1:
- break;
- }
+ if (tzloadflags) {
+ /*
+ * Detect if the timezone file has changed. Check tzloadflags
+ * to ignore TZDEFRULES; the tzfile_changed() function can only
+ * keep state for a single file.
+ */
+ switch (tzfile_changed(name, fid)) {
+ case -1:
+ serrno = errno;
+ _close(fid);
+ return serrno;
+ case 0:
+ _close(fid);
+ return 0;
+ case 1:
+ break;
+ }
}
#endif /* DETECT_TZ_CHANGES */
nread = _read(fid, up->buf, sizeof up->buf);
@@ -696,7 +847,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
correction must differ from the previous one's by 1
second or less, except that the first correction can be
any value; these requirements are more generous than
- RFC 8536, to allow future RFC extensions. */
+ RFC 9636, to allow future RFC extensions. */
if (! (i == 0
|| (prevcorr < corr
? corr == prevcorr + 1
@@ -747,7 +898,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
if (!version)
break;
}
- if (doextend && nread > 2 &&
+ if ((tzloadflags & TZLOAD_TZSTRING) && nread > 2 &&
up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
sp->typecnt + 2 <= TZ_MAX_TYPES) {
struct state *ts = &lsp->u.st;
@@ -822,23 +973,23 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
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. */
+/* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS.
+ Return 0 on success, an errno value on failure. */
static int
-tzload(char const *name, struct state *sp, bool doextend)
+tzload(char const *name, struct state *sp, char tzloadflags)
{
#ifdef ALL_STATE
union local_storage *lsp = malloc(sizeof *lsp);
if (!lsp) {
return HAVE_MALLOC_ERRNO ? errno : ENOMEM;
} else {
- int err = tzloadbody(name, sp, doextend, lsp);
+ int err = tzloadbody(name, sp, tzloadflags, lsp);
free(lsp);
return err;
}
#else
union local_storage ls;
- return tzloadbody(name, sp, doextend, &ls);
+ return tzloadbody(name, sp, tzloadflags, &ls);
#endif
}
@@ -1179,7 +1330,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
sp->leapcnt = basep->leapcnt;
memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis);
} else {
- load_ok = tzload(TZDEFRULES, sp, false) == 0;
+ load_ok = tzload(TZDEFRULES, sp, 0) == 0;
if (!load_ok)
sp->leapcnt = 0; /* So, we're off a little. */
}
@@ -1416,7 +1567,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
static void
gmtload(struct state *const sp)
{
- if (tzload(etc_utc, sp, true) != 0)
+ if (tzload(etc_utc, sp, TZLOAD_TZSTRING) != 0)
tzparse("UTC0", sp, NULL);
}
@@ -1443,10 +1594,13 @@ tzdata_is_fresh(void)
}
#endif /* DETECT_TZ_CHANGES */
+#if !USE_TIMEX_T || !defined TM_GMTOFF
+
/* Initialize *SP to a value appropriate for the TZ setting NAME.
+ Respect TZLOADFLAGS.
Return 0 on success, an errno value on failure. */
static int
-zoneinit(struct state *sp, char const *name)
+zoneinit(struct state *sp, char const *name, char tzloadflags)
{
if (name && ! name[0]) {
/*
@@ -1461,7 +1615,7 @@ zoneinit(struct state *sp, char const *name)
strcpy(sp->chars, utc);
return 0;
} else {
- int err = tzload(name, sp, true);
+ int err = tzload(name, sp, tzloadflags);
if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
err = 0;
if (err == 0)
@@ -1473,14 +1627,13 @@ zoneinit(struct state *sp, char const *name)
static void
tzset_unlocked(void)
{
+ char const *name = getenv("TZ");
#ifdef __FreeBSD__
- tzset_unlocked_name(getenv("TZ"));
+ tzset_unlocked_name(name);
}
static void
tzset_unlocked_name(char const *name)
{
-#else
- char const *name = getenv("TZ");
#endif
struct state *sp = lclptr;
int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
@@ -1491,13 +1644,15 @@ tzset_unlocked_name(char const *name)
if (tzdata_is_fresh() == 0)
#endif /* DETECT_TZ_CHANGES */
return;
-#ifdef ALL_STATE
+# ifdef ALL_STATE
if (! sp)
lclptr = sp = malloc(sizeof *lclptr);
-#endif /* defined ALL_STATE */
+# endif
if (sp) {
- if (zoneinit(sp, name) != 0)
- zoneinit(sp, "");
+ if (zoneinit(sp, name, TZLOAD_FROMENV | TZLOAD_TZSTRING) != 0) {
+ zoneinit(sp, "", 0);
+ strcpy(sp->chars, UNSPEC);
+ }
if (0 < lcl)
strcpy(lcl_TZname, name);
}
@@ -1505,6 +1660,9 @@ tzset_unlocked_name(char const *name)
lcl_is_set = lcl;
}
+#endif
+
+#if !USE_TIMEX_T
void
tzset(void)
{
@@ -1513,6 +1671,7 @@ tzset(void)
tzset_unlocked();
unlock();
}
+#endif
#ifdef __FreeBSD__
void
@@ -1547,14 +1706,14 @@ gmtcheck(void)
#define gmtcheck() _once(&gmt_once, gmtcheck)
#endif
-#if NETBSD_INSPIRED
+#if NETBSD_INSPIRED && !USE_TIMEX_T
timezone_t
tzalloc(char const *name)
{
timezone_t sp = malloc(sizeof *sp);
if (sp) {
- int err = zoneinit(sp, name);
+ int err = zoneinit(sp, name, TZLOAD_TZSTRING);
if (err != 0) {
free(sp);
errno = err;
@@ -1583,6 +1742,8 @@ tzfree(timezone_t sp)
#endif
+#if !USE_TIMEX_T || !defined TM_GMTOFF
+
/*
** 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
@@ -1637,14 +1798,14 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
return NULL; /* "cannot happen" */
result = localsub(sp, &newt, setname, tmp);
if (result) {
-#if defined ckd_add && defined ckd_sub
+# if defined ckd_add && defined ckd_sub
if (t < sp->ats[0]
? ckd_sub(&result->tm_year,
result->tm_year, years)
: ckd_add(&result->tm_year,
result->tm_year, years))
return NULL;
-#else
+# else
register int_fast64_t newy;
newy = result->tm_year;
@@ -1654,7 +1815,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
if (! (INT_MIN <= newy && newy <= INT_MAX))
return NULL;
result->tm_year = newy;
-#endif
+# endif
}
return result;
}
@@ -1683,25 +1844,26 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
result = timesub(&t, ttisp->tt_utoff, sp, tmp);
if (result) {
result->tm_isdst = ttisp->tt_isdst;
-#ifdef TM_ZONE
- result->TM_ZONE = (char *) &sp->chars[ttisp->tt_desigidx];
-#endif /* defined TM_ZONE */
+# ifdef TM_ZONE
+ result->TM_ZONE = UNCONST(&sp->chars[ttisp->tt_desigidx]);
+# endif
if (setname)
update_tzname_etc(sp, ttisp);
}
return result;
}
+#endif
-#if NETBSD_INSPIRED
+#if !USE_TIMEX_T
+# if NETBSD_INSPIRED
struct tm *
localtime_rz(struct state *restrict sp, time_t const *restrict timep,
struct tm *restrict tmp)
{
return localsub(sp, timep, 0, tmp);
}
-
-#endif
+# endif
static struct tm *
localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
@@ -1730,9 +1892,9 @@ localtime_key_init(void)
struct tm *
localtime(const time_t *timep)
{
-#if !SUPPORT_C89
+# if !SUPPORT_C89
static struct tm tm;
-#endif
+# endif
#ifdef __FreeBSD__
struct tm *p_tm = &tm;
@@ -1761,6 +1923,7 @@ localtime_r(const time_t *restrict timep, struct tm *restrict tmp)
{
return localtime_tzset(timep, tmp, false);
}
+#endif
/*
** gmtsub is to gmtime as localsub is to localtime.
@@ -1779,12 +1942,14 @@ gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
** "+xx" or "-xx" if offset is non-zero,
** but this is no time for a treasure hunt.
*/
- tmp->TM_ZONE = ((char *)
- (offset ? wildabbr : gmtptr ? gmtptr->chars : utc));
+ tmp->TM_ZONE = UNCONST(offset ? wildabbr
+ : gmtptr ? gmtptr->chars : utc);
#endif /* defined TM_ZONE */
return result;
}
+#if !USE_TIMEX_T
+
/*
* Re-entrant version of gmtime.
*/
@@ -1806,9 +1971,9 @@ gmtime_key_init(void)
struct tm *
gmtime(const time_t *timep)
{
-#if !SUPPORT_C89
+# if !SUPPORT_C89
static struct tm tm;
-#endif
+# endif
#ifdef __FreeBSD__
struct tm *p_tm = &tm;
@@ -1832,7 +1997,7 @@ gmtime(const time_t *timep)
return gmtime_r(timep, p_tm);
}
-#if STD_INSPIRED
+# if STD_INSPIRED
/* This function is obsolescent and may disappear in future releases.
Callers can instead use localtime_rz with a fixed-offset zone. */
@@ -1854,9 +2019,9 @@ offtime_key_init(void)
struct tm *
offtime(const time_t *timep, long offset)
{
-#if !SUPPORT_C89
+# if !SUPPORT_C89
static struct tm tm;
-#endif
+# endif
#ifdef __FreeBSD__
struct tm *p_tm = &tm;
@@ -1880,6 +2045,7 @@ offtime(const time_t *timep, long offset)
return offtime_r(timep, offset, p_tm);
}
+# endif
#endif
/*
@@ -1938,7 +2104,7 @@ timesub(const time_t *timep, int_fast32_t offset,
dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3;
rem %= SECSPERDAY;
/* y = (EPOCH_YEAR
- + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
+ + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
sans overflow. But calculate against 1570 (EPOCH_YEAR -
YEARSPERREPEAT) instead of against 1970 so that things work
for localtime values before 1970 when time_t is unsigned. */
@@ -2055,17 +2221,17 @@ increment_overflow(int *ip, int j)
}
static bool
-increment_overflow32(int_fast32_t *const lp, int const m)
+increment_overflow_time_iinntt(time_t *tp, iinntt j)
{
#ifdef ckd_add
- return ckd_add(lp, *lp, m);
+ return ckd_add(tp, *tp, j);
#else
- 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 (j < 0
+ ? (TYPE_SIGNED(time_t) ? *tp < TIME_T_MIN - j : *tp <= -1 - j)
+ : TIME_T_MAX - j < *tp)
+ return true;
+ *tp += j;
+ return false;
#endif
}
@@ -2089,30 +2255,6 @@ increment_overflow_time(time_t *tp, int_fast32_t j)
#endif
}
-static bool
-normalize_overflow(int *const tensptr, int *const unitsptr, const int base)
-{
- register int 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
tmcomp(register const struct tm *const atmp,
register const struct tm *const btmp)
@@ -2157,11 +2299,9 @@ time2sub(struct tm *const tmp,
{
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;
+ iinntt y, mday, hour, min, saved_seconds;
time_t newt;
time_t t;
struct tm yourtm, mytm;
@@ -2169,36 +2309,57 @@ time2sub(struct tm *const tmp,
*okayp = false;
mktmcpy(&yourtm, tmp);
+ min = yourtm.tm_min;
if (do_norm_secs) {
- if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
- SECSPERMIN))
- return WRONG;
+ min += yourtm.tm_sec / SECSPERMIN;
+ yourtm.tm_sec %= SECSPERMIN;
+ if (yourtm.tm_sec < 0) {
+ yourtm.tm_sec += SECSPERMIN;
+ min--;
+ }
}
- if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
- return WRONG;
- if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
- return WRONG;
+
+ hour = yourtm.tm_hour;
+ hour += min / MINSPERHOUR;
+ yourtm.tm_min = min % MINSPERHOUR;
+ if (yourtm.tm_min < 0) {
+ yourtm.tm_min += MINSPERHOUR;
+ hour--;
+ }
+
+ mday = yourtm.tm_mday;
+ mday += hour / HOURSPERDAY;
+ yourtm.tm_hour = hour % HOURSPERDAY;
+ if (yourtm.tm_hour < 0) {
+ yourtm.tm_hour += HOURSPERDAY;
+ mday--;
+ }
+
y = yourtm.tm_year;
- if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR))
- return WRONG;
+ y += yourtm.tm_mon / MONSPERYEAR;
+ yourtm.tm_mon %= MONSPERYEAR;
+ if (yourtm.tm_mon < 0) {
+ yourtm.tm_mon += MONSPERYEAR;
+ y--;
+ }
+
/*
** 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)];
+ y += TM_YEAR_BASE;
+
+ while (mday <= 0) {
+ iinntt li = y - (yourtm.tm_mon <= 1);
+ mday += year_lengths[isleap(li)];
+ y--;
}
- 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;
+ while (DAYSPERLYEAR < mday) {
+ iinntt li = y + (1 < yourtm.tm_mon);
+ mday -= year_lengths[isleap(li)];
+ y++;
}
+ yourtm.tm_mday = mday;
for ( ; ; ) {
i = mon_lengths[isleap(y)][yourtm.tm_mon];
if (yourtm.tm_mday <= i)
@@ -2206,16 +2367,14 @@ time2sub(struct tm *const tmp,
yourtm.tm_mday -= i;
if (++yourtm.tm_mon >= MONSPERYEAR) {
yourtm.tm_mon = 0;
- if (increment_overflow32(&y, 1))
- return WRONG;
+ y++;
}
}
#ifdef ckd_add
if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE))
return WRONG;
#else
- if (increment_overflow32(&y, -TM_YEAR_BASE))
- return WRONG;
+ y -= TM_YEAR_BASE;
if (! (INT_MIN <= y && y <= INT_MAX))
return WRONG;
yourtm.tm_year = y;
@@ -2231,9 +2390,8 @@ time2sub(struct tm *const tmp,
** 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;
+ saved_seconds -= SECSPERMIN - 1;
yourtm.tm_sec = SECSPERMIN - 1;
} else {
saved_seconds = yourtm.tm_sec;
@@ -2342,10 +2500,8 @@ time2sub(struct tm *const tmp,
return WRONG;
}
label:
- newt = t + saved_seconds;
- if ((newt < t) != (saved_seconds < 0))
+ if (increment_overflow_time_iinntt(&t, saved_seconds))
return WRONG;
- t = newt;
if (funcp(sp, &t, offset, tmp))
*okayp = true;
return t;
@@ -2442,6 +2598,8 @@ time1(struct tm *const tmp,
return WRONG;
}
+#if !defined TM_GMTOFF || !USE_TIMEX_T
+
static time_t
mktime_tzname(struct state *sp, struct tm *tmp, bool setname)
{
@@ -2453,16 +2611,9 @@ mktime_tzname(struct state *sp, struct tm *tmp, bool setname)
}
}
-#if NETBSD_INSPIRED
-
-time_t
-mktime_z(struct state *restrict sp, struct tm *restrict tmp)
-{
- return mktime_tzname(sp, tmp, false);
-}
-
-#endif
-
+# if USE_TIMEX_T
+static
+# endif
time_t
mktime(struct tm *tmp)
{
@@ -2478,7 +2629,17 @@ mktime(struct tm *tmp)
return t;
}
-#if STD_INSPIRED
+#endif
+
+#if NETBSD_INSPIRED && !USE_TIMEX_T
+time_t
+mktime_z(struct state *restrict sp, struct tm *restrict tmp)
+{
+ return mktime_tzname(sp, tmp, false);
+}
+#endif
+
+#if STD_INSPIRED && !USE_TIMEX_T
/* This function is obsolescent and may disappear in future releases.
Callers can instead use mktime. */
time_t
@@ -2490,12 +2651,14 @@ timelocal(struct tm *tmp)
}
#endif
-#ifndef EXTERN_TIMEOFF
-# ifndef timeoff
-# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
+#if defined TM_GMTOFF || !USE_TIMEX_T
+
+# ifndef EXTERN_TIMEOFF
+# ifndef timeoff
+# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
+# endif
+# define EXTERN_TIMEOFF static
# endif
-# define EXTERN_TIMEOFF static
-#endif
/* This function is obsolescent and may disappear in future releases.
Callers can instead use mktime_z with a fixed-offset zone. */
@@ -2507,7 +2670,9 @@ timeoff(struct tm *tmp, long offset)
gmtcheck();
return time1(tmp, gmtsub, gmtptr, offset);
}
+#endif
+#if !USE_TIMEX_T
time_t
timegm(struct tm *tmp)
{
@@ -2520,6 +2685,7 @@ timegm(struct tm *tmp)
*tmp = tmcpy;
return t;
}
+#endif
static int_fast32_t
leapcorr(struct state const *sp, time_t t)
@@ -2540,15 +2706,16 @@ leapcorr(struct state const *sp, time_t t)
** XXX--is the below the right way to conditionalize??
*/
-#if STD_INSPIRED
+#if !USE_TIMEX_T
+# if STD_INSPIRED
/* 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
+# if NETBSD_INSPIRED
+# define NETBSD_INSPIRED_EXTERN
+# else
+# define NETBSD_INSPIRED_EXTERN static
+# endif
/*
** IEEE Std 1003.1 (POSIX) says that 536457599
@@ -2629,17 +2796,13 @@ posix2time(time_t t)
return t;
}
-#endif /* STD_INSPIRED */
+# endif /* STD_INSPIRED */
-#if TZ_TIME_T
+# if TZ_TIME_T
-# if !USG_COMPAT
-# define daylight 0
-# define timezone 0
-# endif
-# if !ALTZONE
-# define altzone 0
-# endif
+# if !USG_COMPAT
+# define timezone 0
+# endif
/* Convert from the underlying system's time_t to the ersatz time_tz,
which is called 'time_t' in this file. Typically, this merely
@@ -2657,9 +2820,9 @@ time(time_t *p)
{
time_t r = sys_time(0);
if (r != (time_t) -1) {
- int_fast32_t offset = EPOCH_LOCAL ? (daylight ? timezone : altzone) : 0;
- if (increment_overflow32(&offset, -EPOCH_OFFSET)
- || increment_overflow_time(&r, offset)) {
+ iinntt offset = EPOCH_LOCAL ? timezone : 0;
+ if (offset < IINNTT_MIN + EPOCH_OFFSET
+ || increment_overflow_time_iinntt(&r, offset - EPOCH_OFFSET)) {
errno = EOVERFLOW;
r = -1;
}
@@ -2669,4 +2832,5 @@ time(time_t *p)
return r;
}
+# endif
#endif