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