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