"Fossies" - the Fresh Open Source Software Archive

Member "citadel/parsedate.y" (5 Jun 2021, 21372 Bytes) of package /linux/www/citadel.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. See also the last Fossies "Diffs" side-by-side code changes report for "parsedate.y": 8.24_vs_9.01.

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