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