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