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