URI: 
       tparsedate.y - mixmaster - mixmaster 3.0 patched for libressl
  HTML git clone git://parazyd.org/mixmaster.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
       tparsedate.y (23910B)
       ---
            1 %{
            2 /*  $Id: parsedate.y 647 2003-10-25 23:34:13Z weasel $
            3 **
            4 **  Originally written by Steven M. Bellovin <smb@research.att.com> while
            5 **  at the University of North Carolina at Chapel Hill.  Later tweaked by
            6 **  a couple of people on Usenet.  Completely overhauled by Rich $alz
            7 **  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
            8 **  Further revised (removed obsolete constructs and cleaned up timezone
            9 **  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
           10 **  helped in September, 1992.
           11 **
           12 **  This grammar has six shift/reduce conflicts.
           13 **
           14 **  This code is in the public domain and has no copyright.
           15 */
           16 
           17 #include <stdio.h>
           18 #include <string.h>
           19 
           20 // #include "config.h"
           21 // #include "clibrary.h"
           22 #include <ctype.h>
           23 
           24 #if defined(_HPUX_SOURCE)
           25 # include <alloca.h>
           26 #endif
           27 
           28 #ifdef TM_IN_SYS_TIME
           29 # include <sys/time.h>
           30 #else
           31 # include <time.h>
           32 #endif
           33 
           34 // #include "libinn.h"
           35 
           36 /* Used for iterating through arrays.  ARRAY_SIZE returns the number of
           37    elements in the array (useful for a < upper bound in a for loop) and
           38    ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
           39    legal to refer to such a pointer as long as it's never dereferenced). */
           40 #define ARRAY_SIZE(array)       (sizeof(array) / sizeof((array)[0]))
           41 #define ARRAY_END(array)        (&(array)[ARRAY_SIZE(array)])
           42 
           43 /* On some systems, the macros defined by <ctype.h> are only vaild on ASCII
           44    characters (those characters that isascii() says are ASCII).  This comes
           45    into play when applying <ctype.h> macros to eight-bit data.  autoconf
           46    checks for this with as part of AC_HEADER_STDC, so if autoconf doesn't
           47    think our headers are standard, check isascii() first. */
           48 #if STDC_HEADERS
           49 # define CTYPE(isXXXXX, c) (isXXXXX((unsigned char)(c)))
           50 #else
           51 # define CTYPE(isXXXXX, c) \
           52     (isascii((unsigned char)(c)) && isXXXXX((unsigned char)(c)))
           53 #endif
           54 
           55 
           56 #define yylhs                date_yylhs
           57 #define yylen                date_yylen
           58 #define yydefred        date_yydefred
           59 #define yydgoto                date_yydgoto
           60 #define yysindex        date_yysindex
           61 #define yyrindex        date_yyrindex
           62 #define yygindex        date_yygindex
           63 #define yytable                date_yytable
           64 #define yycheck                date_yycheck
           65 #define yyparse                date_parse
           66 #define yylex                date_lex
           67 #define yyerror                date_error
           68 #define yymaxdepth        date_yymaxdepth
           69 
           70 
           71 static int date_lex(void);
           72 
           73 
           74     /* See the LeapYears table in Convert. */
           75 #define EPOCH                1970
           76 #define END_OF_TIME        2038
           77     /* Constants for general time calculations. */
           78 #define DST_OFFSET        1
           79 #define SECSPERDAY        (24L * 60L * 60L)
           80     /* Readability for TABLE stuff. */
           81 #define HOUR(x)                (x * 60)
           82 
           83 #define LPAREN                '('
           84 #define RPAREN                ')'
           85 #define IS7BIT(x)        ((unsigned int)(x) < 0200)
           86 
           87 
           88 /*
           89 **  An entry in the lexical lookup table.
           90 */
           91 typedef struct _TABLE {
           92     const char * name;
           93     int          type;
           94     time_t       value;
           95 } TABLE;
           96 
           97 /*
           98 **  Daylight-savings mode:  on, off, or not yet known.
           99 */
          100 typedef enum _DSTMODE {
          101     DSTon, DSToff, DSTmaybe
          102 } DSTMODE;
          103 
          104 /*
          105 **  Meridian:  am, pm, or 24-hour style.
          106 */
          107 typedef enum _MERIDIAN {
          108     MERam, MERpm, MER24
          109 } MERIDIAN;
          110 
          111 
          112 /*
          113 **  Global variables.  We could get rid of most of them by using a yacc
          114 **  union, but this is more efficient.  (This routine predates the
          115 **  yacc %union construct.)
          116 */
          117 static char        *yyInput;
          118 static DSTMODE        yyDSTmode;
          119 static int        yyHaveDate;
          120 static int        yyHaveRel;
          121 static int        yyHaveTime;
          122 static time_t        yyTimezone;
          123 static time_t        yyDay;
          124 static time_t        yyHour;
          125 static time_t        yyMinutes;
          126 static time_t        yyMonth;
          127 static time_t        yySeconds;
          128 static time_t        yyYear;
          129 static MERIDIAN        yyMeridian;
          130 static time_t        yyRelMonth;
          131 static time_t        yyRelSeconds;
          132 
          133 
          134 /* extern struct tm        *localtime(); */
          135 
          136 static void                date_error(const char *s);
          137 %}
          138 
          139 %union {
          140     time_t                Number;
          141     enum _MERIDIAN        Meridian;
          142 }
          143 
          144 %token        tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
          145 %token        tUNUMBER tZONE
          146 
          147 %type        <Number>        tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
          148 %type        <Number>        tSNUMBER tUNUMBER tZONE numzone zone
          149 %type        <Meridian>        tMERIDIAN o_merid
          150 
          151 %%
          152 
          153 spec        : /* NULL */
          154         | spec item
          155         ;
          156 
          157 item        : time {
          158             yyHaveTime++;
          159 #if        defined(lint)
          160             /* I am compulsive about lint natterings... */
          161             if (yyHaveTime == -1) {
          162                 YYERROR;
          163             }
          164 #endif        /* defined(lint) */
          165         }
          166         | time zone {
          167             yyHaveTime++;
          168             yyTimezone = $2;
          169         }
          170         | date {
          171             yyHaveDate++;
          172         }
          173         | rel {
          174             yyHaveRel = 1;
          175         }
          176         ;
          177 
          178 time        : tUNUMBER o_merid {
          179             if ($1 < 100) {
          180                 yyHour = $1;
          181                 yyMinutes = 0;
          182             }
          183             else {
          184                 yyHour = $1 / 100;
          185                 yyMinutes = $1 % 100;
          186             }
          187             yySeconds = 0;
          188             yyMeridian = $2;
          189         }
          190         | tUNUMBER ':' tUNUMBER o_merid {
          191             yyHour = $1;
          192             yyMinutes = $3;
          193             yySeconds = 0;
          194             yyMeridian = $4;
          195         }
          196         | tUNUMBER ':' tUNUMBER numzone {
          197             yyHour = $1;
          198             yyMinutes = $3;
          199             yyTimezone = $4;
          200             yyMeridian = MER24;
          201             yyDSTmode = DSToff;
          202         }
          203         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
          204             yyHour = $1;
          205             yyMinutes = $3;
          206             yySeconds = $5;
          207             yyMeridian = $6;
          208         }
          209         | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
          210             yyHour = $1;
          211             yyMinutes = $3;
          212             yySeconds = $5;
          213             yyTimezone = $6;
          214             yyMeridian = MER24;
          215             yyDSTmode = DSToff;
          216         }
          217         ;
          218 
          219 zone        : tZONE {
          220             $$ = $1;
          221             yyDSTmode = DSToff;
          222         }
          223         | tDAYZONE {
          224             $$ = $1;
          225             yyDSTmode = DSTon;
          226         }
          227         | tZONE numzone {
          228             /* Only allow "GMT+300" and "GMT-0800" */
          229             if ($1 != 0) {
          230                 YYABORT;
          231             }
          232             $$ = $2;
          233             yyDSTmode = DSToff;
          234         }
          235         | numzone {
          236             $$ = $1;
          237             yyDSTmode = DSToff;
          238         }
          239         ;
          240 
          241 numzone        : tSNUMBER {
          242             int                i;
          243 
          244             /* Unix and GMT and numeric timezones -- a little confusing. */
          245             if ($1 < 0) {
          246                 /* Don't work with negative modulus. */
          247                 $1 = -$1;
          248                 if ($1 > 9999 || (i = $1 % 100) >= 60) {
          249                     YYABORT;
          250                 }
          251                 $$ = ($1 / 100) * 60 + i;
          252             }
          253             else {
          254                 if ($1 > 9999 || (i = $1 % 100) >= 60) {
          255                     YYABORT;
          256                 }
          257                 $$ = -(($1 / 100) * 60 + i);
          258             }
          259         }
          260         ;
          261 
          262 date        : tUNUMBER '/' tUNUMBER {
          263             yyMonth = $1;
          264             yyDay = $3;
          265         }
          266         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
          267             if ($1 > 100) {
          268                 /* assume YYYY/MM/DD format, so need not to add 1900 */
          269                 if ($1 > 999) {
          270                     yyYear = $1;
          271                 } else {
          272                     yyYear = 1900 + $1;
          273                 }
          274                 yyMonth = $3;
          275                 yyDay = $5;
          276             }
          277             else {
          278                 /* assume MM/DD/YY* format */
          279                 yyMonth = $1;
          280                 yyDay = $3;
          281                 if ($5 > 999) {
          282                     /* assume year is YYYY format, so need not to add 1900 */
          283                     yyYear = $5;
          284                 } else if ($5 < 100) {
          285                     /* assume year is YY format, so need to add 1900 */
          286                     yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100;
          287                 } else {
          288                     yyYear = 1900 + $5;
          289                 }
          290             }
          291         }
          292         | tMONTH tUNUMBER {
          293             yyMonth = $1;
          294             yyDay = $2;
          295         }
          296         | tMONTH tUNUMBER ',' tUNUMBER {
          297             yyMonth = $1;
          298             yyDay = $2;
          299             if ($4 > 999) {
          300                 /* assume year is YYYY format, so need not to add 1900 */
          301                 yyYear = $4;
          302             } else if ($4 < 100) {
          303                 /* assume year is YY format, so need to add 1900 */
          304                 yyYear = $4 + (yyYear / 100 + (yyYear % 100 - $4) / 50) * 100;
          305             } else {
          306                 yyYear = 1900 + $4;
          307             }
          308         }
          309         | tUNUMBER tMONTH {
          310             yyDay = $1;
          311             yyMonth = $2;
          312         }
          313         | tUNUMBER tMONTH tUNUMBER {
          314             yyDay = $1;
          315             yyMonth = $2;
          316             if ($3 > 999) {
          317                 /* assume year is YYYY format, so need not to add 1900 */
          318                 yyYear = $3;
          319             } else if ($3 < 100) {
          320                 /* assume year is YY format, so need to add 1900 */
          321                 yyYear = $3 + (yyYear / 100 + (yyYear % 100 - $3) / 50) * 100;
          322             } else {
          323                 yyYear = 1900 + $3;
          324             }
          325         }
          326         | tDAY ',' tUNUMBER tMONTH tUNUMBER {
          327             yyDay = $3;
          328             yyMonth = $4;
          329             if ($5 > 999) {
          330                 /* assume year is YYYY format, so need not to add 1900 */
          331                 yyYear = $5;
          332             } else if ($5 < 100) {
          333                 /* assume year is YY format, so need to add 1900 */
          334                 yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100;
          335             } else {
          336                 yyYear = 1900 + $5;
          337             }
          338         }
          339         ;
          340 
          341 rel        : tSNUMBER tSEC_UNIT {
          342             yyRelSeconds += $1 * $2;
          343         }
          344         | tUNUMBER tSEC_UNIT {
          345             yyRelSeconds += $1 * $2;
          346         }
          347         | tSNUMBER tMONTH_UNIT {
          348             yyRelMonth += $1 * $2;
          349         }
          350         | tUNUMBER tMONTH_UNIT {
          351             yyRelMonth += $1 * $2;
          352         }
          353         ;
          354 
          355 o_merid        : /* NULL */ {
          356             $$ = MER24;
          357         }
          358         | tMERIDIAN {
          359             $$ = $1;
          360         }
          361         ;
          362 
          363 %%
          364 
          365 /* Month and day table. */
          366 static TABLE        MonthDayTable[] = {
          367     { "january",        tMONTH,  1 },
          368     { "february",        tMONTH,  2 },
          369     { "march",                tMONTH,  3 },
          370     { "april",                tMONTH,  4 },
          371     { "may",                tMONTH,  5 },
          372     { "june",                tMONTH,  6 },
          373     { "july",                tMONTH,  7 },
          374     { "august",                tMONTH,  8 },
          375     { "september",        tMONTH,  9 },
          376     { "october",        tMONTH, 10 },
          377     { "november",        tMONTH, 11 },
          378     { "december",        tMONTH, 12 },
          379         /* The value of the day isn't used... */
          380     { "sunday",                tDAY, 0 },
          381     { "monday",                tDAY, 0 },
          382     { "tuesday",        tDAY, 0 },
          383     { "wednesday",        tDAY, 0 },
          384     { "thursday",        tDAY, 0 },
          385     { "friday",                tDAY, 0 },
          386     { "saturday",        tDAY, 0 },
          387 };
          388 
          389 /* Time units table. */
          390 static TABLE        UnitsTable[] = {
          391     { "year",                tMONTH_UNIT,        12 },
          392     { "month",                tMONTH_UNIT,        1 },
          393     { "week",                tSEC_UNIT,        7 * 24 * 60 * 60 },
          394     { "day",                tSEC_UNIT,        1 * 24 * 60 * 60 },
          395     { "hour",                tSEC_UNIT,        60 * 60 },
          396     { "minute",                tSEC_UNIT,        60 },
          397     { "min",                tSEC_UNIT,        60 },
          398     { "second",                tSEC_UNIT,        1 },
          399     { "sec",                tSEC_UNIT,        1 },
          400 };
          401 
          402 /* Timezone table. */
          403 static TABLE        TimezoneTable[] = {
          404     { "gmt",        tZONE,     HOUR( 0) },        /* Greenwich Mean */
          405     { "ut",        tZONE,     HOUR( 0) },        /* Universal */
          406     { "utc",        tZONE,     HOUR( 0) },        /* Universal Coordinated */
          407     { "cut",        tZONE,     HOUR( 0) },        /* Coordinated Universal */
          408     { "z",        tZONE,     HOUR( 0) },        /* Greenwich Mean */
          409     { "wet",        tZONE,     HOUR( 0) },        /* Western European */
          410     { "bst",        tDAYZONE,  HOUR( 0) },        /* British Summer */
          411     { "nst",        tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
          412     { "ndt",        tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
          413     { "ast",        tZONE,     HOUR( 4) },        /* Atlantic Standard */
          414     { "adt",        tDAYZONE,  HOUR( 4) },        /* Atlantic Daylight */
          415     { "est",        tZONE,     HOUR( 5) },        /* Eastern Standard */
          416     { "edt",        tDAYZONE,  HOUR( 5) },        /* Eastern Daylight */
          417     { "cst",        tZONE,     HOUR( 6) },        /* Central Standard */
          418     { "cdt",        tDAYZONE,  HOUR( 6) },        /* Central Daylight */
          419     { "mst",        tZONE,     HOUR( 7) },        /* Mountain Standard */
          420     { "mdt",        tDAYZONE,  HOUR( 7) },        /* Mountain Daylight */
          421     { "pst",        tZONE,     HOUR( 8) },        /* Pacific Standard */
          422     { "pdt",        tDAYZONE,  HOUR( 8) },        /* Pacific Daylight */
          423     { "yst",        tZONE,     HOUR( 9) },        /* Yukon Standard */
          424     { "ydt",        tDAYZONE,  HOUR( 9) },        /* Yukon Daylight */
          425     { "akst",        tZONE,     HOUR( 9) },        /* Alaska Standard */
          426     { "akdt",        tDAYZONE,  HOUR( 9) },        /* Alaska Daylight */
          427     { "hst",        tZONE,     HOUR(10) },        /* Hawaii Standard */
          428     { "hast",        tZONE,     HOUR(10) },        /* Hawaii-Aleutian Standard */
          429     { "hadt",        tDAYZONE,  HOUR(10) },        /* Hawaii-Aleutian Daylight */
          430     { "ces",        tDAYZONE,  -HOUR(1) },        /* Central European Summer */
          431     { "cest",        tDAYZONE,  -HOUR(1) },        /* Central European Summer */
          432     { "mez",        tZONE,     -HOUR(1) },        /* Middle European */
          433     { "mezt",        tDAYZONE,  -HOUR(1) },        /* Middle European Summer */
          434     { "cet",        tZONE,     -HOUR(1) },        /* Central European */
          435     { "met",        tZONE,     -HOUR(1) },        /* Middle European */
          436     { "eet",        tZONE,     -HOUR(2) },        /* Eastern Europe */
          437     { "msk",        tZONE,     -HOUR(3) },        /* Moscow Winter */
          438     { "msd",        tDAYZONE,  -HOUR(3) },        /* Moscow Summer */
          439     { "wast",        tZONE,     -HOUR(8) },        /* West Australian Standard */
          440     { "wadt",        tDAYZONE,  -HOUR(8) },        /* West Australian Daylight */
          441     { "hkt",        tZONE,     -HOUR(8) },        /* Hong Kong */
          442     { "cct",        tZONE,     -HOUR(8) },        /* China Coast */
          443     { "jst",        tZONE,     -HOUR(9) },        /* Japan Standard */
          444     { "kst",        tZONE,     -HOUR(9) },        /* Korean Standard */
          445     { "kdt",        tZONE,     -HOUR(9) },        /* Korean Daylight */
          446     { "cast",        tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
          447     { "cadt",        tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
          448     { "east",        tZONE,     -HOUR(10) },        /* Eastern Australian Standard */
          449     { "eadt",        tDAYZONE,  -HOUR(10) },        /* Eastern Australian Daylight */
          450     { "nzst",        tZONE,     -HOUR(12) },        /* New Zealand Standard */
          451     { "nzdt",        tDAYZONE,  -HOUR(12) },        /* New Zealand Daylight */
          452 
          453     /* For completeness we include the following entries. */
          454 #if        0
          455 
          456     /* Duplicate names.  Either they conflict with a zone listed above
          457      * (which is either more likely to be seen or just been in circulation
          458      * longer), or they conflict with another zone in this section and
          459      * we could not reasonably choose one over the other. */
          460     { "fst",        tZONE,     HOUR( 2) },        /* Fernando De Noronha Standard */
          461     { "fdt",        tDAYZONE,  HOUR( 2) },        /* Fernando De Noronha Daylight */
          462     { "bst",        tZONE,     HOUR( 3) },        /* Brazil Standard */
          463     { "est",        tZONE,     HOUR( 3) },        /* Eastern Standard (Brazil) */
          464     { "edt",        tDAYZONE,  HOUR( 3) },        /* Eastern Daylight (Brazil) */
          465     { "wst",        tZONE,     HOUR( 4) },        /* Western Standard (Brazil) */
          466     { "wdt",        tDAYZONE,  HOUR( 4) },        /* Western Daylight (Brazil) */
          467     { "cst",        tZONE,     HOUR( 5) },        /* Chile Standard */
          468     { "cdt",        tDAYZONE,  HOUR( 5) },        /* Chile Daylight */
          469     { "ast",        tZONE,     HOUR( 5) },        /* Acre Standard */
          470     { "adt",        tDAYZONE,  HOUR( 5) },        /* Acre Daylight */
          471     { "cst",        tZONE,     HOUR( 5) },        /* Cuba Standard */
          472     { "cdt",        tDAYZONE,  HOUR( 5) },        /* Cuba Daylight */
          473     { "est",        tZONE,     HOUR( 6) },        /* Easter Island Standard */
          474     { "edt",        tDAYZONE,  HOUR( 6) },        /* Easter Island Daylight */
          475     { "sst",        tZONE,     HOUR(11) },        /* Samoa Standard */
          476     { "ist",        tZONE,     -HOUR(2) },        /* Israel Standard */
          477     { "idt",        tDAYZONE,  -HOUR(2) },        /* Israel Daylight */
          478     { "idt",        tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
          479     { "ist",        tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
          480     { "cst",         tZONE,     -HOUR(8) },        /* China Standard */
          481     { "cdt",         tDAYZONE,  -HOUR(8) },        /* China Daylight */
          482     { "sst",         tZONE,     -HOUR(8) },        /* Singapore Standard */
          483 
          484     /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
          485     { "gst",        tZONE,     HOUR( 3) },        /* Greenland Standard */
          486     { "wat",        tZONE,     -HOUR(1) },        /* West Africa */
          487     { "at",        tZONE,     HOUR( 2) },        /* Azores */
          488     { "gst",        tZONE,     -HOUR(10) },        /* Guam Standard */
          489     { "nft",        tZONE,     HOUR(3)+30 }, /* Newfoundland */
          490     { "idlw",        tZONE,     HOUR(12) },        /* International Date Line West */
          491     { "mewt",        tZONE,     -HOUR(1) },        /* Middle European Winter */
          492     { "mest",        tDAYZONE,  -HOUR(1) },        /* Middle European Summer */
          493     { "swt",        tZONE,     -HOUR(1) },        /* Swedish Winter */
          494     { "sst",        tDAYZONE,  -HOUR(1) },        /* Swedish Summer */
          495     { "fwt",        tZONE,     -HOUR(1) },        /* French Winter */
          496     { "fst",        tDAYZONE,  -HOUR(1) },        /* French Summer */
          497     { "bt",        tZONE,     -HOUR(3) },        /* Baghdad */
          498     { "it",        tZONE,     -(HOUR(3)+30) }, /* Iran */
          499     { "zp4",        tZONE,     -HOUR(4) },        /* USSR Zone 3 */
          500     { "zp5",        tZONE,     -HOUR(5) },        /* USSR Zone 4 */
          501     { "ist",        tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
          502     { "zp6",        tZONE,     -HOUR(6) },        /* USSR Zone 5 */
          503     { "nst",        tZONE,     -HOUR(7) },        /* North Sumatra */
          504     { "sst",        tZONE,     -HOUR(7) },        /* South Sumatra */
          505     { "jt",        tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
          506     { "nzt",        tZONE,     -HOUR(12) },        /* New Zealand */
          507     { "idle",        tZONE,     -HOUR(12) },        /* International Date Line East */
          508     { "cat",        tZONE,     HOUR(10) },        /* -- expired 1967 */
          509     { "nt",        tZONE,     HOUR(11) },        /* -- expired 1967 */
          510     { "ahst",        tZONE,     HOUR(10) },        /* -- expired 1983 */
          511     { "hdt",        tDAYZONE,  HOUR(10) },        /* -- expired 1986 */
          512 #endif        /* 0 */
          513 };
          514 
          515 
          516 
          517 static void
          518 date_error(const char *s)
          519 {
          520     s = s;                        /* ARGSUSED */
          521     /* NOTREACHED */
          522 }
          523 
          524 
          525 static time_t
          526 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
          527 {
          528     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
          529         return -1;
          530     if (Meridian == MER24) {
          531         if (Hours < 0 || Hours > 23)
          532             return -1;
          533     }
          534     else {
          535         if (Hours < 1 || Hours > 12)
          536             return -1;
          537         if (Hours == 12)
          538             Hours = 0;
          539         if (Meridian == MERpm)
          540             Hours += 12;
          541     }
          542     return (Hours * 60L + Minutes) * 60L + Seconds;
          543 }
          544 
          545 
          546 static time_t
          547 Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes,
          548         time_t Seconds, MERIDIAN Meridian, DSTMODE dst)
          549 {
          550     static int        DaysNormal[13] = {
          551         0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
          552     };
          553     static int        DaysLeap[13] = {
          554         0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
          555     };
          556     static int        LeapYears[] = {
          557         1972, 1976, 1980, 1984, 1988, 1992, 1996,
          558         2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
          559     };
          560     int                        *yp;
          561     int                        *mp;
          562     time_t                Julian;
          563     int                        i;
          564     time_t                tod;
          565 
          566     /* Year should not be passed as a relative value, but absolute one.
          567        so this should not happen, but just ensure it */
          568     if (Year < 0)
          569         Year = -Year;
          570     if (Year < 100) {
          571         Year += 1900;
          572         if (Year < EPOCH)
          573             Year += 100;
          574     }
          575     for (mp = DaysNormal, yp = LeapYears; yp < ARRAY_END(LeapYears); yp++)
          576         if (Year == *yp) {
          577             mp = DaysLeap;
          578             break;
          579         }
          580     if (Year < EPOCH || Year > END_OF_TIME
          581      || Month < 1 || Month > 12
          582      /* NOSTRICT *//* conversion from long may lose accuracy */
          583      || Day < 1 || Day > mp[(int)Month])
          584         return -1;
          585 
          586     Julian = Day - 1 + (Year - EPOCH) * 365;
          587     for (yp = LeapYears; yp < ARRAY_END(LeapYears); yp++, Julian++)
          588         if (Year <= *yp)
          589             break;
          590     for (i = 1; i < Month; i++)
          591         Julian += *++mp;
          592     Julian *= SECSPERDAY;
          593     Julian += yyTimezone * 60L;
          594     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
          595         return -1;
          596     Julian += tod;
          597     tod = Julian;
          598     if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
          599         Julian -= DST_OFFSET * 60 * 60;
          600     return Julian;
          601 }
          602 
          603 
          604 static time_t
          605 DSTcorrect(time_t Start, time_t Future)
          606 {
          607     time_t        StartDay;
          608     time_t        FutureDay;
          609 
          610     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
          611     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
          612     return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
          613 }
          614 
          615 
          616 static time_t
          617 RelativeMonth(time_t Start, time_t RelMonth)
          618 {
          619     struct tm        *tm;
          620     time_t        Month;
          621     time_t        Year;
          622 
          623     tm = localtime(&Start);
          624     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
          625     Year = Month / 12;
          626     Year += 1900;
          627     Month = Month % 12 + 1;
          628     return DSTcorrect(Start,
          629             Convert(Month, (time_t)tm->tm_mday, Year,
          630                 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
          631                 MER24, DSTmaybe));
          632 }
          633 
          634 
          635 static int
          636 LookupWord(char *buff, int length)
          637 {
          638     char *p;
          639     const char *q;
          640     TABLE *tp;
          641     int        c;
          642 
          643     p = buff;
          644     c = p[0];
          645 
          646     /* See if we have an abbreviation for a month. */
          647     if (length == 3 || (length == 4 && p[3] == '.'))
          648         for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++) {
          649             q = tp->name;
          650             if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
          651                 yylval.Number = tp->value;
          652                 return tp->type;
          653             }
          654         }
          655     else
          656         for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++)
          657             if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
          658                 yylval.Number = tp->value;
          659                 return tp->type;
          660             }
          661 
          662     /* Try for a timezone. */
          663     for (tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++)
          664         if (c == tp->name[0] && p[1] == tp->name[1]
          665          && strcmp(p, tp->name) == 0) {
          666             yylval.Number = tp->value;
          667             return tp->type;
          668         }
          669 
          670     /* Try the units table. */
          671     for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++)
          672         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
          673             yylval.Number = tp->value;
          674             return tp->type;
          675         }
          676 
          677     /* Strip off any plural and try the units table again. */
          678     if (--length > 0 && p[length] == 's') {
          679         p[length] = '\0';
          680         for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++)
          681             if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
          682                 p[length] = 's';
          683                 yylval.Number = tp->value;
          684                 return tp->type;
          685             }
          686         p[length] = 's';
          687     }
          688     length++;
          689 
          690     /* Drop out any periods. */
          691     for (p = buff, q = buff; *q; q++)
          692         if (*q != '.')
          693             *p++ = *q;
          694     *p = '\0';
          695 
          696     /* Try the meridians. */
          697     if (buff[1] == 'm' && buff[2] == '\0') {
          698         if (buff[0] == 'a') {
          699             yylval.Meridian = MERam;
          700             return tMERIDIAN;
          701         }
          702         if (buff[0] == 'p') {
          703             yylval.Meridian = MERpm;
          704             return tMERIDIAN;
          705         }
          706     }
          707 
          708     /* If we saw any periods, try the timezones again. */
          709     if (p - buff != length) {
          710         c = buff[0];
          711         for (p = buff, tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++)
          712             if (c == tp->name[0] && p[1] == tp->name[1]
          713             && strcmp(p, tp->name) == 0) {
          714                 yylval.Number = tp->value;
          715                 return tp->type;
          716             }
          717     }
          718 
          719     /* Unknown word -- assume GMT timezone. */
          720     yylval.Number = 0;
          721     return tZONE;
          722 }
          723 
          724 
          725 static int
          726 date_lex(void)
          727 {
          728     char                c;
          729     char                *p;
          730     char                buff[20];
          731     int                        sign;
          732     int                        i;
          733     int                        nesting;
          734 
          735     for ( ; ; ) {
          736         /* Get first character after the whitespace. */
          737         for ( ; ; ) {
          738             while (CTYPE(isspace, (int)*yyInput))
          739                 yyInput++;
          740             c = *yyInput;
          741 
          742             /* Ignore RFC 822 comments, typically time zone names. */
          743             if (c != LPAREN)
          744                 break;
          745             for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
          746                 if (c == LPAREN)
          747                     nesting++;
          748                 else if (!IS7BIT(c) || c == '\0' || c == '\r'
          749                      || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
          750                     /* Lexical error: bad comment. */
          751                     return '?';
          752             yyInput++;
          753         }
          754 
          755         /* A number? */
          756         if (CTYPE(isdigit, (int)c) || c == '-' || c == '+') {
          757             if (c == '-' || c == '+') {
          758                 sign = c == '-' ? -1 : 1;
          759                 yyInput++;
          760                 if (!CTYPE(isdigit, (int)*yyInput))
          761                     /* Skip the plus or minus sign. */
          762                     continue;
          763             }
          764             else
          765                 sign = 0;
          766             for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, (int)c); )
          767                 i = 10 * i + c - '0';
          768             yyInput--;
          769             yylval.Number = sign < 0 ? -i : i;
          770             return sign ? tSNUMBER : tUNUMBER;
          771         }
          772 
          773         /* A word? */
          774         if (CTYPE(isalpha, (int)c)) {
          775             for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, (int)c); )
          776                 if (p < &buff[sizeof buff - 1])
          777                     *p++ = CTYPE(isupper, (int)c) ? tolower(c) : c;
          778             *p = '\0';
          779             yyInput--;
          780             return LookupWord(buff, p - buff);
          781         }
          782 
          783         return *yyInput++;
          784     }
          785 }
          786 
          787 
          788 time_t
          789 parsedate(char *p)
          790 {
          791     time_t                now;
          792     struct tm                *tm;
          793     time_t                Start;
          794 
          795     now = time(NULL);
          796     yyInput = p;
          797 
          798     tm = gmtime(&now);
          799     yyYear = tm->tm_year + 1900;
          800     yyMonth = tm->tm_mon + 1;
          801     yyDay = tm->tm_mday;
          802     yyTimezone = 0;
          803     yyDSTmode = DSTmaybe;
          804     yyHour = 0;
          805     yyMinutes = 0;
          806     yySeconds = 0;
          807     yyMeridian = MER24;
          808     yyRelSeconds = 0;
          809     yyRelMonth = 0;
          810     yyHaveDate = 0;
          811     yyHaveRel = 0;
          812     yyHaveTime = 0;
          813 
          814     if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
          815         return -1;
          816 
          817     if (yyHaveDate || yyHaveTime) {
          818         Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
          819                     yyMeridian, yyDSTmode);
          820         if (Start < 0)
          821             return -1;
          822     }
          823     else {
          824         Start = now;
          825         if (!yyHaveRel)
          826             Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
          827     }
          828 
          829     Start += yyRelSeconds;
          830     if (yyRelMonth)
          831         Start += RelativeMonth(Start, yyRelMonth);
          832 
          833     /* Have to do *something* with a legitimate -1 so it's distinguishable
          834      * from the error return value.  (Alternately could set errno on error.) */
          835     return Start == -1 ? 0 : Start;
          836 }
          837 
          838 
          839 #if        defined(TEST)
          840 
          841 #if        YYDEBUG
          842 extern int        yydebug;
          843 #endif        /* YYDEBUG */
          844 
          845 /* ARGSUSED */
          846 int
          847 main(int ac, char *av[])
          848 {
          849     char        buff[128];
          850     time_t        d;
          851 
          852 #if        YYDEBUG
          853     yydebug = 1;
          854 #endif        /* YYDEBUG */
          855 
          856     printf("Enter date, or blank line to exit.\n\t> ");
          857     for ( ; ; ) {
          858         printf("\t> ");
          859         fflush(stdout);
          860         if (gets(buff) == NULL || buff[0] == '\n')
          861             break;
          862 #if        YYDEBUG
          863         if (strcmp(buff, "yydebug") == 0) {
          864             yydebug = !yydebug;
          865             printf("yydebug = %s\n", yydebug ? "on" : "off");
          866             continue;
          867         }
          868 #endif        /* YYDEBUG */
          869         d = parsedate(buff);
          870         if (d == -1)
          871             printf("Bad format - couldn't convert.\n");
          872         else
          873             printf("%s", ctime(&d));
          874     }
          875 
          876     exit(0);
          877     /* NOTREACHED */
          878 }
          879 #endif        /* defined(TEST) */