home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume27 / trn-3.3 / part08 / parsedate.y < prev    next >
Text File  |  1993-11-27  |  21KB  |  812 lines

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