LCOV - code coverage report
Current view: top level - source3/modules - getdate.y (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 0 327 0.0 %
Date: 2024-06-13 04:01:37 Functions: 0 8 0.0 %

          Line data    Source code
       1             : %{
       2             : /* Parse a string into an internal time stamp.
       3             :    Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
       4             : 
       5             :    This program is free software; you can redistribute it and/or modify
       6             :    it under the terms of the GNU General Public License as published by
       7             :    the Free Software Foundation; either version 2, or (at your option)
       8             :    any later version.
       9             : 
      10             :    This program is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :    GNU General Public License for more details.
      14             : 
      15             :    You should have received a copy of the GNU General Public License
      16             :    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
      17             : 
      18             : /* Originally written by Steven M. Bellovin <smb@research.att.com> while
      19             :    at the University of North Carolina at Chapel Hill.  Later tweaked by
      20             :    a couple of people on Usenet.  Completely overhauled by Rich $alz
      21             :    <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
      22             : 
      23             :    Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
      24             :    the right thing about local DST.  Unlike previous versions, this
      25             :    version is reentrant.  */
      26             : 
      27             : #ifdef HAVE_CONFIG_H
      28             : # include <config.h>
      29             : # ifdef HAVE_ALLOCA_H
      30             : #  include <alloca.h>
      31             : # endif
      32             : #endif
      33             : 
      34             : /* Since the code of getdate.y is not included in the Emacs executable
      35             :    itself, there is no need to #define static in this file.  Even if
      36             :    the code were included in the Emacs executable, it probably
      37             :    wouldn't do any harm to #undef it here; this will only cause
      38             :    problems if we try to write to a static variable, which I don't
      39             :    think this code needs to do.  */
      40             : #ifdef emacs
      41             : # undef static
      42             : #endif
      43             : 
      44             : #include <ctype.h>
      45             : #include <string.h>
      46             : 
      47             : #ifdef HAVE_STDLIB_H
      48             : # include <stdlib.h> /* for `free'; used by Bison 1.27 */
      49             : #endif
      50             : 
      51             : #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
      52             : # define IN_CTYPE_DOMAIN(c) 1
      53             : #else
      54             : # define IN_CTYPE_DOMAIN(c) isascii (c)
      55             : #endif
      56             : 
      57             : #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
      58             : #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
      59             : #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
      60             : #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
      61             : 
      62             : /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
      63             :    - Its arg may be any int or unsigned int; it need not be an unsigned char.
      64             :    - It's guaranteed to evaluate its argument exactly once.
      65             :    - It's typically faster.
      66             :    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
      67             :    ISDIGIT_LOCALE unless it's important to use the locale's definition
      68             :    of `digit' even when the host does not conform to POSIX.  */
      69             : #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
      70             : 
      71             : #if STDC_HEADERS || HAVE_STRING_H
      72             : # include <string.h>
      73             : #endif
      74             : 
      75             : #ifndef HAVE___ATTRIBUTE__
      76             : # define __attribute__(x)
      77             : #endif
      78             : 
      79             : #ifndef ATTRIBUTE_UNUSED
      80             : # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
      81             : #endif
      82             : 
      83             : #define EPOCH_YEAR 1970
      84             : #define TM_YEAR_BASE 1900
      85             : 
      86             : #define HOUR(x) ((x) * 60)
      87             : 
      88             : /* An integer value, and the number of digits in its textual
      89             :    representation.  */
      90             : typedef struct
      91             : {
      92             :   int value;
      93             :   int digits;
      94             : } textint;
      95             : 
      96             : /* An entry in the lexical lookup table.  */
      97             : typedef struct
      98             : {
      99             :   char const *name;
     100             :   int type;
     101             :   int value;
     102             : } table;
     103             : 
     104             : /* Meridian: am, pm, or 24-hour style.  */
     105             : enum { MERam, MERpm, MER24 };
     106             : 
     107             : /* Information passed to and from the parser.  */
     108             : struct parser_control
     109             : {
     110             :   /* The input string remaining to be parsed. */
     111             :   const char *input;
     112             : 
     113             :   /* N, if this is the Nth Tuesday.  */
     114             :   int day_ordinal;
     115             : 
     116             :   /* Day of week; Sunday is 0.  */
     117             :   int day_number;
     118             : 
     119             :   /* tm_isdst flag for the local zone.  */
     120             :   int local_isdst;
     121             : 
     122             :   /* Time zone, in minutes east of UTC.  */
     123             :   int time_zone;
     124             : 
     125             :   /* Style used for time.  */
     126             :   int meridian;
     127             : 
     128             :   /* Gregorian year, month, day, hour, minutes, and seconds.  */
     129             :   textint year;
     130             :   int month;
     131             :   int day;
     132             :   int hour;
     133             :   int minutes;
     134             :   int seconds;
     135             : 
     136             :   /* Relative year, month, day, hour, minutes, and seconds.  */
     137             :   int rel_year;
     138             :   int rel_month;
     139             :   int rel_day;
     140             :   int rel_hour;
     141             :   int rel_minutes;
     142             :   int rel_seconds;
     143             : 
     144             :   /* Counts of nonterminals of various flavors parsed so far.  */
     145             :   int dates_seen;
     146             :   int days_seen;
     147             :   int local_zones_seen;
     148             :   int rels_seen;
     149             :   int times_seen;
     150             :   int zones_seen;
     151             : 
     152             :   /* Table of local time zone abbrevations, terminated by a null entry.  */
     153             :   table local_time_zone_table[3];
     154             : };
     155             : 
     156             : %}
     157             : 
     158             : %lex-param     {struct parser_control *pc}
     159             : %parse-param   {struct parser_control *pc}
     160             : 
     161             : /* We want a reentrant parser.  */
     162             : %pure-parser
     163             : 
     164             : /* This grammar has 13 shift/reduce conflicts. */
     165             : %expect 13
     166             : 
     167             : %union
     168             : {
     169             :   int intval;
     170             :   textint textintval;
     171             : }
     172             : 
     173             : %{
     174             : 
     175             : static int yyerror(struct parser_control *, const char *);
     176             : static int yylex(YYSTYPE *, struct parser_control *);
     177             : 
     178             : %}
     179             : 
     180             : %token tAGO tDST
     181             : 
     182             : %token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
     183             : %token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE
     184             : 
     185             : %token <textintval> tSNUMBER tUNUMBER
     186             : 
     187             : %type <intval> o_merid
     188             : 
     189             : %%
     190             : 
     191             : spec:
     192             :     /* empty */
     193             :   | spec item
     194             :   ;
     195             : 
     196             : item:
     197             :     time
     198           0 :       { pc->times_seen++; }
     199             :   | local_zone
     200           0 :       { pc->local_zones_seen++; }
     201             :   | zone
     202           0 :       { pc->zones_seen++; }
     203             :   | date
     204           0 :       { pc->dates_seen++; }
     205             :   | day
     206           0 :       { pc->days_seen++; }
     207             :   | rel
     208           0 :       { pc->rels_seen++; }
     209             :   | number
     210             :   ;
     211             : 
     212             : time:
     213             :     tUNUMBER tMERIDIAN
     214             :       {
     215           0 :         pc->hour = $1.value;
     216           0 :         pc->minutes = 0;
     217           0 :         pc->seconds = 0;
     218           0 :         pc->meridian = $2;
     219             :       }
     220             :   | tUNUMBER ':' tUNUMBER o_merid
     221             :       {
     222           0 :         pc->hour = $1.value;
     223           0 :         pc->minutes = $3.value;
     224           0 :         pc->seconds = 0;
     225           0 :         pc->meridian = $4;
     226             :       }
     227             :   | tUNUMBER ':' tUNUMBER tSNUMBER
     228             :       {
     229           0 :         pc->hour = $1.value;
     230           0 :         pc->minutes = $3.value;
     231           0 :         pc->meridian = MER24;
     232           0 :         pc->zones_seen++;
     233           0 :         pc->time_zone = $4.value % 100 + ($4.value / 100) * 60;
     234             :       }
     235             :   | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid
     236             :       {
     237           0 :         pc->hour = $1.value;
     238           0 :         pc->minutes = $3.value;
     239           0 :         pc->seconds = $5.value;
     240           0 :         pc->meridian = $6;
     241             :       }
     242             :   | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER
     243             :       {
     244           0 :         pc->hour = $1.value;
     245           0 :         pc->minutes = $3.value;
     246           0 :         pc->seconds = $5.value;
     247           0 :         pc->meridian = MER24;
     248           0 :         pc->zones_seen++;
     249           0 :         pc->time_zone = $6.value % 100 + ($6.value / 100) * 60;
     250             :       }
     251             :   ;
     252             : 
     253             : local_zone:
     254             :     tLOCAL_ZONE
     255           0 :       { pc->local_isdst = $1; }
     256             :   | tLOCAL_ZONE tDST
     257           0 :       { pc->local_isdst = $1 < 0 ? 1 : $1 + 1; }
     258             :   ;
     259             : 
     260             : zone:
     261             :     tZONE
     262           0 :       { pc->time_zone = $1; }
     263             :   | tDAYZONE
     264           0 :       { pc->time_zone = $1 + 60; }
     265             :   | tZONE tDST
     266           0 :       { pc->time_zone = $1 + 60; }
     267             :   ;
     268             : 
     269             : day:
     270             :     tDAY
     271             :       {
     272           0 :         pc->day_ordinal = 1;
     273           0 :         pc->day_number = $1;
     274             :       }
     275             :   | tDAY ','
     276             :       {
     277           0 :         pc->day_ordinal = 1;
     278           0 :         pc->day_number = $1;
     279             :       }
     280             :   | tUNUMBER tDAY
     281             :       {
     282           0 :         pc->day_ordinal = $1.value;
     283           0 :         pc->day_number = $2;
     284             :       }
     285             :   ;
     286             : 
     287             : date:
     288             :     tUNUMBER '/' tUNUMBER
     289             :       {
     290           0 :         pc->month = $1.value;
     291           0 :         pc->day = $3.value;
     292             :       }
     293             :   | tUNUMBER '/' tUNUMBER '/' tUNUMBER
     294             :       {
     295             :         /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
     296             :            otherwise as MM/DD/YY.
     297             :            The goal in recognizing YYYY/MM/DD is solely to support legacy
     298             :            machine-generated dates like those in an RCS log listing.  If
     299             :            you want portability, use the ISO 8601 format.  */
     300           0 :         if (4 <= $1.digits)
     301             :           {
     302           0 :             pc->year = $1;
     303           0 :             pc->month = $3.value;
     304           0 :             pc->day = $5.value;
     305             :           }
     306             :         else
     307             :           {
     308           0 :             pc->month = $1.value;
     309           0 :             pc->day = $3.value;
     310           0 :             pc->year = $5;
     311             :           }
     312             :       }
     313             :   | tUNUMBER tSNUMBER tSNUMBER
     314             :       {
     315             :         /* ISO 8601 format.  YYYY-MM-DD.  */
     316           0 :         pc->year = $1;
     317           0 :         pc->month = -$2.value;
     318           0 :         pc->day = -$3.value;
     319             :       }
     320             :   | tUNUMBER tMONTH tSNUMBER
     321             :       {
     322             :         /* e.g. 17-JUN-1992.  */
     323           0 :         pc->day = $1.value;
     324           0 :         pc->month = $2;
     325           0 :         pc->year.value = -$3.value;
     326           0 :         pc->year.digits = $3.digits;
     327             :       }
     328             :   | tMONTH tUNUMBER
     329             :       {
     330           0 :         pc->month = $1;
     331           0 :         pc->day = $2.value;
     332             :       }
     333             :   | tMONTH tUNUMBER ',' tUNUMBER
     334             :       {
     335           0 :         pc->month = $1;
     336           0 :         pc->day = $2.value;
     337           0 :         pc->year = $4;
     338             :       }
     339             :   | tUNUMBER tMONTH
     340             :       {
     341           0 :         pc->day = $1.value;
     342           0 :         pc->month = $2;
     343             :       }
     344             :   | tUNUMBER tMONTH tUNUMBER
     345             :       {
     346           0 :         pc->day = $1.value;
     347           0 :         pc->month = $2;
     348           0 :         pc->year = $3;
     349             :       }
     350             :   ;
     351             : 
     352             : rel:
     353             :     relunit tAGO
     354             :       {
     355           0 :         pc->rel_seconds = -pc->rel_seconds;
     356           0 :         pc->rel_minutes = -pc->rel_minutes;
     357           0 :         pc->rel_hour = -pc->rel_hour;
     358           0 :         pc->rel_day = -pc->rel_day;
     359           0 :         pc->rel_month = -pc->rel_month;
     360           0 :         pc->rel_year = -pc->rel_year;
     361             :       }
     362             :   | relunit
     363             :   ;
     364             : 
     365             : relunit:
     366             :     tUNUMBER tYEAR_UNIT
     367           0 :       { pc->rel_year += $1.value * $2; }
     368             :   | tSNUMBER tYEAR_UNIT
     369           0 :       { pc->rel_year += $1.value * $2; }
     370             :   | tYEAR_UNIT
     371           0 :       { pc->rel_year += $1; }
     372             :   | tUNUMBER tMONTH_UNIT
     373           0 :       { pc->rel_month += $1.value * $2; }
     374             :   | tSNUMBER tMONTH_UNIT
     375           0 :       { pc->rel_month += $1.value * $2; }
     376             :   | tMONTH_UNIT
     377           0 :       { pc->rel_month += $1; }
     378             :   | tUNUMBER tDAY_UNIT
     379           0 :       { pc->rel_day += $1.value * $2; }
     380             :   | tSNUMBER tDAY_UNIT
     381           0 :       { pc->rel_day += $1.value * $2; }
     382             :   | tDAY_UNIT
     383           0 :       { pc->rel_day += $1; }
     384             :   | tUNUMBER tHOUR_UNIT
     385           0 :       { pc->rel_hour += $1.value * $2; }
     386             :   | tSNUMBER tHOUR_UNIT
     387           0 :       { pc->rel_hour += $1.value * $2; }
     388             :   | tHOUR_UNIT
     389           0 :       { pc->rel_hour += $1; }
     390             :   | tUNUMBER tMINUTE_UNIT
     391           0 :       { pc->rel_minutes += $1.value * $2; }
     392             :   | tSNUMBER tMINUTE_UNIT
     393           0 :       { pc->rel_minutes += $1.value * $2; }
     394             :   | tMINUTE_UNIT
     395           0 :       { pc->rel_minutes += $1; }
     396             :   | tUNUMBER tSEC_UNIT
     397           0 :       { pc->rel_seconds += $1.value * $2; }
     398             :   | tSNUMBER tSEC_UNIT
     399           0 :       { pc->rel_seconds += $1.value * $2; }
     400             :   | tSEC_UNIT
     401           0 :       { pc->rel_seconds += $1; }
     402             :   ;
     403             : 
     404             : number:
     405             :     tUNUMBER
     406             :       {
     407           0 :         if (pc->dates_seen
     408           0 :             && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
     409           0 :           pc->year = $1;
     410             :         else
     411             :           {
     412           0 :             if (4 < $1.digits)
     413             :               {
     414           0 :                 pc->dates_seen++;
     415           0 :                 pc->day = $1.value % 100;
     416           0 :                 pc->month = ($1.value / 100) % 100;
     417           0 :                 pc->year.value = $1.value / 10000;
     418           0 :                 pc->year.digits = $1.digits - 4;
     419             :               }
     420             :             else
     421             :               {
     422           0 :                 pc->times_seen++;
     423           0 :                 if ($1.digits <= 2)
     424             :                   {
     425           0 :                     pc->hour = $1.value;
     426           0 :                     pc->minutes = 0;
     427             :                   }
     428             :                 else
     429             :                   {
     430           0 :                     pc->hour = $1.value / 100;
     431           0 :                     pc->minutes = $1.value % 100;
     432             :                   }
     433           0 :                 pc->seconds = 0;
     434           0 :                 pc->meridian = MER24;
     435             :               }
     436             :           }
     437             :       }
     438             :   ;
     439             : 
     440             : o_merid:
     441             :     /* empty */
     442           0 :       { $$ = MER24; }
     443             :   | tMERIDIAN
     444           0 :       { $$ = $1; }
     445             :   ;
     446             : 
     447             : %%
     448             : 
     449             : /* Include this file down here because bison inserts code above which
     450             :    may define-away `const'.  We want the prototype for get_date to have
     451             :    the same signature as the function definition.  */
     452             : #include "modules/getdate.h"
     453             : 
     454             : #ifndef gmtime
     455             : struct tm *gmtime (const time_t *);
     456             : #endif
     457             : #ifndef localtime
     458             : struct tm *localtime (const time_t *);
     459             : #endif
     460             : #ifndef mktime
     461             : time_t mktime (struct tm *);
     462             : #endif
     463             : 
     464             : static table const meridian_table[] =
     465             : {
     466             :   { "AM",   tMERIDIAN, MERam },
     467             :   { "A.M.", tMERIDIAN, MERam },
     468             :   { "PM",   tMERIDIAN, MERpm },
     469             :   { "P.M.", tMERIDIAN, MERpm },
     470             :   { 0, 0, 0 }
     471             : };
     472             : 
     473             : static table const dst_table[] =
     474             : {
     475             :   { "DST", tDST, 0 }
     476             : };
     477             : 
     478             : static table const month_and_day_table[] =
     479             : {
     480             :   { "JANUARY",        tMONTH,  1 },
     481             :   { "FEBRUARY",       tMONTH,  2 },
     482             :   { "MARCH",  tMONTH,  3 },
     483             :   { "APRIL",  tMONTH,  4 },
     484             :   { "MAY",    tMONTH,  5 },
     485             :   { "JUNE",   tMONTH,  6 },
     486             :   { "JULY",   tMONTH,  7 },
     487             :   { "AUGUST", tMONTH,  8 },
     488             :   { "SEPTEMBER",tMONTH,        9 },
     489             :   { "SEPT",   tMONTH,  9 },
     490             :   { "OCTOBER",        tMONTH, 10 },
     491             :   { "NOVEMBER",       tMONTH, 11 },
     492             :   { "DECEMBER",       tMONTH, 12 },
     493             :   { "SUNDAY", tDAY,    0 },
     494             :   { "MONDAY", tDAY,    1 },
     495             :   { "TUESDAY",        tDAY,    2 },
     496             :   { "TUES",   tDAY,    2 },
     497             :   { "WEDNESDAY",tDAY,  3 },
     498             :   { "WEDNES", tDAY,    3 },
     499             :   { "THURSDAY",       tDAY,    4 },
     500             :   { "THUR",   tDAY,    4 },
     501             :   { "THURS",  tDAY,    4 },
     502             :   { "FRIDAY", tDAY,    5 },
     503             :   { "SATURDAY",       tDAY,    6 },
     504             :   { 0, 0, 0 }
     505             : };
     506             : 
     507             : static table const time_units_table[] =
     508             : {
     509             :   { "YEAR",   tYEAR_UNIT,      1 },
     510             :   { "MONTH",  tMONTH_UNIT,     1 },
     511             :   { "FORTNIGHT",tDAY_UNIT,    14 },
     512             :   { "WEEK",   tDAY_UNIT,       7 },
     513             :   { "DAY",    tDAY_UNIT,       1 },
     514             :   { "HOUR",   tHOUR_UNIT,      1 },
     515             :   { "MINUTE", tMINUTE_UNIT,    1 },
     516             :   { "MIN",    tMINUTE_UNIT,    1 },
     517             :   { "SECOND", tSEC_UNIT,       1 },
     518             :   { "SEC",    tSEC_UNIT,       1 },
     519             :   { 0, 0, 0 }
     520             : };
     521             : 
     522             : /* Assorted relative-time words. */
     523             : static table const relative_time_table[] =
     524             : {
     525             :   { "TOMORROW",       tMINUTE_UNIT,   24 * 60 },
     526             :   { "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
     527             :   { "TODAY",  tMINUTE_UNIT,    0 },
     528             :   { "NOW",    tMINUTE_UNIT,    0 },
     529             :   { "LAST",   tUNUMBER,       -1 },
     530             :   { "THIS",   tUNUMBER,        0 },
     531             :   { "NEXT",   tUNUMBER,        1 },
     532             :   { "FIRST",  tUNUMBER,        1 },
     533             : /*{ "SECOND", tUNUMBER,        2 }, */
     534             :   { "THIRD",  tUNUMBER,        3 },
     535             :   { "FOURTH", tUNUMBER,        4 },
     536             :   { "FIFTH",  tUNUMBER,        5 },
     537             :   { "SIXTH",  tUNUMBER,        6 },
     538             :   { "SEVENTH",        tUNUMBER,        7 },
     539             :   { "EIGHTH", tUNUMBER,        8 },
     540             :   { "NINTH",  tUNUMBER,        9 },
     541             :   { "TENTH",  tUNUMBER,       10 },
     542             :   { "ELEVENTH",       tUNUMBER,       11 },
     543             :   { "TWELFTH",        tUNUMBER,       12 },
     544             :   { "AGO",    tAGO,            1 },
     545             :   { 0, 0, 0 }
     546             : };
     547             : 
     548             : /* The time zone table.  This table is necessarily incomplete, as time
     549             :    zone abbreviations are ambiguous; e.g. Australians interpret "EST"
     550             :    as Eastern time in Australia, not as US Eastern Standard Time.
     551             :    You cannot rely on getdate to handle arbitrary time zone
     552             :    abbreviations; use numeric abbreviations like `-0500' instead.  */
     553             : static table const time_zone_table[] =
     554             : {
     555             :   { "GMT",    tZONE,     HOUR ( 0) }, /* Greenwich Mean */
     556             :   { "UT",     tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
     557             :   { "UTC",    tZONE,     HOUR ( 0) },
     558             :   { "WET",    tZONE,     HOUR ( 0) }, /* Western European */
     559             :   { "WEST",   tDAYZONE,  HOUR ( 0) }, /* Western European Summer */
     560             :   { "BST",    tDAYZONE,  HOUR ( 0) }, /* British Summer */
     561             :   { "ART",    tZONE,    -HOUR ( 3) }, /* Argentina */
     562             :   { "BRT",    tZONE,    -HOUR ( 3) }, /* Brazil */
     563             :   { "BRST",   tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
     564             :   { "NST",    tZONE,   -(HOUR ( 3) + 30) },   /* Newfoundland Standard */
     565             :   { "NDT",    tDAYZONE,-(HOUR ( 3) + 30) },   /* Newfoundland Daylight */
     566             :   { "AST",    tZONE,    -HOUR ( 4) }, /* Atlantic Standard */
     567             :   { "ADT",    tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
     568             :   { "CLT",    tZONE,    -HOUR ( 4) }, /* Chile */
     569             :   { "CLST",   tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
     570             :   { "EST",    tZONE,    -HOUR ( 5) }, /* Eastern Standard */
     571             :   { "EDT",    tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
     572             :   { "CST",    tZONE,    -HOUR ( 6) }, /* Central Standard */
     573             :   { "CDT",    tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
     574             :   { "MST",    tZONE,    -HOUR ( 7) }, /* Mountain Standard */
     575             :   { "MDT",    tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
     576             :   { "PST",    tZONE,    -HOUR ( 8) }, /* Pacific Standard */
     577             :   { "PDT",    tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
     578             :   { "AKST",   tZONE,    -HOUR ( 9) }, /* Alaska Standard */
     579             :   { "AKDT",   tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
     580             :   { "HST",    tZONE,    -HOUR (10) }, /* Hawaii Standard */
     581             :   { "HAST",   tZONE,    -HOUR (10) }, /* Hawaii-Aleutian Standard */
     582             :   { "HADT",   tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
     583             :   { "SST",    tZONE,    -HOUR (12) }, /* Samoa Standard */
     584             :   { "WAT",    tZONE,     HOUR ( 1) }, /* West Africa */
     585             :   { "CET",    tZONE,     HOUR ( 1) }, /* Central European */
     586             :   { "CEST",   tDAYZONE,  HOUR ( 1) }, /* Central European Summer */
     587             :   { "MET",    tZONE,     HOUR ( 1) }, /* Middle European */
     588             :   { "MEZ",    tZONE,     HOUR ( 1) }, /* Middle European */
     589             :   { "MEST",   tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
     590             :   { "MESZ",   tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
     591             :   { "EET",    tZONE,     HOUR ( 2) }, /* Eastern European */
     592             :   { "EEST",   tDAYZONE,  HOUR ( 2) }, /* Eastern European Summer */
     593             :   { "CAT",    tZONE,     HOUR ( 2) }, /* Central Africa */
     594             :   { "SAST",   tZONE,     HOUR ( 2) }, /* South Africa Standard */
     595             :   { "EAT",    tZONE,     HOUR ( 3) }, /* East Africa */
     596             :   { "MSK",    tZONE,     HOUR ( 3) }, /* Moscow */
     597             :   { "MSD",    tDAYZONE,  HOUR ( 3) }, /* Moscow Daylight */
     598             :   { "IST",    tZONE,    (HOUR ( 5) + 30) },   /* India Standard */
     599             :   { "SGT",    tZONE,     HOUR ( 8) }, /* Singapore */
     600             :   { "KST",    tZONE,     HOUR ( 9) }, /* Korea Standard */
     601             :   { "JST",    tZONE,     HOUR ( 9) }, /* Japan Standard */
     602             :   { "GST",    tZONE,     HOUR (10) }, /* Guam Standard */
     603             :   { "NZST",   tZONE,     HOUR (12) }, /* New Zealand Standard */
     604             :   { "NZDT",   tDAYZONE,  HOUR (12) }, /* New Zealand Daylight */
     605             :   { 0, 0, 0  }
     606             : };
     607             : 
     608             : /* Military time zone table. */
     609             : static table const military_table[] =
     610             : {
     611             :   { "A", tZONE,       -HOUR ( 1) },
     612             :   { "B", tZONE,       -HOUR ( 2) },
     613             :   { "C", tZONE,       -HOUR ( 3) },
     614             :   { "D", tZONE,       -HOUR ( 4) },
     615             :   { "E", tZONE,       -HOUR ( 5) },
     616             :   { "F", tZONE,       -HOUR ( 6) },
     617             :   { "G", tZONE,       -HOUR ( 7) },
     618             :   { "H", tZONE,       -HOUR ( 8) },
     619             :   { "I", tZONE,       -HOUR ( 9) },
     620             :   { "K", tZONE,       -HOUR (10) },
     621             :   { "L", tZONE,       -HOUR (11) },
     622             :   { "M", tZONE,       -HOUR (12) },
     623             :   { "N", tZONE,        HOUR ( 1) },
     624             :   { "O", tZONE,        HOUR ( 2) },
     625             :   { "P", tZONE,        HOUR ( 3) },
     626             :   { "Q", tZONE,        HOUR ( 4) },
     627             :   { "R", tZONE,        HOUR ( 5) },
     628             :   { "S", tZONE,        HOUR ( 6) },
     629             :   { "T", tZONE,        HOUR ( 7) },
     630             :   { "U", tZONE,        HOUR ( 8) },
     631             :   { "V", tZONE,        HOUR ( 9) },
     632             :   { "W", tZONE,        HOUR (10) },
     633             :   { "X", tZONE,        HOUR (11) },
     634             :   { "Y", tZONE,        HOUR (12) },
     635             :   { "Z", tZONE,        HOUR ( 0) },
     636             :   { 0, 0, 0 }
     637             : };
     638             : 
     639             : 
     640             : 
     641             : static int
     642           0 : to_hour (int hours, int meridian)
     643             : {
     644           0 :   switch (meridian)
     645             :     {
     646           0 :     case MER24:
     647           0 :       return 0 <= hours && hours < 24 ? hours : -1;
     648           0 :     case MERam:
     649           0 :       return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
     650           0 :     case MERpm:
     651           0 :       return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
     652           0 :     default:
     653           0 :       abort ();
     654             :     }
     655             :   /* NOTREACHED */
     656             :     return 0;
     657             : }
     658             : 
     659             : static int
     660           0 : to_year (textint textyear)
     661             : {
     662           0 :   int year = textyear.value;
     663             : 
     664           0 :   if (year < 0)
     665           0 :     year = -year;
     666             : 
     667             :   /* XPG4 suggests that years 00-68 map to 2000-2068, and
     668             :      years 69-99 map to 1969-1999.  */
     669           0 :   if (textyear.digits == 2)
     670           0 :     year += year < 69 ? 2000 : 1900;
     671             : 
     672           0 :   return year;
     673             : }
     674             : 
     675             : static table const *
     676           0 : lookup_zone (struct parser_control const *pc, char const *name)
     677             : {
     678             :   table const *tp;
     679             : 
     680             :   /* Try local zone abbreviations first; they're more likely to be right.  */
     681           0 :   for (tp = pc->local_time_zone_table; tp->name; tp++)
     682           0 :     if (strcmp (name, tp->name) == 0)
     683           0 :       return tp;
     684             : 
     685           0 :   for (tp = time_zone_table; tp->name; tp++)
     686           0 :     if (strcmp (name, tp->name) == 0)
     687           0 :       return tp;
     688             : 
     689           0 :   return 0;
     690             : }
     691             : 
     692             : #if ! HAVE_TM_GMTOFF
     693             : /* Yield the difference between *A and *B,
     694             :    measured in seconds, ignoring leap seconds.
     695             :    The body of this function is taken directly from the GNU C Library;
     696             :    see src/strftime.c.  */
     697             : static int
     698           0 : tm_diff (struct tm const *a, struct tm const *b)
     699             : {
     700             :   /* Compute intervening leap days correctly even if year is negative.
     701             :      Take care to avoid int overflow in leap day calculations,
     702             :      but it's OK to assume that A and B are close to each other.  */
     703           0 :   int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
     704           0 :   int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
     705           0 :   int a100 = a4 / 25 - (a4 % 25 < 0);
     706           0 :   int b100 = b4 / 25 - (b4 % 25 < 0);
     707           0 :   int a400 = a100 >> 2;
     708           0 :   int b400 = b100 >> 2;
     709           0 :   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
     710           0 :   int years = a->tm_year - b->tm_year;
     711           0 :   int days = (365 * years + intervening_leap_days
     712           0 :               + (a->tm_yday - b->tm_yday));
     713           0 :   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
     714           0 :                 + (a->tm_min - b->tm_min))
     715           0 :           + (a->tm_sec - b->tm_sec));
     716             : }
     717             : #endif /* ! HAVE_TM_GMTOFF */
     718             : 
     719             : static table const *
     720           0 : lookup_word (struct parser_control const *pc, char *word)
     721             : {
     722             :   char *p;
     723             :   char *q;
     724             :   size_t wordlen;
     725             :   table const *tp;
     726             :   int i;
     727             :   int abbrev;
     728             : 
     729             :   /* Make it uppercase.  */
     730           0 :   for (p = word; *p; p++)
     731           0 :     if (ISLOWER ((unsigned char) *p))
     732           0 :       *p = toupper ((unsigned char) *p);
     733             : 
     734           0 :   for (tp = meridian_table; tp->name; tp++)
     735           0 :     if (strcmp (word, tp->name) == 0)
     736           0 :       return tp;
     737             : 
     738             :   /* See if we have an abbreviation for a month. */
     739           0 :   wordlen = strlen (word);
     740           0 :   abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
     741             : 
     742           0 :   for (tp = month_and_day_table; tp->name; tp++)
     743           0 :     if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
     744           0 :       return tp;
     745             : 
     746           0 :   if ((tp = lookup_zone (pc, word)))
     747           0 :     return tp;
     748             : 
     749           0 :   if (strcmp (word, dst_table[0].name) == 0)
     750           0 :     return dst_table;
     751             : 
     752           0 :   for (tp = time_units_table; tp->name; tp++)
     753           0 :     if (strcmp (word, tp->name) == 0)
     754           0 :       return tp;
     755             : 
     756             :   /* Strip off any plural and try the units table again. */
     757           0 :   if (word[wordlen - 1] == 'S')
     758             :     {
     759           0 :       word[wordlen - 1] = '\0';
     760           0 :       for (tp = time_units_table; tp->name; tp++)
     761           0 :         if (strcmp (word, tp->name) == 0)
     762           0 :           return tp;
     763           0 :       word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
     764             :     }
     765             : 
     766           0 :   for (tp = relative_time_table; tp->name; tp++)
     767           0 :     if (strcmp (word, tp->name) == 0)
     768           0 :       return tp;
     769             : 
     770             :   /* Military time zones. */
     771           0 :   if (wordlen == 1)
     772           0 :     for (tp = military_table; tp->name; tp++)
     773           0 :       if (word[0] == tp->name[0])
     774           0 :         return tp;
     775             : 
     776             :   /* Drop out any periods and try the time zone table again. */
     777           0 :   for (i = 0, p = q = word; (*p = *q); q++)
     778           0 :     if (*q == '.')
     779           0 :       i = 1;
     780             :     else
     781           0 :       p++;
     782           0 :   if (i && (tp = lookup_zone (pc, word)))
     783           0 :     return tp;
     784             : 
     785           0 :   return 0;
     786             : }
     787             : 
     788             : static int
     789           0 : yylex (YYSTYPE *lvalp, struct parser_control *pc)
     790             : {
     791             :   unsigned char c;
     792             :   size_t count;
     793             : 
     794             :   for (;;)
     795             :     {
     796           0 :       while (c = *pc->input, ISSPACE (c))
     797           0 :         pc->input++;
     798             : 
     799           0 :       if (ISDIGIT (c) || c == '-' || c == '+')
     800             :         {
     801             :           char const *p;
     802             :           int sign;
     803             :           int value;
     804           0 :           if (c == '-' || c == '+')
     805             :             {
     806           0 :               sign = c == '-' ? -1 : 1;
     807           0 :               c = *++pc->input;
     808           0 :               if (! ISDIGIT (c))
     809             :                 /* skip the '-' sign */
     810           0 :                 continue;
     811             :             }
     812             :           else
     813           0 :             sign = 0;
     814           0 :           p = pc->input;
     815           0 :           value = 0;
     816             :           do
     817             :             {
     818           0 :               value = 10 * value + c - '0';
     819           0 :               c = *++p;
     820             :             }
     821           0 :           while (ISDIGIT (c));
     822           0 :           lvalp->textintval.value = sign < 0 ? -value : value;
     823           0 :           lvalp->textintval.digits = p - pc->input;
     824           0 :           pc->input = p;
     825           0 :           return sign ? tSNUMBER : tUNUMBER;
     826             :         }
     827             : 
     828           0 :       if (ISALPHA (c))
     829             :         {
     830             :           char buff[20];
     831           0 :           size_t i = 0;
     832             :           table const *tp;
     833             : 
     834             :           do
     835             :             {
     836           0 :               if (i < 20)
     837           0 :                 buff[i++] = c;
     838           0 :               c = *++pc->input;
     839             :             }
     840           0 :           while (ISALPHA (c) || c == '.');
     841             : 
     842           0 :           buff[i] = '\0';
     843           0 :           tp = lookup_word (pc, buff);
     844           0 :           if (! tp)
     845           0 :             return '?';
     846           0 :           lvalp->intval = tp->value;
     847           0 :           return tp->type;
     848             :         }
     849             : 
     850           0 :       if (c != '(')
     851           0 :         return *pc->input++;
     852           0 :       count = 0;
     853             :       do
     854             :         {
     855           0 :           c = *pc->input++;
     856           0 :           if (c == '\0')
     857           0 :             return c;
     858           0 :           if (c == '(')
     859           0 :             count++;
     860           0 :           else if (c == ')')
     861           0 :             count--;
     862             :         }
     863           0 :       while (count > 0);
     864             :     }
     865             : }
     866             : 
     867             : /* Do nothing if the parser reports an error.  */
     868             : static int
     869           0 : yyerror (struct parser_control *pc ATTRIBUTE_UNUSED, const char *s ATTRIBUTE_UNUSED)
     870             : {
     871           0 :   return 0;
     872             : }
     873             : 
     874             : /* Parse a date/time string P.  Return the corresponding time_t value,
     875             :    or (time_t) -1 if there is an error.  P can be an incomplete or
     876             :    relative time specification; if so, use *NOW as the basis for the
     877             :    returned time.  */
     878             : time_t
     879           0 : get_date (const char *p, const time_t *now)
     880             : {
     881           0 :   time_t Start = now ? *now : time (0);
     882           0 :   struct tm *tmp = localtime (&Start);
     883             :   struct tm tm;
     884             :   struct tm tm0;
     885             :   struct parser_control pc;
     886             : 
     887           0 :   if (! tmp)
     888           0 :     return -1;
     889             : 
     890           0 :   pc.input = p;
     891           0 :   pc.year.value = tmp->tm_year + TM_YEAR_BASE;
     892           0 :   pc.year.digits = 4;
     893           0 :   pc.month = tmp->tm_mon + 1;
     894           0 :   pc.day = tmp->tm_mday;
     895           0 :   pc.hour = tmp->tm_hour;
     896           0 :   pc.minutes = tmp->tm_min;
     897           0 :   pc.seconds = tmp->tm_sec;
     898           0 :   tm.tm_isdst = tmp->tm_isdst;
     899             : 
     900           0 :   pc.meridian = MER24;
     901           0 :   pc.rel_seconds = 0;
     902           0 :   pc.rel_minutes = 0;
     903           0 :   pc.rel_hour = 0;
     904           0 :   pc.rel_day = 0;
     905           0 :   pc.rel_month = 0;
     906           0 :   pc.rel_year = 0;
     907           0 :   pc.dates_seen = 0;
     908           0 :   pc.days_seen = 0;
     909           0 :   pc.rels_seen = 0;
     910           0 :   pc.times_seen = 0;
     911           0 :   pc.local_zones_seen = 0;
     912           0 :   pc.zones_seen = 0;
     913             : 
     914             : #ifdef HAVE_STRUCT_TM_TM_ZONE
     915             :   pc.local_time_zone_table[0].name = tmp->tm_zone;
     916             :   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
     917             :   pc.local_time_zone_table[0].value = tmp->tm_isdst;
     918             :   pc.local_time_zone_table[1].name = 0;
     919             : 
     920             :   /* Probe the names used in the next three calendar quarters, looking
     921             :      for a tm_isdst different from the one we already have.  */
     922             :   {
     923             :     int quarter;
     924             :     for (quarter = 1; quarter <= 3; quarter++)
     925             :       {
     926             :         time_t probe = Start + quarter * (90 * 24 * 60 * 60);
     927             :         struct tm *probe_tm = localtime (&probe);
     928             :         if (probe_tm && probe_tm->tm_zone
     929             :             && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
     930             :           {
     931             :               {
     932             :                 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
     933             :                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
     934             :                 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
     935             :                 pc.local_time_zone_table[2].name = 0;
     936             :               }
     937             :             break;
     938             :           }
     939             :       }
     940             :   }
     941             : #else
     942             : #ifdef HAVE_TZNAME
     943             :   {
     944             : # ifndef tzname
     945             :     extern char *tzname[];
     946             : # endif
     947             :     int i;
     948             :     for (i = 0; i < 2; i++)
     949             :       {
     950             :         pc.local_time_zone_table[i].name = tzname[i];
     951             :         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
     952             :         pc.local_time_zone_table[i].value = i;
     953             :       }
     954             :     pc.local_time_zone_table[i].name = 0;
     955             :   }
     956             : #else
     957           0 :   pc.local_time_zone_table[0].name = 0;
     958             : #endif
     959             : #endif
     960             : 
     961           0 :   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
     962           0 :       && ! strcmp (pc.local_time_zone_table[0].name,
     963             :                    pc.local_time_zone_table[1].name))
     964             :     {
     965             :       /* This locale uses the same abbrevation for standard and
     966             :          daylight times.  So if we see that abbreviation, we don't
     967             :          know whether it's daylight time.  */
     968           0 :       pc.local_time_zone_table[0].value = -1;
     969           0 :       pc.local_time_zone_table[1].name = 0;
     970             :     }
     971             : 
     972           0 :   if (yyparse (&pc) != 0
     973           0 :       || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
     974           0 :       || 1 < (pc.local_zones_seen + pc.zones_seen)
     975           0 :       || (pc.local_zones_seen && 1 < pc.local_isdst))
     976           0 :     return -1;
     977             : 
     978           0 :   tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
     979           0 :   tm.tm_mon = pc.month - 1 + pc.rel_month;
     980           0 :   tm.tm_mday = pc.day + pc.rel_day;
     981           0 :   if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
     982             :     {
     983           0 :       tm.tm_hour = to_hour (pc.hour, pc.meridian);
     984           0 :       if (tm.tm_hour < 0)
     985           0 :         return -1;
     986           0 :       tm.tm_min = pc.minutes;
     987           0 :       tm.tm_sec = pc.seconds;
     988             :     }
     989             :   else
     990             :     {
     991           0 :       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
     992             :     }
     993             : 
     994             :   /* Let mktime deduce tm_isdst if we have an absolute time stamp,
     995             :      or if the relative time stamp mentions days, months, or years.  */
     996           0 :   if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
     997           0 :       | pc.rel_month | pc.rel_year)
     998           0 :     tm.tm_isdst = -1;
     999             : 
    1000             :   /* But if the input explicitly specifies local time with or without
    1001             :      DST, give mktime that information.  */
    1002           0 :   if (pc.local_zones_seen)
    1003           0 :     tm.tm_isdst = pc.local_isdst;
    1004             : 
    1005           0 :   tm0 = tm;
    1006             : 
    1007           0 :   Start = mktime (&tm);
    1008             : 
    1009           0 :   if (Start == (time_t) -1)
    1010             :     {
    1011             : 
    1012             :       /* Guard against falsely reporting errors near the time_t boundaries
    1013             :          when parsing times in other time zones.  For example, if the min
    1014             :          time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
    1015             :          of UTC, then the min localtime value is 1970-01-01 08:00:00; if
    1016             :          we apply mktime to 1970-01-01 00:00:00 we will get an error, so
    1017             :          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
    1018             :          zone by 24 hours to compensate.  This algorithm assumes that
    1019             :          there is no DST transition within a day of the time_t boundaries.  */
    1020           0 :       if (pc.zones_seen)
    1021             :         {
    1022           0 :           tm = tm0;
    1023           0 :           if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
    1024             :             {
    1025           0 :               tm.tm_mday++;
    1026           0 :               pc.time_zone += 24 * 60;
    1027             :             }
    1028             :           else
    1029             :             {
    1030           0 :               tm.tm_mday--;
    1031           0 :               pc.time_zone -= 24 * 60;
    1032             :             }
    1033           0 :           Start = mktime (&tm);
    1034             :         }
    1035             : 
    1036           0 :       if (Start == (time_t) -1)
    1037           0 :         return Start;
    1038             :     }
    1039             : 
    1040           0 :   if (pc.days_seen && ! pc.dates_seen)
    1041             :     {
    1042           0 :       tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
    1043           0 :                      + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
    1044           0 :       tm.tm_isdst = -1;
    1045           0 :       Start = mktime (&tm);
    1046           0 :       if (Start == (time_t) -1)
    1047           0 :         return Start;
    1048             :     }
    1049             : 
    1050           0 :   if (pc.zones_seen)
    1051             :     {
    1052           0 :       int delta = pc.time_zone * 60;
    1053             : #ifdef HAVE_TM_GMTOFF
    1054             :       delta -= tm.tm_gmtoff;
    1055             : #else
    1056           0 :       struct tm *gmt = gmtime (&Start);
    1057           0 :       if (! gmt)
    1058           0 :         return -1;
    1059           0 :       delta -= tm_diff (&tm, gmt);
    1060             : #endif
    1061           0 :       if ((Start < Start - delta) != (delta < 0))
    1062           0 :         return -1;      /* time_t overflow */
    1063           0 :       Start -= delta;
    1064             :     }
    1065             : 
    1066             :   /* Add relative hours, minutes, and seconds.  Ignore leap seconds;
    1067             :      i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
    1068             :      leap second.  Typically this is not what the user wants, but it's
    1069             :      too hard to do it the other way, because the time zone indicator
    1070             :      must be applied before relative times, and if mktime is applied
    1071             :      again the time zone will be lost.  */
    1072             :   {
    1073           0 :     time_t t0 = Start;
    1074           0 :     long d1 = 60 * 60 * (long) pc.rel_hour;
    1075           0 :     time_t t1 = t0 + d1;
    1076           0 :     long d2 = 60 * (long) pc.rel_minutes;
    1077           0 :     time_t t2 = t1 + d2;
    1078           0 :     int d3 = pc.rel_seconds;
    1079           0 :     time_t t3 = t2 + d3;
    1080           0 :     if ((d1 / (60 * 60) ^ pc.rel_hour)
    1081           0 :         | (d2 / 60 ^ pc.rel_minutes)
    1082           0 :         | ((t0 + d1 < t0) ^ (d1 < 0))
    1083           0 :         | ((t1 + d2 < t1) ^ (d2 < 0))
    1084           0 :         | ((t2 + d3 < t2) ^ (d3 < 0)))
    1085           0 :       return -1;
    1086           0 :     Start = t3;
    1087             :   }
    1088             : 
    1089           0 :   return Start;
    1090             : }
    1091             : 
    1092             : #if TEST
    1093             : 
    1094             : #include <stdio.h>
    1095             : 
    1096             : int
    1097             : main (int ac, char **av)
    1098             : {
    1099             :   char buff[BUFSIZ];
    1100             :   time_t d;
    1101             : 
    1102             :   printf ("Enter date, or blank line to exit.\n\t> ");
    1103             :   fflush (stdout);
    1104             : 
    1105             :   buff[BUFSIZ - 1] = 0;
    1106             :   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
    1107             :     {
    1108             :       d = get_date (buff, 0);
    1109             :       if (d == (time_t) -1)
    1110             :         printf ("Bad format - couldn't convert.\n");
    1111             :       else
    1112             :         printf ("%s", ctime (&d));
    1113             :       printf ("\t> ");
    1114             :       fflush (stdout);
    1115             :     }
    1116             :   return 0;
    1117             : }
    1118             : #endif /* defined TEST */

Generated by: LCOV version 1.13