summaryrefslogtreecommitdiff
path: root/lib/getdate.y
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2004-10-29 23:43:34 +0000
committerPaul Eggert <eggert@cs.ucla.edu>2004-10-29 23:43:34 +0000
commita4a87ad5bbe2c8ae501d45841832559be73ee459 (patch)
treeaf15c9a49c57aa39eb4f2fc03fab48702b7df9e8 /lib/getdate.y
parentdf8ea7a4e890f5f24e948bac0e7e500122f99a47 (diff)
downloadcoreutils-a4a87ad5bbe2c8ae501d45841832559be73ee459.tar.xz
Sync from gnulib.
Diffstat (limited to 'lib/getdate.y')
-rw-r--r--lib/getdate.y667
1 files changed, 389 insertions, 278 deletions
diff --git a/lib/getdate.y b/lib/getdate.y
index e8cbfeb6b..ec8c19250 100644
--- a/lib/getdate.y
+++ b/lib/getdate.y
@@ -22,16 +22,13 @@
<rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
- the right thing about local DST, and in February 2004 to support
- nanosecond-resolution time stamps. Unlike previous versions, this
- version is reentrant. */
+ the right thing about local DST. Also modified by Paul Eggert
+ <eggert@cs.ucla.edu> in February 2004 to support
+ nanosecond-resolution time stamps, and in October 2004 to support
+ TZ strings in dates. */
/* FIXME: Check for arithmetic overflow in all cases, not just
- some of them.
-
- FIXME: The current code uses 'int' to count seconds; it should use
- something like 'intmax_t' to support time stamps that don't fit in
- 32 bits. */
+ some of them. */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -53,7 +50,11 @@
#include <ctype.h>
#include <limits.h>
-#include <stdlib.h> /* for `free'; used by Bison 1.27 */
+#include <stdlib.h>
+#include <string.h>
+
+#include "setenv.h"
+#include "xalloc.h"
#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
# define IN_CTYPE_DOMAIN(c) 1
@@ -64,23 +65,16 @@
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
-#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
-/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+/* ISDIGIT differs from isdigit, as follows:
- Its arg may be any int or unsigned int; it need not be an unsigned char.
- It's guaranteed to evaluate its argument exactly once.
- It's typically faster.
POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
- ISDIGIT_LOCALE unless it's important to use the locale's definition
+ isdigit unless it's important to use the locale's definition
of `digit' even when the host does not conform to POSIX. */
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
-#include <string.h>
-
-#if USE_UNLOCKED_IO
-# include "unlocked-io.h"
-#endif
-
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
# define __attribute__(x)
#endif
@@ -166,17 +160,17 @@ typedef struct
table local_time_zone_table[3];
} parser_control;
-#define PC (* (parser_control *) parm)
-#define YYLEX_PARAM parm
-#define YYPARSE_PARAM parm
-
-static int yyerror ();
-static int yylex ();
+union YYSTYPE;
+static int yylex (union YYSTYPE *, parser_control *);
+static int yyerror (parser_control *, char *);
%}
-/* We want a reentrant parser. */
-%pure_parser
+/* We want a reentrant parser, even if the TZ manipulation and the calls to
+ localtime and gmtime are not reentrant. */
+%pure-parser
+%parse-param { parser_control *pc }
+%lex-param { parser_control *pc }
/* This grammar has 13 shift/reduce conflicts. */
%expect 13
@@ -209,8 +203,8 @@ spec:
timespec:
'@' seconds
{
- PC.seconds = $2;
- PC.timespec_seen = true;
+ pc->seconds = $2;
+ pc->timespec_seen = true;
}
;
@@ -221,104 +215,104 @@ items:
item:
time
- { PC.times_seen++; }
+ { pc->times_seen++; }
| local_zone
- { PC.local_zones_seen++; }
+ { pc->local_zones_seen++; }
| zone
- { PC.zones_seen++; }
+ { pc->zones_seen++; }
| date
- { PC.dates_seen++; }
+ { pc->dates_seen++; }
| day
- { PC.days_seen++; }
+ { pc->days_seen++; }
| rel
- { PC.rels_seen++; }
+ { pc->rels_seen++; }
| number
;
time:
tUNUMBER tMERIDIAN
{
- PC.hour = $1.value;
- PC.minutes = 0;
- PC.seconds.tv_sec = 0;
- PC.seconds.tv_nsec = 0;
- PC.meridian = $2;
+ pc->hour = $1.value;
+ pc->minutes = 0;
+ pc->seconds.tv_sec = 0;
+ pc->seconds.tv_nsec = 0;
+ pc->meridian = $2;
}
| tUNUMBER ':' tUNUMBER o_merid
{
- PC.hour = $1.value;
- PC.minutes = $3.value;
- PC.seconds.tv_sec = 0;
- PC.seconds.tv_nsec = 0;
- PC.meridian = $4;
+ pc->hour = $1.value;
+ pc->minutes = $3.value;
+ pc->seconds.tv_sec = 0;
+ pc->seconds.tv_nsec = 0;
+ pc->meridian = $4;
}
| tUNUMBER ':' tUNUMBER tSNUMBER
{
- PC.hour = $1.value;
- PC.minutes = $3.value;
- PC.seconds.tv_sec = 0;
- PC.seconds.tv_nsec = 0;
- PC.meridian = MER24;
- PC.zones_seen++;
- PC.time_zone = $4.value % 100 + ($4.value / 100) * 60;
+ pc->hour = $1.value;
+ pc->minutes = $3.value;
+ pc->seconds.tv_sec = 0;
+ pc->seconds.tv_nsec = 0;
+ pc->meridian = MER24;
+ pc->zones_seen++;
+ pc->time_zone = $4.value % 100 + ($4.value / 100) * 60;
}
| tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
{
- PC.hour = $1.value;
- PC.minutes = $3.value;
- PC.seconds = $5;
- PC.meridian = $6;
+ pc->hour = $1.value;
+ pc->minutes = $3.value;
+ pc->seconds = $5;
+ pc->meridian = $6;
}
| tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER
{
- PC.hour = $1.value;
- PC.minutes = $3.value;
- PC.seconds = $5;
- PC.meridian = MER24;
- PC.zones_seen++;
- PC.time_zone = $6.value % 100 + ($6.value / 100) * 60;
+ pc->hour = $1.value;
+ pc->minutes = $3.value;
+ pc->seconds = $5;
+ pc->meridian = MER24;
+ pc->zones_seen++;
+ pc->time_zone = $6.value % 100 + ($6.value / 100) * 60;
}
;
local_zone:
tLOCAL_ZONE
- { PC.local_isdst = $1; }
+ { pc->local_isdst = $1; }
| tLOCAL_ZONE tDST
- { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
+ { pc->local_isdst = $1 < 0 ? 1 : $1 + 1; }
;
zone:
tZONE
- { PC.time_zone = $1; }
+ { pc->time_zone = $1; }
| tDAYZONE
- { PC.time_zone = $1 + 60; }
+ { pc->time_zone = $1 + 60; }
| tZONE tDST
- { PC.time_zone = $1 + 60; }
+ { pc->time_zone = $1 + 60; }
;
day:
tDAY
{
- PC.day_ordinal = 1;
- PC.day_number = $1;
+ pc->day_ordinal = 1;
+ pc->day_number = $1;
}
| tDAY ','
{
- PC.day_ordinal = 1;
- PC.day_number = $1;
+ pc->day_ordinal = 1;
+ pc->day_number = $1;
}
| tUNUMBER tDAY
{
- PC.day_ordinal = $1.value;
- PC.day_number = $2;
+ pc->day_ordinal = $1.value;
+ pc->day_number = $2;
}
;
date:
tUNUMBER '/' tUNUMBER
{
- PC.month = $1.value;
- PC.day = $3.value;
+ pc->month = $1.value;
+ pc->day = $3.value;
}
| tUNUMBER '/' tUNUMBER '/' tUNUMBER
{
@@ -329,119 +323,119 @@ date:
you want portability, use the ISO 8601 format. */
if (4 <= $1.digits)
{
- PC.year = $1;
- PC.month = $3.value;
- PC.day = $5.value;
+ pc->year = $1;
+ pc->month = $3.value;
+ pc->day = $5.value;
}
else
{
- PC.month = $1.value;
- PC.day = $3.value;
- PC.year = $5;
+ pc->month = $1.value;
+ pc->day = $3.value;
+ pc->year = $5;
}
}
| tUNUMBER tSNUMBER tSNUMBER
{
/* ISO 8601 format. YYYY-MM-DD. */
- PC.year = $1;
- PC.month = -$2.value;
- PC.day = -$3.value;
+ pc->year = $1;
+ pc->month = -$2.value;
+ pc->day = -$3.value;
}
| tUNUMBER tMONTH tSNUMBER
{
/* e.g. 17-JUN-1992. */
- PC.day = $1.value;
- PC.month = $2;
- PC.year.value = -$3.value;
- PC.year.digits = $3.digits;
+ pc->day = $1.value;
+ pc->month = $2;
+ pc->year.value = -$3.value;
+ pc->year.digits = $3.digits;
}
| tMONTH tSNUMBER tSNUMBER
{
/* e.g. JUN-17-1992. */
- PC.month = $1;
- PC.day = -$2.value;
- PC.year.value = -$3.value;
- PC.year.digits = $3.digits;
+ pc->month = $1;
+ pc->day = -$2.value;
+ pc->year.value = -$3.value;
+ pc->year.digits = $3.digits;
}
| tMONTH tUNUMBER
{
- PC.month = $1;
- PC.day = $2.value;
+ pc->month = $1;
+ pc->day = $2.value;
}
| tMONTH tUNUMBER ',' tUNUMBER
{
- PC.month = $1;
- PC.day = $2.value;
- PC.year = $4;
+ pc->month = $1;
+ pc->day = $2.value;
+ pc->year = $4;
}
| tUNUMBER tMONTH
{
- PC.day = $1.value;
- PC.month = $2;
+ pc->day = $1.value;
+ pc->month = $2;
}
| tUNUMBER tMONTH tUNUMBER
{
- PC.day = $1.value;
- PC.month = $2;
- PC.year = $3;
+ pc->day = $1.value;
+ pc->month = $2;
+ pc->year = $3;
}
;
rel:
relunit tAGO
{
- PC.rel_ns = -PC.rel_ns;
- PC.rel_seconds = -PC.rel_seconds;
- PC.rel_minutes = -PC.rel_minutes;
- PC.rel_hour = -PC.rel_hour;
- PC.rel_day = -PC.rel_day;
- PC.rel_month = -PC.rel_month;
- PC.rel_year = -PC.rel_year;
+ pc->rel_ns = -pc->rel_ns;
+ pc->rel_seconds = -pc->rel_seconds;
+ pc->rel_minutes = -pc->rel_minutes;
+ pc->rel_hour = -pc->rel_hour;
+ pc->rel_day = -pc->rel_day;
+ pc->rel_month = -pc->rel_month;
+ pc->rel_year = -pc->rel_year;
}
| relunit
;
relunit:
tUNUMBER tYEAR_UNIT
- { PC.rel_year += $1.value * $2; }
+ { pc->rel_year += $1.value * $2; }
| tSNUMBER tYEAR_UNIT
- { PC.rel_year += $1.value * $2; }
+ { pc->rel_year += $1.value * $2; }
| tYEAR_UNIT
- { PC.rel_year += $1; }
+ { pc->rel_year += $1; }
| tUNUMBER tMONTH_UNIT
- { PC.rel_month += $1.value * $2; }
+ { pc->rel_month += $1.value * $2; }
| tSNUMBER tMONTH_UNIT
- { PC.rel_month += $1.value * $2; }
+ { pc->rel_month += $1.value * $2; }
| tMONTH_UNIT
- { PC.rel_month += $1; }
+ { pc->rel_month += $1; }
| tUNUMBER tDAY_UNIT
- { PC.rel_day += $1.value * $2; }
+ { pc->rel_day += $1.value * $2; }
| tSNUMBER tDAY_UNIT
- { PC.rel_day += $1.value * $2; }
+ { pc->rel_day += $1.value * $2; }
| tDAY_UNIT
- { PC.rel_day += $1; }
+ { pc->rel_day += $1; }
| tUNUMBER tHOUR_UNIT
- { PC.rel_hour += $1.value * $2; }
+ { pc->rel_hour += $1.value * $2; }
| tSNUMBER tHOUR_UNIT
- { PC.rel_hour += $1.value * $2; }
+ { pc->rel_hour += $1.value * $2; }
| tHOUR_UNIT
- { PC.rel_hour += $1; }
+ { pc->rel_hour += $1; }
| tUNUMBER tMINUTE_UNIT
- { PC.rel_minutes += $1.value * $2; }
+ { pc->rel_minutes += $1.value * $2; }
| tSNUMBER tMINUTE_UNIT
- { PC.rel_minutes += $1.value * $2; }
+ { pc->rel_minutes += $1.value * $2; }
| tMINUTE_UNIT
- { PC.rel_minutes += $1; }
+ { pc->rel_minutes += $1; }
| tUNUMBER tSEC_UNIT
- { PC.rel_seconds += $1.value * $2; }
+ { pc->rel_seconds += $1.value * $2; }
| tSNUMBER tSEC_UNIT
- { PC.rel_seconds += $1.value * $2; }
+ { pc->rel_seconds += $1.value * $2; }
| tSDECIMAL_NUMBER tSEC_UNIT
- { PC.rel_seconds += $1.tv_sec * $2; PC.rel_ns += $1.tv_nsec * $2; }
+ { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
| tUDECIMAL_NUMBER tSEC_UNIT
- { PC.rel_seconds += $1.tv_sec * $2; PC.rel_ns += $1.tv_nsec * $2; }
+ { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
| tSEC_UNIT
- { PC.rel_seconds += $1; }
+ { pc->rel_seconds += $1; }
;
seconds: signed_seconds | unsigned_seconds;
@@ -461,35 +455,35 @@ unsigned_seconds:
number:
tUNUMBER
{
- if (PC.dates_seen
- && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits))
- PC.year = $1;
+ if (pc->dates_seen
+ && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
+ pc->year = $1;
else
{
if (4 < $1.digits)
{
- PC.dates_seen++;
- PC.day = $1.value % 100;
- PC.month = ($1.value / 100) % 100;
- PC.year.value = $1.value / 10000;
- PC.year.digits = $1.digits - 4;
+ pc->dates_seen++;
+ pc->day = $1.value % 100;
+ pc->month = ($1.value / 100) % 100;
+ pc->year.value = $1.value / 10000;
+ pc->year.digits = $1.digits - 4;
}
else
{
- PC.times_seen++;
+ pc->times_seen++;
if ($1.digits <= 2)
{
- PC.hour = $1.value;
- PC.minutes = 0;
+ pc->hour = $1.value;
+ pc->minutes = 0;
}
else
{
- PC.hour = $1.value / 100;
- PC.minutes = $1.value % 100;
+ pc->hour = $1.value / 100;
+ pc->minutes = $1.value % 100;
}
- PC.seconds.tv_sec = 0;
- PC.seconds.tv_nsec = 0;
- PC.meridian = MER24;
+ pc->seconds.tv_sec = 0;
+ pc->seconds.tv_nsec = 0;
+ pc->meridian = MER24;
}
}
}
@@ -510,7 +504,7 @@ static table const meridian_table[] =
{ "A.M.", tMERIDIAN, MERam },
{ "PM", tMERIDIAN, MERpm },
{ "P.M.", tMERIDIAN, MERpm },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static table const dst_table[] =
@@ -544,7 +538,7 @@ static table const month_and_day_table[] =
{ "THURS", tDAY, 4 },
{ "FRIDAY", tDAY, 5 },
{ "SATURDAY", tDAY, 6 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static table const time_units_table[] =
@@ -559,7 +553,7 @@ static table const time_units_table[] =
{ "MIN", tMINUTE_UNIT, 1 },
{ "SECOND", tSEC_UNIT, 1 },
{ "SEC", tSEC_UNIT, 1 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
/* Assorted relative-time words. */
@@ -585,7 +579,7 @@ static table const relative_time_table[] =
{ "ELEVENTH", tUNUMBER, 11 },
{ "TWELFTH", tUNUMBER, 12 },
{ "AGO", tAGO, 1 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
/* The time zone table. This table is necessarily incomplete, as time
@@ -645,7 +639,7 @@ static table const time_zone_table[] =
{ "GST", tZONE, HOUR (10) }, /* Guam Standard */
{ "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
{ "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
/* Military time zone table. */
@@ -676,7 +670,7 @@ static table const military_table[] =
{ "X", tZONE, HOUR (11) },
{ "Y", tZONE, HOUR (12) },
{ "Z", tZONE, HOUR ( 0) },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
@@ -686,16 +680,14 @@ to_hour (long int hours, int meridian)
{
switch (meridian)
{
+ default: /* Pacify GCC. */
case MER24:
return 0 <= hours && hours < 24 ? hours : -1;
case MERam:
return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
case MERpm:
return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
- default:
- abort ();
}
- /* NOTREACHED */
}
static long int
@@ -728,7 +720,7 @@ lookup_zone (parser_control const *pc, char const *name)
if (strcmp (name, tp->name) == 0)
return tp;
- return 0;
+ return NULL;
}
#if ! HAVE_TM_GMTOFF
@@ -827,7 +819,7 @@ lookup_word (parser_control const *pc, char *word)
if (period_found && (tp = lookup_zone (pc, word)))
return tp;
- return 0;
+ return NULL;
}
static int
@@ -993,11 +985,56 @@ yylex (YYSTYPE *lvalp, parser_control *pc)
/* Do nothing if the parser reports an error. */
static int
-yyerror (char *s ATTRIBUTE_UNUSED)
+yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED)
{
return 0;
}
+/* If *TM0 is the old and *TM1 is the new value of a struct tm after
+ passing it to mktime, return true if it's OK that mktime returned T.
+ It's not OK if *TM0 has out-of-range members. */
+
+static bool
+mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
+{
+ if (t == (time_t) -1)
+ {
+ /* Guard against falsely reporting an error when parsing a time
+ stamp that happens to equal (time_t) -1, on a host that
+ supports such a time stamp. */
+ tm1 = localtime (&t);
+ if (!tm1)
+ return false;
+ }
+
+ return ! ((tm0->tm_sec ^ tm1->tm_sec)
+ | (tm0->tm_min ^ tm1->tm_min)
+ | (tm0->tm_hour ^ tm1->tm_hour)
+ | (tm0->tm_mday ^ tm1->tm_mday)
+ | (tm0->tm_mon ^ tm1->tm_mon)
+ | (tm0->tm_year ^ tm1->tm_year));
+}
+
+/* A reasonable upper bound for the size of ordinary TZ strings.
+ Use heap allocation if TZ's length exceeds this. */
+enum { TZBUFSIZE = 100 };
+
+/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
+ otherwise. */
+static char *
+get_tz (char tzbuf[TZBUFSIZE])
+{
+ char *tz = getenv ("TZ");
+ if (tz)
+ {
+ size_t tzsize = strlen (tz) + 1;
+ tz = (tzsize <= TZBUFSIZE
+ ? memcpy (tzbuf, tz, tzsize)
+ : xmemdup (tz, tzsize));
+ }
+ return tz;
+}
+
/* Parse a date/time string, storing the resulting time value into *RESULT.
The string itself is pointed to by P. Return true if successful.
P can be an incomplete or relative time specification; if so, use
@@ -1012,6 +1049,11 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
struct tm tm0;
parser_control pc;
struct timespec gettime_buffer;
+ unsigned char c;
+ bool tz_was_altered = false;
+ char *tz0 = NULL;
+ char tz0buf[TZBUFSIZE];
+ bool ok = true;
if (! now)
{
@@ -1027,6 +1069,44 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
if (! tmp)
return false;
+ while (c = *p, ISSPACE (c))
+ p++;
+
+ if (strncmp (p, "TZ=\"", 4) == 0)
+ {
+ char const *tzbase = p + 4;
+ size_t tzsize = 1;
+ char const *s;
+
+ for (s = tzbase; *s; s++, tzsize++)
+ if (*s == '\\')
+ {
+ s++;
+ if (! (*s == '\\' || *s == '"'))
+ break;
+ }
+ else if (*s == '"')
+ {
+ char *z;
+ char *tz1;
+ char tz1buf[TZBUFSIZE];
+ bool large_tz = TZBUFSIZE < tzsize;
+ bool setenv_ok;
+ tz0 = get_tz (tz0buf);
+ z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
+ for (s = tzbase; *s != '"'; s++)
+ *z++ = *(s += *s == '\\');
+ *z = '\0';
+ setenv_ok = setenv ("TZ", tz1, 1) == 0;
+ if (large_tz)
+ free (tz1);
+ if (!setenv_ok)
+ goto fail;
+ tz_was_altered = true;
+ p = s + 1;
+ }
+ }
+
pc.input = p;
pc.year.value = tmp->tm_year;
pc.year.value += TM_YEAR_BASE;
@@ -1059,7 +1139,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
pc.local_time_zone_table[0].name = tmp->tm_zone;
pc.local_time_zone_table[0].type = tLOCAL_ZONE;
pc.local_time_zone_table[0].value = tmp->tm_isdst;
- pc.local_time_zone_table[1].name = 0;
+ pc.local_time_zone_table[1].name = NULL;
/* Probe the names used in the next three calendar quarters, looking
for a tm_isdst different from the one we already have. */
@@ -1076,7 +1156,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
pc.local_time_zone_table[1].name = probe_tm->tm_zone;
pc.local_time_zone_table[1].type = tLOCAL_ZONE;
pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
- pc.local_time_zone_table[2].name = 0;
+ pc.local_time_zone_table[2].name = NULL;
}
break;
}
@@ -1095,10 +1175,10 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
pc.local_time_zone_table[i].type = tLOCAL_ZONE;
pc.local_time_zone_table[i].value = i;
}
- pc.local_time_zone_table[i].name = 0;
+ pc.local_time_zone_table[i].name = NULL;
}
#else
- pc.local_time_zone_table[0].name = 0;
+ pc.local_time_zone_table[0].name = NULL;
#endif
#endif
@@ -1110,146 +1190,177 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
daylight times. So if we see that abbreviation, we don't
know whether it's daylight time. */
pc.local_time_zone_table[0].value = -1;
- pc.local_time_zone_table[1].name = 0;
+ pc.local_time_zone_table[1].name = NULL;
}
if (yyparse (&pc) != 0)
- return false;
+ goto fail;
if (pc.timespec_seen)
- {
- *result = pc.seconds;
- return true;
- }
-
- if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
- || 1 < (pc.local_zones_seen + pc.zones_seen)
- || (pc.local_zones_seen && 1 < pc.local_isdst))
- return false;
-
- tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
- tm.tm_mon = pc.month - 1 + pc.rel_month;
- tm.tm_mday = pc.day + pc.rel_day;
- if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
- {
- tm.tm_hour = to_hour (pc.hour, pc.meridian);
- if (tm.tm_hour < 0)
- return false;
- tm.tm_min = pc.minutes;
- tm.tm_sec = pc.seconds.tv_sec;
- }
+ *result = pc.seconds;
else
{
- tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
- pc.seconds.tv_nsec = 0;
- }
-
- /* Let mktime deduce tm_isdst if we have an absolute time stamp,
- or if the relative time stamp mentions days, months, or years. */
- if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
- | pc.rel_month | pc.rel_year)
- tm.tm_isdst = -1;
+ if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
+ || 1 < (pc.local_zones_seen + pc.zones_seen)
+ || (pc.local_zones_seen && 1 < pc.local_isdst))
+ goto fail;
+
+ tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
+ tm.tm_mon = pc.month - 1;
+ tm.tm_mday = pc.day;
+ if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
+ {
+ tm.tm_hour = to_hour (pc.hour, pc.meridian);
+ if (tm.tm_hour < 0)
+ goto fail;
+ tm.tm_min = pc.minutes;
+ tm.tm_sec = pc.seconds.tv_sec;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ pc.seconds.tv_nsec = 0;
+ }
- /* But if the input explicitly specifies local time with or without
- DST, give mktime that information. */
- if (pc.local_zones_seen)
- tm.tm_isdst = pc.local_isdst;
+ /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
+ if (pc.dates_seen | pc.days_seen | pc.times_seen)
+ tm.tm_isdst = -1;
- tm0 = tm;
+ /* But if the input explicitly specifies local time with or without
+ DST, give mktime that information. */
+ if (pc.local_zones_seen)
+ tm.tm_isdst = pc.local_isdst;
- Start = mktime (&tm);
+ tm0 = tm;
- if (Start == (time_t) -1)
- {
+ Start = mktime (&tm);
- /* Guard against falsely reporting errors near the time_t boundaries
- when parsing times in other time zones. For example, if the min
- time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
- of UTC, then the min localtime value is 1970-01-01 08:00:00; if
- we apply mktime to 1970-01-01 00:00:00 we will get an error, so
- we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
- zone by 24 hours to compensate. This algorithm assumes that
- there is no DST transition within a day of the time_t boundaries. */
- if (pc.zones_seen)
+ if (! mktime_ok (&tm0, &tm, Start))
{
- tm = tm0;
- if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
- {
- tm.tm_mday++;
- pc.time_zone += 24 * 60;
- }
+ if (! pc.zones_seen)
+ goto fail;
else
{
- tm.tm_mday--;
- pc.time_zone -= 24 * 60;
+ /* Guard against falsely reporting errors near the time_t
+ boundaries when parsing times in other time zones. For
+ example, suppose the input string "1969-12-31 23:00:00 -0100",
+ the current time zone is 8 hours ahead of UTC, and the min
+ time_t value is 1970-01-01 00:00:00 UTC. Then the min
+ localtime value is 1970-01-01 08:00:00, and mktime will
+ therefore fail on 1969-12-31 23:00:00. To work around the
+ problem, set the time zone to 1 hour behind UTC temporarily
+ by setting TZ="XXX1:00" and try mktime again. */
+
+ long int time_zone = pc.time_zone;
+ long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
+ long int abs_time_zone_hour = abs_time_zone / 60;
+ int abs_time_zone_min = abs_time_zone % 60;
+ char tz1buf[sizeof "XXX+0:00"
+ + sizeof pc.time_zone * CHAR_BIT / 3];
+ if (!tz_was_altered)
+ tz0 = get_tz (tz0buf);
+ sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
+ abs_time_zone_hour, abs_time_zone_min);
+ if (setenv ("TZ", tz1buf, 1) != 0)
+ goto fail;
+ tz_was_altered = true;
+ tm = tm0;
+ Start = mktime (&tm);
+ if (! mktime_ok (&tm0, &tm, Start))
+ goto fail;
}
- Start = mktime (&tm);
}
- if (Start == (time_t) -1)
- return false;
- }
-
- if (pc.days_seen && ! pc.dates_seen)
- {
- tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
- + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
- tm.tm_isdst = -1;
- Start = mktime (&tm);
- if (Start == (time_t) -1)
- return false;
- }
+ if (pc.days_seen && ! pc.dates_seen)
+ {
+ tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
+ + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+ tm.tm_isdst = -1;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ goto fail;
+ }
- if (pc.zones_seen)
- {
- long int delta = pc.time_zone * 60;
- time_t t1;
+ if (pc.zones_seen)
+ {
+ long int delta = pc.time_zone * 60;
+ time_t t1;
#ifdef HAVE_TM_GMTOFF
- delta -= tm.tm_gmtoff;
+ delta -= tm.tm_gmtoff;
#else
- time_t t = Start;
- struct tm const *gmt = gmtime (&t);
- if (! gmt)
- return false;
- delta -= tm_diff (&tm, gmt);
+ time_t t = Start;
+ struct tm const *gmt = gmtime (&t);
+ if (! gmt)
+ goto fail;
+ delta -= tm_diff (&tm, gmt);
#endif
- t1 = Start - delta;
- if ((Start < t1) != (delta < 0))
- return false; /* time_t overflow */
- Start = t1;
+ t1 = Start - delta;
+ if ((Start < t1) != (delta < 0))
+ goto fail; /* time_t overflow */
+ Start = t1;
+ }
+
+ /* Add relative date. */
+ if (pc.rel_year | pc.rel_month | pc.rel_day)
+ {
+ int year = tm.tm_year + pc.rel_year;
+ int month = tm.tm_mon + pc.rel_month;
+ int day = tm.tm_mday + pc.rel_day;
+ if (((year < tm.tm_year) ^ (pc.rel_year < 0))
+ | (month < tm.tm_mon) ^ (pc.rel_month < 0)
+ | (day < tm.tm_mday) ^ (pc.rel_day < 0))
+ goto fail;
+ tm.tm_year = year;
+ tm.tm_mon = month;
+ tm.tm_mday = day;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ goto fail;
+ }
+
+ /* Add relative hours, minutes, and seconds. On hosts that support
+ leap seconds, ignore the possibility of leap seconds; e.g.,
+ "+ 10 minutes" adds 600 seconds, even if one of them is a
+ leap second. Typically this is not what the user wants, but it's
+ too hard to do it the other way, because the time zone indicator
+ must be applied before relative times, and if mktime is applied
+ again the time zone will be lost. */
+ {
+ long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
+ long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
+ time_t t0 = Start;
+ long int d1 = 60 * 60 * pc.rel_hour;
+ time_t t1 = t0 + d1;
+ long int d2 = 60 * pc.rel_minutes;
+ time_t t2 = t1 + d2;
+ long int d3 = pc.rel_seconds;
+ time_t t3 = t2 + d3;
+ long int d4 = (sum_ns - normalized_ns) / BILLION;
+ time_t t4 = t3 + d4;
+
+ if ((d1 / (60 * 60) ^ pc.rel_hour)
+ | (d2 / 60 ^ pc.rel_minutes)
+ | ((t1 < t0) ^ (d1 < 0))
+ | ((t2 < t1) ^ (d2 < 0))
+ | ((t3 < t2) ^ (d3 < 0))
+ | ((t4 < t3) ^ (d4 < 0)))
+ goto fail;
+
+ result->tv_sec = t4;
+ result->tv_nsec = normalized_ns;
+ }
}
- /* Add relative hours, minutes, and seconds. Ignore leap seconds;
- i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
- leap second. Typically this is not what the user wants, but it's
- too hard to do it the other way, because the time zone indicator
- must be applied before relative times, and if mktime is applied
- again the time zone will be lost. */
- {
- long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
- long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
- time_t t0 = Start;
- long int d1 = 60 * 60 * pc.rel_hour;
- time_t t1 = t0 + d1;
- long int d2 = 60 * pc.rel_minutes;
- time_t t2 = t1 + d2;
- long int d3 = pc.rel_seconds;
- time_t t3 = t2 + d3;
- long int d4 = (sum_ns - normalized_ns) / BILLION;
- time_t t4 = t3 + d4;
-
- if ((d1 / (60 * 60) ^ pc.rel_hour)
- | (d2 / 60 ^ pc.rel_minutes)
- | ((t1 < t0) ^ (d1 < 0))
- | ((t2 < t1) ^ (d2 < 0))
- | ((t3 < t2) ^ (d3 < 0))
- | ((t4 < t3) ^ (d4 < 0)))
- return false;
-
- result->tv_sec = t4;
- result->tv_nsec = normalized_ns;
- return true;
- }
+ goto done;
+
+ fail:
+ ok = false;
+ done:
+ if (tz_was_altered)
+ ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
+ if (tz0 != tz0buf)
+ free (tz0);
+ return ok;
}
#if TEST
@@ -1264,7 +1375,7 @@ main (int ac, char **av)
printf ("Enter date, or blank line to exit.\n\t> ");
fflush (stdout);
- buff[BUFSIZ - 1] = 0;
+ buff[BUFSIZ - 1] = '\0';
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
{
struct timespec d;
@@ -1288,4 +1399,4 @@ main (int ac, char **av)
}
return 0;
}
-#endif /* defined TEST */
+#endif /* TEST */