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