]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/find/getdate.y
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 #include <sys/cdefs.h>
16 __FBSDID("$FreeBSD$");
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 #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 #include <time.h>
62
63 #define yyparse getdate_yyparse
64 #define yylex getdate_yylex
65 #define yyerror getdate_yyerror
66
67 static int yyparse(void);
68 static int yylex(void);
69 static int yyerror(const char *);
70
71 time_t get_date(char *);
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     const 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,             0, 0 }
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,             0,              0 }
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,             0,              0 }
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,    0,         0 }
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,     0,      0 }
533 };
534
535 \f
536
537
538 /* ARGSUSED */
539 static int
540 yyerror(const char *s __unused)
541 {
542   return 0;
543 }
544
545
546 static time_t
547 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
548 {
549     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
550         return -1;
551     switch (Meridian) {
552     case MER24:
553         if (Hours < 0 || Hours > 23)
554             return -1;
555         return (Hours * 60L + Minutes) * 60L + Seconds;
556     case MERam:
557         if (Hours < 1 || Hours > 12)
558             return -1;
559         if (Hours == 12)
560             Hours = 0;
561         return (Hours * 60L + Minutes) * 60L + Seconds;
562     case MERpm:
563         if (Hours < 1 || Hours > 12)
564             return -1;
565         if (Hours == 12)
566             Hours = 0;
567         return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
568     default:
569         abort ();
570     }
571     /* NOTREACHED */
572 }
573
574
575 /* Year is either
576    * A negative number, which means to use its absolute value (why?)
577    * A number from 0 to 99, which means a year from 1900 to 1999, or
578    * The actual year (>=100).  */
579 static time_t
580 Convert(time_t Month, time_t Day, time_t Year,
581         time_t Hours, time_t Minutes, time_t Seconds,
582         MERIDIAN Meridian, DSTMODE DSTmode)
583 {
584     static int DaysInMonth[12] = {
585         31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
586     };
587     time_t      tod;
588     time_t      Julian;
589     int         i;
590
591     if (Year < 0)
592         Year = -Year;
593     if (Year < 69)
594         Year += 2000;
595     else if (Year < 100)
596         Year += 1900;
597     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
598                     ? 29 : 28;
599     /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
600        I'm too lazy to try to check for time_t overflow in another way.  */
601     if (Year < EPOCH || Year > 2038
602      || Month < 1 || Month > 12
603      /* Lint fluff:  "conversion from long may lose accuracy" */
604      || Day < 1 || Day > DaysInMonth[(int)--Month])
605         return -1;
606
607     for (Julian = Day - 1, i = 0; i < Month; i++)
608         Julian += DaysInMonth[i];
609     for (i = EPOCH; i < Year; i++)
610         Julian += 365 + (i % 4 == 0);
611     Julian *= SECSPERDAY;
612     Julian += yyTimezone * 60L;
613     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
614         return -1;
615     Julian += tod;
616     if (DSTmode == DSTon
617      || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
618         Julian -= 60 * 60;
619     return Julian;
620 }
621
622
623 static time_t
624 DSTcorrect(time_t Start, time_t Future)
625 {
626     time_t      StartDay;
627     time_t      FutureDay;
628
629     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
630     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
631     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
632 }
633
634
635 static time_t
636 RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
637 {
638     struct tm   *tm;
639     time_t      now;
640
641     now = Start;
642     tm = localtime(&now);
643     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
644     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
645     return DSTcorrect(Start, now);
646 }
647
648
649 static time_t
650 RelativeMonth(time_t Start, time_t RelMonth)
651 {
652     struct tm   *tm;
653     time_t      Month;
654     time_t      Year;
655
656     if (RelMonth == 0)
657         return 0;
658     tm = localtime(&Start);
659     Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
660     Year = Month / 12;
661     Month = Month % 12 + 1;
662     return DSTcorrect(Start,
663             Convert(Month, (time_t)tm->tm_mday, Year,
664                 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
665                 MER24, DSTmaybe));
666 }
667
668
669 static int
670 LookupWord(char *buff)
671 {
672     char        *p;
673     char        *q;
674     const TABLE *tp;
675     int         i;
676     int         abbrev;
677
678     /* Make it lowercase. */
679     for (p = buff; *p; p++)
680         if (isupper(*p))
681             *p = tolower(*p);
682
683     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
684         yylval.Meridian = MERam;
685         return tMERIDIAN;
686     }
687     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
688         yylval.Meridian = MERpm;
689         return tMERIDIAN;
690     }
691
692     /* See if we have an abbreviation for a month. */
693     if (strlen(buff) == 3)
694         abbrev = 1;
695     else if (strlen(buff) == 4 && buff[3] == '.') {
696         abbrev = 1;
697         buff[3] = '\0';
698     }
699     else
700         abbrev = 0;
701
702     for (tp = MonthDayTable; tp->name; tp++) {
703         if (abbrev) {
704             if (strncmp(buff, tp->name, 3) == 0) {
705                 yylval.Number = tp->value;
706                 return tp->type;
707             }
708         }
709         else if (strcmp(buff, tp->name) == 0) {
710             yylval.Number = tp->value;
711             return tp->type;
712         }
713     }
714
715     for (tp = TimezoneTable; tp->name; tp++)
716         if (strcmp(buff, tp->name) == 0) {
717             yylval.Number = tp->value;
718             return tp->type;
719         }
720
721     if (strcmp(buff, "dst") == 0) 
722         return tDST;
723
724     for (tp = UnitsTable; tp->name; tp++)
725         if (strcmp(buff, tp->name) == 0) {
726             yylval.Number = tp->value;
727             return tp->type;
728         }
729
730     /* Strip off any plural and try the units table again. */
731     i = strlen(buff) - 1;
732     if (buff[i] == 's') {
733         buff[i] = '\0';
734         for (tp = UnitsTable; tp->name; tp++)
735             if (strcmp(buff, tp->name) == 0) {
736                 yylval.Number = tp->value;
737                 return tp->type;
738             }
739         buff[i] = 's';          /* Put back for "this" in OtherTable. */
740     }
741
742     for (tp = OtherTable; tp->name; tp++)
743         if (strcmp(buff, tp->name) == 0) {
744             yylval.Number = tp->value;
745             return tp->type;
746         }
747
748     /* Military timezones. */
749     if (buff[1] == '\0' && isalpha(*buff)) {
750         for (tp = MilitaryTable; tp->name; tp++)
751             if (strcmp(buff, tp->name) == 0) {
752                 yylval.Number = tp->value;
753                 return tp->type;
754             }
755     }
756
757     /* Drop out any periods and try the timezone table again. */
758     for (i = 0, p = q = buff; *q; q++)
759         if (*q != '.')
760             *p++ = *q;
761         else
762             i++;
763     *p = '\0';
764     if (i)
765         for (tp = TimezoneTable; tp->name; tp++)
766             if (strcmp(buff, tp->name) == 0) {
767                 yylval.Number = tp->value;
768                 return tp->type;
769             }
770
771     return tID;
772 }
773
774
775 static int
776 yylex(void)
777 {
778     char        c;
779     char        *p;
780     char        buff[20];
781     int         Count;
782     int         sign;
783
784     for ( ; ; ) {
785         while (isspace(*yyInput))
786             yyInput++;
787
788         if (isdigit(c = *yyInput) || c == '-' || c == '+') {
789             if (c == '-' || c == '+') {
790                 sign = c == '-' ? -1 : 1;
791                 if (!isdigit(*++yyInput))
792                     /* skip the '-' sign */
793                     continue;
794             }
795             else
796                 sign = 0;
797             for (yylval.Number = 0; isdigit(c = *yyInput++); )
798                 yylval.Number = 10 * yylval.Number + c - '0';
799             yyInput--;
800             if (sign < 0)
801                 yylval.Number = -yylval.Number;
802             return sign ? tSNUMBER : tUNUMBER;
803         }
804         if (isalpha(c)) {
805             for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
806                 if (p < &buff[sizeof buff - 1])
807                     *p++ = c;
808             *p = '\0';
809             yyInput--;
810             return LookupWord(buff);
811         }
812         if (c != '(')
813             return *yyInput++;
814         Count = 0;
815         do {
816             c = *yyInput++;
817             if (c == '\0')
818                 return c;
819             if (c == '(')
820                 Count++;
821             else if (c == ')')
822                 Count--;
823         } while (Count > 0);
824     }
825 }
826
827 #define TM_YEAR_ORIGIN 1900
828
829 /* Yield A - B, measured in seconds.  */
830 static long
831 difftm (struct tm *a, struct tm *b)
832 {
833   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
834   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
835   int days = (
836               /* difference in day of year */
837               a->tm_yday - b->tm_yday
838               /* + intervening leap days */
839               +  ((ay >> 2) - (by >> 2))
840               -  (ay/100 - by/100)
841               +  ((ay/100 >> 2) - (by/100 >> 2))
842               /* + difference in years * 365 */
843               +  (long)(ay-by) * 365
844               );
845   return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
846               + (a->tm_min - b->tm_min))
847           + (a->tm_sec - b->tm_sec));
848 }
849
850 time_t
851 get_date(char *p)
852 {
853     struct tm           *tm, *gmt_ptr, gmt;
854     int                 tzoff;
855     time_t              Start;
856     time_t              tod;
857     time_t nowtime;
858
859     bzero (&gmt, sizeof(struct tm));
860     yyInput = p;
861
862     (void)time (&nowtime);
863
864     gmt_ptr = gmtime (&nowtime);
865     if (gmt_ptr != NULL)
866     {
867         /* Make a copy, in case localtime modifies *tm (I think
868            that comment now applies to *gmt_ptr, but I am too
869            lazy to dig into how gmtime and locatime allocate the
870            structures they return pointers to).  */
871         gmt = *gmt_ptr;
872     }
873
874     if (! (tm = localtime (&nowtime)))
875         return -1;
876
877     if (gmt_ptr != NULL)
878         tzoff = difftm (&gmt, tm) / 60;
879     else
880         /* We are on a system like VMS, where the system clock is
881            in local time and the system has no concept of timezones.
882            Hopefully we can fake this out (for the case in which the
883            user specifies no timezone) by just saying the timezone
884            is zero.  */
885         tzoff = 0;
886
887     if(tm->tm_isdst)
888         tzoff += 60;
889
890     tm = localtime(&nowtime);
891     yyYear = tm->tm_year + 1900;
892     yyMonth = tm->tm_mon + 1;
893     yyDay = tm->tm_mday;
894     yyTimezone = tzoff;
895     yyDSTmode = DSTmaybe;
896     yyHour = 0;
897     yyMinutes = 0;
898     yySeconds = 0;
899     yyMeridian = MER24;
900     yyRelSeconds = 0;
901     yyRelMonth = 0;
902     yyHaveDate = 0;
903     yyHaveDay = 0;
904     yyHaveRel = 0;
905     yyHaveTime = 0;
906     yyHaveZone = 0;
907
908     if (yyparse()
909      || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
910         return -1;
911
912     if (yyHaveDate || yyHaveTime || yyHaveDay) {
913         Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
914                     yyMeridian, yyDSTmode);
915         if (Start < 0)
916             return -1;
917     }
918     else {
919         Start = nowtime;
920         if (!yyHaveRel)
921             Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
922     }
923
924     Start += yyRelSeconds;
925     Start += RelativeMonth(Start, yyRelMonth);
926
927     if (yyHaveDay && !yyHaveDate) {
928         tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
929         Start += tod;
930     }
931
932     /* Have to do *something* with a legitimate -1 so it's distinguishable
933      * from the error return value.  (Alternately could set errno on error.) */
934     return Start == -1 ? 0 : Start;
935 }
936
937
938 #if     defined(TEST)
939
940 /* ARGSUSED */
941 int
942 main(int ac, char *av[])
943 {
944     char        buff[128];
945     time_t      d;
946
947     (void)printf("Enter date, or blank line to exit.\n\t> ");
948     (void)fflush(stdout);
949     while (gets(buff) && buff[0]) {
950         d = get_date(buff);
951         if (d == -1)
952             (void)printf("Bad format - couldn't convert.\n");
953         else
954             (void)printf("%s", ctime(&d));
955         (void)printf("\t> ");
956         (void)fflush(stdout);
957     }
958     exit(0);
959     /* NOTREACHED */
960 }
961 #endif  /* defined(TEST) */