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