X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fgetdate.y;h=94160b266b2cc887d6f56bdc93be2ce101354b15;hb=c13ca15f1a9efad6c85e2debf5bcc54db425d017;hp=591c7f0fcae1cbfc2c79b5a336c7e00f9f9e3aff;hpb=84005a980aeccf3c9ab68b01ac0964fa0b40212c;p=gnulib.git diff --git a/lib/getdate.y b/lib/getdate.y index 591c7f0fc..94160b266 100644 --- a/lib/getdate.y +++ b/lib/getdate.y @@ -1,8 +1,8 @@ %{ /* Parse a string into an internal time stamp. - Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software - Foundation, Inc. + 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 @@ -60,13 +60,12 @@ # undef static #endif -#include +#include #include #include #include #include -#include "setenv.h" #include "xalloc.h" @@ -79,16 +78,6 @@ of `digit' even when the host does not conform to POSIX. */ #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) -#ifndef __attribute__ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ -# define __attribute__(x) -# endif -#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 @@ -109,12 +98,21 @@ #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. */ +/* long_time_t is a signed integer type that contains all time_t values. */ verify (TYPE_IS_INTEGER (time_t)); -verify (LONG_MIN <= TYPE_MINIMUM (time_t) && TYPE_MAXIMUM (time_t) <= LONG_MAX); +#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. */ @@ -147,7 +145,7 @@ typedef struct long int day; long int hour; long int minutes; - long int seconds; + long_time_t seconds; long int ns; } relative_time; @@ -206,7 +204,71 @@ typedef struct union YYSTYPE; static int yylex (union YYSTYPE *, parser_control *); static int yyerror (parser_control const *, char const *); -static long int time_zone_hhmm (textint, long int); +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; +} %} @@ -230,7 +292,7 @@ static long int time_zone_hhmm (textint, long int); %token tAGO tDST %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT -%token tDAY_UNIT +%token tDAY_UNIT tDAY_SHIFT %token tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN %token tMONTH tORDINAL tZONE @@ -241,7 +303,7 @@ static long int time_zone_hhmm (textint, long int); %type o_colon_minutes o_merid %type seconds signed_seconds unsigned_seconds -%type relunit relunit_snumber +%type relunit relunit_snumber dayshift %% @@ -275,52 +337,39 @@ item: | day { pc->days_seen++; } | rel - { pc->rels_seen = true; } | 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 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 = time_zone_hhmm ($4, $5); + 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 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 = time_zone_hhmm ($6, $7); + pc->time_zone = time_zone_hhmm (pc, $6, $7); } ; @@ -342,16 +391,9 @@ zone: { pc->time_zone = $1; } | tZONE relunit_snumber { pc->time_zone = $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; } + apply_relative_time (pc, $2, 1); } | tZONE tSNUMBER o_colon_minutes - { pc->time_zone = $1 + time_zone_hhmm ($2, $3); } + { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); } | tDAYZONE { pc->time_zone = $1 + 60; } | tZONE tDST @@ -361,12 +403,12 @@ 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 @@ -456,25 +498,11 @@ date: rel: relunit tAGO - { - pc->rel.ns -= $1.ns; - pc->rel.seconds -= $1.seconds; - pc->rel.minutes -= $1.minutes; - pc->rel.hour -= $1.hour; - pc->rel.day -= $1.day; - pc->rel.month -= $1.month; - pc->rel.year -= $1.year; - } + { apply_relative_time (pc, $1, -1); } | relunit - { - pc->rel.ns += $1.ns; - pc->rel.seconds += $1.seconds; - pc->rel.minutes += $1.minutes; - pc->rel.hour += $1.hour; - pc->rel.day += $1.day; - pc->rel.month += $1.month; - pc->rel.year += $1.year; - } + { apply_relative_time (pc, $1, 1); } + | dayshift + { apply_relative_time (pc, $1, 1); } ; relunit: @@ -536,6 +564,11 @@ relunit_snumber: { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; } ; +dayshift: + tDAY_SHIFT + { $$ = RELATIVE_TIME_0; $$.day = $1; } + ; + seconds: signed_seconds | unsigned_seconds; signed_seconds: @@ -552,38 +585,16 @@ 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); + apply_relative_time (pc, $2, 1); } ; @@ -664,10 +675,10 @@ 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 }, + { "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 }, @@ -790,15 +801,33 @@ 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. */ + 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 (textint s, long int mm) +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) - return (s.value / 100) * 60 + s.value % 100; + n_minutes = (s.value / 100) * 60 + s.value % 100; else - return s.value * 60 + (s.negative ? -mm : mm); + 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 @@ -895,7 +924,7 @@ lookup_word (parser_control const *pc, char *word) for (p = word; *p; p++) { unsigned char ch = *p; - *p = toupper (ch); + *p = c_toupper (ch); } for (tp = meridian_table; tp->name; tp++) @@ -960,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 == '+') @@ -971,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 */ @@ -1075,7 +1104,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) } } - if (isalpha (c)) + if (c_isalpha (c)) { char buff[20]; char *p = buff; @@ -1087,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); @@ -1116,8 +1145,8 @@ yylex (YYSTYPE *lvalp, parser_control *pc) /* Do nothing if the parser reports an error. */ static int -yyerror (parser_control const *pc ATTRIBUTE_UNUSED, - char const *s ATTRIBUTE_UNUSED) +yyerror (parser_control const *pc _UNUSED_PARAMETER_, + char const *s _UNUSED_PARAMETER_) { return 0; } @@ -1200,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) @@ -1297,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; @@ -1405,32 +1434,15 @@ 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; } - if (pc.zones_seen) - { - long int delta = pc.time_zone * 60; - time_t t1; -#ifdef HAVE_TM_GMTOFF - delta -= tm.tm_gmtoff; -#else - 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)) - goto fail; /* time_t overflow */ - Start = t1; - } - /* Add relative date. */ if (pc.rel.year | pc.rel.month | pc.rel.day) { @@ -1453,6 +1465,27 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) 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; + time_t t1; +#ifdef HAVE_TM_GMTOFF + delta -= tm.tm_gmtoff; +#else + 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)) + goto fail; /* time_t overflow */ + Start = t1; + } + /* 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 @@ -1468,20 +1501,22 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) 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_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) | ((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; } }