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