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