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