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