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