"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/parsdate.y" (23 Aug 2022, 24451 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bison source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "parsdate.y": 2.6.1_vs_2.6.2.

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