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