Initial commit for Tizen
[profile/extras/shadow-utils.git] / libmisc / 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 13 shift/reduce conflicts.
9 **
10 **  This code is in the public domain and has no copyright.
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 # ifdef FORCE_ALLOCA_H
16 #  include <alloca.h>
17 # endif
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 #include <time.h>
33
34 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
35 # define IN_CTYPE_DOMAIN(c) 1
36 #else
37 # define IN_CTYPE_DOMAIN(c) isascii(c)
38 #endif
39
40 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
41 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
42 #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
43 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
44
45 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
46    - Its arg may be any int or unsigned int; it need not be an unsigned char.
47    - It's guaranteed to evaluate its argument exactly once.
48    - It's typically faster.
49    Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
50    only '0' through '9' are digits.  Prefer ISDIGIT to ISDIGIT_LOCALE unless
51    it's important to use the locale's definition of `digit' even when the
52    host does not conform to Posix.  */
53 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
54
55 #include "getdate.h"
56
57 #if defined (STDC_HEADERS)
58 # include <string.h>
59 #endif
60
61 /* Some old versions of bison generate parsers that use bcopy.
62    That loses on systems that don't provide the function, so we have
63    to redefine it here.  */
64 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
65 # define bcopy(from, to, len) memcpy ((to), (from), (len))
66 #endif
67
68 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
69    as well as gratuitiously global symbol names, so we can have multiple
70    yacc generated parsers in the same program.  Note that these are only
71    the variables produced by yacc.  If other parser generators (bison,
72    byacc, etc) produce additional global names that conflict at link time,
73    then those parser generators need to be fixed instead of adding those
74    names to this list. */
75
76 #define yymaxdepth gd_maxdepth
77 #define yyparse gd_parse
78 #define yylex   gd_lex
79 #define yyerror gd_error
80 #define yylval  gd_lval
81 #define yychar  gd_char
82 #define yydebug gd_debug
83 #define yypact  gd_pact
84 #define yyr1    gd_r1
85 #define yyr2    gd_r2
86 #define yydef   gd_def
87 #define yychk   gd_chk
88 #define yypgo   gd_pgo
89 #define yyact   gd_act
90 #define yyexca  gd_exca
91 #define yyerrflag gd_errflag
92 #define yynerrs gd_nerrs
93 #define yyps    gd_ps
94 #define yypv    gd_pv
95 #define yys     gd_s
96 #define yy_yys  gd_yys
97 #define yystate gd_state
98 #define yytmp   gd_tmp
99 #define yyv     gd_v
100 #define yy_yyv  gd_yyv
101 #define yyval   gd_val
102 #define yylloc  gd_lloc
103 #define yyreds  gd_reds          /* With YYDEBUG defined */
104 #define yytoks  gd_toks          /* With YYDEBUG defined */
105 #define yylhs   gd_yylhs
106 #define yylen   gd_yylen
107 #define yydefred gd_yydefred
108 #define yydgoto gd_yydgoto
109 #define yysindex gd_yysindex
110 #define yyrindex gd_yyrindex
111 #define yygindex gd_yygindex
112 #define yytable  gd_yytable
113 #define yycheck  gd_yycheck
114
115 static int yylex (void);
116 static int yyerror (const char *s);
117
118 #define EPOCH           1970
119 #define HOUR(x)         ((x) * 60)
120
121 #define MAX_BUFF_LEN    128   /* size of buffer to read the date into */
122
123 /*
124 **  An entry in the lexical lookup table.
125 */
126 typedef struct _TABLE {
127     const char  *name;
128     int         type;
129     int         value;
130 } TABLE;
131
132
133 /*
134 **  Meridian:  am, pm, or 24-hour style.
135 */
136 typedef enum _MERIDIAN {
137     MERam, MERpm, MER24
138 } MERIDIAN;
139
140
141 /*
142 **  Global variables.  We could get rid of most of these by using a good
143 **  union as the yacc stack.  (This routine was originally written before
144 **  yacc had the %union construct.)  Maybe someday; right now we only use
145 **  the %union very rarely.
146 */
147 static const char       *yyInput;
148 static int      yyDayOrdinal;
149 static int      yyDayNumber;
150 static int      yyHaveDate;
151 static int      yyHaveDay;
152 static int      yyHaveRel;
153 static int      yyHaveTime;
154 static int      yyHaveZone;
155 static int      yyTimezone;
156 static int      yyDay;
157 static int      yyHour;
158 static int      yyMinutes;
159 static int      yyMonth;
160 static int      yySeconds;
161 static int      yyYear;
162 static MERIDIAN yyMeridian;
163 static int      yyRelDay;
164 static int      yyRelHour;
165 static int      yyRelMinutes;
166 static int      yyRelMonth;
167 static int      yyRelSeconds;
168 static int      yyRelYear;
169
170 %}
171
172 %union {
173     int                 Number;
174     enum _MERIDIAN      Meridian;
175 }
176
177 %token  tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
178 %token  tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
179 %token  tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
180
181 %type   <Number>        tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
182 %type   <Number>        tMONTH tMONTH_UNIT
183 %type   <Number>        tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
184 %type   <Meridian>      tMERIDIAN o_merid
185
186 %%
187
188 spec    : /* NULL */
189         | spec item
190         ;
191
192 item    : time {
193             yyHaveTime++;
194         }
195         | zone {
196             yyHaveZone++;
197         }
198         | date {
199             yyHaveDate++;
200         }
201         | day {
202             yyHaveDay++;
203         }
204         | rel {
205             yyHaveRel++;
206         }
207         | number
208         ;
209
210 time    : tUNUMBER tMERIDIAN {
211             yyHour = $1;
212             yyMinutes = 0;
213             yySeconds = 0;
214             yyMeridian = $2;
215         }
216         | tUNUMBER ':' tUNUMBER o_merid {
217             yyHour = $1;
218             yyMinutes = $3;
219             yySeconds = 0;
220             yyMeridian = $4;
221         }
222         | tUNUMBER ':' tUNUMBER tSNUMBER {
223             yyHour = $1;
224             yyMinutes = $3;
225             yyMeridian = MER24;
226             yyHaveZone++;
227             yyTimezone = ($4 < 0
228                           ? -$4 % 100 + (-$4 / 100) * 60
229                           : - ($4 % 100 + ($4 / 100) * 60));
230         }
231         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
232             yyHour = $1;
233             yyMinutes = $3;
234             yySeconds = $5;
235             yyMeridian = $6;
236         }
237         | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
238             yyHour = $1;
239             yyMinutes = $3;
240             yySeconds = $5;
241             yyMeridian = MER24;
242             yyHaveZone++;
243             yyTimezone = ($6 < 0
244                           ? -$6 % 100 + (-$6 / 100) * 60
245                           : - ($6 % 100 + ($6 / 100) * 60));
246         }
247         ;
248
249 zone    : tZONE {
250             yyTimezone = $1;
251         }
252         | tDAYZONE {
253             yyTimezone = $1 - 60;
254         }
255         |
256           tZONE tDST {
257             yyTimezone = $1 - 60;
258         }
259         ;
260
261 day     : tDAY {
262             yyDayOrdinal = 1;
263             yyDayNumber = $1;
264         }
265         | tDAY ',' {
266             yyDayOrdinal = 1;
267             yyDayNumber = $1;
268         }
269         | tUNUMBER tDAY {
270             yyDayOrdinal = $1;
271             yyDayNumber = $2;
272         }
273         ;
274
275 date    : tUNUMBER '/' tUNUMBER {
276             yyMonth = $1;
277             yyDay = $3;
278         }
279         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
280           /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
281              The goal in recognizing YYYY/MM/DD is solely to support legacy
282              machine-generated dates like those in an RCS log listing.  If
283              you want portability, use the ISO 8601 format.  */
284           if ($1 >= 1000)
285             {
286               yyYear = $1;
287               yyMonth = $3;
288               yyDay = $5;
289             }
290           else
291             {
292               yyMonth = $1;
293               yyDay = $3;
294               yyYear = $5;
295             }
296         }
297         | tUNUMBER tSNUMBER tSNUMBER {
298             /* ISO 8601 format.  yyyy-mm-dd.  */
299             yyYear = $1;
300             yyMonth = -$2;
301             yyDay = -$3;
302         }
303         | tUNUMBER tMONTH tSNUMBER {
304             /* e.g. 17-JUN-1992.  */
305             yyDay = $1;
306             yyMonth = $2;
307             yyYear = -$3;
308         }
309         | tMONTH tUNUMBER {
310             yyMonth = $1;
311             yyDay = $2;
312         }
313         | tMONTH tUNUMBER ',' tUNUMBER {
314             yyMonth = $1;
315             yyDay = $2;
316             yyYear = $4;
317         }
318         | tUNUMBER tMONTH {
319             yyMonth = $2;
320             yyDay = $1;
321         }
322         | tUNUMBER tMONTH tUNUMBER {
323             yyMonth = $2;
324             yyDay = $1;
325             yyYear = $3;
326         }
327         ;
328
329 rel     : relunit tAGO {
330             yyRelSeconds = -yyRelSeconds;
331             yyRelMinutes = -yyRelMinutes;
332             yyRelHour = -yyRelHour;
333             yyRelDay = -yyRelDay;
334             yyRelMonth = -yyRelMonth;
335             yyRelYear = -yyRelYear;
336         }
337         | relunit
338         ;
339
340 relunit : tUNUMBER tYEAR_UNIT {
341             yyRelYear += $1 * $2;
342         }
343         | tSNUMBER tYEAR_UNIT {
344             yyRelYear += $1 * $2;
345         }
346         | tYEAR_UNIT {
347             yyRelYear++;
348         }
349         | tUNUMBER tMONTH_UNIT {
350             yyRelMonth += $1 * $2;
351         }
352         | tSNUMBER tMONTH_UNIT {
353             yyRelMonth += $1 * $2;
354         }
355         | tMONTH_UNIT {
356             yyRelMonth++;
357         }
358         | tUNUMBER tDAY_UNIT {
359             yyRelDay += $1 * $2;
360         }
361         | tSNUMBER tDAY_UNIT {
362             yyRelDay += $1 * $2;
363         }
364         | tDAY_UNIT {
365             yyRelDay++;
366         }
367         | tUNUMBER tHOUR_UNIT {
368             yyRelHour += $1 * $2;
369         }
370         | tSNUMBER tHOUR_UNIT {
371             yyRelHour += $1 * $2;
372         }
373         | tHOUR_UNIT {
374             yyRelHour++;
375         }
376         | tUNUMBER tMINUTE_UNIT {
377             yyRelMinutes += $1 * $2;
378         }
379         | tSNUMBER tMINUTE_UNIT {
380             yyRelMinutes += $1 * $2;
381         }
382         | tMINUTE_UNIT {
383             yyRelMinutes++;
384         }
385         | tUNUMBER tSEC_UNIT {
386             yyRelSeconds += $1 * $2;
387         }
388         | tSNUMBER tSEC_UNIT {
389             yyRelSeconds += $1 * $2;
390         }
391         | tSEC_UNIT {
392             yyRelSeconds++;
393         }
394         ;
395
396 number  : tUNUMBER
397           {
398             if ((yyHaveTime != 0) && (yyHaveDate != 0) && (yyHaveRel == 0))
399               yyYear = $1;
400             else
401               {
402                 if ($1>10000)
403                   {
404                     yyHaveDate++;
405                     yyDay= ($1)%100;
406                     yyMonth= ($1/100)%100;
407                     yyYear = $1/10000;
408                   }
409                 else
410                   {
411                     yyHaveTime++;
412                     if ($1 < 100)
413                       {
414                         yyHour = $1;
415                         yyMinutes = 0;
416                       }
417                     else
418                       {
419                         yyHour = $1 / 100;
420                         yyMinutes = $1 % 100;
421                       }
422                     yySeconds = 0;
423                     yyMeridian = MER24;
424                   }
425               }
426           }
427         ;
428
429 o_merid : /* NULL */
430           {
431             $$ = MER24;
432           }
433         | tMERIDIAN
434           {
435             $$ = $1;
436           }
437         ;
438
439 %%
440
441 /* Month and day table. */
442 static TABLE const MonthDayTable[] = {
443     { "january",        tMONTH,  1 },
444     { "february",       tMONTH,  2 },
445     { "march",          tMONTH,  3 },
446     { "april",          tMONTH,  4 },
447     { "may",            tMONTH,  5 },
448     { "june",           tMONTH,  6 },
449     { "july",           tMONTH,  7 },
450     { "august",         tMONTH,  8 },
451     { "september",      tMONTH,  9 },
452     { "sept",           tMONTH,  9 },
453     { "october",        tMONTH, 10 },
454     { "november",       tMONTH, 11 },
455     { "december",       tMONTH, 12 },
456     { "sunday",         tDAY, 0 },
457     { "monday",         tDAY, 1 },
458     { "tuesday",        tDAY, 2 },
459     { "tues",           tDAY, 2 },
460     { "wednesday",      tDAY, 3 },
461     { "wednes",         tDAY, 3 },
462     { "thursday",       tDAY, 4 },
463     { "thur",           tDAY, 4 },
464     { "thurs",          tDAY, 4 },
465     { "friday",         tDAY, 5 },
466     { "saturday",       tDAY, 6 },
467     { NULL, 0, 0 }
468 };
469
470 /* Time units table. */
471 static TABLE const UnitsTable[] = {
472     { "year",           tYEAR_UNIT,     1 },
473     { "month",          tMONTH_UNIT,    1 },
474     { "fortnight",      tDAY_UNIT,      14 },
475     { "week",           tDAY_UNIT,      7 },
476     { "day",            tDAY_UNIT,      1 },
477     { "hour",           tHOUR_UNIT,     1 },
478     { "minute",         tMINUTE_UNIT,   1 },
479     { "min",            tMINUTE_UNIT,   1 },
480     { "second",         tSEC_UNIT,      1 },
481     { "sec",            tSEC_UNIT,      1 },
482     { NULL, 0, 0 }
483 };
484
485 /* Assorted relative-time words. */
486 static TABLE const OtherTable[] = {
487     { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
488     { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
489     { "today",          tMINUTE_UNIT,   0 },
490     { "now",            tMINUTE_UNIT,   0 },
491     { "last",           tUNUMBER,       -1 },
492     { "this",           tMINUTE_UNIT,   0 },
493     { "next",           tUNUMBER,       2 },
494     { "first",          tUNUMBER,       1 },
495 /*  { "second",         tUNUMBER,       2 }, */
496     { "third",          tUNUMBER,       3 },
497     { "fourth",         tUNUMBER,       4 },
498     { "fifth",          tUNUMBER,       5 },
499     { "sixth",          tUNUMBER,       6 },
500     { "seventh",        tUNUMBER,       7 },
501     { "eighth",         tUNUMBER,       8 },
502     { "ninth",          tUNUMBER,       9 },
503     { "tenth",          tUNUMBER,       10 },
504     { "eleventh",       tUNUMBER,       11 },
505     { "twelfth",        tUNUMBER,       12 },
506     { "ago",            tAGO,   1 },
507     { NULL, 0, 0 }
508 };
509
510 /* The timezone table. */
511 static TABLE const TimezoneTable[] = {
512     { "gmt",    tZONE,     HOUR ( 0) }, /* Greenwich Mean */
513     { "ut",     tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
514     { "utc",    tZONE,     HOUR ( 0) },
515     { "wet",    tZONE,     HOUR ( 0) }, /* Western European */
516     { "bst",    tDAYZONE,  HOUR ( 0) }, /* British Summer */
517     { "wat",    tZONE,     HOUR ( 1) }, /* West Africa */
518     { "at",     tZONE,     HOUR ( 2) }, /* Azores */
519     { "ast",    tZONE,     HOUR ( 4) }, /* Atlantic Standard */
520     { "adt",    tDAYZONE,  HOUR ( 4) }, /* Atlantic Daylight */
521     { "est",    tZONE,     HOUR ( 5) }, /* Eastern Standard */
522     { "edt",    tDAYZONE,  HOUR ( 5) }, /* Eastern Daylight */
523     { "cst",    tZONE,     HOUR ( 6) }, /* Central Standard */
524     { "cdt",    tDAYZONE,  HOUR ( 6) }, /* Central Daylight */
525     { "mst",    tZONE,     HOUR ( 7) }, /* Mountain Standard */
526     { "mdt",    tDAYZONE,  HOUR ( 7) }, /* Mountain Daylight */
527     { "pst",    tZONE,     HOUR ( 8) }, /* Pacific Standard */
528     { "pdt",    tDAYZONE,  HOUR ( 8) }, /* Pacific Daylight */
529     { "yst",    tZONE,     HOUR ( 9) }, /* Yukon Standard */
530     { "ydt",    tDAYZONE,  HOUR ( 9) }, /* Yukon Daylight */
531     { "hst",    tZONE,     HOUR (10) }, /* Hawaii Standard */
532     { "hdt",    tDAYZONE,  HOUR (10) }, /* Hawaii Daylight */
533     { "cat",    tZONE,     HOUR (10) }, /* Central Alaska */
534     { "ahst",   tZONE,     HOUR (10) }, /* Alaska-Hawaii Standard */
535     { "nt",     tZONE,     HOUR (11) }, /* Nome */
536     { "idlw",   tZONE,     HOUR (12) }, /* International Date Line West */
537     { "cet",    tZONE,     -HOUR (1) }, /* Central European */
538     { "met",    tZONE,     -HOUR (1) }, /* Middle European */
539     { "mewt",   tZONE,     -HOUR (1) }, /* Middle European Winter */
540     { "mest",   tDAYZONE,  -HOUR (1) }, /* Middle European Summer */
541     { "mesz",   tDAYZONE,  -HOUR (1) }, /* Middle European Summer */
542     { "swt",    tZONE,     -HOUR (1) }, /* Swedish Winter */
543     { "sst",    tDAYZONE,  -HOUR (1) }, /* Swedish Summer */
544     { "fwt",    tZONE,     -HOUR (1) }, /* French Winter */
545     { "fst",    tDAYZONE,  -HOUR (1) }, /* French Summer */
546     { "eet",    tZONE,     -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
547     { "bt",     tZONE,     -HOUR (3) }, /* Baghdad, USSR Zone 2 */
548     { "zp4",    tZONE,     -HOUR (4) }, /* USSR Zone 3 */
549     { "zp5",    tZONE,     -HOUR (5) }, /* USSR Zone 4 */
550     { "zp6",    tZONE,     -HOUR (6) }, /* USSR Zone 5 */
551     { "wast",   tZONE,     -HOUR (7) }, /* West Australian Standard */
552     { "wadt",   tDAYZONE,  -HOUR (7) }, /* West Australian Daylight */
553     { "cct",    tZONE,     -HOUR (8) }, /* China Coast, USSR Zone 7 */
554     { "jst",    tZONE,     -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
555     { "east",   tZONE,     -HOUR (10) },        /* Eastern Australian Standard */
556     { "eadt",   tDAYZONE,  -HOUR (10) },        /* Eastern Australian Daylight */
557     { "gst",    tZONE,     -HOUR (10) },        /* Guam Standard, USSR Zone 9 */
558     { "nzt",    tZONE,     -HOUR (12) },        /* New Zealand */
559     { "nzst",   tZONE,     -HOUR (12) },        /* New Zealand Standard */
560     { "nzdt",   tDAYZONE,  -HOUR (12) },        /* New Zealand Daylight */
561     { "idle",   tZONE,     -HOUR (12) },        /* International Date Line East */
562     { NULL, 0, 0 }
563 };
564
565 /* Military timezone table. */
566 static TABLE const MilitaryTable[] = {
567     { "a",      tZONE,  HOUR (  1) },
568     { "b",      tZONE,  HOUR (  2) },
569     { "c",      tZONE,  HOUR (  3) },
570     { "d",      tZONE,  HOUR (  4) },
571     { "e",      tZONE,  HOUR (  5) },
572     { "f",      tZONE,  HOUR (  6) },
573     { "g",      tZONE,  HOUR (  7) },
574     { "h",      tZONE,  HOUR (  8) },
575     { "i",      tZONE,  HOUR (  9) },
576     { "k",      tZONE,  HOUR ( 10) },
577     { "l",      tZONE,  HOUR ( 11) },
578     { "m",      tZONE,  HOUR ( 12) },
579     { "n",      tZONE,  HOUR (- 1) },
580     { "o",      tZONE,  HOUR (- 2) },
581     { "p",      tZONE,  HOUR (- 3) },
582     { "q",      tZONE,  HOUR (- 4) },
583     { "r",      tZONE,  HOUR (- 5) },
584     { "s",      tZONE,  HOUR (- 6) },
585     { "t",      tZONE,  HOUR (- 7) },
586     { "u",      tZONE,  HOUR (- 8) },
587     { "v",      tZONE,  HOUR (- 9) },
588     { "w",      tZONE,  HOUR (-10) },
589     { "x",      tZONE,  HOUR (-11) },
590     { "y",      tZONE,  HOUR (-12) },
591     { "z",      tZONE,  HOUR (  0) },
592     { NULL, 0, 0 }
593 };
594
595 \f
596
597
598 static int yyerror (unused const char *s)
599 {
600   return 0;
601 }
602
603 static int ToHour (int Hours, MERIDIAN Meridian)
604 {
605   switch (Meridian)
606     {
607     case MER24:
608       if (Hours < 0 || Hours > 23)
609         return -1;
610       return Hours;
611     case MERam:
612       if (Hours < 1 || Hours > 12)
613         return -1;
614       if (Hours == 12)
615         Hours = 0;
616       return Hours;
617     case MERpm:
618       if (Hours < 1 || Hours > 12)
619         return -1;
620       if (Hours == 12)
621         Hours = 0;
622       return Hours + 12;
623     default:
624       abort ();
625     }
626   /* NOTREACHED */
627 }
628
629 static int ToYear (int Year)
630 {
631   if (Year < 0)
632     Year = -Year;
633
634   /* XPG4 suggests that years 00-68 map to 2000-2068, and
635      years 69-99 map to 1969-1999.  */
636   if (Year < 69)
637     Year += 2000;
638   else if (Year < 100)
639     Year += 1900;
640
641   return Year;
642 }
643
644 static int LookupWord (char *buff)
645 {
646   register char *p;
647   register char *q;
648   register const TABLE *tp;
649   int i;
650   bool abbrev;
651
652   /* Make it lowercase. */
653   for (p = buff; '\0' != *p; p++)
654     if (ISUPPER (*p))
655       *p = tolower (*p);
656
657   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
658     {
659       yylval.Meridian = MERam;
660       return tMERIDIAN;
661     }
662   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
663     {
664       yylval.Meridian = MERpm;
665       return tMERIDIAN;
666     }
667
668   /* See if we have an abbreviation for a month. */
669   if (strlen (buff) == 3)
670     abbrev = true;
671   else if (strlen (buff) == 4 && buff[3] == '.')
672     {
673       abbrev = true;
674       buff[3] = '\0';
675     }
676   else
677     abbrev = false;
678
679   for (tp = MonthDayTable; tp->name; tp++)
680     {
681       if (abbrev)
682         {
683           if (strncmp (buff, tp->name, 3) == 0)
684             {
685               yylval.Number = tp->value;
686               return tp->type;
687             }
688         }
689       else if (strcmp (buff, tp->name) == 0)
690         {
691           yylval.Number = tp->value;
692           return tp->type;
693         }
694     }
695
696   for (tp = TimezoneTable; tp->name; tp++)
697     if (strcmp (buff, tp->name) == 0)
698       {
699         yylval.Number = tp->value;
700         return tp->type;
701       }
702
703   if (strcmp (buff, "dst") == 0)
704     return tDST;
705
706   for (tp = UnitsTable; tp->name; tp++)
707     if (strcmp (buff, tp->name) == 0)
708       {
709         yylval.Number = tp->value;
710         return tp->type;
711       }
712
713   /* Strip off any plural and try the units table again. */
714   i = strlen (buff) - 1;
715   if (buff[i] == 's')
716     {
717       buff[i] = '\0';
718       for (tp = UnitsTable; tp->name; tp++)
719         if (strcmp (buff, tp->name) == 0)
720           {
721             yylval.Number = tp->value;
722             return tp->type;
723           }
724       buff[i] = 's';            /* Put back for "this" in OtherTable. */
725     }
726
727   for (tp = OtherTable; tp->name; tp++)
728     if (strcmp (buff, tp->name) == 0)
729       {
730         yylval.Number = tp->value;
731         return tp->type;
732       }
733
734   /* Military timezones. */
735   if (buff[1] == '\0' && ISALPHA (*buff))
736     {
737       for (tp = MilitaryTable; tp->name; tp++)
738         if (strcmp (buff, tp->name) == 0)
739           {
740             yylval.Number = tp->value;
741             return tp->type;
742           }
743     }
744
745   /* Drop out any periods and try the timezone table again. */
746   for (i = 0, p = q = buff; '\0' != *q; q++)
747     if (*q != '.')
748       *p++ = *q;
749     else
750       i++;
751   *p = '\0';
752   if (0 != i)
753     for (tp = TimezoneTable; NULL != tp->name; tp++)
754       if (strcmp (buff, tp->name) == 0)
755         {
756           yylval.Number = tp->value;
757           return tp->type;
758         }
759
760   return tID;
761 }
762
763 static int
764 yylex (void)
765 {
766   register char c;
767   register char *p;
768   char buff[20];
769   int Count;
770   int sign;
771
772   for (;;)
773     {
774       while (ISSPACE (*yyInput))
775         yyInput++;
776
777       if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
778         {
779           if (c == '-' || c == '+')
780             {
781               sign = c == '-' ? -1 : 1;
782               if (!ISDIGIT (*++yyInput))
783                 /* skip the '-' sign */
784                 continue;
785             }
786           else
787             sign = 0;
788           for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
789             yylval.Number = 10 * yylval.Number + c - '0';
790           yyInput--;
791           if (sign < 0)
792             yylval.Number = -yylval.Number;
793           return (0 != sign) ? tSNUMBER : tUNUMBER;
794         }
795       if (ISALPHA (c))
796         {
797           for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
798             if (p < &buff[sizeof buff - 1])
799               *p++ = c;
800           *p = '\0';
801           yyInput--;
802           return LookupWord (buff);
803         }
804       if (c != '(')
805         return *yyInput++;
806       Count = 0;
807       do
808         {
809           c = *yyInput++;
810           if (c == '\0')
811             return c;
812           if (c == '(')
813             Count++;
814           else if (c == ')')
815             Count--;
816         }
817       while (Count > 0);
818     }
819 }
820
821 #define TM_YEAR_ORIGIN 1900
822
823 /* Yield A - B, measured in seconds.  */
824 static long difftm (struct tm *a, struct tm *b)
825 {
826   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
827   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
828   long days = (
829   /* difference in day of year */
830                 a->tm_yday - b->tm_yday
831   /* + intervening leap days */
832                 + ((ay >> 2) - (by >> 2))
833                 - (ay / 100 - by / 100)
834                 + ((ay / 100 >> 2) - (by / 100 >> 2))
835   /* + difference in years * 365 */
836                 + (long) (ay - by) * 365
837   );
838   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
839                 + (a->tm_min - b->tm_min))
840           + (a->tm_sec - b->tm_sec));
841 }
842
843 time_t get_date (const char *p, const time_t *now)
844 {
845   struct tm tm, tm0, *tmp;
846   time_t Start;
847
848   yyInput = p;
849   Start = now ? *now : time ((time_t *) NULL);
850   tmp = localtime (&Start);
851   yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
852   yyMonth = tmp->tm_mon + 1;
853   yyDay = tmp->tm_mday;
854   yyHour = tmp->tm_hour;
855   yyMinutes = tmp->tm_min;
856   yySeconds = tmp->tm_sec;
857   yyMeridian = MER24;
858   yyRelSeconds = 0;
859   yyRelMinutes = 0;
860   yyRelHour = 0;
861   yyRelDay = 0;
862   yyRelMonth = 0;
863   yyRelYear = 0;
864   yyHaveDate = 0;
865   yyHaveDay = 0;
866   yyHaveRel = 0;
867   yyHaveTime = 0;
868   yyHaveZone = 0;
869
870   if (yyparse ()
871       || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
872     return -1;
873
874   tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
875   tm.tm_mon = yyMonth - 1 + yyRelMonth;
876   tm.tm_mday = yyDay + yyRelDay;
877   if ((yyHaveTime != 0) ||
878       ( (yyHaveRel != 0) && (yyHaveDate == 0) && (yyHaveDay == 0) ))
879     {
880       tm.tm_hour = ToHour (yyHour, yyMeridian);
881       if (tm.tm_hour < 0)
882         return -1;
883       tm.tm_min = yyMinutes;
884       tm.tm_sec = yySeconds;
885     }
886   else
887     {
888       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
889     }
890   tm.tm_hour += yyRelHour;
891   tm.tm_min += yyRelMinutes;
892   tm.tm_sec += yyRelSeconds;
893   tm.tm_isdst = -1;
894   tm0 = tm;
895
896   Start = mktime (&tm);
897
898   if (Start == (time_t) -1)
899     {
900
901       /* Guard against falsely reporting errors near the time_t boundaries
902          when parsing times in other time zones.  For example, if the min
903          time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
904          of UTC, then the min localtime value is 1970-01-01 08:00:00; if
905          we apply mktime to 1970-01-01 00:00:00 we will get an error, so
906          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
907          zone by 24 hours to compensate.  This algorithm assumes that
908          there is no DST transition within a day of the time_t boundaries.  */
909       if (yyHaveZone)
910         {
911           tm = tm0;
912           if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
913             {
914               tm.tm_mday++;
915               yyTimezone -= 24 * 60;
916             }
917           else
918             {
919               tm.tm_mday--;
920               yyTimezone += 24 * 60;
921             }
922           Start = mktime (&tm);
923         }
924
925       if (Start == (time_t) -1)
926         return Start;
927     }
928
929   if (yyHaveDay && !yyHaveDate)
930     {
931       tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
932                      + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
933       Start = mktime (&tm);
934       if (Start == (time_t) -1)
935         return Start;
936     }
937
938   if (yyHaveZone)
939     {
940       long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
941       if ((Start + delta < Start) != (delta < 0))
942         return -1;              /* time_t overflow */
943       Start += delta;
944     }
945
946   return Start;
947 }
948
949 #if     defined (TEST)
950
951 /* ARGSUSED */
952 int
953 main (ac, av)
954      int ac;
955      char *av[];
956 {
957   char buff[MAX_BUFF_LEN + 1];
958   time_t d;
959
960   (void) printf ("Enter date, or blank line to exit.\n\t> ");
961   (void) fflush (stdout);
962
963   buff[MAX_BUFF_LEN] = 0;
964   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
965     {
966       d = get_date (buff, (time_t *) NULL);
967       if (d == -1)
968         (void) printf ("Bad format - couldn't convert.\n");
969       else
970         (void) printf ("%s", ctime (&d));
971       (void) printf ("\t> ");
972       (void) fflush (stdout);
973     }
974   exit (0);
975   /* NOTREACHED */
976 }
977 #endif /* defined (TEST) */