Update tzcode from 2022a to 2023a.
https://github.com/eggert/tz/commit/57b8fc957a608de6f598a9aca5b5e37c845151c3
changes the way overflows are tracked: now compiler builtins
are used instead of manual arithmetics. But as int_fast32_t on
64-bit Android takes 8 bytes, new logic behaves differently.
See time_test.cpp changes for more details.
Changes were applied using following commands:
1) Checkout tzcode repo
2) Prepare patches for all tzcode file using
git diff 2022a 2023a -- <file-name> > <file-name-patch>
3) Apply these patches to files in bionic using
patch -p1 <file-name> <file-name-patch>
Bug: 279742606
Test: CtsBionicTestCases
Test: CtsLibcoreTestCases
Test: CtsLibcoreOjTestCases
Test: atest toybox-tests
Change-Id: I7772a90538b8185bdd2f4be6e9d1740c95509d6c
diff --git a/libc/tzcode/asctime.c b/libc/tzcode/asctime.c
index ce5d4be..4cdfd13 100644
--- a/libc/tzcode/asctime.c
+++ b/libc/tzcode/asctime.c
@@ -17,12 +17,6 @@
#include <stdio.h>
/*
-** Some systems only handle "%.2d"; others only handle "%02d";
-** "%02.2d" makes (most) everybody happy.
-** At least some versions of gcc warn about the %02.2d;
-** we conditionalize below to avoid the warning.
-*/
-/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
@@ -34,24 +28,16 @@
** The ISO C and POSIX standards prohibit padding the year,
** but many implementations pad anyway; most likely the standards are buggy.
*/
-#ifdef __GNUC__
-#define ASCTIME_FMT "%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n"
-#else /* !defined __GNUC__ */
-#define ASCTIME_FMT "%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n"
-#endif /* !defined __GNUC__ */
+static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
-#ifdef __GNUC__
-#define ASCTIME_FMT_B "%s %s%3d %2.2d:%2.2d:%2.2d %s\n"
-#else /* !defined __GNUC__ */
-#define ASCTIME_FMT_B "%s %s%3d %02.2d:%02.2d:%02.2d %s\n"
-#endif /* !defined __GNUC__ */
+static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n";
-#define STD_ASCTIME_BUF_SIZE 26
+enum { STD_ASCTIME_BUF_SIZE = 26 };
/*
** Big enough for something such as
** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
@@ -59,15 +45,23 @@
** seven explicit spaces, two explicit colons, a newline,
** 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
+** as an example; the size expression below is a bound for the system at
** hand.
*/
-#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
+static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
-static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
+/* A similar buffer for ctime.
+ C89 requires that they be the same buffer.
+ This requirement was removed in C99, so support it only if requested,
+ as support is more likely to lead to bugs in badly written programs. */
+#if SUPPORT_C89
+# define buf_ctime buf_asctime
+#else
+static char buf_ctime[sizeof buf_asctime];
+#endif
char *
-asctime_r(register const struct tm *timeptr, char *buf)
+asctime_r(struct tm const *restrict timeptr, char *restrict buf)
{
static const char wday_name[][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
@@ -79,7 +73,7 @@
register const char * wn;
register const char * mn;
char year[INT_STRLEN_MAXIMUM(int) + 2];
- char result[MAX_ASCTIME_BUF_SIZE];
+ char result[sizeof buf_asctime];
if (timeptr == NULL) {
errno = EINVAL;
@@ -107,7 +101,8 @@
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
year);
- if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime)
+ if (strlen(result) < STD_ASCTIME_BUF_SIZE
+ || buf == buf_ctime || buf == buf_asctime)
return strcpy(buf, result);
else {
errno = EOVERFLOW;
@@ -120,3 +115,17 @@
{
return asctime_r(timeptr, buf_asctime);
}
+
+char *
+ctime_r(const time_t *timep, char *buf)
+{
+ struct tm mytm;
+ struct tm *tmp = localtime_r(timep, &mytm);
+ return tmp ? asctime_r(tmp, buf) : NULL;
+}
+
+char *
+ctime(const time_t *timep)
+{
+ return ctime_r(timep, buf_ctime);
+}
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 8ff5cee..5e1181f 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -28,29 +28,22 @@
static void unlock(void) { }
#endif
-#ifndef TZ_ABBR_MAX_LEN
-#define TZ_ABBR_MAX_LEN 16
-#endif /* !defined TZ_ABBR_MAX_LEN */
-
#ifndef TZ_ABBR_CHAR_SET
-#define TZ_ABBR_CHAR_SET \
+# define TZ_ABBR_CHAR_SET \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
#endif /* !defined TZ_ABBR_CHAR_SET */
#ifndef TZ_ABBR_ERR_CHAR
-#define TZ_ABBR_ERR_CHAR '_'
+# define TZ_ABBR_ERR_CHAR '_'
#endif /* !defined TZ_ABBR_ERR_CHAR */
/*
-** SunOS 4.1.1 headers lack O_BINARY.
++** Support non-POSIX platforms that distinguish between text and binary files.
*/
-#ifdef O_BINARY
-#define OPEN_MODE (O_RDONLY | O_BINARY)
-#endif /* defined O_BINARY */
#ifndef O_BINARY
-#define OPEN_MODE O_RDONLY
-#endif /* !defined O_BINARY */
+# define O_BINARY 0
+#endif
#ifndef WILDABBR
/*
@@ -72,12 +65,13 @@
** manual page of what this "time zone abbreviation" means (doing this so
** that tzname[0] has the "normal" length of three characters).
*/
-#define WILDABBR " "
+# define WILDABBR " "
#endif /* !defined WILDABBR */
static const char wildabbr[] = WILDABBR;
-static const char gmt[] = "GMT";
+static char const etc_utc[] = "Etc/UTC";
+static char const *utc = etc_utc + sizeof "Etc/" - 1;
/*
** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
@@ -86,7 +80,7 @@
** for historical reasons, US rules are a common default.
*/
#ifndef TZDEFRULESTRING
-#define TZDEFRULESTRING ",M3.2.0,M11.1.0"
+# define TZDEFRULESTRING ",M3.2.0,M11.1.0"
#endif
struct ttinfo { /* time type information */
@@ -102,9 +96,6 @@
int_fast32_t ls_corr; /* correction to apply */
};
-#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
-#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
-
/* This abbreviation means local time is unspecified. */
static char const UNSPEC[] = "-00";
@@ -112,14 +103,13 @@
This needs to be at least 1 for null termination in case the input
data isn't properly terminated, and it also needs to be big enough
for ttunspecified to work without crashing. */
-enum { CHARS_EXTRA = BIGGEST(sizeof UNSPEC, 2) - 1 };
+enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
-#ifdef TZNAME_MAX
-#define MY_TZNAME_MAX TZNAME_MAX
-#endif /* defined TZNAME_MAX */
-#ifndef TZNAME_MAX
-#define MY_TZNAME_MAX 255
-#endif /* !defined TZNAME_MAX */
+/* Limit to time zone abbreviation length in POSIX-style TZ strings.
+ This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */
+#ifndef TZNAME_MAXIMUM
+# define TZNAME_MAXIMUM 255
+#endif
struct state {
int leapcnt;
@@ -131,9 +121,8 @@
time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + CHARS_EXTRA,
- sizeof gmt),
- (2 * (MY_TZNAME_MAX + 1)))];
+ char chars[max(max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof "UTC"),
+ 2 * (TZNAME_MAXIMUM + 1))];
struct lsinfo lsis[TZ_MAX_LEAPS];
/* The time type to use for early times or if no transitions.
It is always zero for recent tzdb releases.
@@ -174,12 +163,12 @@
#ifndef ALL_STATE
static struct state lclmem;
static struct state gmtmem;
-#define lclptr (&lclmem)
-#define gmtptr (&gmtmem)
+static struct state *const lclptr = &lclmem;
+static struct state *const gmtptr = &gmtmem;
#endif /* State Farm */
#ifndef TZ_STRLEN_MAX
-#define TZ_STRLEN_MAX 255
+# define TZ_STRLEN_MAX 255
#endif /* !defined TZ_STRLEN_MAX */
static char lcl_TZname[TZ_STRLEN_MAX + 1];
@@ -191,9 +180,14 @@
** ctime, gmtime, localtime] return values in one of two static
** objects: a broken-down time structure and an array of char.
** Thanks to Paul Eggert for noting this.
+**
+** This requirement was removed in C99, so support it only if requested,
+** as support is more likely to lead to bugs in badly written programs.
*/
+#if SUPPORT_C89
static struct tm tm;
+#endif
#if 2 <= HAVE_TZNAME + TZ_TIME_T
char * tzname[2] = {
@@ -321,7 +315,7 @@
int stddst_mask = 0;
#if HAVE_TZNAME
- tzname[0] = tzname[1] = (char *) (sp ? wildabbr : gmt);
+ tzname[0] = tzname[1] = (char *) (sp ? wildabbr : utc);
stddst_mask = 3;
#endif
#if USG_COMPAT
@@ -346,27 +340,28 @@
#endif
}
-static void
+/* Replace bogus characters in time zone abbreviations.
+ Return 0 on success, an errno value if a time zone abbreviation is
+ too long. */
+static int
scrub_abbrs(struct state *sp)
{
int i;
- /*
- ** First, replace bogus characters.
- */
+
+ /* Reject overlong abbreviations. */
+ for (i = 0; i < sp->charcnt - (TZNAME_MAXIMUM + 1); ) {
+ int len = strlen(&sp->chars[i]);
+ if (TZNAME_MAXIMUM < len)
+ return EOVERFLOW;
+ i += len + 1;
+ }
+
+ /* 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];
- char *cp = &sp->chars[ttisp->tt_desigidx];
- if (strlen(cp) > TZ_ABBR_MAX_LEN &&
- strcmp(cp, GRANDPARENTED) != 0)
- *(cp + TZ_ABBR_MAX_LEN) = '\0';
- }
+ return 0;
}
/* Input buffer for data read from a compiled tz file. */
@@ -399,8 +394,7 @@
// Android-removed: There is no directory with file-per-time zone on Android.
#ifndef __BIONIC__
/* The file name to be opened. */
- char fullname[BIGGEST(sizeof(struct file_analysis),
- sizeof tzdirslash + 1024)];
+ char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)];
#endif
};
@@ -446,8 +440,7 @@
#endif
if (!doaccess) {
char const *dot;
- size_t namelen = strlen(name);
- if (sizeof lsp->fullname - sizeof tzdirslash <= namelen)
+ if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name))
return ENAMETOOLONG;
/* Create a string "TZDIR/NAME". Using sprintf here
@@ -470,7 +463,7 @@
}
if (doaccess && access(name, R_OK) != 0)
return errno;
- fid = open(name, OPEN_MODE);
+ fid = open(name, O_RDONLY | O_BINARY);
#endif
if (fid < 0)
return errno;
@@ -828,12 +821,14 @@
b < 0 || b >= sp->typecnt)
result = false;
else {
+ /* Compare the relevant members of *AP and *BP.
+ Ignore tt_ttisstd and tt_ttisut, as they are
+ irrelevant now and counting them could cause
+ sp->goahead to mistakenly remain false. */
register const struct ttinfo * ap = &sp->ttis[a];
register const struct ttinfo * bp = &sp->ttis[b];
result = (ap->tt_utoff == bp->tt_utoff
&& ap->tt_isdst == bp->tt_isdst
- && ap->tt_ttisstd == bp->tt_ttisstd
- && ap->tt_ttisut == bp->tt_ttisut
&& (strcmp(&sp->chars[ap->tt_desigidx],
&sp->chars[bp->tt_desigidx])
== 0));
@@ -863,7 +858,7 @@
** Return a pointer to that character.
*/
-static ATTRIBUTE_PURE const char *
+ATTRIBUTE_REPRODUCIBLE static const char *
getzname(register const char *strp)
{
register char c;
@@ -884,7 +879,7 @@
** We don't do any checking here; checking is done later in common-case code.
*/
-static ATTRIBUTE_PURE const char *
+ATTRIBUTE_REPRODUCIBLE static const char *
getqzname(register const char *strp, const int delim)
{
register int c;
@@ -1122,7 +1117,7 @@
value += mon_lengths[leapyear][i] * SECSPERDAY;
break;
- default: UNREACHABLE();
+ default: unreachable();
}
/*
@@ -1144,13 +1139,11 @@
{
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;
+ ptrdiff_t stdlen, dstlen, charcnt;
time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
stdname = name;
@@ -1166,14 +1159,12 @@
name = getzname(name);
stdlen = name - stdname;
}
- if (!stdlen)
+ if (! (0 < stdlen && stdlen <= TZNAME_MAXIMUM))
return false;
name = getoffset(name, &stdoffset);
if (name == NULL)
return false;
charcnt = stdlen + 1;
- if (sizeof sp->chars < charcnt)
- return false;
if (basep) {
if (0 < basep->timecnt)
atlo = basep->ats[basep->timecnt - 1];
@@ -1200,11 +1191,9 @@
name = getzname(name);
dstlen = name - dstname; /* length of DST abbr. */
}
- if (!dstlen)
+ if (! (0 < dstlen && dstlen <= TZNAME_MAXIMUM))
return false;
charcnt += dstlen + 1;
- if (sizeof sp->chars < charcnt)
- return false;
if (*name != '\0' && *name != ',' && *name != ';') {
name = getoffset(name, &dstoffset);
if (name == NULL)
@@ -1420,8 +1409,8 @@
static void
gmtload(struct state *const sp)
{
- if (tzload(gmt, sp, true) != 0)
- tzparse("GMT0", sp, NULL);
+ if (tzload(etc_utc, sp, true) != 0)
+ tzparse("UTC0", sp, NULL);
}
/* Initialize *SP to a value appropriate for the TZ setting NAME.
@@ -1439,7 +1428,7 @@
sp->charcnt = 0;
sp->goback = sp->goahead = false;
init_ttinfo(&sp->ttis[0], 0, false, 0);
- strcpy(sp->chars, gmt);
+ strcpy(sp->chars, utc);
sp->defaulttype = 0;
return 0;
} else {
@@ -1447,7 +1436,7 @@
if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
err = 0;
if (err == 0)
- scrub_abbrs(sp);
+ err = scrub_abbrs(sp);
return err;
}
}
@@ -1557,7 +1546,7 @@
** set the applicable parts of tzname, timezone and altzone;
** however, it's OK to omit this step if the timezone 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,
+** SETNAME's type is int_fast32_t for compatibility with gmtsub,
** but it is actually a boolean and its value should be 0 or 1.
*/
@@ -1601,6 +1590,14 @@
return NULL; /* "cannot happen" */
result = localsub(sp, &newt, setname, tmp);
if (result) {
+#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
register int_fast64_t newy;
newy = result->tm_year;
@@ -1610,6 +1607,7 @@
if (! (INT_MIN <= newy && newy <= INT_MAX))
return NULL;
result->tm_year = newy;
+#endif
}
return result;
}
@@ -1650,7 +1648,8 @@
#if NETBSD_INSPIRED
struct tm *
-localtime_rz(struct state *sp, time_t const *timep, struct tm *tmp)
+localtime_rz(struct state *restrict sp, time_t const *restrict timep,
+ struct tm *restrict tmp)
{
return localsub(sp, timep, 0, tmp);
}
@@ -1681,11 +1680,14 @@
struct tm *
localtime(const time_t *timep)
{
+#if !SUPPORT_C89
+ static struct tm tm;
+#endif
return localtime_tzset(timep, &tm);
}
struct tm *
-localtime_r(const time_t *timep, struct tm *tmp)
+localtime_r(const time_t *restrict timep, struct tm *restrict tmp)
{
return localtime_tzset(timep, tmp);
}
@@ -1695,8 +1697,8 @@
*/
static struct tm *
-gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
- struct tm *tmp)
+gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
+ int_fast32_t offset, struct tm *tmp)
{
register struct tm * result;
@@ -1708,7 +1710,7 @@
** but this is no time for a treasure hunt.
*/
tmp->TM_ZONE = ((char *)
- (offset ? wildabbr : gmtptr ? gmtptr->chars : gmt));
+ (offset ? wildabbr : gmtptr ? gmtptr->chars : utc));
#endif /* defined TM_ZONE */
return result;
}
@@ -1718,7 +1720,7 @@
*/
struct tm *
-gmtime_r(const time_t *timep, struct tm *tmp)
+gmtime_r(time_t const *restrict timep, struct tm *restrict tmp)
{
gmtcheck();
return gmtsub(gmtptr, timep, 0, tmp);
@@ -1727,19 +1729,26 @@
struct tm *
gmtime(const time_t *timep)
{
+#if !SUPPORT_C89
+ static struct tm tm;
+#endif
return gmtime_r(timep, &tm);
}
-#ifdef STD_INSPIRED
+#if STD_INSPIRED
struct tm *
offtime(const time_t *timep, long offset)
{
gmtcheck();
+
+#if !SUPPORT_C89
+ static struct tm tm;
+#endif
return gmtsub(gmtptr, timep, offset, &tm);
}
-#endif /* defined STD_INSPIRED */
+#endif
/*
** Return the number of leap years through the end of the given year
@@ -1825,6 +1834,12 @@
y = newy;
}
+#ifdef ckd_add
+ if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+#else
if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) {
int signed_y = y;
tmp->tm_year = signed_y - TM_YEAR_BASE;
@@ -1835,6 +1850,7 @@
errno = EOVERFLOW;
return NULL;
}
+#endif
tmp->tm_yday = idays;
/*
** The "extra" mods below avoid overflow problems.
@@ -1868,27 +1884,6 @@
return tmp;
}
-char *
-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))
-*/
- struct tm *tmp = localtime(timep);
- return tmp ? asctime(tmp) : NULL;
-}
-
-char *
-ctime_r(const time_t *timep, char *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
@@ -1899,7 +1894,7 @@
*/
#ifndef WRONG
-#define WRONG (-1)
+# define WRONG (-1)
#endif /* !defined WRONG */
/*
@@ -1909,6 +1904,9 @@
static bool
increment_overflow(int *ip, int j)
{
+#ifdef ckd_add
+ return ckd_add(ip, *ip, j);
+#else
register int const i = *ip;
/*
@@ -1921,22 +1919,30 @@
return true;
*ip += j;
return false;
+#endif
}
static bool
increment_overflow32(int_fast32_t *const lp, int const m)
{
+#ifdef ckd_add
+ return ckd_add(lp, *lp, m);
+#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;
+#endif
}
static bool
increment_overflow_time(time_t *tp, int_fast32_t j)
{
+#ifdef ckd_add
+ return ckd_add(tp, *tp, j);
+#else
/*
** This is like
** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
@@ -1948,6 +1954,7 @@
return true;
*tp += j;
return false;
+#endif
}
static bool
@@ -1990,6 +1997,23 @@
return result;
}
+/* Copy to *DEST from *SRC. Copy only the members needed for mktime,
+ as other members might not be initialized. */
+static void
+mktmcpy(struct tm *dest, struct tm const *src)
+{
+ dest->tm_sec = src->tm_sec;
+ dest->tm_min = src->tm_min;
+ dest->tm_hour = src->tm_hour;
+ dest->tm_mday = src->tm_mday;
+ dest->tm_mon = src->tm_mon;
+ dest->tm_year = src->tm_year;
+ dest->tm_isdst = src->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ dest->TM_GMTOFF = src->TM_GMTOFF;
+#endif
+}
+
static time_t
time2sub(struct tm *const tmp,
struct tm *(*funcp)(struct state const *, time_t const *,
@@ -2011,7 +2035,8 @@
struct tm yourtm, mytm;
*okayp = false;
- yourtm = *tmp;
+ mktmcpy(&yourtm, tmp);
+
if (do_norm_secs) {
if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
SECSPERMIN))
@@ -2053,14 +2078,19 @@
return WRONG;
}
}
+#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;
if (! (INT_MIN <= y && y <= INT_MAX))
return WRONG;
yourtm.tm_year = y;
+#endif
if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
saved_seconds = 0;
- else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) {
/*
** We can't set tm_sec to 0, because that might push the
** time below the minimum representable time.
@@ -2120,10 +2150,10 @@
&& (yourtm.TM_GMTOFF < 0
? (-SECSPERDAY <= yourtm.TM_GMTOFF
&& (mytm.TM_GMTOFF <=
- (SMALLEST(INT_FAST32_MAX, LONG_MAX)
+ (min(INT_FAST32_MAX, LONG_MAX)
+ yourtm.TM_GMTOFF)))
: (yourtm.TM_GMTOFF <= SECSPERDAY
- && ((BIGGEST(INT_FAST32_MIN, LONG_MIN)
+ && ((max(INT_FAST32_MIN, LONG_MIN)
+ yourtm.TM_GMTOFF)
<= mytm.TM_GMTOFF)))) {
/* MYTM matches YOURTM except with the wrong UT offset.
@@ -2294,7 +2324,7 @@
#if NETBSD_INSPIRED
time_t
-mktime_z(struct state *sp, struct tm *tmp)
+mktime_z(struct state *restrict sp, struct tm *restrict tmp)
{
return mktime_tzname(sp, tmp, false);
}
@@ -2324,8 +2354,7 @@
return t;
}
-#ifdef STD_INSPIRED
-
+#if STD_INSPIRED
time_t
timelocal(struct tm *tmp)
{
@@ -2333,13 +2362,9 @@
tmp->tm_isdst = -1; /* in case it wasn't initialized */
return mktime(tmp);
}
-
-time_t
-timegm(struct tm *tmp)
-{
- return timeoff(tmp, 0);
-}
-
+#else
+static
+#endif
time_t
timeoff(struct tm *tmp, long offset)
{
@@ -2349,7 +2374,18 @@
return time1(tmp, gmtsub, gmtptr, offset);
}
-#endif /* defined STD_INSPIRED */
+time_t
+timegm(struct tm *tmp)
+{
+ time_t t;
+ struct tm tmcpy;
+ mktmcpy(&tmcpy, tmp);
+ tmcpy.tm_wday = -1;
+ t = timeoff(&tmcpy, 0);
+ if (0 <= tmcpy.tm_wday)
+ *tmp = tmcpy;
+ return t;
+}
static int_fast32_t
leapcorr(struct state const *sp, time_t t)
@@ -2370,7 +2406,7 @@
** XXX--is the below the right way to conditionalize??
*/
-#ifdef STD_INSPIRED
+#if STD_INSPIRED
/* NETBSD_INSPIRED_EXTERN functions are exported to callers if
NETBSD_INSPIRED is defined, and are private otherwise. */
@@ -2455,7 +2491,7 @@
return t;
}
-#endif /* defined STD_INSPIRED */
+#endif /* STD_INSPIRED */
#if TZ_TIME_T
diff --git a/libc/tzcode/private.h b/libc/tzcode/private.h
index 4c03324..838ab2b 100644
--- a/libc/tzcode/private.h
+++ b/libc/tzcode/private.h
@@ -17,6 +17,36 @@
** Thank you!
*/
+/* PORT_TO_C89 means the code should work even if the underlying
+ compiler and library support only C89. SUPPORT_C89 means the
+ tzcode library should support C89 callers in addition to the usual
+ support for C99-and-later callers. These macros are obsolescent,
+ and the plan is to remove them along with any code needed only when
+ they are nonzero. */
+#ifndef PORT_TO_C89
+# define PORT_TO_C89 0
+#endif
+#ifndef SUPPORT_C89
+# define SUPPORT_C89 0
+#endif
+
+#ifndef __STDC_VERSION__
+# define __STDC_VERSION__ 0
+#endif
+
+/* Define true, false and bool if they don't work out of the box. */
+#if PORT_TO_C89 && __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#elif __STDC_VERSION__ < 202311
+# include <stdbool.h>
+#endif
+
+#if __STDC_VERSION__ < 202311
+# define static_assert(cond) extern int static_assert_check[(cond) ? 1 : -1]
+#endif
+
/*
** zdump has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides.
@@ -36,79 +66,86 @@
*/
#ifndef HAVE_DECL_ASCTIME_R
-#define HAVE_DECL_ASCTIME_R 1
+# define HAVE_DECL_ASCTIME_R 1
#endif
-#if !defined HAVE_GENERIC && defined __has_extension
+#if !defined HAVE__GENERIC && defined __has_extension
# if __has_extension(c_generic_selections)
-# define HAVE_GENERIC 1
+# define HAVE__GENERIC 1
# else
-# define HAVE_GENERIC 0
+# define HAVE__GENERIC 0
# endif
#endif
/* _Generic is buggy in pre-4.9 GCC. */
-#if !defined HAVE_GENERIC && defined __GNUC__
-# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
+#if !defined HAVE__GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__
+# define HAVE__GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
#endif
-#ifndef HAVE_GENERIC
-# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
+#ifndef HAVE__GENERIC
+# define HAVE__GENERIC (201112 <= __STDC_VERSION__)
#endif
+#if !defined HAVE_GETTEXT && defined __has_include
+# if __has_include(<libintl.h>)
+# define HAVE_GETTEXT true
+# endif
+#endif
#ifndef HAVE_GETTEXT
-#define HAVE_GETTEXT 0
-#endif /* !defined HAVE_GETTEXT */
+# define HAVE_GETTEXT false
+#endif
#ifndef HAVE_INCOMPATIBLE_CTIME_R
-#define HAVE_INCOMPATIBLE_CTIME_R 0
+# define HAVE_INCOMPATIBLE_CTIME_R 0
#endif
#ifndef HAVE_LINK
-#define HAVE_LINK 1
+# define HAVE_LINK 1
#endif /* !defined HAVE_LINK */
#ifndef HAVE_MALLOC_ERRNO
-#define HAVE_MALLOC_ERRNO 1
+# define HAVE_MALLOC_ERRNO 1
#endif
#ifndef HAVE_POSIX_DECLS
-#define HAVE_POSIX_DECLS 1
+# define HAVE_POSIX_DECLS 1
#endif
-#ifndef HAVE_STDBOOL_H
-#define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__)
+#ifndef HAVE_SETENV
+# define HAVE_SETENV 1
#endif
#ifndef HAVE_STRDUP
-#define HAVE_STRDUP 1
-#endif
-
-#ifndef HAVE_STRTOLL
-#define HAVE_STRTOLL 1
+# define HAVE_STRDUP 1
#endif
#ifndef HAVE_SYMLINK
-#define HAVE_SYMLINK 1
+# define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
+#if !defined HAVE_SYS_STAT_H && defined __has_include
+# if !__has_include(<sys/stat.h>)
+# define HAVE_SYS_STAT_H false
+# endif
+#endif
#ifndef HAVE_SYS_STAT_H
-#define HAVE_SYS_STAT_H 1
-#endif /* !defined HAVE_SYS_STAT_H */
+# define HAVE_SYS_STAT_H true
+#endif
+#if !defined HAVE_UNISTD_H && defined __has_include
+# if !__has_include(<unistd.h>)
+# define HAVE_UNISTD_H false
+# endif
+#endif
#ifndef HAVE_UNISTD_H
-#define HAVE_UNISTD_H 1
-#endif /* !defined HAVE_UNISTD_H */
-
-#ifndef HAVE_UTMPX_H
-#define HAVE_UTMPX_H 1
-#endif /* !defined HAVE_UTMPX_H */
+# define HAVE_UNISTD_H true
+#endif
#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
+# define asctime_r _incompatible_asctime_r
+# define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
@@ -118,15 +155,17 @@
/* Enable strtoimax on pre-C99 Solaris 11. */
#define __EXTENSIONS__ 1
-/* To avoid having 'stat' fail unnecessarily with errno == EOVERFLOW,
- enable large files on GNUish systems ... */
+/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
+ On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
+ setting _TIME_BITS to 64 does not work. The code does not
+ otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
+ use off_t or functions like 'stat' that depend on off_t. */
#ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
#endif
-/* ... and on AIX ... */
-#define _LARGE_FILES 1
-/* ... and enable large inode numbers on Mac OS X 10.5 and later. */
-#define _DARWIN_USE_64_BIT_INODE 1
+#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
+# define _TIME_BITS 64
+#endif
/*
** Nested includes
@@ -157,8 +196,11 @@
#undef tzalloc
#undef tzfree
-#include <sys/types.h> /* for time_t */
+#include <stddef.h>
#include <string.h>
+#if !PORT_TO_C89
+# include <inttypes.h>
+#endif
#include <limits.h> /* for CHAR_BIT et al. */
#include <stdlib.h>
@@ -168,6 +210,9 @@
# define EINVAL ERANGE
#endif
+#ifndef ELOOP
+# define ELOOP EINVAL
+#endif
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
@@ -182,11 +227,11 @@
#endif
#if HAVE_GETTEXT
-#include <libintl.h>
+# include <libintl.h>
#endif /* HAVE_GETTEXT */
#if HAVE_UNISTD_H
-#include <unistd.h> /* for R_OK, and other POSIX goodness */
+# include <unistd.h> /* for R_OK, and other POSIX goodness */
#endif /* HAVE_UNISTD_H */
#ifndef HAVE_STRFTIME_L
@@ -222,24 +267,29 @@
#endif
#ifndef R_OK
-#define R_OK 4
+# define R_OK 4
#endif /* !defined R_OK */
+#if PORT_TO_C89
+
/*
** Define HAVE_STDINT_H's default value here, rather than at the
** start, since __GLIBC__ and INTMAX_MAX's values depend on
-** previously-included files. glibc 2.1 and Solaris 10 and later have
+** previously included files. glibc 2.1 and Solaris 10 and later have
** stdint.h, even with pre-C99 compilers.
*/
+#if !defined HAVE_STDINT_H && defined __has_include
+# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
+#endif
#ifndef HAVE_STDINT_H
-#define HAVE_STDINT_H \
+# define HAVE_STDINT_H \
(199901 <= __STDC_VERSION__ \
- || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
+ || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
|| __CYGWIN__ || INTMAX_MAX)
#endif /* !defined HAVE_STDINT_H */
#if HAVE_STDINT_H
-#include <stdint.h>
+# include <stdint.h>
#endif /* !HAVE_STDINT_H */
#ifndef HAVE_INTTYPES_H
@@ -250,36 +300,36 @@
#endif
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
-#ifdef __LONG_LONG_MAX__
+#if defined __LONG_LONG_MAX__ && !defined __STRICT_ANSI__
# ifndef LLONG_MAX
# define LLONG_MAX __LONG_LONG_MAX__
# endif
# ifndef LLONG_MIN
# define LLONG_MIN (-1 - LLONG_MAX)
# endif
+# ifndef ULLONG_MAX
+# define ULLONG_MAX (LLONG_MAX * 2ull + 1)
+# 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
-# 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
-typedef long int_fast64_t;
+# if 1 <= LONG_MAX >> 31 >> 31
+typedef long int_fast64_t;
# define INT_FAST64_MIN LONG_MIN
# define INT_FAST64_MAX LONG_MAX
+# else
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef long long int_fast64_t;
+# define INT_FAST64_MIN LLONG_MIN
+# define INT_FAST64_MAX LLONG_MAX
# endif
#endif
#ifndef PRIdFAST64
-# if INT_FAST64_MAX == LLONG_MAX
-# define PRIdFAST64 "lld"
-# else
+# if INT_FAST64_MAX == LONG_MAX
# define PRIdFAST64 "ld"
+# else
+# define PRIdFAST64 "lld"
# endif
#endif
@@ -302,6 +352,9 @@
#ifndef INTMAX_MAX
# ifdef LLONG_MAX
typedef long long intmax_t;
+# ifndef HAVE_STRTOLL
+# define HAVE_STRTOLL true
+# endif
# if HAVE_STRTOLL
# define strtoimax strtoll
# endif
@@ -325,70 +378,183 @@
# endif
#endif
+#ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t))
+#endif
+
#ifndef UINT_FAST32_MAX
typedef unsigned long uint_fast32_t;
#endif
#ifndef UINT_FAST64_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
-typedef unsigned long long uint_fast64_t;
+# if 3 <= ULONG_MAX >> 31 >> 31
+typedef unsigned long uint_fast64_t;
+# define UINT_FAST64_MAX ULONG_MAX
# 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;
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef unsigned long long uint_fast64_t;
+# define UINT_FAST64_MAX ULLONG_MAX
# endif
#endif
#ifndef UINTMAX_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef ULLONG_MAX
typedef unsigned long long uintmax_t;
+# define UINTMAX_MAX ULLONG_MAX
# else
typedef unsigned long uintmax_t;
+# define UINTMAX_MAX ULONG_MAX
# endif
#endif
#ifndef PRIuMAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef ULLONG_MAX
# define PRIuMAX "llu"
# else
# define PRIuMAX "lu"
# endif
#endif
-#ifndef INT32_MAX
-#define INT32_MAX 0x7fffffff
-#endif /* !defined INT32_MAX */
-#ifndef INT32_MIN
-#define INT32_MIN (-1 - INT32_MAX)
-#endif /* !defined INT32_MIN */
-
#ifndef SIZE_MAX
-#define SIZE_MAX ((size_t) -1)
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#endif /* PORT_TO_C89 */
+
+/* The maximum size of any created object, as a signed integer.
+ Although the C standard does not outright prohibit larger objects,
+ behavior is undefined if the result of pointer subtraction does not
+ fit into ptrdiff_t, and the code assumes in several places that
+ pointer subtraction works. As a practical matter it's OK to not
+ support objects larger than this. */
+#define INDEX_MAX ((ptrdiff_t) min(PTRDIFF_MAX, SIZE_MAX))
+
+/* Support ckd_add, ckd_sub, ckd_mul on C23 or recent-enough GCC-like
+ hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
+#if !defined HAVE_STDCKDINT_H && defined __has_include
+# if __has_include(<stdckdint.h>)
+# define HAVE_STDCKDINT_H true
+# endif
+#endif
+#ifdef HAVE_STDCKDINT_H
+# if HAVE_STDCKDINT_H
+# include <stdckdint.h>
+# endif
+#elif defined __EDG__
+/* Do nothing, to work around EDG bug <https://bugs.gnu.org/53256>. */
+#elif defined __has_builtin
+# if __has_builtin(__builtin_add_overflow)
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_sub_overflow)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_mul_overflow)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+# endif
+#elif 7 <= __GNUC__
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
#endif
#if 3 <= __GNUC__
-# define ATTRIBUTE_CONST __attribute__((const))
-# define ATTRIBUTE_MALLOC __attribute__((__malloc__))
-# define ATTRIBUTE_PURE __attribute__((__pure__))
-# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
+# define ATTRIBUTE_MALLOC __attribute__((malloc))
+# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
#else
-# define ATTRIBUTE_CONST /* empty */
# define ATTRIBUTE_MALLOC /* empty */
-# define ATTRIBUTE_PURE /* empty */
# define ATTRIBUTE_FORMAT(spec) /* empty */
#endif
-#if !defined _Noreturn && __STDC_VERSION__ < 201112
-# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
-# define _Noreturn __attribute__((__noreturn__))
+#if (defined __has_c_attribute \
+ && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__))
+# define HAVE___HAS_C_ATTRIBUTE true
+#else
+# define HAVE___HAS_C_ATTRIBUTE false
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(deprecated)
+# define ATTRIBUTE_DEPRECATED [[deprecated]]
+# endif
+#endif
+#ifndef ATTRIBUTE_DEPRECATED
+# if 3 < __GNUC__ + (2 <= __GNUC_MINOR__)
+# define ATTRIBUTE_DEPRECATED __attribute__((deprecated))
# else
-# define _Noreturn
+# define ATTRIBUTE_DEPRECATED /* empty */
# endif
#endif
-#if __STDC_VERSION__ < 199901 && !defined restrict
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(fallthrough)
+# define ATTRIBUTE_FALLTHROUGH [[fallthrough]]
+# endif
+#endif
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if 7 <= __GNUC__
+# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough))
+# else
+# define ATTRIBUTE_FALLTHROUGH ((void) 0)
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(maybe_unused)
+# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
+# endif
+#endif
+#ifndef ATTRIBUTE_MAYBE_UNUSED
+# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused))
+# else
+# define ATTRIBUTE_MAYBE_UNUSED /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(noreturn)
+# define ATTRIBUTE_NORETURN [[noreturn]]
+# endif
+#endif
+#ifndef ATTRIBUTE_NORETURN
+# if 201112 <= __STDC_VERSION__
+# define ATTRIBUTE_NORETURN _Noreturn
+# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
+# define ATTRIBUTE_NORETURN __attribute__((noreturn))
+# else
+# define ATTRIBUTE_NORETURN /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(reproducible)
+# define ATTRIBUTE_REPRODUCIBLE [[reproducible]]
+# endif
+#endif
+#ifndef ATTRIBUTE_REPRODUCIBLE
+# if 3 <= __GNUC__
+# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure))
+# else
+# define ATTRIBUTE_REPRODUCIBLE /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(unsequenced)
+# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
+# endif
+#endif
+#ifndef ATTRIBUTE_UNSEQUENCED
+# if 3 <= __GNUC__
+# define ATTRIBUTE_UNSEQUENCED __attribute__((const))
+# else
+# define ATTRIBUTE_UNSEQUENCED /* empty */
+# endif
+#endif
+
+#if (__STDC_VERSION__ < 199901 && !defined restrict \
+ && (PORT_TO_C89 || defined _MSC_VER))
# define restrict /* empty */
#endif
@@ -505,11 +671,16 @@
# define altzone tz_altzone
# endif
-char *asctime(struct tm const *);
+# if __STDC_VERSION__ < 202311
+# define DEPRECATED_IN_C23 /* empty */
+# else
+# define DEPRECATED_IN_C23 ATTRIBUTE_DEPRECATED
+# endif
+DEPRECATED_IN_C23 char *asctime(struct tm const *);
char *asctime_r(struct tm const *restrict, char *restrict);
-char *ctime(time_t const *);
+DEPRECATED_IN_C23 char *ctime(time_t const *);
char *ctime_r(time_t const *, char *);
-double difftime(time_t, time_t) ATTRIBUTE_CONST;
+ATTRIBUTE_UNSEQUENCED double difftime(time_t, time_t);
size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict);
# if HAVE_STRFTIME_L
@@ -522,9 +693,24 @@
struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
time_t mktime(struct tm *);
time_t time(time_t *);
+time_t timegm(struct tm *);
void tzset(void);
#endif
+#ifndef HAVE_DECL_TIMEGM
+# if (202311 <= __STDC_VERSION__ \
+ || defined __GLIBC__ || defined __tm_zone /* musl */ \
+ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# define HAVE_DECL_TIMEGM true
+# else
+# define HAVE_DECL_TIMEGM false
+# endif
+#endif
+#if !HAVE_DECL_TIMEGM && !defined timegm
+time_t timegm(struct tm *);
+#endif
+
#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
extern char *asctime_r(struct tm const *restrict, char *restrict);
#endif
@@ -557,13 +743,13 @@
** declarations if time_tz is defined.
*/
-#ifdef STD_INSPIRED
+#ifndef STD_INSPIRED
+# define STD_INSPIRED 0
+#endif
+#if STD_INSPIRED
# if TZ_TIME_T || !defined offtime
struct tm *offtime(time_t const *, long);
# endif
-# if TZ_TIME_T || !defined timegm
-time_t timegm(struct tm *);
-# endif
# if TZ_TIME_T || !defined timelocal
time_t timelocal(struct tm *);
# endif
@@ -581,6 +767,7 @@
/* 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 __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
@@ -606,12 +793,12 @@
time_t mktime_z(timezone_t restrict, struct tm *restrict);
timezone_t tzalloc(char const *);
void tzfree(timezone_t);
-# ifdef STD_INSPIRED
+# if STD_INSPIRED
# if TZ_TIME_T || !defined posix2time_z
-time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
+ATTRIBUTE_REPRODUCIBLE time_t posix2time_z(timezone_t, time_t);
# endif
# if TZ_TIME_T || !defined time2posix_z
-time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
+ATTRIBUTE_REPRODUCIBLE time_t time2posix_z(timezone_t, time_t);
# endif
# endif
#endif
@@ -620,18 +807,15 @@
** Finally, some convenience items.
*/
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#else
-# define true 1
-# define false 0
-# define bool int
-#endif
-
-#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
+#define TYPE_BIT(type) (CHAR_BIT * (ptrdiff_t) sizeof(type))
#define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+/* Minimum and maximum of two values. Use lower case to avoid
+ naming clashes with standard include files. */
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
/* 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. */
@@ -651,7 +835,7 @@
This implementation assumes no padding if time_t is signed and
either the compiler lacks support for _Generic or time_t is not one
of the standard signed integer types. */
-#if HAVE_GENERIC
+#if HAVE__GENERIC
# define TIME_T_MIN \
_Generic((time_t) 0, \
signed char: SCHAR_MIN, short: SHRT_MIN, \
@@ -664,10 +848,23 @@
int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
default: TIME_T_MAX_NO_PADDING) \
: (time_t) -1)
+enum { SIGNED_PADDING_CHECK_NEEDED
+ = _Generic((time_t) 0,
+ signed char: false, short: false,
+ int: false, long: false, long long: false,
+ default: true) };
#else
# define TIME_T_MIN TIME_T_MIN_NO_PADDING
# define TIME_T_MAX TIME_T_MAX_NO_PADDING
+enum { SIGNED_PADDING_CHECK_NEEDED = true };
#endif
+/* Try to check the padding assumptions. Although TIME_T_MAX and the
+ following check can both have undefined behavior on oddball
+ platforms due to shifts exceeding widths of signed integers, these
+ platforms' compilers are likely to diagnose these issues in integer
+ constant expressions, so it shouldn't hurt to check statically. */
+static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
+ || TIME_T_MAX >> (TYPE_BIT(time_t) - 2) == 1);
/*
** 302 / 1000 is log10(2.0) rounded up.
@@ -689,21 +886,30 @@
# define INITIALIZE(x)
#endif
+/* Whether memory access must strictly follow the C standard.
+ If 0, it's OK to read uninitialized storage so long as the value is
+ not relied upon. Defining it to 0 lets mktime access parts of
+ struct tm that might be uninitialized, as a heuristic when the
+ standard doesn't say what to return and when tm_gmtoff can help
+ mktime likely infer a better value. */
#ifndef UNINIT_TRAP
# define UNINIT_TRAP 0
#endif
#ifdef DEBUG
-# define UNREACHABLE() abort()
-#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
-# define UNREACHABLE() __builtin_unreachable()
-#elif defined __has_builtin
-# if __has_builtin(__builtin_unreachable)
-# define UNREACHABLE() __builtin_unreachable()
+# undef unreachable
+# define unreachable() abort()
+#elif !defined unreachable
+# ifdef __has_builtin
+# if __has_builtin(__builtin_unreachable)
+# define unreachable() __builtin_unreachable()
+# endif
+# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
+# define unreachable() __builtin_unreachable()
# endif
-#endif
-#ifndef UNREACHABLE
-# define UNREACHABLE() ((void) 0)
+# ifndef unreachable
+# define unreachable() ((void) 0)
+# endif
#endif
/*
@@ -725,53 +931,61 @@
#if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r
#undef ctime_r
-char *asctime_r(struct tm const *, char *);
+char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime_r(time_t const *, char *);
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Handy macros that are independent of tzfile implementation. */
-#define SECSPERMIN 60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
-#define DAYSPERNYEAR 365
-#define DAYSPERLYEAR 366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
+enum {
+ SECSPERMIN = 60,
+ MINSPERHOUR = 60,
+ SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
+ HOURSPERDAY = 24,
+ DAYSPERWEEK = 7,
+ DAYSPERNYEAR = 365,
+ DAYSPERLYEAR = DAYSPERNYEAR + 1,
+ MONSPERYEAR = 12,
+ YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
+};
-#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+
#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-#define TM_TUESDAY 2
-#define TM_WEDNESDAY 3
-#define TM_THURSDAY 4
-#define TM_FRIDAY 5
-#define TM_SATURDAY 6
+enum {
+ TM_SUNDAY,
+ TM_MONDAY,
+ TM_TUESDAY,
+ TM_WEDNESDAY,
+ TM_THURSDAY,
+ TM_FRIDAY,
+ TM_SATURDAY
+};
-#define TM_JANUARY 0
-#define TM_FEBRUARY 1
-#define TM_MARCH 2
-#define TM_APRIL 3
-#define TM_MAY 4
-#define TM_JUNE 5
-#define TM_JULY 6
-#define TM_AUGUST 7
-#define TM_SEPTEMBER 8
-#define TM_OCTOBER 9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
+enum {
+ TM_JANUARY,
+ TM_FEBRUARY,
+ TM_MARCH,
+ TM_APRIL,
+ TM_MAY,
+ TM_JUNE,
+ TM_JULY,
+ TM_AUGUST,
+ TM_SEPTEMBER,
+ TM_OCTOBER,
+ TM_NOVEMBER,
+ TM_DECEMBER
+};
-#define TM_YEAR_BASE 1900
-#define TM_WDAY_BASE TM_MONDAY
-
-#define EPOCH_YEAR 1970
-#define EPOCH_WDAY TM_THURSDAY
+enum {
+ TM_YEAR_BASE = 1900,
+ TM_WDAY_BASE = TM_MONDAY,
+ EPOCH_YEAR = 1970,
+ EPOCH_WDAY = TM_THURSDAY
+};
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c
index d04c5ba..4cde556 100644
--- a/libc/tzcode/strftime.c
+++ b/libc/tzcode/strftime.c
@@ -71,8 +71,6 @@
const char * date_fmt;
};
-#define Locale (&C_time_locale)
-
static const struct lc_time_T C_time_locale = {
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -128,13 +126,14 @@
static char * _yconv(int, int, bool, bool, char *, const char *, int);
#ifndef YEAR_2000_NAME
-#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+# define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
#endif /* !defined YEAR_2000_NAME */
#if HAVE_STRFTIME_L
size_t
-strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
- locale_t locale)
+strftime_l(char *restrict s, size_t maxsize, char const *restrict format,
+ struct tm const *restrict t,
+ ATTRIBUTE_MAYBE_UNUSED locale_t locale)
{
/* Just call strftime, as only the C locale is supported. */
return strftime(s, maxsize, format, t);
@@ -144,7 +143,8 @@
#define FORCE_LOWER_CASE 0x100 /* Android extension. */
size_t
-strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
+strftime(char *restrict s, size_t maxsize, char const *restrict format,
+ struct tm const *restrict t)
{
char * p;
int saved_errno = errno;
@@ -217,6 +217,8 @@
_fmt(const char *format, const struct tm *t, char *pt,
const char *ptlim, enum warn *warnp)
{
+ struct lc_time_T const *Locale = &C_time_locale;
+
for ( ; *format; ++format) {
if (*format == '%') {
int modifier = 0;
@@ -378,12 +380,21 @@
char buf[INT_STRLEN_MAXIMUM(time64_t) + 1] __attribute__((__uninitialized__));
time64_t mkt;
- tm = *t;
+ tm.tm_sec = t->tm_sec;
+ tm.tm_min = t->tm_min;
+ tm.tm_hour = t->tm_hour;
+ tm.tm_mday = t->tm_mday;
+ tm.tm_mon = t->tm_mon;
+ tm.tm_year = t->tm_year;
+ tm.tm_isdst = t->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ tm.TM_GMTOFF = t->TM_GMTOFF;
+#endif
mkt = mktime64(&tm);
- /* There is no portable, definitive
- test for whether whether mktime
- succeeded, so treat (time_t) -1 as
- the success that it might be. */
+ /* If mktime fails, %s expands to the
+ value of (time_t) -1 as a failure
+ marker; this is better in practice
+ than strftime failing. */
if (TYPE_SIGNED(time64_t)) {
intmax_t n = mkt;
sprintf(buf, "%"PRIdMAX, n);
@@ -607,19 +618,19 @@
# endif
negative = diff < 0;
if (diff == 0) {
-#ifdef TM_ZONE
+# ifdef TM_ZONE
// Android-changed: do not use TM_ZONE as it is as it may be null.
{
const char* zone = _safe_tm_zone(t);
negative = zone[0] == '-';
}
-#else
+# else
negative = t->tm_isdst < 0;
-# if HAVE_TZNAME
+# if HAVE_TZNAME
if (tzname[t->tm_isdst != 0][0] == '-')
negative = true;
+# endif
# endif
-#endif
}
if (negative) {
sign = "-";
@@ -748,7 +759,7 @@
register int lead;
register int trail;
-#define DIVISOR 100
+ int DIVISOR = 100;
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR;
diff --git a/libc/tzcode/tzfile.h b/libc/tzcode/tzfile.h
index c5f9967..7ac8b51 100644
--- a/libc/tzcode/tzfile.h
+++ b/libc/tzcode/tzfile.h
@@ -22,15 +22,15 @@
*/
#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+# define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
+# define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
-#define TZDEFRULES "posixrules"
+# define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
@@ -103,21 +103,25 @@
*/
#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 2000
+/* This must be at least 242 for Europe/London with 'zic -b fat'. */
+# define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
-/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+/* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */
+# define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+/* This must be at least 40 for America/Anchorage. */
+# define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+/* This must be at least 27 for leap seconds from 1972 through mid-2023.
+ There's a plan to discontinue leap seconds by 2035. */
+# define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#define SECSPERMIN 60