X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fgetdate.y;h=94160b266b2cc887d6f56bdc93be2ce101354b15;hb=c13ca15f1a9efad6c85e2debf5bcc54db425d017;hp=2f72f045a32e1533acb56d7bdd3ac399f22c4383;hpb=aa33a4bea6151c466f7728cfe222ea753567a1b5;p=gnulib.git diff --git a/lib/getdate.y b/lib/getdate.y index 2f72f045a..94160b266 100644 --- a/lib/getdate.y +++ b/lib/getdate.y @@ -1,11 +1,13 @@ %{ /* Parse a string into an internal time stamp. - Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + Free Software Foundation, Inc. + + 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 @@ -13,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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 @@ -30,13 +31,24 @@ /* FIXME: Check for arithmetic overflow in all cases, not just some of them. */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "getdate.h" -#include +#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 + +/* Tell Bison how much stack space is needed. 20 should be plenty for + this grammar, which is not right recursive. Beware setting it too + high, since that might cause problems on machines whose + implementations have lame stack-overflow checking. */ +#define YYMAXDEPTH 20 +#define YYINITDEPTH YYMAXDEPTH /* Since the code of getdate.y is not included in the Emacs executable itself, there is no need to #define static in this file. Even if @@ -48,42 +60,24 @@ # undef static #endif -#include +#include #include #include #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) -#endif - -#ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#endif - /* Shift A right by B bits portably, by dividing A by 2**B and truncating towards minus infinity. A and B should be free of side effects, and B should be in the range 0 <= B <= INT_BITS - 2, where @@ -104,10 +98,27 @@ #define HOUR(x) ((x) * 60) +/* long_time_t is a signed integer type that contains all time_t values. */ +verify (TYPE_IS_INTEGER (time_t)); +#if TIME_T_FITS_IN_LONG_INT +typedef long int long_time_t; +#else +typedef time_t long_time_t; +#endif + +/* Lots of this code assumes time_t and time_t-like values fit into + long_time_t. */ +verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t) + && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t)); + +/* FIXME: It also assumes that signed integer overflow silently wraps around, + but this is not true any more with recent versions of GCC 4. */ + /* An integer value, and the number of digits in its textual representation. */ typedef struct { + bool negative; long int value; size_t digits; } textint; @@ -125,6 +136,25 @@ enum { MERam, MERpm, MER24 }; enum { BILLION = 1000000000, LOG10_BILLION = 9 }; +/* Relative times. */ +typedef struct +{ + /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */ + long int year; + long int month; + long int day; + long int hour; + long int minutes; + long_time_t seconds; + long int ns; +} relative_time; + +#if HAVE_COMPOUND_LITERALS +# define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 }) +#else +static relative_time const RELATIVE_TIME_0; +#endif + /* Information passed to and from the parser. */ typedef struct { @@ -155,20 +185,15 @@ typedef struct struct timespec seconds; /* includes nanoseconds */ /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */ - long int rel_year; - long int rel_month; - long int rel_day; - long int rel_hour; - long int rel_minutes; - long int rel_seconds; - long int rel_ns; - - /* Counts of nonterminals of various flavors parsed so far. */ + relative_time rel; + + /* Presence or counts of nonterminals of various flavors parsed so far. */ bool timespec_seen; + bool rels_seen; size_t dates_seen; size_t days_seen; size_t local_zones_seen; - size_t rels_seen; + size_t dsts_seen; size_t times_seen; size_t zones_seen; @@ -178,7 +203,72 @@ 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 (parser_control *, 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; + } + } +} + +/* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */ +static void +apply_relative_time (parser_control *pc, relative_time rel, int factor) +{ + pc->rel.ns += factor * rel.ns; + pc->rel.seconds += factor * rel.seconds; + pc->rel.minutes += factor * rel.minutes; + pc->rel.hour += factor * rel.hour; + pc->rel.day += factor * rel.day; + pc->rel.month += factor * rel.month; + pc->rel.year += factor * rel.year; + pc->rels_seen = true; +} + +/* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */ +static void +set_hhmmss (parser_control *pc, long int hour, long int minutes, + time_t sec, long int nsec) +{ + pc->hour = hour; + pc->minutes = minutes; + pc->seconds.tv_sec = sec; + pc->seconds.tv_nsec = nsec; +} %} @@ -188,27 +278,33 @@ static int yyerror (parser_control *, char *); %parse-param { parser_control *pc } %lex-param { parser_control *pc } -/* This grammar has 13 shift/reduce conflicts. */ -%expect 13 +/* This grammar has 20 shift/reduce conflicts. */ +%expect 20 %union { long int intval; textint textintval; struct timespec timespec; + relative_time rel; } %token tAGO tDST -%token tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN -%token tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE +%token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT +%token tDAY_UNIT tDAY_SHIFT + +%token tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN +%token tMONTH tORDINAL tZONE %token tSNUMBER tUNUMBER %token tSDECIMAL_NUMBER tUDECIMAL_NUMBER -%type o_merid +%type o_colon_minutes o_merid %type seconds signed_seconds unsigned_seconds +%type relunit relunit_snumber dayshift + %% spec: @@ -241,65 +337,63 @@ item: | day { pc->days_seen++; } | rel - { pc->rels_seen++; } | number + | hybrid ; time: tUNUMBER tMERIDIAN { - pc->hour = $1.value; - pc->minutes = 0; - pc->seconds.tv_sec = 0; - pc->seconds.tv_nsec = 0; + set_hhmmss (pc, $1.value, 0, 0, 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; + set_hhmmss (pc, $1.value, $3.value, 0, 0); pc->meridian = $4; } - | tUNUMBER ':' tUNUMBER tSNUMBER + | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes { - pc->hour = $1.value; - pc->minutes = $3.value; - pc->seconds.tv_sec = 0; - pc->seconds.tv_nsec = 0; + set_hhmmss (pc, $1.value, $3.value, 0, 0); pc->meridian = MER24; pc->zones_seen++; - pc->time_zone = $4.value % 100 + ($4.value / 100) * 60; + pc->time_zone = time_zone_hhmm (pc, $4, $5); } | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid { - pc->hour = $1.value; - pc->minutes = $3.value; - pc->seconds = $5; + set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); pc->meridian = $6; } - | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER + | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes { - pc->hour = $1.value; - pc->minutes = $3.value; - pc->seconds = $5; + set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); pc->meridian = MER24; pc->zones_seen++; - pc->time_zone = $6.value % 100 + ($6.value / 100) * 60; + pc->time_zone = time_zone_hhmm (pc, $6, $7); } ; local_zone: tLOCAL_ZONE - { pc->local_isdst = $1; } + { + pc->local_isdst = $1; + pc->dsts_seen += (0 < $1); + } | tLOCAL_ZONE tDST - { pc->local_isdst = $1 < 0 ? 1 : $1 + 1; } + { + pc->local_isdst = 1; + pc->dsts_seen += (0 < $1) + 1; + } ; zone: tZONE { pc->time_zone = $1; } + | tZONE relunit_snumber + { pc->time_zone = $1; + apply_relative_time (pc, $2, 1); } + | tZONE tSNUMBER o_colon_minutes + { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); } | tDAYZONE { pc->time_zone = $1 + 60; } | tZONE tDST @@ -309,14 +403,19 @@ zone: day: tDAY { - pc->day_ordinal = 1; + pc->day_ordinal = 0; pc->day_number = $1; } | tDAY ',' { - pc->day_ordinal = 1; + pc->day_ordinal = 0; pc->day_number = $1; } + | tORDINAL tDAY + { + pc->day_ordinal = $1; + pc->day_number = $2; + } | tUNUMBER tDAY { pc->day_ordinal = $1.value; @@ -399,59 +498,75 @@ date: 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; - } + { apply_relative_time (pc, $1, -1); } | relunit + { apply_relative_time (pc, $1, 1); } + | dayshift + { apply_relative_time (pc, $1, 1); } ; relunit: - tUNUMBER tYEAR_UNIT - { pc->rel_year += $1.value * $2; } - | tSNUMBER tYEAR_UNIT - { pc->rel_year += $1.value * $2; } + tORDINAL tYEAR_UNIT + { $$ = RELATIVE_TIME_0; $$.year = $1; } + | tUNUMBER tYEAR_UNIT + { $$ = RELATIVE_TIME_0; $$.year = $1.value; } | tYEAR_UNIT - { pc->rel_year += $1; } + { $$ = RELATIVE_TIME_0; $$.year = 1; } + | tORDINAL tMONTH_UNIT + { $$ = RELATIVE_TIME_0; $$.month = $1; } | tUNUMBER tMONTH_UNIT - { pc->rel_month += $1.value * $2; } - | tSNUMBER tMONTH_UNIT - { pc->rel_month += $1.value * $2; } + { $$ = RELATIVE_TIME_0; $$.month = $1.value; } | tMONTH_UNIT - { pc->rel_month += $1; } + { $$ = RELATIVE_TIME_0; $$.month = 1; } + | tORDINAL tDAY_UNIT + { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; } | tUNUMBER tDAY_UNIT - { pc->rel_day += $1.value * $2; } - | tSNUMBER tDAY_UNIT - { pc->rel_day += $1.value * $2; } + { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; } | tDAY_UNIT - { pc->rel_day += $1; } + { $$ = RELATIVE_TIME_0; $$.day = $1; } + | tORDINAL tHOUR_UNIT + { $$ = RELATIVE_TIME_0; $$.hour = $1; } | tUNUMBER tHOUR_UNIT - { pc->rel_hour += $1.value * $2; } - | tSNUMBER tHOUR_UNIT - { pc->rel_hour += $1.value * $2; } + { $$ = RELATIVE_TIME_0; $$.hour = $1.value; } | tHOUR_UNIT - { pc->rel_hour += $1; } + { $$ = RELATIVE_TIME_0; $$.hour = 1; } + | tORDINAL tMINUTE_UNIT + { $$ = RELATIVE_TIME_0; $$.minutes = $1; } | tUNUMBER tMINUTE_UNIT - { pc->rel_minutes += $1.value * $2; } - | tSNUMBER tMINUTE_UNIT - { pc->rel_minutes += $1.value * $2; } + { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; } | tMINUTE_UNIT - { pc->rel_minutes += $1; } + { $$ = RELATIVE_TIME_0; $$.minutes = 1; } + | tORDINAL tSEC_UNIT + { $$ = RELATIVE_TIME_0; $$.seconds = $1; } | tUNUMBER tSEC_UNIT - { pc->rel_seconds += $1.value * $2; } - | tSNUMBER tSEC_UNIT - { pc->rel_seconds += $1.value * $2; } + { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; } | tSDECIMAL_NUMBER tSEC_UNIT - { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; } + { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; } | tUDECIMAL_NUMBER tSEC_UNIT - { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; } + { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; } | tSEC_UNIT - { pc->rel_seconds += $1; } + { $$ = RELATIVE_TIME_0; $$.seconds = 1; } + | relunit_snumber + ; + +relunit_snumber: + tSNUMBER tYEAR_UNIT + { $$ = RELATIVE_TIME_0; $$.year = $1.value; } + | tSNUMBER tMONTH_UNIT + { $$ = RELATIVE_TIME_0; $$.month = $1.value; } + | tSNUMBER tDAY_UNIT + { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; } + | tSNUMBER tHOUR_UNIT + { $$ = RELATIVE_TIME_0; $$.hour = $1.value; } + | tSNUMBER tMINUTE_UNIT + { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; } + | tSNUMBER tSEC_UNIT + { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; } + ; + +dayshift: + tDAY_SHIFT + { $$ = RELATIVE_TIME_0; $$.day = $1; } ; seconds: signed_seconds | unsigned_seconds; @@ -470,41 +585,26 @@ unsigned_seconds: number: tUNUMBER + { digits_to_date_time (pc, $1); } + ; + +hybrid: + tUNUMBER relunit_snumber { - 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; - } - 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); + apply_relative_time (pc, $2, 1); } ; +o_colon_minutes: + /* empty */ + { $$ = -1; } + | ':' tUNUMBER + { $$ = $2.value; } + ; + o_merid: /* empty */ { $$ = MER24; } @@ -575,29 +675,40 @@ static table const time_units_table[] = /* Assorted relative-time words. */ static table const relative_time_table[] = { - { "TOMORROW", tDAY_UNIT, 1 }, - { "YESTERDAY",tDAY_UNIT, -1 }, - { "TODAY", tDAY_UNIT, 0 }, - { "NOW", tDAY_UNIT, 0 }, - { "LAST", tUNUMBER, -1 }, - { "THIS", tUNUMBER, 0 }, - { "NEXT", tUNUMBER, 1 }, - { "FIRST", tUNUMBER, 1 }, -/*{ "SECOND", tUNUMBER, 2 }, */ - { "THIRD", tUNUMBER, 3 }, - { "FOURTH", tUNUMBER, 4 }, - { "FIFTH", tUNUMBER, 5 }, - { "SIXTH", tUNUMBER, 6 }, - { "SEVENTH", tUNUMBER, 7 }, - { "EIGHTH", tUNUMBER, 8 }, - { "NINTH", tUNUMBER, 9 }, - { "TENTH", tUNUMBER, 10 }, - { "ELEVENTH", tUNUMBER, 11 }, - { "TWELFTH", tUNUMBER, 12 }, + { "TOMORROW", tDAY_SHIFT, 1 }, + { "YESTERDAY",tDAY_SHIFT, -1 }, + { "TODAY", tDAY_SHIFT, 0 }, + { "NOW", tDAY_SHIFT, 0 }, + { "LAST", tORDINAL, -1 }, + { "THIS", tORDINAL, 0 }, + { "NEXT", tORDINAL, 1 }, + { "FIRST", tORDINAL, 1 }, +/*{ "SECOND", tORDINAL, 2 }, */ + { "THIRD", tORDINAL, 3 }, + { "FOURTH", tORDINAL, 4 }, + { "FIFTH", tORDINAL, 5 }, + { "SIXTH", tORDINAL, 6 }, + { "SEVENTH", tORDINAL, 7 }, + { "EIGHTH", tORDINAL, 8 }, + { "NINTH", tORDINAL, 9 }, + { "TENTH", tORDINAL, 10 }, + { "ELEVENTH", tORDINAL, 11 }, + { "TWELFTH", tORDINAL, 12 }, { "AGO", tAGO, 1 }, { NULL, 0, 0 } }; +/* The universal time zone table. These labels can be used even for + time stamps that would not otherwise be valid, e.g., GMT time + stamps in London during summer. */ +static table const universal_time_zone_table[] = +{ + { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */ + { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ + { "UTC", tZONE, HOUR ( 0) }, + { NULL, 0, 0 } +}; + /* The time zone table. This table is necessarily incomplete, as time zone abbreviations are ambiguous; e.g. Australians interpret "EST" as Eastern time in Australia, not as US Eastern Standard Time. @@ -605,9 +716,6 @@ static table const relative_time_table[] = abbreviations; use numeric abbreviations like `-0500' instead. */ static table const time_zone_table[] = { - { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */ - { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ - { "UTC", tZONE, HOUR ( 0) }, { "WET", tZONE, HOUR ( 0) }, /* Western European */ { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */ { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */ @@ -655,7 +763,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 */ - { NULL, 0, 0 } + { NULL, 0, 0 } }; /* Military time zone table. */ @@ -691,6 +799,37 @@ static table const military_table[] = +/* Convert a time zone expressed as HH:MM into an integer count of + minutes. If MM is negative, then S is of the form HHMM and needs + to be picked apart; otherwise, S is of the form HH. As specified in + http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow + only valid TZ range, and consider first two digits as hours, if no + minutes specified. */ + +static long int +time_zone_hhmm (parser_control *pc, textint s, long int mm) +{ + long int n_minutes; + + /* If the length of S is 1 or 2 and no minutes are specified, + interpret it as a number of hours. */ + if (s.digits <= 2 && mm < 0) + s.value *= 100; + + if (mm < 0) + n_minutes = (s.value / 100) * 60 + s.value % 100; + else + n_minutes = s.value * 60 + (s.negative ? -mm : mm); + + /* If the absolute number of minutes is larger than 24 hours, + arrange to reject it by incrementing pc->zones_seen. Thus, + we allow only values in the range UTC-24:00 to UTC+24:00. */ + if (24 * 60 < abs (n_minutes)) + pc->zones_seen++; + + return n_minutes; +} + static int to_hour (long int hours, int meridian) { @@ -727,7 +866,12 @@ lookup_zone (parser_control const *pc, char const *name) { table const *tp; - /* Try local zone abbreviations first; they're more likely to be right. */ + for (tp = universal_time_zone_table; tp->name; tp++) + if (strcmp (name, tp->name) == 0) + return tp; + + /* Try local zone abbreviations before those in time_zone_table, as + the local ones are more likely to be right. */ for (tp = pc->local_time_zone_table; tp->name; tp++) if (strcmp (name, tp->name) == 0) return tp; @@ -780,8 +924,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 = c_toupper (ch); } for (tp = meridian_table; tp->name; tp++) @@ -846,7 +989,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) for (;;) { - while (c = *pc->input, ISSPACE (c)) + while (c = *pc->input, c_isspace (c)) pc->input++; if (ISDIGIT (c) || c == '-' || c == '+') @@ -857,7 +1000,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; - while (c = *++pc->input, ISSPACE (c)) + while (c = *++pc->input, c_isspace (c)) continue; if (! ISDIGIT (c)) /* skip the '-' sign */ @@ -942,6 +1085,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) } else { + lvalp->textintval.negative = sign < 0; if (sign < 0) { lvalp->textintval.value = - value; @@ -960,7 +1104,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) } } - if (ISALPHA (c)) + if (c_isalpha (c)) { char buff[20]; char *p = buff; @@ -972,7 +1116,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) *p++ = c; c = *++pc->input; } - while (ISALPHA (c) || c == '.'); + while (c_isalpha (c) || c == '.'); *p = '\0'; tp = lookup_word (pc, buff); @@ -1001,7 +1145,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 _UNUSED_PARAMETER_, + char const *s _UNUSED_PARAMETER_) { return 0; } @@ -1073,8 +1218,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) if (! now) { - if (gettime (&gettime_buffer) != 0) - return false; + gettime (&gettime_buffer); now = &gettime_buffer; } @@ -1085,7 +1229,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, c_isspace (c)) p++; if (strncmp (p, "TZ=\"", 4) == 0) @@ -1123,10 +1267,16 @@ 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; - pc.year.digits = 4; + pc.year.digits = 0; pc.month = tmp->tm_mon + 1; pc.day = tmp->tm_mday; pc.hour = tmp->tm_hour; @@ -1136,19 +1286,14 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) tm.tm_isdst = tmp->tm_isdst; pc.meridian = MER24; - pc.rel_ns = 0; - pc.rel_seconds = 0; - pc.rel_minutes = 0; - pc.rel_hour = 0; - pc.rel_day = 0; - pc.rel_month = 0; - pc.rel_year = 0; + pc.rel = RELATIVE_TIME_0; pc.timespec_seen = false; + pc.rels_seen = false; pc.dates_seen = 0; pc.days_seen = 0; - pc.rels_seen = 0; pc.times_seen = 0; pc.local_zones_seen = 0; + pc.dsts_seen = 0; pc.zones_seen = 0; #if HAVE_STRUCT_TM_TM_ZONE @@ -1181,7 +1326,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; @@ -1216,9 +1361,8 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) *result = pc.seconds; else { - 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)) + if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen + | (pc.local_zones_seen + pc.zones_seen))) goto fail; tm.tm_year = to_year (pc.year) - TM_YEAR_BASE; @@ -1290,13 +1434,39 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) 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))); + + 7 * (pc.day_ordinal + - (0 < pc.day_ordinal + && tm.tm_wday != pc.day_number))); tm.tm_isdst = -1; Start = mktime (&tm); if (Start == (time_t) -1) goto fail; } + /* 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; + 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; + } + + /* The only "output" of this if-block is an updated Start value, + so this block must follow others that clobber Start. */ if (pc.zones_seen) { long int delta = pc.time_zone * 60; @@ -1316,24 +1486,6 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) 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 @@ -1342,27 +1494,29 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) 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 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; + long int d1 = 60 * 60 * pc.rel.hour; time_t t1 = t0 + d1; - long int d2 = 60 * pc.rel_minutes; + long int d2 = 60 * pc.rel.minutes; time_t t2 = t1 + d2; - long int d3 = pc.rel_seconds; - time_t t3 = t2 + d3; + long_time_t d3 = pc.rel.seconds; + long_time_t t3 = t2 + d3; long int d4 = (sum_ns - normalized_ns) / BILLION; - time_t t4 = t3 + d4; + long_time_t t4 = t3 + d4; + time_t t5 = t4; - if ((d1 / (60 * 60) ^ pc.rel_hour) - | (d2 / 60 ^ pc.rel_minutes) + 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))) + | ((t4 < t3) ^ (d4 < 0)) + | (t5 != t4)) goto fail; - result->tv_sec = t4; + result->tv_sec = t5; result->tv_nsec = normalized_ns; } }