X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=lib%2Fgetdate.y;h=3c4a04000f676a754aa9a15580d7a16fc76bb694;hb=1ffb4377a444fb7150030acfe843bc20cfba5d5a;hp=eb1fe7bef7ebc854208a95ec0a6598a2e6d14484;hpb=abdde50e1b20a7cf1199d14a29d09b1dc5423380;p=chaz%2Ftar diff --git a/lib/getdate.y b/lib/getdate.y index eb1fe7b..3c4a040 100644 --- a/lib/getdate.y +++ b/lib/getdate.y @@ -1,6 +1,6 @@ %{ /* Parse a string into an internal time stamp. - Copyright 1999 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2002, 2003 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 @@ -27,11 +27,10 @@ #ifdef HAVE_CONFIG_H # include -# ifdef HAVE_ALLOCA_H -# include -# endif #endif +#include + /* 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 the code were included in the Emacs executable, it probably @@ -63,10 +62,9 @@ - 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 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that - only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless - it's important to use the locale's definition of `digit' even when the - host does not conform to Posix. */ + POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to + ISDIGIT_LOCALE 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) (c) - '0' <= 9) #if STDC_HEADERS || HAVE_STRING_H @@ -82,10 +80,18 @@ #endif #define EPOCH_YEAR 1970 -#define TM_YEAR_ORIGIN 1900 +#define TM_YEAR_BASE 1900 #define HOUR(x) ((x) * 60) +/* An integer value, and the number of digits in its textual + representation. */ +typedef struct +{ + int value; + int digits; +} textint; + /* An entry in the lexical lookup table. */ typedef struct { @@ -98,8 +104,8 @@ typedef struct enum { MERam, MERpm, MER24 }; /* Information passed to and from the parser. */ -struct parser_control -{ +typedef struct +{ /* The input string remaining to be parsed. */ const char *input; @@ -119,7 +125,7 @@ struct parser_control int meridian; /* Gregorian year, month, day, hour, minutes, and seconds. */ - int year; + textint year; int month; int day; int hour; @@ -144,12 +150,11 @@ struct parser_control /* Table of local time zone abbrevations, terminated by a null entry. */ table local_time_zone_table[3]; -}; +} parser_control; -#define PC (* (struct parser_control *) parm) +#define PC (* (parser_control *) parm) #define YYLEX_PARAM parm #define YYPARSE_PARAM parm -#define YYSTYPE int static int yyerror (); static int yylex (); @@ -162,9 +167,20 @@ static int yylex (); /* This grammar has 13 shift/reduce conflicts. */ %expect 13 -%token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID -%token tLOCAL_ZONE tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT -%token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE +%union +{ + int intval; + textint textintval; +} + +%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 tSNUMBER tUNUMBER + +%type o_merid %% @@ -192,41 +208,41 @@ item: time: tUNUMBER tMERIDIAN { - PC.hour = $1; + PC.hour = $1.value; PC.minutes = 0; PC.seconds = 0; PC.meridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { - PC.hour = $1; - PC.minutes = $3; + PC.hour = $1.value; + PC.minutes = $3.value; PC.seconds = 0; PC.meridian = $4; } | tUNUMBER ':' tUNUMBER tSNUMBER { - PC.hour = $1; - PC.minutes = $3; + PC.hour = $1.value; + PC.minutes = $3.value; PC.meridian = MER24; PC.zones_seen++; - PC.time_zone = $4 % 100 + ($4 / 100) * 60; + PC.time_zone = $4.value % 100 + ($4.value / 100) * 60; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { - PC.hour = $1; - PC.minutes = $3; - PC.seconds = $5; + PC.hour = $1.value; + PC.minutes = $3.value; + PC.seconds = $5.value; PC.meridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { - PC.hour = $1; - PC.minutes = $3; - PC.seconds = $5; + PC.hour = $1.value; + PC.minutes = $3.value; + PC.seconds = $5.value; PC.meridian = MER24; PC.zones_seen++; - PC.time_zone = $6 % 100 + ($6 / 100) * 60; + PC.time_zone = $6.value % 100 + ($6.value / 100) * 60; } ; @@ -259,7 +275,7 @@ day: } | tUNUMBER tDAY { - PC.day_ordinal = $1; + PC.day_ordinal = $1.value; PC.day_number = $2; } ; @@ -267,25 +283,26 @@ day: date: tUNUMBER '/' tUNUMBER { - PC.month = $1; - PC.day = $3; + PC.month = $1.value; + PC.day = $3.value; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { - /* Interpret as YYYY/MM/DD if 1000 <= $1, otherwise as MM/DD/YY. + /* Interpret as YYYY/MM/DD if the first value has 4 or more digits, + otherwise as MM/DD/YY. The goal in recognizing YYYY/MM/DD is solely to support legacy machine-generated dates like those in an RCS log listing. If you want portability, use the ISO 8601 format. */ - if (1000 <= $1) + if (4 <= $1.digits) { PC.year = $1; - PC.month = $3; - PC.day = $5; + PC.month = $3.value; + PC.day = $5.value; } else { - PC.month = $1; - PC.day = $3; + PC.month = $1.value; + PC.day = $3.value; PC.year = $5; } } @@ -293,36 +310,37 @@ date: { /* ISO 8601 format. YYYY-MM-DD. */ PC.year = $1; - PC.month = -$2; - PC.day = -$3; + PC.month = -$2.value; + PC.day = -$3.value; } | tUNUMBER tMONTH tSNUMBER { /* e.g. 17-JUN-1992. */ - PC.day = $1; + PC.day = $1.value; PC.month = $2; - PC.year = -$3; + PC.year.value = -$3.value; + PC.year.digits = $3.digits; } | tMONTH tUNUMBER { PC.month = $1; - PC.day = $2; + PC.day = $2.value; } | tMONTH tUNUMBER ',' tUNUMBER { PC.month = $1; - PC.day = $2; + PC.day = $2.value; PC.year = $4; } | tUNUMBER tMONTH { + PC.day = $1.value; PC.month = $2; - PC.day = $1; } | tUNUMBER tMONTH tUNUMBER { + PC.day = $1.value; PC.month = $2; - PC.day = $1; PC.year = $3; } ; @@ -342,39 +360,39 @@ rel: relunit: tUNUMBER tYEAR_UNIT - { PC.rel_year += $1 * $2; } + { PC.rel_year += $1.value * $2; } | tSNUMBER tYEAR_UNIT - { PC.rel_year += $1 * $2; } + { PC.rel_year += $1.value * $2; } | tYEAR_UNIT { PC.rel_year += $1; } | tUNUMBER tMONTH_UNIT - { PC.rel_month += $1 * $2; } + { PC.rel_month += $1.value * $2; } | tSNUMBER tMONTH_UNIT - { PC.rel_month += $1 * $2; } + { PC.rel_month += $1.value * $2; } | tMONTH_UNIT { PC.rel_month += $1; } | tUNUMBER tDAY_UNIT - { PC.rel_day += $1 * $2; } + { PC.rel_day += $1.value * $2; } | tSNUMBER tDAY_UNIT - { PC.rel_day += $1 * $2; } + { PC.rel_day += $1.value * $2; } | tDAY_UNIT { PC.rel_day += $1; } | tUNUMBER tHOUR_UNIT - { PC.rel_hour += $1 * $2; } + { PC.rel_hour += $1.value * $2; } | tSNUMBER tHOUR_UNIT - { PC.rel_hour += $1 * $2; } + { PC.rel_hour += $1.value * $2; } | tHOUR_UNIT { PC.rel_hour += $1; } | tUNUMBER tMINUTE_UNIT - { PC.rel_minutes += $1 * $2; } + { PC.rel_minutes += $1.value * $2; } | tSNUMBER tMINUTE_UNIT - { PC.rel_minutes += $1 * $2; } + { PC.rel_minutes += $1.value * $2; } | tMINUTE_UNIT { PC.rel_minutes += $1; } | tUNUMBER tSEC_UNIT - { PC.rel_seconds += $1 * $2; } + { PC.rel_seconds += $1.value * $2; } | tSNUMBER tSEC_UNIT - { PC.rel_seconds += $1 * $2; } + { PC.rel_seconds += $1.value * $2; } | tSEC_UNIT { PC.rel_seconds += $1; } ; @@ -382,29 +400,31 @@ relunit: number: tUNUMBER { - if (PC.dates_seen && ! PC.rels_seen && (PC.times_seen || 100 <= $1)) + if (PC.dates_seen + && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits)) PC.year = $1; else { - if (10000 < $1) + if (4 < $1.digits) { PC.dates_seen++; - PC.day = $1 % 100; - PC.month = ($1 / 100) % 100; - PC.year = $1 / 10000; + 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 < 100) + if ($1.digits <= 2) { - PC.hour = $1; + PC.hour = $1.value; PC.minutes = 0; } else { - PC.hour = $1 / 100; - PC.minutes = $1 % 100; + PC.hour = $1.value / 100; + PC.minutes = $1.value % 100; } PC.seconds = 0; PC.meridian = MER24; @@ -426,6 +446,7 @@ o_merid: may define-away `const'. We want the prototype for get_date to have the same signature as the function definition. */ #include "getdate.h" +#include "unlocked-io.h" #ifndef gmtime struct tm *gmtime (); @@ -632,23 +653,23 @@ to_hour (int hours, int meridian) } static int -to_year (int year) +to_year (textint textyear) { + int year = textyear.value; + if (year < 0) year = -year; /* XPG4 suggests that years 00-68 map to 2000-2068, and years 69-99 map to 1969-1999. */ - if (year < 69) - year += 2000; - else if (year < 100) - year += 1900; + if (textyear.digits == 2) + year += year < 69 ? 2000 : 1900; return year; } static table const * -lookup_zone (struct parser_control const *pc, char const *name) +lookup_zone (parser_control const *pc, char const *name) { table const *tp; @@ -664,29 +685,35 @@ lookup_zone (struct parser_control const *pc, char const *name) return 0; } -/* Yield A - B, measured in seconds. */ +#if ! HAVE_TM_GMTOFF +/* Yield the difference between *A and *B, + measured in seconds, ignoring leap seconds. + The body of this function is taken directly from the GNU C Library; + see src/strftime.c. */ static int -difftm (struct tm *a, struct tm *b) +tm_diff (struct tm const *a, struct tm const *b) { - int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); - int by = b->tm_year + (TM_YEAR_ORIGIN - 1); - int days = ( - /* difference in day of year */ - a->tm_yday - b->tm_yday - /* + intervening leap days */ - + ((ay >> 2) - (by >> 2)) - - (ay / 100 - by / 100) - + ((ay / 100 >> 2) - (by / 100 >> 2)) - /* + difference in years * 365 */ - + (int) (ay - by) * 365 - ); + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid int overflow in leap day calculations, + but it's OK to assume that A and B are close to each other. */ + int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3); + int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3); + int a100 = a4 / 25 - (a4 % 25 < 0); + int b100 = b4 / 25 - (b4 % 25 < 0); + int a400 = a100 >> 2; + int b400 = b100 >> 2; + int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); + int years = a->tm_year - b->tm_year; + int days = (365 * years + intervening_leap_days + + (a->tm_yday - b->tm_yday)); return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + (a->tm_min - b->tm_min)) + (a->tm_sec - b->tm_sec)); } +#endif /* ! HAVE_TM_GMTOFF */ static table const * -lookup_word (struct parser_control const *pc, char *word) +lookup_word (parser_control const *pc, char *word) { char *p; char *q; @@ -755,7 +782,7 @@ lookup_word (struct parser_control const *pc, char *word) } static int -yylex (YYSTYPE *lvalp, struct parser_control *pc) +yylex (YYSTYPE *lvalp, parser_control *pc) { unsigned char c; int count; @@ -767,21 +794,30 @@ yylex (YYSTYPE *lvalp, struct parser_control *pc) if (ISDIGIT (c) || c == '-' || c == '+') { + char const *p; int sign; + int value; if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; - if (! ISDIGIT (*++pc->input)) + c = *++pc->input; + if (! ISDIGIT (c)) /* skip the '-' sign */ continue; } else sign = 0; - for (*lvalp = 0; ISDIGIT (c = *pc->input++);) - *lvalp = 10 * *lvalp + (c - '0'); - pc->input--; - if (sign < 0) - *lvalp = - *lvalp; + p = pc->input; + value = 0; + do + { + value = 10 * value + c - '0'; + c = *++p; + } + while (ISDIGIT (c)); + lvalp->textintval.value = sign < 0 ? -value : value; + lvalp->textintval.digits = p - pc->input; + pc->input = p; return sign ? tSNUMBER : tUNUMBER; } @@ -802,8 +838,8 @@ yylex (YYSTYPE *lvalp, struct parser_control *pc) *p = '\0'; tp = lookup_word (pc, buff); if (! tp) - return tID; - *lvalp = tp->value; + return '?'; + lvalp->intval = tp->value; return tp->type; } @@ -842,13 +878,14 @@ get_date (const char *p, const time_t *now) struct tm *tmp = localtime (&Start); struct tm tm; struct tm tm0; - struct parser_control pc; + parser_control pc; if (! tmp) return -1; pc.input = p; - pc.year = tmp->tm_year + TM_YEAR_ORIGIN; + pc.year.value = tmp->tm_year + TM_YEAR_BASE; + pc.year.digits = 4; pc.month = tmp->tm_mon + 1; pc.day = tmp->tm_mday; pc.hour = tmp->tm_hour; @@ -870,7 +907,7 @@ get_date (const char *p, const time_t *now) pc.local_zones_seen = 0; pc.zones_seen = 0; -#if HAVE_TM_ZONE +#if HAVE_STRUCT_TM_TM_ZONE 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; @@ -934,7 +971,7 @@ get_date (const char *p, const time_t *now) || (pc.local_zones_seen && 1 < pc.local_isdst)) return -1; - tm.tm_year = to_year (pc.year) - TM_YEAR_ORIGIN + pc.rel_year; + 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)) @@ -949,13 +986,11 @@ get_date (const char *p, const time_t *now) { tm.tm_hour = tm.tm_min = tm.tm_sec = 0; } - tm.tm_hour += pc.rel_hour; - tm.tm_min += pc.rel_minutes; - tm.tm_sec += pc.rel_seconds; /* 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) + if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day + | pc.rel_month | pc.rel_year) tm.tm_isdst = -1; /* But if the input explicitly specifies local time with or without @@ -981,7 +1016,7 @@ get_date (const char *p, const time_t *now) if (pc.zones_seen) { tm = tm0; - if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_ORIGIN) + if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE) { tm.tm_mday++; pc.time_zone += 24 * 60; @@ -1002,6 +1037,7 @@ get_date (const char *p, const time_t *now) { 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 Start; @@ -1009,16 +1045,43 @@ get_date (const char *p, const time_t *now) if (pc.zones_seen) { - int delta; + int delta = pc.time_zone * 60; +#ifdef HAVE_TM_GMTOFF + delta -= tm.tm_gmtoff; +#else struct tm *gmt = gmtime (&Start); if (! gmt) return -1; - delta = pc.time_zone * 60 + difftm (gmt, &tm); + delta -= tm_diff (&tm, gmt); +#endif if ((Start < Start - delta) != (delta < 0)) return -1; /* time_t overflow */ Start -= delta; } + /* 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. */ + { + time_t t0 = Start; + long d1 = 60 * 60 * (long) pc.rel_hour; + time_t t1 = t0 + d1; + long d2 = 60 * (long) pc.rel_minutes; + time_t t2 = t1 + d2; + int d3 = pc.rel_seconds; + time_t t3 = t2 + d3; + if ((d1 / (60 * 60) ^ pc.rel_hour) + | (d2 / 60 ^ pc.rel_minutes) + | ((t0 + d1 < t0) ^ (d1 < 0)) + | ((t1 + d2 < t1) ^ (d2 < 0)) + | ((t2 + d3 < t2) ^ (d3 < 0))) + return -1; + Start = t3; + } + return Start; }