"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/parsdate.y" (3 Oct 2014, 24137 Bytes) of package /linux/misc/tin-2.4.1.tar.gz:


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.

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