home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / devel / tcl / tclx7_31.z / tclx7_31 / tcldev / tclX7.3a-p1 / src / tclXgetdate.y < prev    next >
Encoding:
Text File  |  1993-11-19  |  25.9 KB  |  906 lines

  1. /* 
  2.  * tclXgetdate.y --
  3.  *
  4.  * Contains yacc grammer for parsing date and time strings based on getdate.y.
  5.  *-----------------------------------------------------------------------------
  6.  * Copyright 1992-1993 Karl Lehenbauer and Mark Diekhans.
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11.  * Mark Diekhans make no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without express or
  13.  * implied warranty.
  14.  *-----------------------------------------------------------------------------
  15.  * $Id: tclXgetdate.y,v 3.0 1993/11/19 06:59:42 markd Rel $
  16.  *-----------------------------------------------------------------------------
  17.  * This code is a modified version of getdate.y.  It was changed to be able
  18.  * to convert a larger range of years along with other tweaks to make it more
  19.  * portable.  The following header is for the version of getdate.y that this
  20.  * code is based on, theys guys are the real heros here.
  21.  *-----------------------------------------------------------------------------
  22.  * $Revision: 3.0 $
  23.  *
  24.  *  Originally written by Steven M. Bellovin <smb@research.att.com> while
  25.  *  at the University of North Carolina at Chapel Hill.  Later tweaked by
  26.  *  a couple of people on Usenet.  Completely overhauled by Rich $alz
  27.  *  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990;
  28.  *  send any email to Rich.
  29.  *
  30.  *  This grammar has eight shift/reduce conflicts.
  31.  *
  32.  *  This code is in the public domain and has no copyright.
  33.  *-----------------------------------------------------------------------------
  34.  */
  35. %{
  36. /*
  37.  * WARNING: External functions in this file are prototyped in tclExtdInt.h,
  38.  * but that file is not included as it breaks SVR4.
  39.  */
  40. #include <stdio.h>
  41. #include <sys/types.h>
  42. #include <ctype.h>
  43. #include <time.h>
  44. #include "tcl.h"
  45.  
  46. #define yyparse         date_parse
  47. #define yylex           date_lex
  48. #define yyerror         date_error
  49.  
  50. #define EPOCH           1970
  51. #define START_OF_TIME   1902
  52. #define END_OF_TIME     2037
  53.  
  54. #define HOUR(x)         ((int) (60 * x))
  55. #define SECSPERDAY      (24L * 60L * 60L)
  56.  
  57.  
  58. /*
  59. **  An entry in the lexical lookup table.
  60. */
  61. typedef struct _TABLE {
  62.     char        *name;
  63.     int         type;
  64.     time_t      value;
  65. } TABLE;
  66.  
  67.  
  68. /*
  69. **  Daylight-savings mode:  on, off, or not yet known.
  70. */
  71. typedef enum _DSTMODE {
  72.     DSTon, DSToff, DSTmaybe
  73. } DSTMODE;
  74.  
  75. /*
  76. **  Meridian:  am, pm, or 24-hour style.
  77. */
  78. typedef enum _MERIDIAN {
  79.     MERam, MERpm, MER24
  80. } MERIDIAN;
  81.  
  82.  
  83. /*
  84. **  Global variables.  We could get rid of most of these by using a good
  85. **  union as the yacc stack.  (This routine was originally written before
  86. **  yacc had the %union construct.)  Maybe someday; right now we only use
  87. **  the %union very rarely.
  88. */
  89. static char     *yyInput;
  90. static DSTMODE  yyDSTmode;
  91. static time_t   yyDayOrdinal;
  92. static time_t   yyDayNumber;
  93. static int      yyHaveDate;
  94. static int      yyHaveDay;
  95. static int      yyHaveRel;
  96. static int      yyHaveTime;
  97. static int      yyHaveZone;
  98. static time_t   yyTimezone;
  99. static time_t   yyDay;
  100. static time_t   yyHour;
  101. static time_t   yyMinutes;
  102. static time_t   yyMonth;
  103. static time_t   yySeconds;
  104. static time_t   yyYear;
  105. static MERIDIAN yyMeridian;
  106. static time_t   yyRelMonth;
  107. static time_t   yyRelSeconds;
  108.  
  109.  
  110. extern struct tm  *localtime();
  111. %}
  112.  
  113. %union {
  114.     time_t              Number;
  115.     enum _MERIDIAN      Meridian;
  116. }
  117.  
  118. %token  tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
  119. %token  tSEC_UNIT tSNUMBER tUNUMBER tZONE tEPOCH
  120.  
  121. %type   <Number>        tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
  122. %type   <Number>        tSEC_UNIT tSNUMBER tUNUMBER tZONE
  123. %type   <Meridian>      tMERIDIAN o_merid
  124.  
  125. %%
  126.  
  127. spec    : /* NULL */
  128.         | spec item
  129.         ;
  130.  
  131. item    : time {
  132.             yyHaveTime++;
  133.         }
  134.         | zone {
  135.             yyHaveZone++;
  136.         }
  137.         | date {
  138.             yyHaveDate++;
  139.         }
  140.         | day {
  141.             yyHaveDay++;
  142.         }
  143.         | rel {
  144.             yyHaveRel++;
  145.         }
  146.         | number
  147.         ;
  148.  
  149. time    : tUNUMBER tMERIDIAN {
  150.             yyHour = $1;
  151.             yyMinutes = 0;
  152.             yySeconds = 0;
  153.             yyMeridian = $2;
  154.         }
  155.         | tUNUMBER ':' tUNUMBER o_merid {
  156.             yyHour = $1;
  157.             yyMinutes = $3;
  158.             yySeconds = 0;
  159.             yyMeridian = $4;
  160.         }
  161.         | tUNUMBER ':' tUNUMBER tSNUMBER {
  162.             yyHour = $1;
  163.             yyMinutes = $3;
  164.             yyMeridian = MER24;
  165.             yyDSTmode = DSToff;
  166.             yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
  167.         }
  168.         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
  169.             yyHour = $1;
  170.             yyMinutes = $3;
  171.             yySeconds = $5;
  172.             yyMeridian = $6;
  173.         }
  174.         | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
  175.             yyHour = $1;
  176.             yyMinutes = $3;
  177.             yySeconds = $5;
  178.             yyMeridian = MER24;
  179.             yyDSTmode = DSToff;
  180.             yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
  181.         }
  182.         ;
  183.  
  184. zone    : tZONE {
  185.             yyTimezone = $1;
  186.             yyDSTmode = DSToff;
  187.         }
  188.         | tDAYZONE {
  189.             yyTimezone = $1;
  190.             yyDSTmode = DSTon;
  191.         }
  192.         ;
  193.  
  194. day     : tDAY {
  195.             yyDayOrdinal = 1;
  196.             yyDayNumber = $1;
  197.         }
  198.         | tDAY ',' {
  199.             yyDayOrdinal = 1;
  200.             yyDayNumber = $1;
  201.         }
  202.         | tUNUMBER tDAY {
  203.             yyDayOrdinal = $1;
  204.             yyDayNumber = $2;
  205.         }
  206.         ;
  207.  
  208. date    : tUNUMBER '/' tUNUMBER {
  209.             yyMonth = $1;
  210.             yyDay = $3;
  211.         }
  212.         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
  213.             yyMonth = $1;
  214.             yyDay = $3;
  215.             yyYear = $5;
  216.         }
  217.         | tMONTH tUNUMBER {
  218.             yyMonth = $1;
  219.             yyDay = $2;
  220.         }
  221.         | tMONTH tUNUMBER ',' tUNUMBER {
  222.             yyMonth = $1;
  223.             yyDay = $2;
  224.             yyYear = $4;
  225.         }
  226.         | tUNUMBER tMONTH {
  227.             yyMonth = $2;
  228.             yyDay = $1;
  229.         }
  230.           | tEPOCH {
  231.                 yyMonth = 1;
  232.                 yyDay = 1;
  233.                 yyYear = EPOCH;
  234.           }
  235.         | tUNUMBER tMONTH tUNUMBER {
  236.             yyMonth = $2;
  237.             yyDay = $1;
  238.             yyYear = $3;
  239.         }
  240.         ;
  241.  
  242. rel     : relunit tAGO {
  243.             yyRelSeconds = -yyRelSeconds;
  244.             yyRelMonth = -yyRelMonth;
  245.         }
  246.         | relunit
  247.         ;
  248.  
  249. relunit : tUNUMBER tMINUTE_UNIT {
  250.             yyRelSeconds += $1 * $2 * 60L;
  251.         }
  252.         | tSNUMBER tMINUTE_UNIT {
  253.             yyRelSeconds += $1 * $2 * 60L;
  254.         }
  255.         | tMINUTE_UNIT {
  256.             yyRelSeconds += $1 * 60L;
  257.         }
  258.         | tSNUMBER tSEC_UNIT {
  259.             yyRelSeconds += $1;
  260.         }
  261.         | tUNUMBER tSEC_UNIT {
  262.             yyRelSeconds += $1;
  263.         }
  264.         | tSEC_UNIT {
  265.             yyRelSeconds++;
  266.         }
  267.         | tSNUMBER tMONTH_UNIT {
  268.             yyRelMonth += $1 * $2;
  269.         }
  270.         | tUNUMBER tMONTH_UNIT {
  271.             yyRelMonth += $1 * $2;
  272.         }
  273.         | tMONTH_UNIT {
  274.             yyRelMonth += $1;
  275.         }
  276.         ;
  277.  
  278. number  : tUNUMBER {
  279.             if (yyHaveTime && yyHaveDate && !yyHaveRel)
  280.                 yyYear = $1;
  281.             else {
  282.                 yyHaveTime++;
  283.                 if ($1 < 100) {
  284.                     yyHour = $1;
  285.                     yyMinutes = 0;
  286.                 }
  287.                 else {
  288.                     yyHour = $1 / 100;
  289.                     yyMinutes = $1 % 100;
  290.                 }
  291.                 yySeconds = 0;
  292.                 yyMeridian = MER24;
  293.             }
  294.         }
  295.         ;
  296.  
  297. o_merid : /* NULL */ {
  298.             $$ = MER24;
  299.         }
  300.         | tMERIDIAN {
  301.             $$ = $1;
  302.         }
  303.         ;
  304.  
  305. %%
  306.  
  307. /*
  308.  * Month and day table.
  309.  */
  310. static TABLE    MonthDayTable[] = {
  311.     { "january",        tMONTH,  1 },
  312.     { "february",       tMONTH,  2 },
  313.     { "march",          tMONTH,  3 },
  314.     { "april",          tMONTH,  4 },
  315.     { "may",            tMONTH,  5 },
  316.     { "june",           tMONTH,  6 },
  317.     { "july",           tMONTH,  7 },
  318.     { "august",         tMONTH,  8 },
  319.     { "september",      tMONTH,  9 },
  320.     { "sept",           tMONTH,  9 },
  321.     { "october",        tMONTH, 10 },
  322.     { "november",       tMONTH, 11 },
  323.     { "december",       tMONTH, 12 },
  324.     { "sunday",         tDAY, 0 },
  325.     { "monday",         tDAY, 1 },
  326.     { "tuesday",        tDAY, 2 },
  327.     { "tues",           tDAY, 2 },
  328.     { "wednesday",      tDAY, 3 },
  329.     { "wednes",         tDAY, 3 },
  330.     { "thursday",       tDAY, 4 },
  331.     { "thur",           tDAY, 4 },
  332.     { "thurs",          tDAY, 4 },
  333.     { "friday",         tDAY, 5 },
  334.     { "saturday",       tDAY, 6 },
  335.     { NULL }
  336. };
  337.  
  338. /*
  339.  * Time units table.
  340.  */
  341. static TABLE    UnitsTable[] = {
  342.     { "year",           tMONTH_UNIT,    12 },
  343.     { "month",          tMONTH_UNIT,    1 },
  344.     { "fortnight",      tMINUTE_UNIT,   14 * 24 * 60 },
  345.     { "week",           tMINUTE_UNIT,   7 * 24 * 60 },
  346.     { "day",            tMINUTE_UNIT,   1 * 24 * 60 },
  347.     { "hour",           tMINUTE_UNIT,   60 },
  348.     { "minute",         tMINUTE_UNIT,   1 },
  349.     { "min",            tMINUTE_UNIT,   1 },
  350.     { "second",         tSEC_UNIT,      1 },
  351.     { "sec",            tSEC_UNIT,      1 },
  352.     { NULL }
  353. };
  354.  
  355. /*
  356.  * Assorted relative-time words.
  357.  */
  358. static TABLE    OtherTable[] = {
  359.     { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
  360.     { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
  361.     { "today",          tMINUTE_UNIT,   0 },
  362.     { "now",            tMINUTE_UNIT,   0 },
  363.     { "last",           tUNUMBER,       -1 },
  364.     { "this",           tMINUTE_UNIT,   0 },
  365.     { "next",           tUNUMBER,       2 },
  366. #if 0
  367.     { "first",          tUNUMBER,       1 },
  368. /*  { "second",         tUNUMBER,       2 }, */
  369.     { "third",          tUNUMBER,       3 },
  370.     { "fourth",         tUNUMBER,       4 },
  371.     { "fifth",          tUNUMBER,       5 },
  372.     { "sixth",          tUNUMBER,       6 },
  373.     { "seventh",        tUNUMBER,       7 },
  374.     { "eighth",         tUNUMBER,       8 },
  375.     { "ninth",          tUNUMBER,       9 },
  376.     { "tenth",          tUNUMBER,       10 },
  377.     { "eleventh",       tUNUMBER,       11 },
  378.     { "twelfth",        tUNUMBER,       12 },
  379. #endif
  380.     { "ago",            tAGO,   1 },
  381.     { "epoch",          tEPOCH,   0 },
  382.     { NULL }
  383. };
  384.  
  385. /*
  386.  * The timezone table.  (Note: This table was modified to not use any floating
  387.  * point constants to work around an SGI compiler bug).
  388.  */
  389. static TABLE    TimezoneTable[] = {
  390.     { "gmt",    tZONE,     HOUR( 0) },      /* Greenwich Mean */
  391.     { "ut",     tZONE,     HOUR( 0) },      /* Universal (Coordinated) */
  392.     { "utc",    tZONE,     HOUR( 0) },
  393.     { "wet",    tZONE,     HOUR( 0) } ,     /* Western European */
  394.     { "bst",    tDAYZONE,  HOUR( 0) },      /* British Summer */
  395.     { "wat",    tZONE,     HOUR( 1) },      /* West Africa */
  396.     { "at",     tZONE,     HOUR( 2) },      /* Azores */
  397. #if     0
  398.     /* For completeness.  BST is also British Summer, and GST is
  399.      * also Guam Standard. */
  400.     { "bst",    tZONE,     HOUR( 3) },      /* Brazil Standard */
  401.     { "gst",    tZONE,     HOUR( 3) },      /* Greenland Standard */
  402. #endif
  403.     { "nft",    tZONE,     HOUR( 7/2) },    /* Newfoundland */
  404.     { "nst",    tZONE,     HOUR( 7/2) },    /* Newfoundland Standard */
  405.     { "ndt",    tDAYZONE,  HOUR( 7/2) },    /* Newfoundland Daylight */
  406.     { "ast",    tZONE,     HOUR( 4) },      /* Atlantic Standard */
  407.     { "adt",    tDAYZONE,  HOUR( 4) },      /* Atlantic Daylight */
  408.     { "est",    tZONE,     HOUR( 5) },      /* Eastern Standard */
  409.     { "edt",    tDAYZONE,  HOUR( 5) },      /* Eastern Daylight */
  410.     { "cst",    tZONE,     HOUR( 6) },      /* Central Standard */
  411.     { "cdt",    tDAYZONE,  HOUR( 6) },      /* Central Daylight */
  412.     { "mst",    tZONE,     HOUR( 7) },      /* Mountain Standard */
  413.     { "mdt",    tDAYZONE,  HOUR( 7) },      /* Mountain Daylight */
  414.     { "pst",    tZONE,     HOUR( 8) },      /* Pacific Standard */
  415.     { "pdt",    tDAYZONE,  HOUR( 8) },      /* Pacific Daylight */
  416.     { "yst",    tZONE,     HOUR( 9) },      /* Yukon Standard */
  417.     { "ydt",    tDAYZONE,  HOUR( 9) },      /* Yukon Daylight */
  418.     { "hst",    tZONE,     HOUR(10) },      /* Hawaii Standard */
  419.     { "hdt",    tDAYZONE,  HOUR(10) },      /* Hawaii Daylight */
  420.     { "cat",    tZONE,     HOUR(10) },      /* Central Alaska */
  421.     { "ahst",   tZONE,     HOUR(10) },      /* Alaska-Hawaii Standard */
  422.     { "nt",     tZONE,     HOUR(11) },      /* Nome */
  423.     { "idlw",   tZONE,     HOUR(12) },      /* International Date Line West */
  424.     { "cet",    tZONE,    -HOUR( 1) },      /* Central European */
  425.     { "met",    tZONE,    -HOUR( 1) },      /* Middle European */
  426.     { "mewt",   tZONE,    -HOUR( 1) },      /* Middle European Winter */
  427.     { "mest",   tDAYZONE, -HOUR( 1) },      /* Middle European Summer */
  428.     { "swt",    tZONE,    -HOUR( 1) },      /* Swedish Winter */
  429.     { "sst",    tDAYZONE, -HOUR( 1) },      /* Swedish Summer */
  430.     { "fwt",    tZONE,    -HOUR( 1) },      /* French Winter */
  431.     { "fst",    tDAYZONE, -HOUR( 1) },      /* French Summer */
  432.     { "eet",    tZONE,    -HOUR( 2) },      /* Eastern Europe, USSR Zone 1 */
  433.     { "bt",     tZONE,    -HOUR( 3) },      /* Baghdad, USSR Zone 2 */
  434.     { "it",     tZONE,    -HOUR( 7/2) },    /* Iran */
  435.     { "zp4",    tZONE,    -HOUR( 4) },      /* USSR Zone 3 */
  436.     { "zp5",    tZONE,    -HOUR( 5) },      /* USSR Zone 4 */
  437.     { "ist",    tZONE,    -HOUR(11/2) },    /* Indian Standard */
  438.     { "zp6",    tZONE,    -HOUR( 6) },      /* USSR Zone 5 */
  439. #if     0
  440.     /* For completeness.  NST is also Newfoundland Stanard, nad SST is
  441.      * also Swedish Summer. */
  442.     { "nst",    tZONE,    -HOUR(13/2) },    /* North Sumatra */
  443.     { "sst",    tZONE,    -HOUR( 7) },      /* South Sumatra, USSR Zone 6 */
  444. #endif  /* 0 */
  445.     { "wast",   tZONE,    -HOUR( 7) },      /* West Australian Standard */
  446.     { "wadt",   tDAYZONE, -HOUR( 7) },      /* West Australian Daylight */
  447.     { "jt",     tZONE,    -HOUR(15/2) },    /* Java (3pm in Cronusland!) */
  448.     { "cct",    tZONE,    -HOUR( 8) },      /* China Coast, USSR Zone 7 */
  449.     { "jst",    tZONE,    -HOUR( 9) },      /* Japan Standard, USSR Zone 8 */
  450.     { "cast",   tZONE,    -HOUR(19/2) },    /* Central Australian Standard */
  451.     { "cadt",   tDAYZONE, -HOUR(19/2) },    /* Central Australian Daylight */
  452.     { "east",   tZONE,    -HOUR(10) },      /* Eastern Australian Standard */
  453.     { "eadt",   tDAYZONE, -HOUR(10) },      /* Eastern Australian Daylight */
  454.     { "gst",    tZONE,    -HOUR(10) },      /* Guam Standard, USSR Zone 9 */
  455.     { "nzt",    tZONE,    -HOUR(12) },      /* New Zealand */
  456.     { "nzst",   tZONE,    -HOUR(12) },      /* New Zealand Standard */
  457.     { "nzdt",   tDAYZONE, -HOUR(12) },      /* New Zealand Daylight */
  458.     { "idle",   tZONE,    -HOUR(12) },      /* International Date Line East */
  459.     {  NULL  }
  460. };
  461.  
  462. /* Military timezone table. */
  463. static TABLE    MilitaryTable[] = {
  464.     { "a",      tZONE,  HOUR(  1) },
  465.     { "b",      tZONE,  HOUR(  2) },
  466.     { "c",      tZONE,  HOUR(  3) },
  467.     { "d",      tZONE,  HOUR(  4) },
  468.     { "e",      tZONE,  HOUR(  5) },
  469.     { "f",      tZONE,  HOUR(  6) },
  470.     { "g",      tZONE,  HOUR(  7) },
  471.     { "h",      tZONE,  HOUR(  8) },
  472.     { "i",      tZONE,  HOUR(  9) },
  473.     { "k",      tZONE,  HOUR( 10) },
  474.     { "l",      tZONE,  HOUR( 11) },
  475.     { "m",      tZONE,  HOUR( 12) },
  476.     { "n",      tZONE,  HOUR(- 1) },
  477.     { "o",      tZONE,  HOUR(- 2) },
  478.     { "p",      tZONE,  HOUR(- 3) },
  479.     { "q",      tZONE,  HOUR(- 4) },
  480.     { "r",      tZONE,  HOUR(- 5) },
  481.     { "s",      tZONE,  HOUR(- 6) },
  482.     { "t",      tZONE,  HOUR(- 7) },
  483.     { "u",      tZONE,  HOUR(- 8) },
  484.     { "v",      tZONE,  HOUR(- 9) },
  485.     { "w",      tZONE,  HOUR(-10) },
  486.     { "x",      tZONE,  HOUR(-11) },
  487.     { "y",      tZONE,  HOUR(-12) },
  488.     { "z",      tZONE,  HOUR(  0) },
  489.     { NULL }
  490. };
  491.  
  492. /*
  493.  * Prototypes of internal functions.
  494.  */
  495. static
  496. yyerror _ANSI_ARGS_((char *s));
  497.  
  498. static time_t
  499. ToSeconds _ANSI_ARGS_((time_t      Hours,
  500.                        time_t      Minutes,
  501.                        time_t      Seconds,
  502.                        MERIDIAN    Meridian));
  503.  
  504. static int
  505. Convert _ANSI_ARGS_((time_t      Month,
  506.                      time_t      Day,
  507.                      time_t      Year,
  508.                      time_t      Hours,
  509.                      time_t      Minutes,
  510.                      time_t      Seconds,
  511.                      MERIDIAN    Meridia,
  512.                      DSTMODE     DSTmode,
  513.                      time_t     *TimePtr));
  514.  
  515. static time_t
  516. DSTcorrect _ANSI_ARGS_((time_t      Start,
  517.                         time_t      Future));
  518.  
  519. static time_t
  520. RelativeDate _ANSI_ARGS_((time_t      Start,
  521.                           time_t      DayOrdinal,
  522.                           time_t      DayNumber));
  523.  
  524. static int
  525. RelativeMonth _ANSI_ARGS_((time_t      Start,
  526.                            time_t      RelMonth,
  527.                            time_t     *TimePtr));
  528. static int
  529. LookupWord _ANSI_ARGS_((char  *buff));
  530.  
  531. static int
  532. yylex ();
  533.  
  534.  
  535. /*
  536.  * Dump error messages in the bit bucket, not to useful.
  537.  */
  538. static
  539. yyerror(s)
  540.     char  *s;
  541. {
  542. }
  543.  
  544.  
  545. static time_t
  546. ToSeconds(Hours, Minutes, Seconds, Meridian)
  547.     time_t      Hours;
  548.     time_t      Minutes;
  549.     time_t      Seconds;
  550.     MERIDIAN    Meridian;
  551. {
  552.     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
  553.         return -1;
  554.     switch (Meridian) {
  555.     case MER24:
  556.         if (Hours < 0 || Hours > 23)
  557.             return -1;
  558.         return (Hours * 60L + Minutes) * 60L + Seconds;
  559.     case MERam:
  560.         if (Hours < 1 || Hours > 12)
  561.             return -1;
  562.         return ((Hours % 12) * 60L + Minutes) * 60L + Seconds;
  563.     case MERpm:
  564.         if (Hours < 1 || Hours > 12)
  565.             return -1;
  566.         return (((Hours % 12) + 12) * 60L + Minutes) * 60L + Seconds;
  567.     }
  568.     /* NOTREACHED */
  569. }
  570.  
  571.  
  572. static int
  573. Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode, TimePtr)
  574.     time_t      Month;
  575.     time_t      Day;
  576.     time_t      Year;
  577.     time_t      Hours;
  578.     time_t      Minutes;
  579.     time_t      Seconds;
  580.     MERIDIAN    Meridian;
  581.     DSTMODE     DSTmode;
  582.     time_t     *TimePtr;
  583. {
  584.     static int  DaysInMonth[12] = {
  585.         31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  586.     };
  587.     time_t      tod;
  588.     time_t      Julian;
  589.     int         i;
  590.  
  591.     if (Year < 0)
  592.         Year = -Year;
  593.     if (Year < 100)
  594.         Year += 1900;
  595.     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
  596.                     ? 29 : 28;
  597.     if (Month < 1 || Month > 12
  598.      || Year < START_OF_TIME || Year > END_OF_TIME
  599.      || Day < 1 || Day > DaysInMonth[(int)--Month])
  600.         return -1;
  601.  
  602.     for (Julian = Day - 1, i = 0; i < Month; i++)
  603.         Julian += DaysInMonth[i];
  604.     if (Year >= EPOCH) {
  605.         for (i = EPOCH; i < Year; i++)
  606.             Julian += 365 + (i % 4 == 0);
  607.     } else {
  608.         for (i = Year; i < EPOCH; i++)
  609.             Julian -= 365 + (i % 4 == 0);
  610.     }
  611.     Julian *= SECSPERDAY;
  612.     Julian += yyTimezone * 60L;
  613.     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
  614.         return -1;
  615.     Julian += tod;
  616.     if (DSTmode == DSTon
  617.      || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
  618.         Julian -= 60 * 60;
  619.     *TimePtr = Julian;
  620.     return 0;
  621. }
  622.  
  623.  
  624. static time_t
  625. DSTcorrect(Start, Future)
  626.     time_t      Start;
  627.     time_t      Future;
  628. {
  629.     time_t      StartDay;
  630.     time_t      FutureDay;
  631.  
  632.     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
  633.     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
  634.     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
  635. }
  636.  
  637.  
  638. static time_t
  639. RelativeDate(Start, DayOrdinal, DayNumber)
  640.     time_t      Start;
  641.     time_t      DayOrdinal;
  642.     time_t      DayNumber;
  643. {
  644.     struct tm   *tm;
  645.     time_t      now;
  646.  
  647.     now = Start;
  648.     tm = localtime(&now);
  649.     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
  650.     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
  651.     return DSTcorrect(Start, now);
  652. }
  653.  
  654.  
  655. static int
  656. RelativeMonth(Start, RelMonth, TimePtr)
  657.     time_t      Start;
  658.     time_t      RelMonth;
  659.     time_t     *TimePtr;
  660. {
  661.     struct tm   *tm;
  662.     time_t      Month;
  663.     time_t      Year;
  664.     time_t      Julian;
  665.  
  666.     if (RelMonth == 0) {
  667.         *TimePtr = 0;
  668.         return 0;
  669.     }
  670.     tm = localtime(&Start);
  671.     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
  672.     Year = Month / 12;
  673.     Month = Month % 12 + 1;
  674.     if (Convert(Month, (time_t)tm->tm_mday, Year,
  675.                 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
  676.                 MER24, DSTmaybe, &Julian) < 0)
  677.         return -1;
  678.     *TimePtr = DSTcorrect(Start, Julian);
  679.     return 0;
  680. }
  681.  
  682.  
  683. static int
  684. LookupWord(buff)
  685.     char                *buff;
  686. {
  687.     register char       *p;
  688.     register char       *q;
  689.     register TABLE      *tp;
  690.     int                 i;
  691.     int                 abbrev;
  692.  
  693.     /* Make it lowercase. */
  694.     for (p = buff; *p; p++)
  695.         if (isupper(*p))
  696.             *p = tolower(*p);
  697.  
  698.     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
  699.         yylval.Meridian = MERam;
  700.         return tMERIDIAN;
  701.     }
  702.     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
  703.         yylval.Meridian = MERpm;
  704.         return tMERIDIAN;
  705.     }
  706.  
  707.     /* See if we have an abbreviation for a month. */
  708.     if (strlen(buff) == 3)
  709.         abbrev = 1;
  710.     else if (strlen(buff) == 4 && buff[3] == '.') {
  711.         abbrev = 1;
  712.         buff[3] = '\0';
  713.     }
  714.     else
  715.         abbrev = 0;
  716.  
  717.     for (tp = MonthDayTable; tp->name; tp++) {
  718.         if (abbrev) {
  719.             if (strncmp(buff, tp->name, 3) == 0) {
  720.                 yylval.Number = tp->value;
  721.                 return tp->type;
  722.             }
  723.         }
  724.         else if (strcmp(buff, tp->name) == 0) {
  725.             yylval.Number = tp->value;
  726.             return tp->type;
  727.         }
  728.     }
  729.  
  730.     for (tp = TimezoneTable; tp->name; tp++)
  731.         if (strcmp(buff, tp->name) == 0) {
  732.             yylval.Number = tp->value;
  733.             return tp->type;
  734.         }
  735.  
  736.     for (tp = UnitsTable; tp->name; tp++)
  737.         if (strcmp(buff, tp->name) == 0) {
  738.             yylval.Number = tp->value;
  739.             return tp->type;
  740.         }
  741.  
  742.     /* Strip off any plural and try the units table again. */
  743.     i = strlen(buff) - 1;
  744.     if (buff[i] == 's') {
  745.         buff[i] = '\0';
  746.         for (tp = UnitsTable; tp->name; tp++)
  747.             if (strcmp(buff, tp->name) == 0) {
  748.                 yylval.Number = tp->value;
  749.                 return tp->type;
  750.             }
  751.     }
  752.  
  753.     for (tp = OtherTable; tp->name; tp++)
  754.         if (strcmp(buff, tp->name) == 0) {
  755.             yylval.Number = tp->value;
  756.             return tp->type;
  757.         }
  758.  
  759.     /* Military timezones. */
  760.     if (buff[1] == '\0' && isalpha(*buff)) {
  761.         for (tp = MilitaryTable; tp->name; tp++)
  762.             if (strcmp(buff, tp->name) == 0) {
  763.                 yylval.Number = tp->value;
  764.                 return tp->type;
  765.             }
  766.     }
  767.  
  768.     /* Drop out any periods and try the timezone table again. */
  769.     for (i = 0, p = q = buff; *q; q++)
  770.         if (*q != '.')
  771.             *p++ = *q;
  772.         else
  773.             i++;
  774.     *p = '\0';
  775.     if (i)
  776.         for (tp = TimezoneTable; tp->name; tp++)
  777.             if (strcmp(buff, tp->name) == 0) {
  778.                 yylval.Number = tp->value;
  779.                 return tp->type;
  780.             }
  781.  
  782.     return tID;
  783. }
  784.  
  785.  
  786. static int
  787. yylex()
  788. {
  789.     register char       c;
  790.     register char       *p;
  791.     char                buff[20];
  792.     int                 Count;
  793.     int                 sign;
  794.  
  795.     for ( ; ; ) {
  796.         while (isspace((unsigned char) (*yyInput)))
  797.             yyInput++;
  798.  
  799.         if (isdigit(c = *yyInput) || c == '-' || c == '+') {
  800.             if (c == '-' || c == '+') {
  801.                 sign = c == '-' ? -1 : 1;
  802.                 if (!isdigit(*++yyInput))
  803.                     /* skip the '-' sign */
  804.                     continue;
  805.             }
  806.             else
  807.                 sign = 0;
  808.             for (yylval.Number = 0; isdigit(c = *yyInput++); )
  809.                 yylval.Number = 10 * yylval.Number + c - '0';
  810.             yyInput--;
  811.             if (sign < 0)
  812.                 yylval.Number = -yylval.Number;
  813.             return sign ? tSNUMBER : tUNUMBER;
  814.         }
  815.         if (isalpha(c)) {
  816.             for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
  817.                 if (p < &buff[sizeof buff - 1])
  818.                     *p++ = c;
  819.             *p = '\0';
  820.             yyInput--;
  821.             return LookupWord(buff);
  822.         }
  823.         if (c != '(')
  824.             return *yyInput++;
  825.         Count = 0;
  826.         do {
  827.             c = *yyInput++;
  828.             if (c == '\0')
  829.                 return c;
  830.             if (c == '(')
  831.                 Count++;
  832.             else if (c == ')')
  833.                 Count--;
  834.         } while (Count > 0);
  835.     }
  836. }
  837.  
  838. /*
  839.  * Specify zone is of -5000 to force GMT.  (This allows BST to work).
  840.  */
  841.  
  842. int
  843. Tcl_GetDate(p, now, zone, timePtr)
  844.     char          *p;
  845.     time_t         now;
  846.     long           zone;
  847.     time_t        *timePtr;
  848. {
  849.     struct tm           *tm;
  850.     time_t              Start;
  851.     time_t              Time;
  852.     time_t              tod;
  853.  
  854.     yyInput = p;
  855.     tm = localtime(&now);
  856.     yyYear = tm->tm_year;
  857.     yyMonth = tm->tm_mon + 1;
  858.     yyDay = tm->tm_mday;
  859.     yyTimezone = zone;
  860.     if (zone == -50000) {
  861.         yyDSTmode = DSToff;  /* assume GMT */
  862.         yyTimezone = 0;
  863.     } else {
  864.         yyDSTmode = DSTmaybe;
  865.     }
  866.     yyHour = 0;
  867.     yyMinutes = 0;
  868.     yySeconds = 0;
  869.     yyMeridian = MER24;
  870.     yyRelSeconds = 0;
  871.     yyRelMonth = 0;
  872.     yyHaveDate = 0;
  873.     yyHaveDay = 0;
  874.     yyHaveRel = 0;
  875.     yyHaveTime = 0;
  876.     yyHaveZone = 0;
  877.  
  878.     if (yyparse()
  879.      || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
  880.         return -1;
  881.  
  882.     if (yyHaveDate || yyHaveTime || yyHaveDay) {
  883.         if (Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
  884.                     yyMeridian, yyDSTmode, &Start) < 0)
  885.             return -1;
  886.     }
  887.     else {
  888.         Start = now;
  889.         if (!yyHaveRel)
  890.             Start -= ((tm->tm_hour * 60L) + tm->tm_min * 60L) + tm->tm_sec;
  891.     }
  892.  
  893.     Start += yyRelSeconds;
  894.     if (RelativeMonth(Start, yyRelMonth, &Time) < 0)
  895.         return -1;
  896.     Start += Time;
  897.  
  898.     if (yyHaveDay && !yyHaveDate) {
  899.         tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
  900.         Start += tod;
  901.     }
  902.  
  903.     *timePtr = Start;
  904.     return 0;
  905. }
  906.