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