2020-11-11 Paul Eggert <eggert@cs.ucla.edu>
+ parse-datetime: streamline overflow checking
+ When parse-datetime.y’s overflow code was written, INT_ADD_WRAPV
+ did not work for unsigned destinations, and since time_t might
+ be unsigned that meant it did not work for time_t destinations.
+ This limitation of INT_ADD_WRAPV has been fixed, so we can
+ now streamline parse-datetime.y a bit.
+ * lib/parse-datetime.y: Do not include limits.h, as LONG_MAX
+ has not been used for a while.
+ (yylex, parse_datetime2): Assume C99 declarations after statements.
+ (yyles): Use INT_SUBTRACT_WRAPV instead of an explicit comparison
+ to TYPE_MINIMUM.
+ (parse_datetime2): No need for time_overflow now that
+ INT_ADD_WRAPV works for unsigned results.
+
parse-datetime-tests: port to Alpine Linux 3.12.1
* tests/test-parse-datetime.c: Include errno.h for errno,
and unistd.h for _SC_TZNAME_MAX and sysconf.
Modified by Paul Eggert <eggert@twinsun.com> in 1999 to do the
right thing about local DST. Also modified by Paul Eggert
<eggert@cs.ucla.edu> in 2004 to support nanosecond-resolution
- timestamps, in 2004 to support TZ strings in dates, and in 2017 to
+ timestamps, in 2004 to support TZ strings in dates, and in 2017 and 2020 to
check for integer overflow and to support longer-than-'long'
'time_t' and 'tv_nsec'. */
#include <inttypes.h>
#include <c-ctype.h>
-#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
if (c_isdigit (c) || c == '-' || c == '+')
{
- char const *p;
+ char const *p = pc->input;
int sign;
- intmax_t value = 0;
if (c == '-' || c == '+')
{
sign = c == '-' ? -1 : 1;
- while (c = *++pc->input, c_isspace (c))
+ while (c = *(pc->input = ++p), c_isspace (c))
continue;
if (! c_isdigit (c))
/* skip the '-' sign */
}
else
sign = 0;
- p = pc->input;
+ time_t value = 0;
do
{
if (INT_MULTIPLY_WRAPV (value, 10, &value))
if ((c == '.' || c == ',') && c_isdigit (p[1]))
{
- time_t s;
- int ns;
+ time_t s = value;
int digits;
- if (time_overflow (value))
- return '?';
- s = value;
-
/* Accumulate fraction, to ns precision. */
p++;
- ns = *p++ - '0';
+ int ns = *p++ - '0';
for (digits = 2; digits <= LOG10_BILLION; digits++)
{
ns *= 10;
negative. */
if (sign < 0 && ns)
{
- if (s == TYPE_MINIMUM (time_t))
+ if (INT_SUBTRACT_WRAPV (s, 1, &s))
return '?';
- s--;
ns = BILLION - ns;
}
int quarter;
for (quarter = 1; quarter <= 3; quarter++)
{
- intmax_t iprobe;
- if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &iprobe)
- || time_overflow (iprobe))
+ time_t probe;
+ if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &probe))
break;
- time_t probe = iprobe;
struct tm probe_tm;
if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
&& probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
so this block must follow others that clobber Start. */
if (pc.zones_seen)
{
- intmax_t delta = pc.time_zone, t1;
bool overflow = false;
#ifdef HAVE_TM_GMTOFF
long int utcoff = tm.tm_gmtoff;
? tm_diff (&tm, &gmt)
: (overflow = true, 0));
#endif
- overflow |= INT_SUBTRACT_WRAPV (delta, utcoff, &delta);
+ intmax_t delta;
+ overflow |= INT_SUBTRACT_WRAPV (pc.time_zone, utcoff, &delta);
+ time_t t1;
overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1);
- if (overflow || time_overflow (t1))
+ if (overflow)
{
if (pc.parse_datetime_debug)
dbg_printf (_("error: timezone %d caused time_t overflow\n"),
intmax_t sum_ns = orig_ns + pc.rel.ns;
int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
int d4 = (sum_ns - normalized_ns) / BILLION;
- intmax_t d1, t1, d2, t2, t3, t4;
+ intmax_t d1, t1, d2, t2, t3;
+ time_t t4;
if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1)
|| INT_ADD_WRAPV (Start, d1, &t1)
|| INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2)
|| INT_ADD_WRAPV (t1, d2, &t2)
|| INT_ADD_WRAPV (t2, pc.rel.seconds, &t3)
- || INT_ADD_WRAPV (t3, d4, &t4)
- || time_overflow (t4))
+ || INT_ADD_WRAPV (t3, d4, &t4))
{
if (pc.parse_datetime_debug)
dbg_printf (_("error: adding relative time caused an "