This splits the old parse-datetime into two parts; the first is parse-datetime2 which supports all the new bells and whistles, the second is parse-datetime, which reverts to its original intent. This avoids some bogus diagnostics when build GNU Tar with gcc -flto -fanalyze and with --enable-gcc-warnings. And it slims down the executable a bit. * NEWS: Mention this. * lib/parse-datetime.y (parser_control) [!GNULIB_PARSE_DATETIME2]: Omit parse_datetime_debug member. (debugging): New function. Use it everywhere the old code would load parse_datetime_debug. (parse_datetime_body): New static function, with the body of the old parse_datetime2. Set pc.parse_datetime_debug only if GNULIB_PARSE_DATETIME2. (parse_datetime2, parse_datetime): Use this new function. (parse_datetime2) [!GNULIB_PARSE_DATETIME2]: Remove. --- ChangeLog | 21 ++++++++ NEWS | 5 ++ lib/parse-datetime.y | 122 +++++++++++++++++++++++++++---------------- 3 files changed, 102 insertions(+), 46 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 9bc60f88e..f4bf4fced 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2021-03-01 Paul Eggert <egg...@cs.ucla.edu> + + parse-datetime2: new module + This splits the old parse-datetime into two parts; the + first is parse-datetime2 which supports all the new bells + and whistles, the second is parse-datetime, which reverts to + its original intent. This avoids some bogus diagnostics + when build GNU Tar with gcc -flto -fanalyze and + with --enable-gcc-warnings. And it slims down the + executable a bit. + * NEWS: Mention this. + * lib/parse-datetime.y (parser_control) [!GNULIB_PARSE_DATETIME2]: + Omit parse_datetime_debug member. + (debugging): New function. Use it everywhere the old code + would load parse_datetime_debug. + (parse_datetime_body): New static function, with the body + of the old parse_datetime2. Set pc.parse_datetime_debug + only if GNULIB_PARSE_DATETIME2. + (parse_datetime2, parse_datetime): Use this new function. + (parse_datetime2) [!GNULIB_PARSE_DATETIME2]: Remove. + 2021-02-27 Bruno Haible <br...@clisp.org> string-buffer: Fixes. diff --git a/NEWS b/NEWS index c347ecd96..318055ae8 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,11 @@ User visible incompatible changes Date Modules Changes +2021-02-28 parse-datetime The parse_datetime2 function has been moved + to the new parse-datetime2 module, so that + programs that need just parse_datetime need + not build the fancier function. + 2020-12-23 execute These functions no longer execute scripts without spawn-pipe '#!' marker through /bin/sh. To execute such a posix_spawn script as a shell script, either add a '#!/bin/sh' diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y index b8a832fcd..552fe5c90 100644 --- a/lib/parse-datetime.y +++ b/lib/parse-datetime.y @@ -221,8 +221,10 @@ typedef struct idx_t zones_seen; bool year_seen; +#ifdef GNULIB_PARSE_DATETIME2 /* Print debugging output to stderr. */ bool parse_datetime_debug; +#endif /* Which of the 'seen' parts have been printed when debugging. */ bool debug_dates_seen; @@ -239,6 +241,16 @@ typedef struct table local_time_zone_table[3]; } parser_control; +static bool +debugging (parser_control const *pc) +{ +#ifdef GNULIB_PARSE_DATETIME2 + return pc->parse_datetime_debug; +#else + return false; +#endif +} + union YYSTYPE; static int yylex (union YYSTYPE *, parser_control *); static int yyerror (parser_control const *, char const *); @@ -421,7 +433,7 @@ debug_print_current_time (char const *item, parser_control *pc) { bool space = false; - if (!pc->parse_datetime_debug) + if (!debugging (pc)) return; /* no newline, more items printed below */ @@ -521,7 +533,7 @@ debug_print_relative_time (char const *item, parser_control const *pc) { bool space = false; - if (!pc->parse_datetime_debug) + if (!debugging (pc)) return; /* no newline, more items printed below */ @@ -802,7 +814,7 @@ date: you want portability, use the ISO 8601 format. */ if (4 <= $1.digits) { - if (pc->parse_datetime_debug) + if (debugging (pc)) { intmax_t digits = $1.digits; dbg_printf (_("warning: value %"PRIdMAX" has %"PRIdMAX" digits. " @@ -816,7 +828,7 @@ date: } else { - if (pc->parse_datetime_debug) + if (debugging (pc)) dbg_printf (_("warning: value %"PRIdMAX" has less than 4 digits. " "Assuming MM/DD/YY[YY]\n"), $1.value); @@ -1504,7 +1516,7 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) tp = lookup_word (pc, buff); if (! tp) { - if (pc->parse_datetime_debug) + if (debugging (pc)) dbg_printf (_("error: unknown word '%s'\n"), buff); return '?'; } @@ -1651,7 +1663,7 @@ debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1, const bool dst_shift = eq_sec && eq_min && !eq_hour && eq_mday && eq_month && eq_year; - if (!pc->parse_datetime_debug) + if (!debugging (pc)) return; dbg_printf (_("error: invalid date/time value:\n")); @@ -1690,29 +1702,15 @@ debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1, : _("missing timezone"))); } -/* The original interface: run with debug=false and the default timezone. */ -bool -parse_datetime (struct timespec *result, char const *p, - struct timespec const *now) -{ - char const *tzstring = getenv ("TZ"); - timezone_t tz = tzalloc (tzstring); - if (!tz) - return false; - bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring); - tzfree (tz); - return ok; -} - /* 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 *NOW as the basis for the returned time. Default to timezone TZDEFAULT, which corresponds to tzalloc (TZSTRING). */ -bool -parse_datetime2 (struct timespec *result, char const *p, - struct timespec const *now, unsigned int flags, - timezone_t tzdefault, char const *tzstring) +static bool +parse_datetime_body (struct timespec *result, char const *p, + struct timespec const *now, unsigned int flags, + timezone_t tzdefault, char const *tzstring) { struct tm tm; struct tm tm0; @@ -1803,10 +1801,12 @@ parse_datetime2 (struct timespec *result, char const *p, parser_control pc; pc.input = p; +#ifdef GNULIB_PARSE_DATETIME2 pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0; +#endif if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: initial year out of range\n")); goto fail; } @@ -1900,7 +1900,7 @@ parse_datetime2 (struct timespec *result, char const *p, if (yyparse (&pc) != 0) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf ((input_sentinel <= pc.input ? _("error: parsing failed\n") : _("error: parsing failed, stopped at '%s'\n")), @@ -1911,7 +1911,7 @@ parse_datetime2 (struct timespec *result, char const *p, /* Determine effective timezone source. */ - if (pc.parse_datetime_debug) + if (debugging (&pc)) { dbg_printf (_("input timezone: ")); @@ -1953,7 +1953,7 @@ parse_datetime2 (struct timespec *result, char const *p, if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen | (pc.local_zones_seen + pc.zones_seen))) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) { if (pc.times_seen > 1) dbg_printf ("error: seen multiple time parts\n"); @@ -1969,11 +1969,11 @@ parse_datetime2 (struct timespec *result, char const *p, goto fail; } - if (! to_tm_year (pc.year, pc.parse_datetime_debug, &tm.tm_year) + if (! to_tm_year (pc.year, debugging (&pc), &tm.tm_year) || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon) || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: year, month, or day overflow\n")); goto fail; } @@ -1984,14 +1984,14 @@ parse_datetime2 (struct timespec *result, char const *p, { char const *mrd = (pc.meridian == MERam ? "am" : pc.meridian == MERpm ?"pm" : ""); - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"), pc.hour, mrd); goto fail; } tm.tm_min = pc.minutes; tm.tm_sec = pc.seconds.tv_sec; - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf ((pc.times_seen ? _("using specified time as starting value: '%s'\n") : _("using current time as starting value: '%s'\n")), @@ -2001,7 +2001,7 @@ parse_datetime2 (struct timespec *result, char const *p, { tm.tm_hour = tm.tm_min = tm.tm_sec = 0; pc.seconds.tv_nsec = 0; - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf ("warning: using midnight as starting time: 00:00:00\n"); } @@ -2047,7 +2047,7 @@ parse_datetime2 (struct timespec *result, char const *p, timezone_t tz2 = tzalloc (tz2buf); if (!tz2) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf); goto fail; } @@ -2092,7 +2092,7 @@ parse_datetime2 (struct timespec *result, char const *p, if (Start == (time_t) -1) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: day '%s' " "(day ordinal=%"PRIdMAX" number=%d) " "resulted in an invalid date: '%s'\n"), @@ -2103,14 +2103,14 @@ parse_datetime2 (struct timespec *result, char const *p, goto fail; } - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("new start date: '%s' is '%s'\n"), str_days (&pc, dbg_ord, sizeof dbg_ord), debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm)); } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { if (!pc.dates_seen && !pc.days_seen) dbg_printf (_("using current date as starting value: '%s'\n"), @@ -2128,7 +2128,7 @@ parse_datetime2 (struct timespec *result, char const *p, /* Add relative date. */ if (pc.rel.year | pc.rel.month | pc.rel.day) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) { if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15) dbg_printf (_("warning: when adding relative months/years, " @@ -2145,7 +2145,7 @@ parse_datetime2 (struct timespec *result, char const *p, || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month) || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__); goto fail; } @@ -2159,7 +2159,7 @@ parse_datetime2 (struct timespec *result, char const *p, Start = mktime_z (tz, &tm); if (Start == (time_t) -1) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: adding relative date resulted " "in an invalid date: '%s'\n"), debug_strfdatetime (&tm, &pc, dbg_tm, @@ -2167,7 +2167,7 @@ parse_datetime2 (struct timespec *result, char const *p, goto fail; } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { dbg_printf (_("after date adjustment " "(%+"PRIdMAX" years, %+"PRIdMAX" months, " @@ -2244,7 +2244,7 @@ parse_datetime2 (struct timespec *result, char const *p, overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1); if (overflow) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: timezone %d caused time_t overflow\n"), pc.time_zone); goto fail; @@ -2252,7 +2252,7 @@ parse_datetime2 (struct timespec *result, char const *p, Start = t1; } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { intmax_t Starti = Start; dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"), @@ -2282,7 +2282,7 @@ parse_datetime2 (struct timespec *result, char const *p, || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3) || INT_ADD_WRAPV (t3, d4, &t4)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: adding relative time caused an " "overflow\n")); goto fail; @@ -2291,7 +2291,7 @@ parse_datetime2 (struct timespec *result, char const *p, result->tv_sec = t4; result->tv_nsec = normalized_ns; - if (pc.parse_datetime_debug + if (debugging (&pc) && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns)) { dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, " @@ -2322,7 +2322,7 @@ parse_datetime2 (struct timespec *result, char const *p, } } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { /* Special case: using 'date -u' simply set TZ=UTC0 */ if (! tzstring) @@ -2373,6 +2373,36 @@ parse_datetime2 (struct timespec *result, char const *p, return ok; } +#ifdef GNULIB_PARSE_DATETIME2 +/* 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 + *NOW as the basis for the returned time. Default to timezone + TZDEFAULT, which corresponds to tzalloc (TZSTRING). */ +bool +parse_datetime2 (struct timespec *result, char const *p, + struct timespec const *now, unsigned int flags, + timezone_t tzdefault, char const *tzstring) +{ + return parse_datetime_body (result, p, now, flags, tzdefault, tzstring); +} +#endif + + +/* The plain interface: run with debug=false and the default timezone. */ +bool +parse_datetime (struct timespec *result, char const *p, + struct timespec const *now) +{ + char const *tzstring = getenv ("TZ"); + timezone_t tz = tzalloc (tzstring); + if (!tz) + return false; + bool ok = parse_datetime_body (result, p, now, 0, tz, tzstring); + tzfree (tz); + return ok; +} + #if TEST int -- 2.29.2