Merge in differences from the resolver's version.
[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 #ifndef GETDATE_IGNORE_TIMEZONE
840
841 /* Yield A - B, measured in seconds. */
842 static long
843 difftm (a, b)
844 struct tm *a, *b;
845 {
846 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
847 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
848 long days = (
849 /* difference in day of year */
850 a->tm_yday - b->tm_yday
851 /* + intervening leap days */
852 + ((ay >> 2) - (by >> 2))
853 - (ay / 100 - by / 100)
854 + ((ay / 100 >> 2) - (by / 100 >> 2))
855 /* + difference in years * 365 */
856 + (long) (ay - by) * 365
857 );
858 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
859 + (a->tm_min - b->tm_min))
860 + (a->tm_sec - b->tm_sec));
861 }
862
863 #endif
864
865 time_t
866 get_date (p, now)
867 const char *p;
868 const time_t *now;
869 {
870 struct tm tm, tm0, *tmp;
871 time_t Start;
872
873 yyInput = p;
874 Start = now ? *now : time ((time_t *) NULL);
875 tmp = localtime (&Start);
876 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
877 yyMonth = tmp->tm_mon + 1;
878 yyDay = tmp->tm_mday;
879 yyHour = tmp->tm_hour;
880 yyMinutes = tmp->tm_min;
881 yySeconds = tmp->tm_sec;
882 yyMeridian = MER24;
883 yyRelSeconds = 0;
884 yyRelMinutes = 0;
885 yyRelHour = 0;
886 yyRelDay = 0;
887 yyRelMonth = 0;
888 yyRelYear = 0;
889 yyHaveDate = 0;
890 yyHaveDay = 0;
891 yyHaveRel = 0;
892 yyHaveTime = 0;
893 yyHaveZone = 0;
894
895 if (yyparse ()
896 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
897 return -1;
898
899 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
900 tm.tm_mon = yyMonth - 1 + yyRelMonth;
901 tm.tm_mday = yyDay + yyRelDay;
902 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
903 {
904 tm.tm_hour = ToHour (yyHour, yyMeridian);
905 if (tm.tm_hour < 0)
906 return -1;
907 tm.tm_min = yyMinutes;
908 tm.tm_sec = yySeconds;
909 }
910 else
911 {
912 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
913 }
914 tm.tm_hour += yyRelHour;
915 tm.tm_min += yyRelMinutes;
916 tm.tm_sec += yyRelSeconds;
917 tm.tm_isdst = -1;
918 tm0 = tm;
919
920 Start = mktime (&tm);
921
922 if (Start == (time_t) -1)
923 {
924
925 /* Guard against falsely reporting errors near the time_t boundaries
926 when parsing times in other time zones. For example, if the min
927 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
928 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
929 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
930 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
931 zone by 24 hours to compensate. This algorithm assumes that
932 there is no DST transition within a day of the time_t boundaries. */
933
934 #ifndef GETDATE_IGNORE_TIMEZONE
935 if (yyHaveZone)
936 {
937 tm = tm0;
938 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
939 {
940 tm.tm_mday++;
941 yyTimezone -= 24 * 60;
942 }
943 else
944 {
945 tm.tm_mday--;
946 yyTimezone += 24 * 60;
947 }
948 Start = mktime (&tm);
949 }
950 #endif
951
952 if (Start == (time_t) -1)
953 return Start;
954 }
955
956 if (yyHaveDay && !yyHaveDate)
957 {
958 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
959 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
960 Start = mktime (&tm);
961 if (Start == (time_t) -1)
962 return Start;
963 }
964
965 #ifndef GETDATE_IGNORE_TIMEZONE
966 if (yyHaveZone)
967 {
968 long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
969 if ((Start + delta < Start) != (delta < 0))
970 return -1; /* time_t overflow */
971 Start += delta;
972 }
973 #endif
974
975 return Start;
976 }
977
978 #if defined (TEST)
979
980 /* ARGSUSED */
981 int
982 main (ac, av)
983 int ac;
984 char *av[];
985 {
986 char buff[MAX_BUFF_LEN + 1];
987 time_t d;
988
989 (void) printf ("Enter date, or blank line to exit.\n\t> ");
990 (void) fflush (stdout);
991
992 buff[MAX_BUFF_LEN] = 0;
993 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
994 {
995 d = get_date (buff, (time_t *) NULL);
996 if (d == -1)
997 (void) printf ("Bad format - couldn't convert.\n");
998 else
999 (void) printf ("%s", ctime (&d));
1000 (void) printf ("\t> ");
1001 (void) fflush (stdout);
1002 }
1003 exit (0);
1004 /* NOTREACHED */
1005 }
1006 #endif /* defined (TEST) */