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