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