Remap yacc globals to have gd_ prefix.
[gnulib.git] / lib / getdate.y
1 %{
2 /*
3 **  Originally written by Steven M. Bellovin <smb@research.att.com> while
4 **  at the University of North Carolina at Chapel Hill.  Later tweaked by
5 **  a couple of people on Usenet.  Completely overhauled by Rich $alz
6 **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
7 **  send any email to Rich.
8 **
9 **  This grammar has 10 shift/reduce conflicts.
10 **
11 **  This code is in the public domain and has no copyright.
12 */
13 /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
14 /* SUPPRESS 288 on yyerrlab *//* Label unused */
15
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18
19 #ifdef FORCE_ALLOCA_H
20 #include <alloca.h>
21 #endif
22 #endif
23
24 /* Since the code of getdate.y is not included in the Emacs executable
25    itself, there is no need to #define static in this file.  Even if
26    the code were included in the Emacs executable, it probably
27    wouldn't do any harm to #undef it here; this will only cause
28    problems if we try to write to a static variable, which I don't
29    think this code needs to do.  */
30 #ifdef emacs
31 #undef static
32 #endif
33
34 #include <stdio.h>
35 #include <ctype.h>
36
37 #if defined (STDC_HEADERS) || !defined (isascii)
38 # define ISASCII(c) 1
39 #else
40 # define ISASCII(c) isascii(c)
41 #endif
42
43 #define ISSPACE(c) (ISASCII (c) && isspace (c))
44 #define ISALPHA(c) (ISASCII (c) && isalpha (c))
45 #define ISUPPER(c) (ISASCII (c) && isupper (c))
46 #define ISDIGIT(c) (ISASCII (c) && isdigit (c))
47
48 #if     defined (vms)
49 #include <types.h>
50 #include <time.h>
51 #else
52 #include <sys/types.h>
53 #ifdef TIME_WITH_SYS_TIME
54 #include <sys/time.h>
55 #include <time.h>
56 #else
57 #ifdef HAVE_SYS_TIME_H
58 #include <sys/time.h>
59 #else
60 #include <time.h>
61 #endif
62 #endif
63
64 #ifdef timezone
65 #undef timezone /* needed for sgi */
66 #endif
67
68 #if defined (HAVE_SYS_TIMEB_H)
69 #include <sys/timeb.h>
70 #else
71
72 /* get_date uses the obsolete `struct timeb' in its interface!  FIXME.
73    Since some systems don't have it, we define it here;
74    callers must do likewise.  */
75 struct timeb
76   {
77     time_t              time;           /* Seconds since the epoch      */
78     unsigned short      millitm;        /* Field not used               */
79     short               timezone;       /* Minutes west of GMT          */
80     short               dstflag;        /* Field not used               */
81 };
82 #endif /* defined (HAVE_SYS_TIMEB_H) */
83
84 #endif  /* defined (vms) */
85
86 #if defined (STDC_HEADERS) || defined (USG)
87 #include <string.h>
88 #endif
89
90 /* Some old versions of bison generate parsers that use bcopy.
91    That loses on systems that don't provide the function, so we have
92    to redefine it here.  */
93 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
94 #define bcopy(from, to, len) memcpy ((to), (from), (len))
95 #endif
96
97 extern struct tm        *gmtime ();
98 extern struct tm        *localtime ();
99
100 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
101    as well as gratuitiously global symbol names, so we can have multiple
102    yacc generated parsers in the same program.  Note that these are only
103    the variables produced by yacc.  If other parser generators (bison,
104    byacc, etc) produce additional global names that conflict at link time,
105    then those parser generators need to be fixed instead of adding those
106    names to this list. */
107
108 #define yymaxdepth gd_maxdepth
109 #define yyparse gd_parse
110 #define yylex   gd_lex
111 #define yyerror gd_error
112 #define yylval  gd_lval
113 #define yychar  gd_char
114 #define yydebug gd_debug
115 #define yypact  gd_pact
116 #define yyr1    gd_r1
117 #define yyr2    gd_r2
118 #define yydef   gd_def
119 #define yychk   gd_chk
120 #define yypgo   gd_pgo
121 #define yyact   gd_act
122 #define yyexca  gd_exca
123 #define yyerrflag gd_errflag
124 #define yynerrs gd_nerrs
125 #define yyps    gd_ps
126 #define yypv    gd_pv
127 #define yys     gd_s
128 #define yy_yys  gd_yys
129 #define yystate gd_state
130 #define yytmp   gd_tmp
131 #define yyv     gd_v
132 #define yy_yyv  gd_yyv
133 #define yyval   gd_val
134 #define yylloc  gd_lloc
135 #define yyreds  gd_reds          /* With YYDEBUG defined */
136 #define yytoks  gd_toks          /* With YYDEBUG defined */
137 #define yylhs   gd_yylhs
138 #define yylen   gd_yylen
139 #define yydefred gd_yydefred
140 #define yydgoto gd_yydgoto
141 #define yysindex gd_yysindex
142 #define yyrindex gd_yyrindex
143 #define yygindex gd_yygindex
144 #define yytable  gd_yytable
145 #define yycheck  gd_yycheck
146
147 static int yylex ();
148 static int yyerror ();
149
150 #define EPOCH           1970
151 #define DOOMSDAY        2038
152 #define HOUR(x)         ((time_t)(x) * 60)
153 #define SECSPERDAY      (24L * 60L * 60L)
154
155 #define MAX_BUFF_LEN    128   /* size of buffer to read the date into */
156
157 /*
158 **  An entry in the lexical lookup table.
159 */
160 typedef struct _TABLE {
161     const char  *name;
162     int         type;
163     time_t      value;
164 } TABLE;
165
166
167 /*
168 **  Daylight-savings mode:  on, off, or not yet known.
169 */
170 typedef enum _DSTMODE {
171     DSTon, DSToff, DSTmaybe
172 } DSTMODE;
173
174 /*
175 **  Meridian:  am, pm, or 24-hour style.
176 */
177 typedef enum _MERIDIAN {
178     MERam, MERpm, MER24
179 } MERIDIAN;
180
181
182 /*
183 **  Global variables.  We could get rid of most of these by using a good
184 **  union as the yacc stack.  (This routine was originally written before
185 **  yacc had the %union construct.)  Maybe someday; right now we only use
186 **  the %union very rarely.
187 */
188 static char     *yyInput;
189 static DSTMODE  yyDSTmode;
190 static time_t   yyDayOrdinal;
191 static time_t   yyDayNumber;
192 static int      yyHaveDate;
193 static int      yyHaveDay;
194 static int      yyHaveRel;
195 static int      yyHaveTime;
196 static int      yyHaveZone;
197 static time_t   yyTimezone;
198 static time_t   yyDay;
199 static time_t   yyHour;
200 static time_t   yyMinutes;
201 static time_t   yyMonth;
202 static time_t   yySeconds;
203 static time_t   yyYear;
204 static MERIDIAN yyMeridian;
205 static time_t   yyRelMonth;
206 static time_t   yyRelSeconds;
207
208 %}
209
210 %union {
211     time_t              Number;
212     enum _MERIDIAN      Meridian;
213 }
214
215 %token  tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
216 %token  tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
217
218 %type   <Number>        tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
219 %type   <Number>        tSEC_UNIT tSNUMBER tUNUMBER tZONE
220 %type   <Meridian>      tMERIDIAN o_merid
221
222 %%
223
224 spec    : /* NULL */
225         | spec item
226         ;
227
228 item    : time {
229             yyHaveTime++;
230         }
231         | zone {
232             yyHaveZone++;
233         }
234         | date {
235             yyHaveDate++;
236         }
237         | day {
238             yyHaveDay++;
239         }
240         | rel {
241             yyHaveRel++;
242         }
243         | number
244         ;
245
246 time    : tUNUMBER tMERIDIAN {
247             yyHour = $1;
248             yyMinutes = 0;
249             yySeconds = 0;
250             yyMeridian = $2;
251         }
252         | tUNUMBER ':' tUNUMBER o_merid {
253             yyHour = $1;
254             yyMinutes = $3;
255             yySeconds = 0;
256             yyMeridian = $4;
257         }
258         | tUNUMBER ':' tUNUMBER tSNUMBER {
259             yyHour = $1;
260             yyMinutes = $3;
261             yyMeridian = MER24;
262             yyDSTmode = DSToff;
263             yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
264         }
265         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
266             yyHour = $1;
267             yyMinutes = $3;
268             yySeconds = $5;
269             yyMeridian = $6;
270         }
271         | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
272             yyHour = $1;
273             yyMinutes = $3;
274             yySeconds = $5;
275             yyMeridian = MER24;
276             yyDSTmode = DSToff;
277             yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
278         }
279         ;
280
281 zone    : tZONE {
282             yyTimezone = $1;
283             yyDSTmode = DSToff;
284         }
285         | tDAYZONE {
286             yyTimezone = $1;
287             yyDSTmode = DSTon;
288         }
289         |
290           tZONE tDST {
291             yyTimezone = $1;
292             yyDSTmode = DSTon;
293         }
294         ;
295
296 day     : tDAY {
297             yyDayOrdinal = 1;
298             yyDayNumber = $1;
299         }
300         | tDAY ',' {
301             yyDayOrdinal = 1;
302             yyDayNumber = $1;
303         }
304         | tUNUMBER tDAY {
305             yyDayOrdinal = $1;
306             yyDayNumber = $2;
307         }
308         ;
309
310 date    : tUNUMBER '/' tUNUMBER {
311             yyMonth = $1;
312             yyDay = $3;
313         }
314         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
315             yyMonth = $1;
316             yyDay = $3;
317             yyYear = $5;
318         }
319         | tUNUMBER tSNUMBER tSNUMBER {
320             /* ISO 8601 format.  yyyy-mm-dd.  */
321             yyYear = $1;
322             yyMonth = -$2;
323             yyDay = -$3;
324         }
325         | tUNUMBER tMONTH tSNUMBER {
326             /* e.g. 17-JUN-1992.  */
327             yyDay = $1;
328             yyMonth = $2;
329             yyYear = -$3;
330         }
331         | tMONTH tUNUMBER {
332             yyMonth = $1;
333             yyDay = $2;
334         }
335         | tMONTH tUNUMBER ',' tUNUMBER {
336             yyMonth = $1;
337             yyDay = $2;
338             yyYear = $4;
339         }
340         | tUNUMBER tMONTH {
341             yyMonth = $2;
342             yyDay = $1;
343         }
344         | tUNUMBER tMONTH tUNUMBER {
345             yyMonth = $2;
346             yyDay = $1;
347             yyYear = $3;
348         }
349         ;
350
351 rel     : relunit tAGO {
352             yyRelSeconds = -yyRelSeconds;
353             yyRelMonth = -yyRelMonth;
354         }
355         | relunit
356         ;
357
358 relunit : tUNUMBER tMINUTE_UNIT {
359             yyRelSeconds += $1 * $2 * 60L;
360         }
361         | tSNUMBER tMINUTE_UNIT {
362             yyRelSeconds += $1 * $2 * 60L;
363         }
364         | tMINUTE_UNIT {
365             yyRelSeconds += $1 * 60L;
366         }
367         | tSNUMBER tSEC_UNIT {
368             yyRelSeconds += $1;
369         }
370         | tUNUMBER tSEC_UNIT {
371             yyRelSeconds += $1;
372         }
373         | tSEC_UNIT {
374             yyRelSeconds++;
375         }
376         | tSNUMBER tMONTH_UNIT {
377             yyRelMonth += $1 * $2;
378         }
379         | tUNUMBER tMONTH_UNIT {
380             yyRelMonth += $1 * $2;
381         }
382         | tMONTH_UNIT {
383             yyRelMonth += $1;
384         }
385         ;
386
387 number  : tUNUMBER {
388             if (yyHaveTime && yyHaveDate && !yyHaveRel)
389                 yyYear = $1;
390             else {
391                 if ($1>10000) {
392                     yyHaveDate++;
393                     yyDay= ($1)%100;
394                     yyMonth= ($1/100)%100;
395                     yyYear = $1/10000;
396                 }
397                 else {
398                     yyHaveTime++;
399                     if ($1 < 100) {
400                         yyHour = $1;
401                         yyMinutes = 0;
402                     }
403                     else {
404                         yyHour = $1 / 100;
405                         yyMinutes = $1 % 100;
406                     }
407                     yySeconds = 0;
408                     yyMeridian = MER24;
409                 }
410             }
411         }
412         ;
413
414 o_merid : /* NULL */ {
415             $$ = MER24;
416         }
417         | tMERIDIAN {
418             $$ = $1;
419         }
420         ;
421
422 %%
423
424 /* Month and day table. */
425 static TABLE const MonthDayTable[] = {
426     { "january",        tMONTH,  1 },
427     { "february",       tMONTH,  2 },
428     { "march",          tMONTH,  3 },
429     { "april",          tMONTH,  4 },
430     { "may",            tMONTH,  5 },
431     { "june",           tMONTH,  6 },
432     { "july",           tMONTH,  7 },
433     { "august",         tMONTH,  8 },
434     { "september",      tMONTH,  9 },
435     { "sept",           tMONTH,  9 },
436     { "october",        tMONTH, 10 },
437     { "november",       tMONTH, 11 },
438     { "december",       tMONTH, 12 },
439     { "sunday",         tDAY, 0 },
440     { "monday",         tDAY, 1 },
441     { "tuesday",        tDAY, 2 },
442     { "tues",           tDAY, 2 },
443     { "wednesday",      tDAY, 3 },
444     { "wednes",         tDAY, 3 },
445     { "thursday",       tDAY, 4 },
446     { "thur",           tDAY, 4 },
447     { "thurs",          tDAY, 4 },
448     { "friday",         tDAY, 5 },
449     { "saturday",       tDAY, 6 },
450     { NULL }
451 };
452
453 /* Time units table. */
454 static TABLE const UnitsTable[] = {
455     { "year",           tMONTH_UNIT,    12 },
456     { "month",          tMONTH_UNIT,    1 },
457     { "fortnight",      tMINUTE_UNIT,   14 * 24 * 60 },
458     { "week",           tMINUTE_UNIT,   7 * 24 * 60 },
459     { "day",            tMINUTE_UNIT,   1 * 24 * 60 },
460     { "hour",           tMINUTE_UNIT,   60 },
461     { "minute",         tMINUTE_UNIT,   1 },
462     { "min",            tMINUTE_UNIT,   1 },
463     { "second",         tSEC_UNIT,      1 },
464     { "sec",            tSEC_UNIT,      1 },
465     { NULL }
466 };
467
468 /* Assorted relative-time words. */
469 static TABLE const OtherTable[] = {
470     { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
471     { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
472     { "today",          tMINUTE_UNIT,   0 },
473     { "now",            tMINUTE_UNIT,   0 },
474     { "last",           tUNUMBER,       -1 },
475     { "this",           tMINUTE_UNIT,   0 },
476     { "next",           tUNUMBER,       2 },
477     { "first",          tUNUMBER,       1 },
478 /*  { "second",         tUNUMBER,       2 }, */
479     { "third",          tUNUMBER,       3 },
480     { "fourth",         tUNUMBER,       4 },
481     { "fifth",          tUNUMBER,       5 },
482     { "sixth",          tUNUMBER,       6 },
483     { "seventh",        tUNUMBER,       7 },
484     { "eighth",         tUNUMBER,       8 },
485     { "ninth",          tUNUMBER,       9 },
486     { "tenth",          tUNUMBER,       10 },
487     { "eleventh",       tUNUMBER,       11 },
488     { "twelfth",        tUNUMBER,       12 },
489     { "ago",            tAGO,   1 },
490     { NULL }
491 };
492
493 /* The timezone table. */
494 /* Some of these are commented out because a time_t can't store a float. */
495 static TABLE const TimezoneTable[] = {
496     { "gmt",    tZONE,     HOUR ( 0) }, /* Greenwich Mean */
497     { "ut",     tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
498     { "utc",    tZONE,     HOUR ( 0) },
499     { "wet",    tZONE,     HOUR ( 0) }, /* Western European */
500     { "bst",    tDAYZONE,  HOUR ( 0) }, /* British Summer */
501     { "wat",    tZONE,     HOUR ( 1) }, /* West Africa */
502     { "at",     tZONE,     HOUR ( 2) }, /* Azores */
503 #if     0
504     /* For completeness.  BST is also British Summer, and GST is
505      * also Guam Standard. */
506     { "bst",    tZONE,     HOUR ( 3) }, /* Brazil Standard */
507     { "gst",    tZONE,     HOUR ( 3) }, /* Greenland Standard */
508 #endif
509 #if 0
510     { "nft",    tZONE,     HOUR (3.5) },        /* Newfoundland */
511     { "nst",    tZONE,     HOUR (3.5) },        /* Newfoundland Standard */
512     { "ndt",    tDAYZONE,  HOUR (3.5) },        /* Newfoundland Daylight */
513 #endif
514     { "ast",    tZONE,     HOUR ( 4) }, /* Atlantic Standard */
515     { "adt",    tDAYZONE,  HOUR ( 4) }, /* Atlantic Daylight */
516     { "est",    tZONE,     HOUR ( 5) }, /* Eastern Standard */
517     { "edt",    tDAYZONE,  HOUR ( 5) }, /* Eastern Daylight */
518     { "cst",    tZONE,     HOUR ( 6) }, /* Central Standard */
519     { "cdt",    tDAYZONE,  HOUR ( 6) }, /* Central Daylight */
520     { "mst",    tZONE,     HOUR ( 7) }, /* Mountain Standard */
521     { "mdt",    tDAYZONE,  HOUR ( 7) }, /* Mountain Daylight */
522     { "pst",    tZONE,     HOUR ( 8) }, /* Pacific Standard */
523     { "pdt",    tDAYZONE,  HOUR ( 8) }, /* Pacific Daylight */
524     { "yst",    tZONE,     HOUR ( 9) }, /* Yukon Standard */
525     { "ydt",    tDAYZONE,  HOUR ( 9) }, /* Yukon Daylight */
526     { "hst",    tZONE,     HOUR (10) }, /* Hawaii Standard */
527     { "hdt",    tDAYZONE,  HOUR (10) }, /* Hawaii Daylight */
528     { "cat",    tZONE,     HOUR (10) }, /* Central Alaska */
529     { "ahst",   tZONE,     HOUR (10) }, /* Alaska-Hawaii Standard */
530     { "nt",     tZONE,     HOUR (11) }, /* Nome */
531     { "idlw",   tZONE,     HOUR (12) }, /* International Date Line West */
532     { "cet",    tZONE,     -HOUR (1) }, /* Central European */
533     { "met",    tZONE,     -HOUR (1) }, /* Middle European */
534     { "mewt",   tZONE,     -HOUR (1) }, /* Middle European Winter */
535     { "mest",   tDAYZONE,  -HOUR (1) }, /* Middle European Summer */
536     { "mesz",   tDAYZONE,  -HOUR (1) }, /* Middle European Summer */
537     { "swt",    tZONE,     -HOUR (1) }, /* Swedish Winter */
538     { "sst",    tDAYZONE,  -HOUR (1) }, /* Swedish Summer */
539     { "fwt",    tZONE,     -HOUR (1) }, /* French Winter */
540     { "fst",    tDAYZONE,  -HOUR (1) }, /* French Summer */
541     { "eet",    tZONE,     -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
542     { "bt",     tZONE,     -HOUR (3) }, /* Baghdad, USSR Zone 2 */
543 #if 0
544     { "it",     tZONE,     -HOUR (3.5) },/* Iran */
545 #endif
546     { "zp4",    tZONE,     -HOUR (4) }, /* USSR Zone 3 */
547     { "zp5",    tZONE,     -HOUR (5) }, /* USSR Zone 4 */
548 #if 0
549     { "ist",    tZONE,     -HOUR (5.5) },/* Indian Standard */
550 #endif
551     { "zp6",    tZONE,     -HOUR (6) }, /* USSR Zone 5 */
552 #if     0
553     /* For completeness.  NST is also Newfoundland Standard, and SST is
554      * also Swedish Summer. */
555     { "nst",    tZONE,     -HOUR (6.5) },/* North Sumatra */
556     { "sst",    tZONE,     -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
557 #endif  /* 0 */
558     { "wast",   tZONE,     -HOUR (7) }, /* West Australian Standard */
559     { "wadt",   tDAYZONE,  -HOUR (7) }, /* West Australian Daylight */
560 #if 0
561     { "jt",     tZONE,     -HOUR (7.5) },/* Java (3pm in Cronusland!) */
562 #endif
563     { "cct",    tZONE,     -HOUR (8) }, /* China Coast, USSR Zone 7 */
564     { "jst",    tZONE,     -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
565 #if 0
566     { "cast",   tZONE,     -HOUR (9.5) },/* Central Australian Standard */
567     { "cadt",   tDAYZONE,  -HOUR (9.5) },/* Central Australian Daylight */
568 #endif
569     { "east",   tZONE,     -HOUR (10) },        /* Eastern Australian Standard */
570     { "eadt",   tDAYZONE,  -HOUR (10) },        /* Eastern Australian Daylight */
571     { "gst",    tZONE,     -HOUR (10) },        /* Guam Standard, USSR Zone 9 */
572     { "nzt",    tZONE,     -HOUR (12) },        /* New Zealand */
573     { "nzst",   tZONE,     -HOUR (12) },        /* New Zealand Standard */
574     { "nzdt",   tDAYZONE,  -HOUR (12) },        /* New Zealand Daylight */
575     { "idle",   tZONE,     -HOUR (12) },        /* International Date Line East */
576     {  NULL  }
577 };
578
579 /* Military timezone table. */
580 static TABLE const MilitaryTable[] = {
581     { "a",      tZONE,  HOUR (  1) },
582     { "b",      tZONE,  HOUR (  2) },
583     { "c",      tZONE,  HOUR (  3) },
584     { "d",      tZONE,  HOUR (  4) },
585     { "e",      tZONE,  HOUR (  5) },
586     { "f",      tZONE,  HOUR (  6) },
587     { "g",      tZONE,  HOUR (  7) },
588     { "h",      tZONE,  HOUR (  8) },
589     { "i",      tZONE,  HOUR (  9) },
590     { "k",      tZONE,  HOUR ( 10) },
591     { "l",      tZONE,  HOUR ( 11) },
592     { "m",      tZONE,  HOUR ( 12) },
593     { "n",      tZONE,  HOUR (- 1) },
594     { "o",      tZONE,  HOUR (- 2) },
595     { "p",      tZONE,  HOUR (- 3) },
596     { "q",      tZONE,  HOUR (- 4) },
597     { "r",      tZONE,  HOUR (- 5) },
598     { "s",      tZONE,  HOUR (- 6) },
599     { "t",      tZONE,  HOUR (- 7) },
600     { "u",      tZONE,  HOUR (- 8) },
601     { "v",      tZONE,  HOUR (- 9) },
602     { "w",      tZONE,  HOUR (-10) },
603     { "x",      tZONE,  HOUR (-11) },
604     { "y",      tZONE,  HOUR (-12) },
605     { "z",      tZONE,  HOUR (  0) },
606     { NULL }
607 };
608
609 \f
610
611
612 /* ARGSUSED */
613 static int
614 yyerror (s)
615     char        *s;
616 {
617   return 0;
618 }
619
620
621 static time_t
622 ToSeconds (Hours, Minutes, Seconds, Meridian)
623     time_t      Hours;
624     time_t      Minutes;
625     time_t      Seconds;
626     MERIDIAN    Meridian;
627 {
628   if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
629     return -1;
630   switch (Meridian) {
631   case MER24:
632     if (Hours < 0 || Hours > 23)
633       return -1;
634     return (Hours * 60L + Minutes) * 60L + Seconds;
635   case MERam:
636     if (Hours < 1 || Hours > 12)
637       return -1;
638     if (Hours == 12)
639       Hours = 0;
640     return (Hours * 60L + Minutes) * 60L + Seconds;
641   case MERpm:
642     if (Hours < 1 || Hours > 12)
643       return -1;
644     if (Hours == 12)
645       Hours = 0;
646     return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
647   default:
648     abort ();
649   }
650   /* NOTREACHED */
651 }
652
653
654 static time_t
655 Convert (Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
656     time_t      Month;
657     time_t      Day;
658     time_t      Year;
659     time_t      Hours;
660     time_t      Minutes;
661     time_t      Seconds;
662     MERIDIAN    Meridian;
663     DSTMODE     DSTmode;
664 {
665   static int DaysInMonth[12] = {
666     31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
667   };
668   time_t        tod;
669   time_t        Julian;
670   int           i;
671
672   if (Year < 0)
673     Year = -Year;
674   if (Year < DOOMSDAY-2000)
675     Year += 2000;
676   else if (Year < 100)
677     Year += 1900;
678   DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
679     ? 29 : 28;
680   if (Year < EPOCH || Year >= DOOMSDAY
681       || Month < 1 || Month > 12
682       /* Lint fluff:  "conversion from long may lose accuracy" */
683       || Day < 1 || Day > DaysInMonth[(int)--Month])
684     return -1;
685
686   for (Julian = Day - 1, i = 0; i < Month; i++)
687     Julian += DaysInMonth[i];
688   for (i = EPOCH; i < Year; i++)
689     Julian += 365 + (i % 4 == 0);
690   Julian *= SECSPERDAY;
691   Julian += yyTimezone * 60L;
692   if ((tod = ToSeconds (Hours, Minutes, Seconds, Meridian)) < 0)
693     return -1;
694   Julian += tod;
695   if (DSTmode == DSTon
696       || (DSTmode == DSTmaybe && localtime (&Julian)->tm_isdst))
697     Julian -= 60 * 60;
698   return Julian;
699 }
700
701
702 static time_t
703 DSTcorrect (Start, Future)
704     time_t      Start;
705     time_t      Future;
706 {
707   time_t        StartDay;
708   time_t        FutureDay;
709
710   StartDay = (localtime (&Start)->tm_hour + 1) % 24;
711   FutureDay = (localtime (&Future)->tm_hour + 1) % 24;
712   return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
713 }
714
715
716 static time_t
717 RelativeDate (Start, DayOrdinal, DayNumber)
718     time_t      Start;
719     time_t      DayOrdinal;
720     time_t      DayNumber;
721 {
722   struct tm     *tm;
723   time_t        now;
724
725   now = Start;
726   tm = localtime (&now);
727   now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
728   now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
729   return DSTcorrect (Start, now);
730 }
731
732
733 static time_t
734 RelativeMonth (Start, RelMonth)
735     time_t      Start;
736     time_t      RelMonth;
737 {
738   struct tm     *tm;
739   time_t        Month;
740   time_t        Year;
741
742   if (RelMonth == 0)
743     return 0;
744   tm = localtime (&Start);
745   Month = 12 * (1900 + tm->tm_year) + tm->tm_mon + RelMonth;
746   Year = Month / 12;
747   Month = Month % 12 + 1;
748   return DSTcorrect (Start,
749                      Convert (Month, (time_t)tm->tm_mday, Year,
750                               (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
751                               MER24, DSTmaybe));
752 }
753
754
755 static int
756 LookupWord (buff)
757     char                *buff;
758 {
759   register char *p;
760   register char *q;
761   register const TABLE  *tp;
762   int                   i;
763   int                   abbrev;
764
765   /* Make it lowercase. */
766   for (p = buff; *p; p++)
767     if (ISUPPER (*p))
768       *p = tolower (*p);
769
770   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) {
771     yylval.Meridian = MERam;
772     return tMERIDIAN;
773   }
774   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) {
775     yylval.Meridian = MERpm;
776     return tMERIDIAN;
777   }
778
779   /* See if we have an abbreviation for a month. */
780   if (strlen (buff) == 3)
781     abbrev = 1;
782   else if (strlen (buff) == 4 && buff[3] == '.') {
783     abbrev = 1;
784     buff[3] = '\0';
785   }
786   else
787     abbrev = 0;
788
789   for (tp = MonthDayTable; tp->name; tp++) {
790     if (abbrev) {
791       if (strncmp (buff, tp->name, 3) == 0) {
792         yylval.Number = tp->value;
793         return tp->type;
794       }
795     }
796     else if (strcmp (buff, tp->name) == 0) {
797       yylval.Number = tp->value;
798       return tp->type;
799     }
800   }
801
802   for (tp = TimezoneTable; tp->name; tp++)
803     if (strcmp (buff, tp->name) == 0) {
804       yylval.Number = tp->value;
805       return tp->type;
806     }
807
808   if (strcmp (buff, "dst") == 0)
809     return tDST;
810
811   for (tp = UnitsTable; tp->name; tp++)
812     if (strcmp (buff, tp->name) == 0) {
813       yylval.Number = tp->value;
814       return tp->type;
815     }
816
817   /* Strip off any plural and try the units table again. */
818   i = strlen (buff) - 1;
819   if (buff[i] == 's') {
820     buff[i] = '\0';
821     for (tp = UnitsTable; tp->name; tp++)
822       if (strcmp (buff, tp->name) == 0) {
823         yylval.Number = tp->value;
824         return tp->type;
825       }
826     buff[i] = 's';              /* Put back for "this" in OtherTable. */
827   }
828
829   for (tp = OtherTable; tp->name; tp++)
830     if (strcmp (buff, tp->name) == 0) {
831       yylval.Number = tp->value;
832       return tp->type;
833     }
834
835   /* Military timezones. */
836   if (buff[1] == '\0' && ISALPHA (*buff)) {
837     for (tp = MilitaryTable; tp->name; tp++)
838       if (strcmp (buff, tp->name) == 0) {
839         yylval.Number = tp->value;
840         return tp->type;
841       }
842   }
843
844   /* Drop out any periods and try the timezone table again. */
845   for (i = 0, p = q = buff; *q; q++)
846     if (*q != '.')
847       *p++ = *q;
848     else
849       i++;
850   *p = '\0';
851   if (i)
852     for (tp = TimezoneTable; tp->name; tp++)
853       if (strcmp (buff, tp->name) == 0) {
854         yylval.Number = tp->value;
855         return tp->type;
856       }
857
858   return tID;
859 }
860
861
862 static int
863 yylex ()
864 {
865   register char c;
866   register char *p;
867   char          buff[20];
868   int                   Count;
869   int                   sign;
870
871   for ( ; ; ) {
872     while (ISSPACE (*yyInput))
873       yyInput++;
874
875     if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') {
876       if (c == '-' || c == '+') {
877         sign = c == '-' ? -1 : 1;
878         if (!ISDIGIT (*++yyInput))
879           /* skip the '-' sign */
880           continue;
881       }
882       else
883         sign = 0;
884       for (yylval.Number = 0; ISDIGIT (c = *yyInput++); )
885         yylval.Number = 10 * yylval.Number + c - '0';
886       yyInput--;
887       if (sign < 0)
888         yylval.Number = -yylval.Number;
889       return sign ? tSNUMBER : tUNUMBER;
890     }
891     if (ISALPHA (c)) {
892       for (p = buff; ISALPHA (c = *yyInput++) || c == '.'; )
893         if (p < &buff[sizeof buff - 1])
894           *p++ = c;
895       *p = '\0';
896       yyInput--;
897       return LookupWord (buff);
898     }
899     if (c != '(')
900       return *yyInput++;
901     Count = 0;
902     do {
903       c = *yyInput++;
904       if (c == '\0')
905         return c;
906       if (c == '(')
907         Count++;
908       else if (c == ')')
909         Count--;
910     } while (Count > 0);
911   }
912 }
913
914 #define TM_YEAR_ORIGIN 1900
915
916 /* Yield A - B, measured in seconds.  */
917 static long
918 difftm (a, b)
919      struct tm *a, *b;
920 {
921   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
922   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
923   long days = (
924                /* difference in day of year */
925                a->tm_yday - b->tm_yday
926                /* + intervening leap days */
927                +  ((ay >> 2) - (by >> 2))
928                -  (ay/100 - by/100)
929                +  ((ay/100 >> 2) - (by/100 >> 2))
930                /* + difference in years * 365 */
931                +  (long)(ay-by) * 365
932                );
933   return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
934               + (a->tm_min - b->tm_min))
935           + (a->tm_sec - b->tm_sec));
936 }
937
938 time_t
939 get_date (p, now)
940     char                *p;
941     struct timeb        *now;
942 {
943   struct tm             *tm, gmt;
944   struct timeb  ftz;
945   time_t                Start;
946   time_t                tod;
947
948   yyInput = p;
949   if (now == NULL) {
950     now = &ftz;
951     (void)time (&ftz.time);
952
953     if (! (tm = gmtime (&ftz.time)))
954       return -1;
955     gmt = *tm;                  /* Make a copy, in case localtime modifies *tm.  */
956
957     if (! (tm = localtime (&ftz.time)))
958       return -1;
959
960     ftz.timezone = difftm (&gmt, tm) / 60;
961     if (tm->tm_isdst)
962       ftz.timezone += 60;
963   }
964
965   tm = localtime (&now->time);
966   yyYear = tm->tm_year;
967   yyMonth = tm->tm_mon + 1;
968   yyDay = tm->tm_mday;
969   yyTimezone = now->timezone;
970   yyDSTmode = DSTmaybe;
971   yyHour = 0;
972   yyMinutes = 0;
973   yySeconds = 0;
974   yyMeridian = MER24;
975   yyRelSeconds = 0;
976   yyRelMonth = 0;
977   yyHaveDate = 0;
978   yyHaveDay = 0;
979   yyHaveRel = 0;
980   yyHaveTime = 0;
981   yyHaveZone = 0;
982
983   if (yyparse ()
984       || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
985     return -1;
986
987   if (yyHaveDate || yyHaveTime || yyHaveDay) {
988     Start = Convert (yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
989                      yyMeridian, yyDSTmode);
990     if (Start < 0)
991       return -1;
992   }
993   else {
994     Start = now->time;
995     if (!yyHaveRel)
996       Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
997   }
998
999   Start += yyRelSeconds;
1000   Start += RelativeMonth (Start, yyRelMonth);
1001
1002   if (yyHaveDay && !yyHaveDate) {
1003     tod = RelativeDate (Start, yyDayOrdinal, yyDayNumber);
1004     Start += tod;
1005   }
1006
1007   /* Have to do *something* with a legitimate -1 so it's distinguishable
1008    * from the error return value.  (Alternately could set errno on error.) */
1009   return Start == -1 ? 0 : Start;
1010 }
1011
1012
1013 #if     defined (TEST)
1014
1015 /* ARGSUSED */
1016 int
1017 main (ac, av)
1018     int         ac;
1019     char        *av[];
1020 {
1021   char buff[MAX_BUFF_LEN + 1];
1022   time_t d;
1023
1024   (void)printf ("Enter date, or blank line to exit.\n\t> ");
1025   (void)fflush (stdout);
1026
1027   buff[MAX_BUFF_LEN] = 0;
1028   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) {
1029     d = get_date (buff, (struct timeb *)NULL);
1030     if (d == -1)
1031       (void)printf ("Bad format - couldn't convert.\n");
1032     else
1033       (void)printf ("%s", ctime (&d));
1034     (void)printf ("\t> ");
1035     (void)fflush (stdout);
1036   }
1037   exit (0);
1038   /* NOTREACHED */
1039 }
1040 #endif  /* defined (TEST) */