X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fgetdate.y;h=1deec5160de23f166e0e569778e8824cc4e56d4a;hb=95454b6b2dbb9f57bf5623a0fd8d40fb2433c4d7;hp=dafc0eae476ac0a646dc3c1c10c0b4d3112f990d;hpb=5cc13c0f36d18dd428c7cceebd30ff8de6e2ff1e;p=gnulib.git diff --git a/lib/getdate.y b/lib/getdate.y index dafc0eae4..1deec5160 100644 --- a/lib/getdate.y +++ b/lib/getdate.y @@ -1,13 +1,13 @@ %{ /* Parse a string into an internal time stamp. - Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,8 +15,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ /* Originally written by Steven M. Bellovin while at the University of North Carolina at Chapel Hill. Later tweaked by @@ -32,12 +31,14 @@ /* FIXME: Check for arithmetic overflow in all cases, not just some of them. */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "getdate.h" +#include "intprops.h" +#include "timespec.h" +#include "verify.h" + /* There's no need to extend the stack, so there's no need to involve alloca. */ #define YYSTACK_USE_ALLOCA 0 @@ -65,30 +66,22 @@ #include #include -#include "setenv.h" #include "xalloc.h" -#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII) -# define IN_CTYPE_DOMAIN(c) 1 -#else -# define IN_CTYPE_DOMAIN(c) isascii (c) -#endif - -#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)) /* 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. + - Its arg may be any int or unsigned int; it need not be an unsigned char + or EOF. - It's typically faster. POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to 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) -#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ -# define __attribute__(x) +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ +# define __attribute__(x) +# endif #endif #ifndef ATTRIBUTE_UNUSED @@ -115,6 +108,13 @@ #define HOUR(x) ((x) * 60) +/* Lots of this code assumes time_t and time_t-like values fit into + long int. It also assumes that signed integer overflow silently + wraps around, but there's no portable way to check for that at + compile-time. */ +verify (TYPE_IS_INTEGER (time_t)); +verify (LONG_MIN <= TYPE_MINIMUM (time_t) && TYPE_MAXIMUM (time_t) <= LONG_MAX); + /* An integer value, and the number of digits in its textual representation. */ typedef struct @@ -204,9 +204,48 @@ typedef struct union YYSTYPE; static int yylex (union YYSTYPE *, parser_control *); -static int yyerror (parser_control *, char *); +static int yyerror (parser_control const *, char const *); static long int time_zone_hhmm (textint, long int); +/* Extract into *PC any date and time info from a string of digits + of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY, + YYYY, ...). */ +static void +digits_to_date_time (parser_control *pc, textint text_int) +{ + if (pc->dates_seen && ! pc->year.digits + && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits)) + pc->year = text_int; + else + { + if (4 < text_int.digits) + { + pc->dates_seen++; + pc->day = text_int.value % 100; + pc->month = (text_int.value / 100) % 100; + pc->year.value = text_int.value / 10000; + pc->year.digits = text_int.digits - 4; + } + else + { + pc->times_seen++; + if (text_int.digits <= 2) + { + pc->hour = text_int.value; + pc->minutes = 0; + } + else + { + pc->hour = text_int.value / 100; + pc->minutes = text_int.value % 100; + } + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + pc->meridian = MER24; + } + } +} + %} /* We want a reentrant parser, even if the TZ manipulation and the calls to @@ -276,6 +315,7 @@ item: | rel { pc->rels_seen = true; } | number + | hybrid ; time: @@ -551,38 +591,23 @@ unsigned_seconds: number: tUNUMBER + { digits_to_date_time (pc, $1); } + ; + +hybrid: + tUNUMBER relunit_snumber { - if (pc->dates_seen && ! pc->year.digits - && ! 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; - } - else - { - pc->times_seen++; - if ($1.digits <= 2) - { - pc->hour = $1.value; - pc->minutes = 0; - } - else - { - pc->hour = $1.value / 100; - pc->minutes = $1.value % 100; - } - pc->seconds.tv_sec = 0; - pc->seconds.tv_nsec = 0; - pc->meridian = MER24; - } - } + /* Hybrid all-digit and relative offset, so that we accept e.g., + "YYYYMMDD +N days" as well as "YYYYMMDD N days". */ + digits_to_date_time (pc, $1); + pc->rel.ns += $2.ns; + pc->rel.seconds += $2.seconds; + pc->rel.minutes += $2.minutes; + pc->rel.hour += $2.hour; + pc->rel.day += $2.day; + pc->rel.month += $2.month; + pc->rel.year += $2.year; + pc->rels_seen = true; } ; @@ -894,8 +919,7 @@ lookup_word (parser_control const *pc, char *word) for (p = word; *p; p++) { unsigned char ch = *p; - if (ISLOWER (ch)) - *p = toupper (ch); + *p = toupper (ch); } for (tp = meridian_table; tp->name; tp++) @@ -960,7 +984,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) for (;;) { - while (c = *pc->input, ISSPACE (c)) + while (c = *pc->input, isspace (c)) pc->input++; if (ISDIGIT (c) || c == '-' || c == '+') @@ -971,7 +995,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; - while (c = *++pc->input, ISSPACE (c)) + while (c = *++pc->input, isspace (c)) continue; if (! ISDIGIT (c)) /* skip the '-' sign */ @@ -1075,7 +1099,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) } } - if (ISALPHA (c)) + if (isalpha (c)) { char buff[20]; char *p = buff; @@ -1087,7 +1111,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) *p++ = c; c = *++pc->input; } - while (ISALPHA (c) || c == '.'); + while (isalpha (c) || c == '.'); *p = '\0'; tp = lookup_word (pc, buff); @@ -1116,7 +1140,8 @@ yylex (YYSTYPE *lvalp, parser_control *pc) /* Do nothing if the parser reports an error. */ static int -yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED) +yyerror (parser_control const *pc ATTRIBUTE_UNUSED, + char const *s ATTRIBUTE_UNUSED) { return 0; } @@ -1199,7 +1224,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) if (! tmp) return false; - while (c = *p, ISSPACE (c)) + while (c = *p, isspace (c)) p++; if (strncmp (p, "TZ=\"", 4) == 0) @@ -1237,6 +1262,12 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) } } + /* As documented, be careful to treat the empty string just like + a date string of "0". Without this, an empty string would be + declared invalid when parsed during a DST transition. */ + if (*p == '\0') + p = "0"; + pc.input = p; pc.year.value = tmp->tm_year; pc.year.value += TM_YEAR_BASE; @@ -1290,7 +1321,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) #else #if HAVE_TZNAME { -# ifndef tzname +# if !HAVE_DECL_TZNAME extern char *tzname[]; # endif int i; @@ -1437,6 +1468,10 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) tm.tm_year = year; tm.tm_mon = month; tm.tm_mday = day; + tm.tm_hour = tm0.tm_hour; + tm.tm_min = tm0.tm_min; + tm.tm_sec = tm0.tm_sec; + tm.tm_isdst = tm0.tm_isdst; Start = mktime (&tm); if (Start == (time_t) -1) goto fail;