]> Dogcows Code - chaz/tar/blob - lib/getdate.y
(TimezoneTable): Modernize to current tz data.
[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. This table is necessarily incomplete, as time
530 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
531 as Eastern time in Australia, not as US Eastern Standard Time.
532 You cannot rely on getdate to handle arbitrary time zone
533 abbreviations; use numeric abbreviations like `-0500' instead. */
534 static TABLE const TimezoneTable[] = {
535 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
536 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
537 { "utc", tZONE, HOUR ( 0) },
538 { "wet", tZONE, HOUR ( 0) }, /* Western European */
539 { "west", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
540 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
541 { "art", tZONE, HOUR ( 3) }, /* Argentina */
542 { "brt", tZONE, HOUR ( 3) }, /* Brazil */
543 { "brst", tDAYZONE, HOUR ( 3) }, /* Brazil Summer */
544 { "nst", tZONE, HOUR ( 3) + 30 }, /* Newfoundland Standard */
545 { "ndt", tDAYZONE, HOUR ( 3) + 30 }, /* Newfoundland Daylight */
546 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
547 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
548 { "clt", tZONE, HOUR ( 4) }, /* Chile */
549 { "clst", tDAYZONE, HOUR ( 4) }, /* Chile Summer */
550 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
551 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
552 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
553 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
554 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
555 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
556 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
557 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
558 { "akst", tZONE, HOUR ( 9) }, /* Alaska Standard */
559 { "akdt", tDAYZONE, HOUR ( 9) }, /* Alaska Daylight */
560 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
561 { "hast", tZONE, HOUR (10) }, /* Hawaii-Aleutian Standard */
562 { "hadt", tDAYZONE, HOUR (10) }, /* Hawaii-Aleutian Daylight */
563 { "sst", tZONE, HOUR (12) }, /* Samoa Standard */
564 { "wat", tZONE, -HOUR (1) }, /* West Africa */
565 { "cet", tZONE, -HOUR (1) }, /* Central European */
566 { "cest", tDAYZONE, -HOUR (1) }, /* Central European Summer */
567 { "met", tZONE, -HOUR (1) }, /* Middle European */
568 { "mez", tZONE, -HOUR (1) }, /* Middle European */
569 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
570 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
571 { "eet", tZONE, -HOUR (2) }, /* Eastern European */
572 { "eest", tDAYZONE, -HOUR (2) }, /* Eastern European Summer */
573 { "cat", tZONE, -HOUR (2) }, /* Central Africa */
574 { "sast", tZONE, -HOUR (2) }, /* South Africa Standard */
575 { "eat", tZONE, -HOUR (3) }, /* East Africa */
576 { "msk", tZONE, -HOUR (3) }, /* Moscow */
577 { "msd", tDAYZONE, -HOUR (3) }, /* Moscow Daylight */
578 { "ist", tZONE, - (HOUR (5) + 30) }, /* India Standard */
579 { "sgt", tZONE, -HOUR (8) }, /* Singapore */
580 { "kst", tZONE, -HOUR (9) }, /* Korea Standard */
581 { "jst", tZONE, -HOUR (9) }, /* Japan Standard */
582 { "gst", tZONE, -HOUR (10) }, /* Guam Standard */
583 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
584 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
585 { NULL, 0, 0 }
586 };
587
588 /* Military timezone table. */
589 static TABLE const MilitaryTable[] = {
590 { "a", tZONE, HOUR ( 1) },
591 { "b", tZONE, HOUR ( 2) },
592 { "c", tZONE, HOUR ( 3) },
593 { "d", tZONE, HOUR ( 4) },
594 { "e", tZONE, HOUR ( 5) },
595 { "f", tZONE, HOUR ( 6) },
596 { "g", tZONE, HOUR ( 7) },
597 { "h", tZONE, HOUR ( 8) },
598 { "i", tZONE, HOUR ( 9) },
599 { "k", tZONE, HOUR ( 10) },
600 { "l", tZONE, HOUR ( 11) },
601 { "m", tZONE, HOUR ( 12) },
602 { "n", tZONE, HOUR (- 1) },
603 { "o", tZONE, HOUR (- 2) },
604 { "p", tZONE, HOUR (- 3) },
605 { "q", tZONE, HOUR (- 4) },
606 { "r", tZONE, HOUR (- 5) },
607 { "s", tZONE, HOUR (- 6) },
608 { "t", tZONE, HOUR (- 7) },
609 { "u", tZONE, HOUR (- 8) },
610 { "v", tZONE, HOUR (- 9) },
611 { "w", tZONE, HOUR (-10) },
612 { "x", tZONE, HOUR (-11) },
613 { "y", tZONE, HOUR (-12) },
614 { "z", tZONE, HOUR ( 0) },
615 { NULL, 0, 0 }
616 };
617
618 \f
619
620
621 /* ARGSUSED */
622 static int
623 yyerror (s)
624 char *s ATTRIBUTE_UNUSED;
625 {
626 return 0;
627 }
628
629 static int
630 ToHour (Hours, Meridian)
631 int Hours;
632 MERIDIAN Meridian;
633 {
634 switch (Meridian)
635 {
636 case MER24:
637 if (Hours < 0 || Hours > 23)
638 return -1;
639 return Hours;
640 case MERam:
641 if (Hours < 1 || Hours > 12)
642 return -1;
643 if (Hours == 12)
644 Hours = 0;
645 return Hours;
646 case MERpm:
647 if (Hours < 1 || Hours > 12)
648 return -1;
649 if (Hours == 12)
650 Hours = 0;
651 return Hours + 12;
652 default:
653 abort ();
654 }
655 /* NOTREACHED */
656 }
657
658 static int
659 ToYear (Year)
660 int Year;
661 {
662 if (Year < 0)
663 Year = -Year;
664
665 /* XPG4 suggests that years 00-68 map to 2000-2068, and
666 years 69-99 map to 1969-1999. */
667 if (Year < 69)
668 Year += 2000;
669 else if (Year < 100)
670 Year += 1900;
671
672 return Year;
673 }
674
675 static int
676 LookupWord (buff)
677 char *buff;
678 {
679 register char *p;
680 register char *q;
681 register const TABLE *tp;
682 int i;
683 int abbrev;
684
685 /* Make it lowercase. */
686 for (p = buff; *p; p++)
687 if (ISUPPER ((unsigned char) *p))
688 *p = tolower (*p);
689
690 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
691 {
692 yylval.Meridian = MERam;
693 return tMERIDIAN;
694 }
695 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
696 {
697 yylval.Meridian = MERpm;
698 return tMERIDIAN;
699 }
700
701 /* See if we have an abbreviation for a month. */
702 if (strlen (buff) == 3)
703 abbrev = 1;
704 else if (strlen (buff) == 4 && buff[3] == '.')
705 {
706 abbrev = 1;
707 buff[3] = '\0';
708 }
709 else
710 abbrev = 0;
711
712 for (tp = MonthDayTable; tp->name; tp++)
713 {
714 if (abbrev)
715 {
716 if (strncmp (buff, tp->name, 3) == 0)
717 {
718 yylval.Number = tp->value;
719 return tp->type;
720 }
721 }
722 else if (strcmp (buff, tp->name) == 0)
723 {
724 yylval.Number = tp->value;
725 return tp->type;
726 }
727 }
728
729 for (tp = TimezoneTable; tp->name; tp++)
730 if (strcmp (buff, tp->name) == 0)
731 {
732 yylval.Number = tp->value;
733 return tp->type;
734 }
735
736 if (strcmp (buff, "dst") == 0)
737 return tDST;
738
739 for (tp = UnitsTable; tp->name; tp++)
740 if (strcmp (buff, tp->name) == 0)
741 {
742 yylval.Number = tp->value;
743 return tp->type;
744 }
745
746 /* Strip off any plural and try the units table again. */
747 i = strlen (buff) - 1;
748 if (buff[i] == 's')
749 {
750 buff[i] = '\0';
751 for (tp = UnitsTable; tp->name; tp++)
752 if (strcmp (buff, tp->name) == 0)
753 {
754 yylval.Number = tp->value;
755 return tp->type;
756 }
757 buff[i] = 's'; /* Put back for "this" in OtherTable. */
758 }
759
760 for (tp = OtherTable; tp->name; tp++)
761 if (strcmp (buff, tp->name) == 0)
762 {
763 yylval.Number = tp->value;
764 return tp->type;
765 }
766
767 /* Military timezones. */
768 if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff))
769 {
770 for (tp = MilitaryTable; tp->name; tp++)
771 if (strcmp (buff, tp->name) == 0)
772 {
773 yylval.Number = tp->value;
774 return tp->type;
775 }
776 }
777
778 /* Drop out any periods and try the timezone table again. */
779 for (i = 0, p = q = buff; *q; q++)
780 if (*q != '.')
781 *p++ = *q;
782 else
783 i++;
784 *p = '\0';
785 if (i)
786 for (tp = TimezoneTable; tp->name; tp++)
787 if (strcmp (buff, tp->name) == 0)
788 {
789 yylval.Number = tp->value;
790 return tp->type;
791 }
792
793 return tID;
794 }
795
796 static int
797 yylex ()
798 {
799 register unsigned char c;
800 register char *p;
801 char buff[20];
802 int Count;
803 int sign;
804
805 for (;;)
806 {
807 while (ISSPACE ((unsigned char) *yyInput))
808 yyInput++;
809
810 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
811 {
812 if (c == '-' || c == '+')
813 {
814 sign = c == '-' ? -1 : 1;
815 if (!ISDIGIT (*++yyInput))
816 /* skip the '-' sign */
817 continue;
818 }
819 else
820 sign = 0;
821 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
822 yylval.Number = 10 * yylval.Number + c - '0';
823 yyInput--;
824 if (sign < 0)
825 yylval.Number = -yylval.Number;
826 return sign ? tSNUMBER : tUNUMBER;
827 }
828 if (ISALPHA (c))
829 {
830 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
831 if (p < &buff[sizeof buff - 1])
832 *p++ = c;
833 *p = '\0';
834 yyInput--;
835 return LookupWord (buff);
836 }
837 if (c != '(')
838 return *yyInput++;
839 Count = 0;
840 do
841 {
842 c = *yyInput++;
843 if (c == '\0')
844 return c;
845 if (c == '(')
846 Count++;
847 else if (c == ')')
848 Count--;
849 }
850 while (Count > 0);
851 }
852 }
853
854 #define TM_YEAR_ORIGIN 1900
855
856 /* Yield A - B, measured in seconds. */
857 static long
858 difftm (struct tm *a, struct tm *b)
859 {
860 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
861 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
862 long days = (
863 /* difference in day of year */
864 a->tm_yday - b->tm_yday
865 /* + intervening leap days */
866 + ((ay >> 2) - (by >> 2))
867 - (ay / 100 - by / 100)
868 + ((ay / 100 >> 2) - (by / 100 >> 2))
869 /* + difference in years * 365 */
870 + (long) (ay - by) * 365
871 );
872 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
873 + (a->tm_min - b->tm_min))
874 + (a->tm_sec - b->tm_sec));
875 }
876
877 time_t
878 get_date (const char *p, const time_t *now)
879 {
880 struct tm tm, tm0, *tmp;
881 time_t Start;
882
883 yyInput = p;
884 Start = now ? *now : time ((time_t *) NULL);
885 tmp = localtime (&Start);
886 if (!tmp)
887 return -1;
888 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
889 yyMonth = tmp->tm_mon + 1;
890 yyDay = tmp->tm_mday;
891 yyHour = tmp->tm_hour;
892 yyMinutes = tmp->tm_min;
893 yySeconds = tmp->tm_sec;
894 tm.tm_isdst = tmp->tm_isdst;
895 yyMeridian = MER24;
896 yyRelSeconds = 0;
897 yyRelMinutes = 0;
898 yyRelHour = 0;
899 yyRelDay = 0;
900 yyRelMonth = 0;
901 yyRelYear = 0;
902 yyHaveDate = 0;
903 yyHaveDay = 0;
904 yyHaveRel = 0;
905 yyHaveTime = 0;
906 yyHaveZone = 0;
907
908 if (yyparse ()
909 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
910 return -1;
911
912 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
913 tm.tm_mon = yyMonth - 1 + yyRelMonth;
914 tm.tm_mday = yyDay + yyRelDay;
915 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
916 {
917 tm.tm_hour = ToHour (yyHour, yyMeridian);
918 if (tm.tm_hour < 0)
919 return -1;
920 tm.tm_min = yyMinutes;
921 tm.tm_sec = yySeconds;
922 }
923 else
924 {
925 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
926 }
927 tm.tm_hour += yyRelHour;
928 tm.tm_min += yyRelMinutes;
929 tm.tm_sec += yyRelSeconds;
930
931 /* Let mktime deduce tm_isdst if we have an absolute timestamp,
932 or if the relative timestamp mentions days, months, or years. */
933 if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear)
934 tm.tm_isdst = -1;
935
936 tm0 = tm;
937
938 Start = mktime (&tm);
939
940 if (Start == (time_t) -1)
941 {
942
943 /* Guard against falsely reporting errors near the time_t boundaries
944 when parsing times in other time zones. For example, if the min
945 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
946 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
947 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
948 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
949 zone by 24 hours to compensate. This algorithm assumes that
950 there is no DST transition within a day of the time_t boundaries. */
951 if (yyHaveZone)
952 {
953 tm = tm0;
954 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
955 {
956 tm.tm_mday++;
957 yyTimezone -= 24 * 60;
958 }
959 else
960 {
961 tm.tm_mday--;
962 yyTimezone += 24 * 60;
963 }
964 Start = mktime (&tm);
965 }
966
967 if (Start == (time_t) -1)
968 return Start;
969 }
970
971 if (yyHaveDay && !yyHaveDate)
972 {
973 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
974 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
975 Start = mktime (&tm);
976 if (Start == (time_t) -1)
977 return Start;
978 }
979
980 if (yyHaveZone)
981 {
982 long delta;
983 struct tm *gmt = gmtime (&Start);
984 if (!gmt)
985 return -1;
986 delta = yyTimezone * 60L + difftm (&tm, gmt);
987 if ((Start + delta < Start) != (delta < 0))
988 return -1; /* time_t overflow */
989 Start += delta;
990 }
991
992 return Start;
993 }
994
995 #if defined (TEST)
996
997 /* ARGSUSED */
998 int
999 main (ac, av)
1000 int ac;
1001 char *av[];
1002 {
1003 char buff[MAX_BUFF_LEN + 1];
1004 time_t d;
1005
1006 (void) printf ("Enter date, or blank line to exit.\n\t> ");
1007 (void) fflush (stdout);
1008
1009 buff[MAX_BUFF_LEN] = 0;
1010 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
1011 {
1012 d = get_date (buff, (time_t *) NULL);
1013 if (d == -1)
1014 (void) printf ("Bad format - couldn't convert.\n");
1015 else
1016 (void) printf ("%s", ctime (&d));
1017 (void) printf ("\t> ");
1018 (void) fflush (stdout);
1019 }
1020 exit (0);
1021 /* NOTREACHED */
1022 }
1023 #endif /* defined (TEST) */
This page took 0.074208 seconds and 5 git commands to generate.