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