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