]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/fifolog/lib/getdate.y
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / fifolog / lib / 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 **  Picked up from CVS and slightly cleaned up by to WARNS=5 level by
13 **  Poul-Henning Kamp <phk@FreeBSD.org>
14 **
15 ** $FreeBSD$
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/time.h>
24
25 #include "libfifolog.h"
26
27 #define yylex getdate_yylex
28 #define yyerror getdate_yyerror
29
30 static int yylex(void);
31 static int yyerror(const char *);
32
33 #define EPOCH           1970
34 #define HOUR(x)         ((time_t)(x) * 60)
35 #define SECSPERDAY      (24L * 60L * 60L)
36
37
38 /*
39 **  An entry in the lexical lookup table.
40 */
41 typedef struct _TABLE {
42     const char  *name;
43     int         type;
44     time_t      value;
45 } TABLE;
46
47
48 /*
49 **  Daylight-savings mode:  on, off, or not yet known.
50 */
51 typedef enum _DSTMODE {
52     DSToff, DSTon, DSTmaybe
53 } DSTMODE;
54
55 /*
56 **  Meridian:  am, pm, or 24-hour style.
57 */
58 typedef enum _MERIDIAN {
59     MERam, MERpm, MER24
60 } MERIDIAN;
61
62
63 /*
64 **  Global variables.  We could get rid of most of these by using a good
65 **  union as the yacc stack.  (This routine was originally written before
66 **  yacc had the %union construct.)  Maybe someday; right now we only use
67 **  the %union very rarely.
68 */
69 static char     *yyInput;
70 static DSTMODE  yyDSTmode;
71 static time_t   yyDayOrdinal;
72 static time_t   yyDayNumber;
73 static int      yyHaveDate;
74 static int      yyHaveDay;
75 static int      yyHaveRel;
76 static int      yyHaveTime;
77 static int      yyHaveZone;
78 static time_t   yyTimezone;
79 static time_t   yyDay;
80 static time_t   yyHour;
81 static time_t   yyMinutes;
82 static time_t   yyMonth;
83 static time_t   yySeconds;
84 static time_t   yyYear;
85 static MERIDIAN yyMeridian;
86 static time_t   yyRelMonth;
87 static time_t   yyRelSeconds;
88
89 %}
90
91 %union {
92     time_t              Number;
93     enum _MERIDIAN      Meridian;
94 }
95
96 %token  tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
97 %token  tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
98
99 %type   <Number>        tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
100 %type   <Number>        tSEC_UNIT tSNUMBER tUNUMBER tZONE
101 %type   <Meridian>      tMERIDIAN o_merid
102
103 %%
104
105 spec    : /* NULL */
106         | spec item
107         ;
108
109 item    : time {
110             yyHaveTime++;
111         }
112         | zone {
113             yyHaveZone++;
114         }
115         | date {
116             yyHaveDate++;
117         }
118         | day {
119             yyHaveDay++;
120         }
121         | rel {
122             yyHaveRel++;
123         }
124         | cvsstamp {
125             yyHaveTime++;
126             yyHaveDate++;
127             yyHaveZone++;
128         }
129         | number
130         ;
131
132 cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
133             yyYear = $1;
134             if (yyYear < 100) yyYear += 1900;
135             yyMonth = $3;
136             yyDay = $5;
137             yyHour = $7;
138             yyMinutes = $9;
139             yySeconds = $11;
140             yyDSTmode = DSToff;
141             yyTimezone = 0;
142         }
143         ;
144
145 time    : tUNUMBER tMERIDIAN {
146             yyHour = $1;
147             yyMinutes = 0;
148             yySeconds = 0;
149             yyMeridian = $2;
150         }
151         | tUNUMBER ':' tUNUMBER o_merid {
152             yyHour = $1;
153             yyMinutes = $3;
154             yySeconds = 0;
155             yyMeridian = $4;
156         }
157         | tUNUMBER ':' tUNUMBER tSNUMBER {
158             yyHour = $1;
159             yyMinutes = $3;
160             yyMeridian = MER24;
161             yyDSTmode = DSToff;
162             yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
163         }
164         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
165             yyHour = $1;
166             yyMinutes = $3;
167             yySeconds = $5;
168             yyMeridian = $6;
169         }
170         | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
171             yyHour = $1;
172             yyMinutes = $3;
173             yySeconds = $5;
174             yyMeridian = MER24;
175             yyDSTmode = DSToff;
176             yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
177         }
178         ;
179
180 zone    : tZONE {
181             yyTimezone = $1;
182             yyDSTmode = DSToff;
183         }
184         | tDAYZONE {
185             yyTimezone = $1;
186             yyDSTmode = DSTon;
187         }
188         |
189           tZONE tDST {
190             yyTimezone = $1;
191             yyDSTmode = DSTon;
192         }
193         ;
194
195 day     : tDAY {
196             yyDayOrdinal = 1;
197             yyDayNumber = $1;
198         }
199         | tDAY ',' {
200             yyDayOrdinal = 1;
201             yyDayNumber = $1;
202         }
203         | tUNUMBER tDAY {
204             yyDayOrdinal = $1;
205             yyDayNumber = $2;
206         }
207         ;
208
209 date    : tUNUMBER '/' tUNUMBER {
210             yyMonth = $1;
211             yyDay = $3;
212         }
213         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
214             if ($1 >= 100) {
215                 yyYear = $1;
216                 yyMonth = $3;
217                 yyDay = $5;
218             } else {
219                 yyMonth = $1;
220                 yyDay = $3;
221                 yyYear = $5;
222             }
223         }
224         | tUNUMBER tSNUMBER tSNUMBER {
225             /* ISO 8601 format.  yyyy-mm-dd.  */
226             yyYear = $1;
227             yyMonth = -$2;
228             yyDay = -$3;
229         }
230         | tUNUMBER tMONTH tSNUMBER {
231             /* e.g. 17-JUN-1992.  */
232             yyDay = $1;
233             yyMonth = $2;
234             yyYear = -$3;
235         }
236         | tMONTH tUNUMBER {
237             yyMonth = $1;
238             yyDay = $2;
239         }
240         | tMONTH tUNUMBER ',' tUNUMBER {
241             yyMonth = $1;
242             yyDay = $2;
243             yyYear = $4;
244         }
245         | tUNUMBER tMONTH {
246             yyMonth = $2;
247             yyDay = $1;
248         }
249         | tUNUMBER tMONTH tUNUMBER {
250             yyMonth = $2;
251             yyDay = $1;
252             yyYear = $3;
253         }
254         ;
255
256 rel     : relunit tAGO {
257             yyRelSeconds = -yyRelSeconds;
258             yyRelMonth = -yyRelMonth;
259         }
260         | relunit
261         ;
262
263 relunit : tUNUMBER tMINUTE_UNIT {
264             yyRelSeconds += $1 * $2 * 60L;
265         }
266         | tSNUMBER tMINUTE_UNIT {
267             yyRelSeconds += $1 * $2 * 60L;
268         }
269         | tMINUTE_UNIT {
270             yyRelSeconds += $1 * 60L;
271         }
272         | tSNUMBER tSEC_UNIT {
273             yyRelSeconds += $1;
274         }
275         | tUNUMBER tSEC_UNIT {
276             yyRelSeconds += $1;
277         }
278         | tSEC_UNIT {
279             yyRelSeconds++;
280         }
281         | tSNUMBER tMONTH_UNIT {
282             yyRelMonth += $1 * $2;
283         }
284         | tUNUMBER tMONTH_UNIT {
285             yyRelMonth += $1 * $2;
286         }
287         | tMONTH_UNIT {
288             yyRelMonth += $1;
289         }
290         ;
291
292 number  : tUNUMBER {
293             if (yyHaveTime && yyHaveDate && !yyHaveRel)
294                 yyYear = $1;
295             else {
296                 if($1>10000) {
297                     yyHaveDate++;
298                     yyDay= ($1)%100;
299                     yyMonth= ($1/100)%100;
300                     yyYear = $1/10000;
301                 }
302                 else {
303                     yyHaveTime++;
304                     if ($1 < 100) {
305                         yyHour = $1;
306                         yyMinutes = 0;
307                     }
308                     else {
309                         yyHour = $1 / 100;
310                         yyMinutes = $1 % 100;
311                     }
312                     yySeconds = 0;
313                     yyMeridian = MER24;
314                 }
315             }
316         }
317         ;
318
319 o_merid : /* NULL */ {
320             $$ = MER24;
321         }
322         | tMERIDIAN {
323             $$ = $1;
324         }
325         ;
326
327 %%
328
329 /* Month and day table. */
330 static TABLE const MonthDayTable[] = {
331     { "january",        tMONTH,  1 },
332     { "february",       tMONTH,  2 },
333     { "march",          tMONTH,  3 },
334     { "april",          tMONTH,  4 },
335     { "may",            tMONTH,  5 },
336     { "june",           tMONTH,  6 },
337     { "july",           tMONTH,  7 },
338     { "august",         tMONTH,  8 },
339     { "september",      tMONTH,  9 },
340     { "sept",           tMONTH,  9 },
341     { "october",        tMONTH, 10 },
342     { "november",       tMONTH, 11 },
343     { "december",       tMONTH, 12 },
344     { "sunday",         tDAY, 0 },
345     { "monday",         tDAY, 1 },
346     { "tuesday",        tDAY, 2 },
347     { "tues",           tDAY, 2 },
348     { "wednesday",      tDAY, 3 },
349     { "wednes",         tDAY, 3 },
350     { "thursday",       tDAY, 4 },
351     { "thur",           tDAY, 4 },
352     { "thurs",          tDAY, 4 },
353     { "friday",         tDAY, 5 },
354     { "saturday",       tDAY, 6 },
355     { NULL,             0,    0 }
356 };
357
358 /* Time units table. */
359 static TABLE const UnitsTable[] = {
360     { "year",           tMONTH_UNIT,    12 },
361     { "month",          tMONTH_UNIT,    1 },
362     { "fortnight",      tMINUTE_UNIT,   14 * 24 * 60 },
363     { "week",           tMINUTE_UNIT,   7 * 24 * 60 },
364     { "day",            tMINUTE_UNIT,   1 * 24 * 60 },
365     { "hour",           tMINUTE_UNIT,   60 },
366     { "minute",         tMINUTE_UNIT,   1 },
367     { "min",            tMINUTE_UNIT,   1 },
368     { "second",         tSEC_UNIT,      1 },
369     { "sec",            tSEC_UNIT,      1 },
370     { NULL,             0,              0 }
371 };
372
373 /* Assorted relative-time words. */
374 static TABLE const OtherTable[] = {
375     { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
376     { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
377     { "today",          tMINUTE_UNIT,   0 },
378     { "now",            tMINUTE_UNIT,   0 },
379     { "last",           tUNUMBER,       -1 },
380     { "this",           tMINUTE_UNIT,   0 },
381     { "next",           tUNUMBER,       2 },
382     { "first",          tUNUMBER,       1 },
383 /*  { "second",         tUNUMBER,       2 }, */
384     { "third",          tUNUMBER,       3 },
385     { "fourth",         tUNUMBER,       4 },
386     { "fifth",          tUNUMBER,       5 },
387     { "sixth",          tUNUMBER,       6 },
388     { "seventh",        tUNUMBER,       7 },
389     { "eighth",         tUNUMBER,       8 },
390     { "ninth",          tUNUMBER,       9 },
391     { "tenth",          tUNUMBER,       10 },
392     { "eleventh",       tUNUMBER,       11 },
393     { "twelfth",        tUNUMBER,       12 },
394     { "ago",            tAGO,           1 },
395     { NULL,             0,              0 }
396 };
397
398 /* The timezone table. */
399 /* Some of these are commented out because a time_t can't store a float. */
400 static TABLE const TimezoneTable[] = {
401     { "gmt",    tZONE,     HOUR( 0) },  /* Greenwich Mean */
402     { "ut",     tZONE,     HOUR( 0) },  /* Universal (Coordinated) */
403     { "utc",    tZONE,     HOUR( 0) },
404     { "wet",    tZONE,     HOUR( 0) },  /* Western European */
405     { "bst",    tDAYZONE,  HOUR( 0) },  /* British Summer */
406     { "wat",    tZONE,     HOUR( 1) },  /* West Africa */
407     { "at",     tZONE,     HOUR( 2) },  /* Azores */
408 #if     0
409     /* For completeness.  BST is also British Summer, and GST is
410      * also Guam Standard. */
411     { "bst",    tZONE,     HOUR( 3) },  /* Brazil Standard */
412     { "gst",    tZONE,     HOUR( 3) },  /* Greenland Standard */
413 #endif
414 #if 0
415     { "nft",    tZONE,     HOUR(3.5) }, /* Newfoundland */
416     { "nst",    tZONE,     HOUR(3.5) }, /* Newfoundland Standard */
417     { "ndt",    tDAYZONE,  HOUR(3.5) }, /* Newfoundland Daylight */
418 #endif
419     { "ast",    tZONE,     HOUR( 4) },  /* Atlantic Standard */
420     { "adt",    tDAYZONE,  HOUR( 4) },  /* Atlantic Daylight */
421     { "est",    tZONE,     HOUR( 5) },  /* Eastern Standard */
422     { "edt",    tDAYZONE,  HOUR( 5) },  /* Eastern Daylight */
423     { "cst",    tZONE,     HOUR( 6) },  /* Central Standard */
424     { "cdt",    tDAYZONE,  HOUR( 6) },  /* Central Daylight */
425     { "mst",    tZONE,     HOUR( 7) },  /* Mountain Standard */
426     { "mdt",    tDAYZONE,  HOUR( 7) },  /* Mountain Daylight */
427     { "pst",    tZONE,     HOUR( 8) },  /* Pacific Standard */
428     { "pdt",    tDAYZONE,  HOUR( 8) },  /* Pacific Daylight */
429     { "yst",    tZONE,     HOUR( 9) },  /* Yukon Standard */
430     { "ydt",    tDAYZONE,  HOUR( 9) },  /* Yukon Daylight */
431     { "hst",    tZONE,     HOUR(10) },  /* Hawaii Standard */
432     { "hdt",    tDAYZONE,  HOUR(10) },  /* Hawaii Daylight */
433     { "cat",    tZONE,     HOUR(10) },  /* Central Alaska */
434     { "ahst",   tZONE,     HOUR(10) },  /* Alaska-Hawaii Standard */
435     { "nt",     tZONE,     HOUR(11) },  /* Nome */
436     { "idlw",   tZONE,     HOUR(12) },  /* International Date Line West */
437     { "cet",    tZONE,     -HOUR(1) },  /* Central European */
438     { "met",    tZONE,     -HOUR(1) },  /* Middle European */
439     { "mewt",   tZONE,     -HOUR(1) },  /* Middle European Winter */
440     { "mest",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
441     { "swt",    tZONE,     -HOUR(1) },  /* Swedish Winter */
442     { "sst",    tDAYZONE,  -HOUR(1) },  /* Swedish Summer */
443     { "fwt",    tZONE,     -HOUR(1) },  /* French Winter */
444     { "fst",    tDAYZONE,  -HOUR(1) },  /* French Summer */
445     { "eet",    tZONE,     -HOUR(2) },  /* Eastern Europe, USSR Zone 1 */
446     { "bt",     tZONE,     -HOUR(3) },  /* Baghdad, USSR Zone 2 */
447 #if 0
448     { "it",     tZONE,     -HOUR(3.5) },/* Iran */
449 #endif
450     { "zp4",    tZONE,     -HOUR(4) },  /* USSR Zone 3 */
451     { "zp5",    tZONE,     -HOUR(5) },  /* USSR Zone 4 */
452 #if 0
453     { "ist",    tZONE,     -HOUR(5.5) },/* Indian Standard */
454 #endif
455     { "zp6",    tZONE,     -HOUR(6) },  /* USSR Zone 5 */
456 #if     0
457     /* For completeness.  NST is also Newfoundland Stanard, and SST is
458      * also Swedish Summer. */
459     { "nst",    tZONE,     -HOUR(6.5) },/* North Sumatra */
460     { "sst",    tZONE,     -HOUR(7) },  /* South Sumatra, USSR Zone 6 */
461 #endif  /* 0 */
462     { "wast",   tZONE,     -HOUR(7) },  /* West Australian Standard */
463     { "wadt",   tDAYZONE,  -HOUR(7) },  /* West Australian Daylight */
464 #if 0
465     { "jt",     tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
466 #endif
467     { "cct",    tZONE,     -HOUR(8) },  /* China Coast, USSR Zone 7 */
468     { "jst",    tZONE,     -HOUR(9) },  /* Japan Standard, USSR Zone 8 */
469 #if 0
470     { "cast",   tZONE,     -HOUR(9.5) },/* Central Australian Standard */
471     { "cadt",   tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
472 #endif
473     { "east",   tZONE,     -HOUR(10) }, /* Eastern Australian Standard */
474     { "eadt",   tDAYZONE,  -HOUR(10) }, /* Eastern Australian Daylight */
475     { "gst",    tZONE,     -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
476     { "nzt",    tZONE,     -HOUR(12) }, /* New Zealand */
477     { "nzst",   tZONE,     -HOUR(12) }, /* New Zealand Standard */
478     { "nzdt",   tDAYZONE,  -HOUR(12) }, /* New Zealand Daylight */
479     { "idle",   tZONE,     -HOUR(12) }, /* International Date Line East */
480     {  NULL,    0,         0 }
481 };
482
483 /* Military timezone table. */
484 static TABLE const MilitaryTable[] = {
485     { "a",      tZONE,  HOUR(  1) },
486     { "b",      tZONE,  HOUR(  2) },
487     { "c",      tZONE,  HOUR(  3) },
488     { "d",      tZONE,  HOUR(  4) },
489     { "e",      tZONE,  HOUR(  5) },
490     { "f",      tZONE,  HOUR(  6) },
491     { "g",      tZONE,  HOUR(  7) },
492     { "h",      tZONE,  HOUR(  8) },
493     { "i",      tZONE,  HOUR(  9) },
494     { "k",      tZONE,  HOUR( 10) },
495     { "l",      tZONE,  HOUR( 11) },
496     { "m",      tZONE,  HOUR( 12) },
497     { "n",      tZONE,  HOUR(- 1) },
498     { "o",      tZONE,  HOUR(- 2) },
499     { "p",      tZONE,  HOUR(- 3) },
500     { "q",      tZONE,  HOUR(- 4) },
501     { "r",      tZONE,  HOUR(- 5) },
502     { "s",      tZONE,  HOUR(- 6) },
503     { "t",      tZONE,  HOUR(- 7) },
504     { "u",      tZONE,  HOUR(- 8) },
505     { "v",      tZONE,  HOUR(- 9) },
506     { "w",      tZONE,  HOUR(-10) },
507     { "x",      tZONE,  HOUR(-11) },
508     { "y",      tZONE,  HOUR(-12) },
509     { "z",      tZONE,  HOUR(  0) },
510     { NULL,     0,  0 }
511 };
512
513 \f
514
515
516 /* ARGSUSED */
517 static int
518 yyerror(const char *s __unused)
519 {
520   return 0;
521 }
522
523
524 static time_t
525 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
526 {
527     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
528         return -1;
529     switch (Meridian) {
530     case MER24:
531         if (Hours < 0 || Hours > 23)
532             return -1;
533         return (Hours * 60L + Minutes) * 60L + Seconds;
534     case MERam:
535         if (Hours < 1 || Hours > 12)
536             return -1;
537         if (Hours == 12)
538             Hours = 0;
539         return (Hours * 60L + Minutes) * 60L + Seconds;
540     case MERpm:
541         if (Hours < 1 || Hours > 12)
542             return -1;
543         if (Hours == 12)
544             Hours = 0;
545         return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
546     default:
547         abort ();
548     }
549     /* NOTREACHED */
550 }
551
552
553 /* Year is either
554    * A negative number, which means to use its absolute value (why?)
555    * A number from 0 to 99, which means a year from 1900 to 1999, or
556    * The actual year (>=100).  */
557 static time_t
558 Convert(time_t Month, time_t Day, time_t Year,
559     time_t Hours, time_t Minutes, time_t Seconds,
560     MERIDIAN Meridian, DSTMODE DSTmode)
561 {
562     static int DaysInMonth[12] = {
563         31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
564     };
565     time_t      tod;
566     time_t      Julian;
567     int         i;
568     struct tm   *ltm;
569
570     if (Year < 0)
571         Year = -Year;
572     if (Year < 69)
573         Year += 2000;
574     else if (Year < 100)
575         Year += 1900;
576     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
577                     ? 29 : 28;
578     /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
579        I'm too lazy to try to check for time_t overflow in another way.  */
580     if (Year < EPOCH || Year > 2038
581      || Month < 1 || Month > 12
582      /* Lint fluff:  "conversion from long may lose accuracy" */
583      || Day < 1 || Day > DaysInMonth[(int)--Month])
584         /* FIXME:
585          * It would be nice to set a global error string here.
586          * "February 30 is not a valid date" is much more informative than
587          * "Can't parse date/time: 100 months" when the user input was
588          * "100 months" and addition resolved that to February 30, for
589          * example.  See rcs2-7 in src/sanity.sh for more. */
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     ltm = localtime(&Julian);
602 fprintf(stderr, "DST %d TZ %s %d\n", DSTmode, ltm->tm_zone, ltm->tm_isdst);
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 time_t
817 get_date(char *p)
818 {
819     struct tm           *tm, gmt;
820     time_t              Start;
821     time_t              tod;
822     time_t nowtime;
823     struct tm *gmt_ptr;
824
825     yyInput = p;
826
827     (void)time (&nowtime);
828
829     gmt_ptr = gmtime (&nowtime);
830     if (gmt_ptr != NULL)
831     {
832         /* Make a copy, in case localtime modifies *tm (I think
833            that comment now applies to *gmt_ptr, but I am too
834            lazy to dig into how gmtime and locatime allocate the
835            structures they return pointers to).  */
836         gmt = *gmt_ptr;
837     }
838
839     if (! (tm = localtime (&nowtime)))
840         return -1;
841
842     tm = localtime(&nowtime);
843     yyYear = tm->tm_year + 1900;
844     yyMonth = tm->tm_mon + 1;
845     yyDay = tm->tm_mday;
846     yyTimezone = tm->tm_gmtoff;
847     yyDSTmode = DSTmaybe;
848     yyHour = 0;
849     yyMinutes = 0;
850     yySeconds = 0;
851     yyMeridian = MER24;
852     yyRelSeconds = 0;
853     yyRelMonth = 0;
854     yyHaveDate = 0;
855     yyHaveDay = 0;
856     yyHaveRel = 0;
857     yyHaveTime = 0;
858     yyHaveZone = 0;
859
860     if (yyparse()
861      || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
862         return -1;
863
864     if (yyHaveDate || yyHaveTime || yyHaveDay) {
865         Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
866                     yyMeridian, yyDSTmode);
867         if (Start < 0)
868             return -1;
869     }
870     else {
871         Start = nowtime;
872         if (!yyHaveRel)
873             Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
874     }
875
876     Start += yyRelSeconds;
877     Start += RelativeMonth(Start, yyRelMonth);
878
879     if (yyHaveDay && !yyHaveDate) {
880         tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
881         Start += tod;
882     }
883
884     /* Have to do *something* with a legitimate -1 so it's distinguishable
885      * from the error return value.  (Alternately could set errno on error.) */
886     return Start == -1 ? 0 : Start;
887 }