1 %{
2 /*
3 * Imported from
4 * PHP CVS 1.56.2.2
5 * Fri May 20 07:14:01 2005
6 * http://cvs.php.net/php-src/ext/standard/parsedate.y
7 *
8 * and patched from there
9 *
10 * 1.59 removed this from PHP CVS and replaced it with entirely new
11 * code written under the PHP license:
12 * http://viewcvs.php.net/viewcvs.cgi/php-src/ext/date/lib/
13 * That code is not used here and cannot be used.
14 *
15 * The old version is now in the CVS Attic:
16 * http://viewcvs.php.net/viewcvs.cgi/php-src/ext/standard/Attic/parsedate.y
17 */
18
19
20 /*
21 ** Originally written by Steven M. Bellovin <smb@research.att.com> while
22 ** at the University of North Carolina at Chapel Hill. Later tweaked by
23 ** a couple of people on Usenet. Completely overhauled by Rich $alz
24 ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
25 **
26 ** This code is in the public domain and has no copyright.
27 */
28
29
30 #ifdef HAVE_CONFIG_H
31 #include <raptor_config.h>
32 #endif
33
34 #ifdef WIN32
35 #include <win32_raptor_config.h>
36 #endif
37
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <time.h>
41 #include <ctype.h>
42
43 #ifdef HAVE_SYS_TIME_H
44 # include <sys/time.h>
45 #endif
46
47 #if HAVE_STDLIB_H
48 #include <stdlib.h>
49 #endif
50
51 #if defined(_HPUX_SOURCE)
52 #include <alloca.h>
53 #endif
54
55 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
56 # define IN_CTYPE_DOMAIN(c) 1
57 #else
58 # define IN_CTYPE_DOMAIN(c) isascii(c)
59 #endif
60
61 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
62 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
63 #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
64 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
65
66 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
67 - Its arg may be any int or unsigned int; it need not be an unsigned char.
68 - It's guaranteed to evaluate its argument exactly once.
69 - It's typically faster.
70 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
71 only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
72 it's important to use the locale's definition of `digit' even when the
73 host does not conform to Posix. */
74 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
75
76 #if defined (STDC_HEADERS) || defined (USG)
77 # include <string.h>
78 #endif
79
80 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
81 # define __attribute__(x)
82 #endif
83
84 #ifndef ATTRIBUTE_UNUSED
85 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
86 #endif
87
88 /* Some old versions of bison generate parsers that use bcopy.
89 That loses on systems that don't provide the function, so we have
90 to redefine it here. */
91 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
92 # define bcopy(from, to, len) memcpy ((to), (from), (len))
93 #endif
94
95 /* Prototypes */
96 static int raptor_parsedate_error(const char *msg);
97 time_t raptor_parse_date(const char *p, time_t *now);
98
99
100 #define EPOCH 1970
101 #define HOUR(x) ((x) * 60)
102
103 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
104
105 /*
106 ** An entry in the lexical lookup table.
107 */
108 typedef struct _TABLE {
109 const char *name;
110 int type;
111 int value;
112 } TABLE;
113
114
115 /*
116 ** Meridian: am, pm, or 24-hour style.
117 */
118 typedef enum _MERIDIAN {
119 MERam, MERpm, MER24
120 } MERIDIAN;
121
122 struct date_yy {
123 const char *yyInput;
124 int yyDayOrdinal;
125 int yyDayNumber;
126 int yyHaveDate;
127 int yyHaveDay;
128 int yyHaveRel;
129 int yyHaveTime;
130 int yyHaveZone;
131 int yyTimezone;
132 int yyDay;
133 int yyHour;
134 int yyMinutes;
135 int yyMonth;
136 int yySeconds;
137 int yyYear;
138 MERIDIAN yyMeridian;
139 int yyRelDay;
140 int yyRelHour;
141 int yyRelMinutes;
142 int yyRelMonth;
143 int yyRelSeconds;
144 int yyRelYear;
145 };
146
147 typedef union _date_ll {
148 int Number;
149 enum _MERIDIAN Meridian;
150 } date_ll;
151
152 #define YYPARSE_PARAM parm
153 #define YYLEX_PARAM parm
154 #define YYSTYPE date_ll
155 #define YYLTYPE void
156
157 static int yylex (YYSTYPE *lvalp, void *parm);
158
159 static int ToHour (int Hours, MERIDIAN Meridian);
160 static int ToYear (int Year);
161 static int LookupWord (YYSTYPE *lvalp, char *buff);
162
163 %}
164
165 /* This grammar has 56 shift/reduce conflicts. */
166 %expect 56
167 %pure_parser
168
169 %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID tTZONE tWZONE tZZONE
170 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
171 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
172
173 %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
174 %type <Number> tMONTH tMONTH_UNIT
175 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE tTZONE tWZONE tZZONE
176 %type <Meridian> tMERIDIAN
177
178 %%
179
180 spec : /* NULL */
181 | spec item
182 ;
183
184 item : time {
185 ((struct date_yy *)parm)->yyHaveTime++;
186 }
187 | zone {
188 ((struct date_yy *)parm)->yyHaveZone++;
189 }
190 | date {
191 ((struct date_yy *)parm)->yyHaveDate++;
192 }
193 | day {
194 ((struct date_yy *)parm)->yyHaveDay++;
195 }
196 | rel {
197 ((struct date_yy *)parm)->yyHaveRel++;
198 }
199 | number
200 | o_merid
201 ;
202
203 time : tUNUMBER tMERIDIAN {
204 ((struct date_yy *)parm)->yyHour = $1;
205 ((struct date_yy *)parm)->yyMinutes = 0;
206 ((struct date_yy *)parm)->yySeconds = 0;
207 ((struct date_yy *)parm)->yyMeridian = $2;
208 }
209 | iso8601time_colon
210 /* | pgsqltime ... shares a common spec with ISO8601 */
211 ;
212
213 iso8601time_colon: HMStime_with_colon sec_fraction_part rel {
214 ((struct date_yy *)parm)->yyMeridian = MER24;
215 }
216 | HMtime_with_colon sec_fraction_part rel {
217 ((struct date_yy *)parm)->yyMeridian = MER24;
218 ((struct date_yy *)parm)->yySeconds = 0;
219 }
220 | HMStime_with_colon sec_fraction_part iso8601zonepart {
221 ((struct date_yy *)parm)->yyMeridian = MER24;
222 }
223 | HMtime_with_colon sec_fraction_part iso8601zonepart {
224 ((struct date_yy *)parm)->yyMeridian = MER24;
225 ((struct date_yy *)parm)->yySeconds = 0;
226 }
227 ;
228
229 iso8601zonepart: zonepart_numeric_without_colon {
230 ((struct date_yy *)parm)->yyHaveZone++;
231 }
232 | zonepart_numeric_with_colon {
233 ((struct date_yy *)parm)->yyHaveZone++;
234 }
235 | zone {
236 ((struct date_yy *)parm)->yyHaveZone++;
237 }
238 | /* empty */
239 ;
240
241 sec_fraction_part: '.' tUNUMBER {
242 }
243 | /* empty */
244 ;
245
246 zonepart_numeric_without_colon: tSNUMBER {
247 /* format: [+-]hhmm */
248 if ($1 <= -100 || $1 >= 100) {
249 ((struct date_yy *)parm)->yyTimezone = (-$1 / 100) * 60 + (-$1 % 100);
250 } else if ($1 >= -99 || $1 <= 99) {
251 ((struct date_yy *)parm)->yyTimezone = -$1 * 60;
252 }
253 }
254 ;
255
256 zonepart_numeric_with_colon: tSNUMBER ':' tUNUMBER {
257 /* format: [+-]hh:mm */
258 ((struct date_yy *)parm)->yyTimezone = -$1 * 60 + ($1 > 0 ? -$3: $3);
259 }
260 ;
261
262 HMStime_with_colon: HMtime_with_colon ':' tUNUMBER {
263 /* format: hh:mm:ss */
264 ((struct date_yy *)parm)->yySeconds = $3;
265 }
266 ;
267
268 HMtime_with_colon: tUNUMBER ':' tUNUMBER {
269 /* format: hh:mm */
270 ((struct date_yy *)parm)->yyHour = $1;
271 ((struct date_yy *)parm)->yyMinutes = $3;
272 }
273 ;
274
275
276 /* we have to deal with a special case for the datetime format
277 of XML Schema here: '2003-11-18T22:40:00Z'
278 the combination of a 'T' timezone specifier later followed
279 by a 'Z' is now recognized and allowed
280 TODO: change the grammer so that the exact positions are checked
281 right now '2003-11-18 22:40:00 TZ' is also accepted (hartmut)
282 */
283
284 zone : tTZONE {
285 ((struct date_yy *)parm)->yyTimezone = $1;
286 }
287 | tWZONE {
288 ((struct date_yy *)parm)->yyTimezone = $1;
289 }
290 | tZZONE {
291 ((struct date_yy *)parm)->yyTimezone = $1;
292 }
293 | tZONE {
294 ((struct date_yy *)parm)->yyTimezone = $1;
295 }
296 | tDAYZONE {
297 ((struct date_yy *)parm)->yyTimezone = $1 - 60;
298 }
299 | tZONE tDST {
300 ((struct date_yy *)parm)->yyTimezone = $1 - 60;
301 }
302 ;
303
304 day : tDAY {
305 ((struct date_yy *)parm)->yyDayOrdinal = 1;
306 ((struct date_yy *)parm)->yyDayNumber = $1;
307 }
308 | tDAY ',' {
309 ((struct date_yy *)parm)->yyDayOrdinal = 1;
310 ((struct date_yy *)parm)->yyDayNumber = $1;
311 }
312 | tUNUMBER tDAY {
313 ((struct date_yy *)parm)->yyDayOrdinal = $1;
314 ((struct date_yy *)parm)->yyDayNumber = $2;
315 }
316 ;
317
318 date : tUNUMBER '/' tUNUMBER {
319 ((struct date_yy *)parm)->yyMonth = $1;
320 ((struct date_yy *)parm)->yyDay = $3;
321 }
322 | tMONTH tUNUMBER tUNUMBER ':' tUNUMBER ':' tUNUMBER tUNUMBER {
323 ((struct date_yy *)parm)->yyYear = $8;
324 ((struct date_yy *)parm)->yyMonth = $1;
325 ((struct date_yy *)parm)->yyDay = $2;
326
327 ((struct date_yy *)parm)->yyHour = $3;
328 ((struct date_yy *)parm)->yyMinutes = $5;
329 ((struct date_yy *)parm)->yySeconds = $7;
330
331 ((struct date_yy *)parm)->yyHaveTime = 1;
332 }
333 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
334 /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
335 The goal in recognizing YYYY/MM/DD is solely to support legacy
336 machine-generated dates like those in an RCS log listing. If
337 you want portability, use the ISO 8601 format. */
338 if ($1 >= 1000)
339 {
340 ((struct date_yy *)parm)->yyYear = $1;
341 ((struct date_yy *)parm)->yyMonth = $3;
342 ((struct date_yy *)parm)->yyDay = $5;
343 }
344 else
345 {
346 ((struct date_yy *)parm)->yyMonth = $1;
347 ((struct date_yy *)parm)->yyDay = $3;
348 ((struct date_yy *)parm)->yyYear = $5;
349 }
350 }
351 | iso8601date
352 | iso8601datetime {
353 ((struct date_yy *)parm)->yyHaveTime++;
354 }
355 | tUNUMBER tMONTH tSNUMBER {
356 /* e.g. 17-JUN-1992. */
357 ((struct date_yy *)parm)->yyDay = $1;
358 ((struct date_yy *)parm)->yyMonth = $2;
359 ((struct date_yy *)parm)->yyYear = -$3;
360 }
361 | tMONTH tUNUMBER tUNUMBER {
362 ((struct date_yy *)parm)->yyMonth = $1;
363 ((struct date_yy *)parm)->yyDay = $2;
364 ((struct date_yy *)parm)->yyYear = $3;
365 }
366 | tMONTH tUNUMBER {
367 ((struct date_yy *)parm)->yyMonth = $1;
368 if ($2 > 1000) {
369 ((struct date_yy *)parm)->yyYear = $2;
370 } else {
371 ((struct date_yy *)parm)->yyDay = $2;
372 }
373 }
374 | tMONTH tUNUMBER ',' tUNUMBER {
375 ((struct date_yy *)parm)->yyMonth = $1;
376 ((struct date_yy *)parm)->yyDay = $2;
377 ((struct date_yy *)parm)->yyYear = $4;
378 }
379 | tUNUMBER tMONTH {
380 ((struct date_yy *)parm)->yyMonth = $2;
381 if ($1 > 1000) {
382 ((struct date_yy *)parm)->yyYear = $1;
383 } else {
384 ((struct date_yy *)parm)->yyDay = $1;
385 }
386 }
387 | tUNUMBER tMONTH tUNUMBER {
388 ((struct date_yy *)parm)->yyMonth = $2;
389 ((struct date_yy *)parm)->yyDay = $1;
390 ((struct date_yy *)parm)->yyYear = $3;
391 }
392 ;
393
394 iso8601datetime: iso8601date tTZONE iso8601time
395 | tUNUMBER tTZONE iso8601time {
396 int i = $1;
397
398 if (i >= 10000) {
399 /* format: yyyymmdd */
400 ((struct date_yy *)parm)->yyYear = i / 10000;
401 i %= 10000;
402 ((struct date_yy *)parm)->yyMonth = i / 100;
403 i %= 100;
404 ((struct date_yy *)parm)->yyDay = i;
405 } else if (i >= 1000 && i <= 9999) {
406 /* format: yyyy */
407 ((struct date_yy *)parm)->yyYear = i;
408 ((struct date_yy *)parm)->yyDay= 1;
409 ((struct date_yy *)parm)->yyMonth = 1;
410 }
411 }
412 ;
413
414 iso8601date: tUNUMBER tSNUMBER tSNUMBER {
415 /* ISO 8601 format. yyyy-mm-dd. */
416 ((struct date_yy *)parm)->yyYear = $1;
417 ((struct date_yy *)parm)->yyMonth = -$2;
418 ((struct date_yy *)parm)->yyDay = -$3;
419 }
420 | tUNUMBER tSNUMBER {
421 /* ISO 8601 format yyyy-mm */
422 ((struct date_yy *)parm)->yyYear = $1;
423 ((struct date_yy *)parm)->yyMonth = -$2;
424 ((struct date_yy *)parm)->yyDay = 1;
425 }
426 | tUNUMBER iso8601weekspec {
427 const int om = (1 + 9) % 12; /* offset month */
428 const int oy = $1 - 1; /* offset year */
429
430 ((struct date_yy *)parm)->yyYear = $1;
431 ((struct date_yy *)parm)->yyMonth = 1;
432 /* Zeller's formula */
433 ((struct date_yy *)parm)->yyDay -= ((13 * om + 12) / 5 +
434 oy + oy / 4 + oy / 400 - oy / 100) % 7 - 1;
435 }
436 ;
437
438 iso8601weekspec: tWZONE tUNUMBER {
439 ((struct date_yy *)parm)->yyDay = ($2 / 10) * 7 + ($2 % 10) - 8;
440 }
441 | tWZONE tUNUMBER tSNUMBER {
442 ((struct date_yy *)parm)->yyDay = $2 * 7 - $3 - 8;
443 }
444 ;
445
446 iso8601time:
447 iso8601time_colon
448 | tUNUMBER sec_fraction_part iso8601zonepart {
449 int i = $1;
450
451 if (i <= -100000 || i >= 100000) {
452 ((struct date_yy *)parm)->yyHour = i / 10000;
453 i %= 10000;
454 ((struct date_yy *)parm)->yyMinutes = i / 100;
455 i %= 100;
456 ((struct date_yy *)parm)->yySeconds = i;
457 } else if (i <= -1000 || i >= 1000) {
458 ((struct date_yy *)parm)->yyHour = i / 100;
459 i %= 100;
460 ((struct date_yy *)parm)->yyMinutes = i;
461 ((struct date_yy *)parm)->yySeconds = 0;
462 } else if (i >= -99 || i <= 99) {
463 ((struct date_yy *)parm)->yyHour = $1;
464 ((struct date_yy *)parm)->yyMinutes = 0;
465 ((struct date_yy *)parm)->yySeconds = 0;
466 } else {
467 ((struct date_yy *)parm)->yyHaveTime = 0;
468 }
469 ((struct date_yy *)parm)->yyMeridian = MER24;
470 }
471 ;
472
473 rel : relunit tAGO {
474 ((struct date_yy *)parm)->yyRelSeconds =
475 -((struct date_yy *)parm)->yyRelSeconds;
476 ((struct date_yy *)parm)->yyRelMinutes =
477 -((struct date_yy *)parm)->yyRelMinutes;
478 ((struct date_yy *)parm)->yyRelHour =
479 -((struct date_yy *)parm)->yyRelHour;
480 ((struct date_yy *)parm)->yyRelDay =
481 -((struct date_yy *)parm)->yyRelDay;
482 ((struct date_yy *)parm)->yyRelMonth =
483 -((struct date_yy *)parm)->yyRelMonth;
484 ((struct date_yy *)parm)->yyRelYear =
485 -((struct date_yy *)parm)->yyRelYear;
486 }
487 | relunit
488 ;
489
490 relunit : tUNUMBER tYEAR_UNIT {
491 ((struct date_yy *)parm)->yyRelYear += $1 * $2;
492 }
493 | tSNUMBER tYEAR_UNIT {
494 ((struct date_yy *)parm)->yyRelYear += $1 * $2;
495 }
496 | tYEAR_UNIT {
497 ((struct date_yy *)parm)->yyRelYear += $1;
498 }
499 | tUNUMBER tMONTH_UNIT {
500 ((struct date_yy *)parm)->yyRelMonth += $1 * $2;
501 }
502 | tSNUMBER tMONTH_UNIT {
503 ((struct date_yy *)parm)->yyRelMonth += $1 * $2;
504 }
505 | tMONTH_UNIT {
506 ((struct date_yy *)parm)->yyRelMonth += $1;
507 }
508 | tUNUMBER tDAY_UNIT {
509 ((struct date_yy *)parm)->yyRelDay += $1 * $2;
510 }
511 | tSNUMBER tDAY_UNIT {
512 ((struct date_yy *)parm)->yyRelDay += $1 * $2;
513 }
514 | tDAY_UNIT {
515 ((struct date_yy *)parm)->yyRelDay += $1;
516 }
517 | tUNUMBER tHOUR_UNIT {
518 ((struct date_yy *)parm)->yyRelHour += $1 * $2;
519 }
520 | tSNUMBER tHOUR_UNIT {
521 ((struct date_yy *)parm)->yyRelHour += $1 * $2;
522 }
523 | tHOUR_UNIT {
524 ((struct date_yy *)parm)->yyRelHour += $1;
525 }
526 | tUNUMBER tMINUTE_UNIT {
527 ((struct date_yy *)parm)->yyRelMinutes += $1 * $2;
528 }
529 | tSNUMBER tMINUTE_UNIT {
530 ((struct date_yy *)parm)->yyRelMinutes += $1 * $2;
531 }
532 | tMINUTE_UNIT {
533 ((struct date_yy *)parm)->yyRelMinutes += $1;
534 }
535 | tUNUMBER tSEC_UNIT {
536 ((struct date_yy *)parm)->yyRelSeconds += $1 * $2;
537 }
538 | tSNUMBER tSEC_UNIT {
539 ((struct date_yy *)parm)->yyRelSeconds += $1 * $2;
540 }
541 | tSEC_UNIT {
542 ((struct date_yy *)parm)->yyRelSeconds += $1;
543 }
544 ;
545
546 number : tUNUMBER
547 {
548 if (((struct date_yy *)parm)->yyHaveTime &&
549 ((struct date_yy *)parm)->yyHaveDate &&
550 !((struct date_yy *)parm)->yyHaveRel)
551 ((struct date_yy *)parm)->yyYear = $1;
552 else
553 {
554 if ($1>10000)
555 {
556 ((struct date_yy *)parm)->yyHaveDate++;
557 ((struct date_yy *)parm)->yyDay= ($1)%100;
558 ((struct date_yy *)parm)->yyMonth= ($1/100)%100;
559 ((struct date_yy *)parm)->yyYear = $1/10000;
560 }
561 else
562 {
563 ((struct date_yy *)parm)->yyHaveTime++;
564 if ($1 < 100)
565 {
566 ((struct date_yy *)parm)->yyHour = $1;
567 ((struct date_yy *)parm)->yyMinutes = 0;
568 }
569 else
570 {
571 ((struct date_yy *)parm)->yyHour = $1 / 100;
572 ((struct date_yy *)parm)->yyMinutes = $1 % 100;
573 }
574 ((struct date_yy *)parm)->yySeconds = 0;
575 ((struct date_yy *)parm)->yyMeridian = MER24;
576 }
577 }
578 }
579 ;
580
581 o_merid : tMERIDIAN
582 {
583 ((struct date_yy *)parm)->yyMeridian = $1;
584 }
585 ;
586
587 %%
588
589 time_t get_date (char *p, time_t *now);
590
591 /* Month and day table. */
592 static TABLE const MonthDayTable[] = {
593 { "january", tMONTH, 1 },
594 { "february", tMONTH, 2 },
595 { "march", tMONTH, 3 },
596 { "april", tMONTH, 4 },
597 { "may", tMONTH, 5 },
598 { "june", tMONTH, 6 },
599 { "july", tMONTH, 7 },
600 { "august", tMONTH, 8 },
601 { "september", tMONTH, 9 },
602 { "sept", tMONTH, 9 },
603 { "october", tMONTH, 10 },
604 { "november", tMONTH, 11 },
605 { "december", tMONTH, 12 },
606 { "sunday", tDAY, 0 },
607 { "monday", tDAY, 1 },
608 { "tuesday", tDAY, 2 },
609 { "tues", tDAY, 2 },
610 { "wednesday", tDAY, 3 },
611 { "wednes", tDAY, 3 },
612 { "thursday", tDAY, 4 },
613 { "thur", tDAY, 4 },
614 { "thurs", tDAY, 4 },
615 { "friday", tDAY, 5 },
616 { "saturday", tDAY, 6 },
617 { NULL, 0, 0 }
618 };
619
620 /* Time units table. */
621 static TABLE const UnitsTable[] = {
622 { "year", tYEAR_UNIT, 1 },
623 { "month", tMONTH_UNIT, 1 },
624 { "fortnight", tDAY_UNIT, 14 },
625 { "week", tDAY_UNIT, 7 },
626 { "day", tDAY_UNIT, 1 },
627 { "hour", tHOUR_UNIT, 1 },
628 { "minute", tMINUTE_UNIT, 1 },
629 { "min", tMINUTE_UNIT, 1 },
630 { "second", tSEC_UNIT, 1 },
631 { "sec", tSEC_UNIT, 1 },
632 { NULL, 0, 0 }
633 };
634
635 /* Assorted relative-time words. */
636 static TABLE const OtherTable[] = {
637 { "tomorrow", tDAY_UNIT, 1 },
638 { "yesterday", tDAY_UNIT, -1 },
639 { "today", tDAY_UNIT, 0 },
640 { "now", tDAY_UNIT, 0 },
641 { "last", tUNUMBER, -1 },
642 { "this", tUNUMBER, 0 },
643 { "next", tUNUMBER, 1 },
644 { "first", tUNUMBER, 1 },
645 /* { "second", tUNUMBER, 2 }, */
646 { "third", tUNUMBER, 3 },
647 { "fourth", tUNUMBER, 4 },
648 { "fifth", tUNUMBER, 5 },
649 { "sixth", tUNUMBER, 6 },
650 { "seventh", tUNUMBER, 7 },
651 { "eighth", tUNUMBER, 8 },
652 { "ninth", tUNUMBER, 9 },
653 { "tenth", tUNUMBER, 10 },
654 { "eleventh", tUNUMBER, 11 },
655 { "twelfth", tUNUMBER, 12 },
656 { "ago", tAGO, 1 },
657 { NULL, 0, 0 }
658 };
659
660 /* The timezone table. */
661 static TABLE const TimezoneTable[] = {
662 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
663 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
664 { "utc", tZONE, HOUR ( 0) },
665 { "wet", tZONE, HOUR ( 0) }, /* Western European */
666 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
667 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
668 { "at", tZONE, HOUR ( 2) }, /* Azores */
669 #if 0
670 /* For completeness. BST is also British Summer, and GST is
671 * also Guam Standard. */
672 { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
673 { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
674 #endif
675 #if 0
676 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
677 { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
678 { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
679 #endif
680 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
681 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
682 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
683 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
684 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
685 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
686 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
687 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
688 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
689 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
690 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
691 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
692 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
693 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
694 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
695 { "akst", tZONE, HOUR (10) }, /* Alaska Standard */
696 { "akdt", tZONE, HOUR (10) }, /* Alaska Daylight */
697 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
698 { "nt", tZONE, HOUR (11) }, /* Nome */
699 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
700 { "cet", tZONE, -HOUR (1) }, /* Central European */
701 { "cest", tDAYZONE, -HOUR (1) }, /* Central European Summer */
702 { "met", tZONE, -HOUR (1) }, /* Middle European */
703 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
704 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
705 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
706 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
707 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
708 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
709 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
710 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
711 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
712 #if 0
713 { "it", tZONE, -HOUR (3.5) },/* Iran */
714 #endif
715 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
716 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
717 #if 0
718 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
719 #endif
720 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
721 #if 0
722 /* For completeness. NST is also Newfoundland Standard, and SST is
723 * also Swedish Summer. */
724 { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
725 { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
726 #endif /* 0 */
727 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
728 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
729 #if 0
730 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
731 #endif
732 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
733 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
734 #if 0
735 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
736 { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
737 #endif
738 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
739 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
740 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
741 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
742 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
743 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
744 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
745 { NULL, 0, 0 }
746 };
747
748 /* Military timezone table. */
749 static TABLE const MilitaryTable[] = {
750 { "a", tZONE, HOUR (- 1) },
751 { "b", tZONE, HOUR (- 2) },
752 { "c", tZONE, HOUR (- 3) },
753 { "d", tZONE, HOUR (- 4) },
754 { "e", tZONE, HOUR (- 5) },
755 { "f", tZONE, HOUR (- 6) },
756 { "g", tZONE, HOUR (- 7) },
757 { "h", tZONE, HOUR (- 8) },
758 { "i", tZONE, HOUR (- 9) },
759 { "k", tZONE, HOUR (-10) },
760 { "l", tZONE, HOUR (-11) },
761 { "m", tZONE, HOUR (-12) },
762 { "n", tZONE, HOUR ( 1) },
763 { "o", tZONE, HOUR ( 2) },
764 { "p", tZONE, HOUR ( 3) },
765 { "q", tZONE, HOUR ( 4) },
766 { "r", tZONE, HOUR ( 5) },
767 { "s", tZONE, HOUR ( 6) },
768 { "t", tTZONE, HOUR ( 7) },
769 { "u", tZONE, HOUR ( 8) },
770 { "v", tZONE, HOUR ( 9) },
771 { "w", tWZONE, HOUR ( 10) },
772 { "x", tZONE, HOUR ( 11) },
773 { "y", tZONE, HOUR ( 12) },
774 { "z", tZZONE, HOUR ( 0) },
775 { NULL, 0, 0 }
776 };
777
778
779
780
781 /* ARGSUSED */
782 static int
yyerror(const char * s)783 yyerror(const char *s)
784 {
785 return 0;
786 }
787
788 static int
ToHour(int Hours,MERIDIAN Meridian)789 ToHour(int Hours, MERIDIAN Meridian)
790 {
791 switch (Meridian)
792 {
793 case MER24:
794 if (Hours < 0 || Hours > 23)
795 return -1;
796 return Hours;
797 case MERam:
798 if (Hours < 1 || Hours > 12)
799 return -1;
800 if (Hours == 12)
801 Hours = 0;
802 return Hours;
803 case MERpm:
804 if (Hours < 1 || Hours > 12)
805 return -1;
806 if (Hours == 12)
807 Hours = 0;
808 return Hours + 12;
809 default:
810 #ifdef RAPTOR_DEBUG
811 fprintf(stderr, "%s:%d:%s: UNKNOWN Meridian %d - add a new case",
812 __FILE__, __LINE__, __func__, (int)Meridian);
813 #endif
814 return -1;
815 }
816 /* NOTREACHED */
817 }
818
819 static int
ToYear(int Year)820 ToYear(int Year)
821 {
822 if (Year < 0)
823 Year = -Year;
824
825 /* XPG4 suggests that years 00-68 map to 2000-2068, and
826 years 69-99 map to 1969-1999. */
827 if (Year < 69)
828 Year += 2000;
829 else if (Year < 100)
830 Year += 1900;
831
832 return Year;
833 }
834
835 static int
LookupWord(YYSTYPE * lvalp,char * buff)836 LookupWord (YYSTYPE *lvalp, char *buff)
837 {
838 char *p;
839 char *q;
840 const TABLE *tp;
841 int i;
842 int abbrev;
843
844 /* Make it lowercase. */
845 for (p = buff; *p; p++)
846 if (ISUPPER ((unsigned char) *p))
847 *p = tolower (*p);
848
849 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
850 {
851 lvalp->Meridian = MERam;
852 return tMERIDIAN;
853 }
854 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
855 {
856 lvalp->Meridian = MERpm;
857 return tMERIDIAN;
858 }
859
860 /* See if we have an abbreviation for a month. */
861 if (strlen (buff) == 3)
862 abbrev = 1;
863 else if (strlen (buff) == 4 && buff[3] == '.')
864 {
865 abbrev = 1;
866 buff[3] = '\0';
867 }
868 else
869 abbrev = 0;
870
871 for (tp = MonthDayTable; tp->name; tp++)
872 {
873 if (abbrev)
874 {
875 if (strncmp (buff, tp->name, 3) == 0)
876 {
877 lvalp->Number = tp->value;
878 return tp->type;
879 }
880 }
881 else if (strcmp (buff, tp->name) == 0)
882 {
883 lvalp->Number = tp->value;
884 return tp->type;
885 }
886 }
887
888 for (tp = TimezoneTable; tp->name; tp++)
889 if (strcmp (buff, tp->name) == 0)
890 {
891 lvalp->Number = tp->value;
892 return tp->type;
893 }
894
895 if (strcmp (buff, "dst") == 0)
896 return tDST;
897
898 for (tp = UnitsTable; tp->name; tp++)
899 if (strcmp (buff, tp->name) == 0)
900 {
901 lvalp->Number = tp->value;
902 return tp->type;
903 }
904
905 /* Strip off any plural and try the units table again. */
906 i = strlen (buff) - 1;
907 if (buff[i] == 's')
908 {
909 buff[i] = '\0';
910 for (tp = UnitsTable; tp->name; tp++)
911 if (strcmp (buff, tp->name) == 0)
912 {
913 lvalp->Number = tp->value;
914 return tp->type;
915 }
916 buff[i] = 's'; /* Put back for "this" in OtherTable. */
917 }
918
919 for (tp = OtherTable; tp->name; tp++)
920 if (strcmp (buff, tp->name) == 0)
921 {
922 lvalp->Number = tp->value;
923 return tp->type;
924 }
925
926 /* Military timezones. */
927 if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff))
928 {
929 for (tp = MilitaryTable; tp->name; tp++)
930 if (strcmp (buff, tp->name) == 0)
931 {
932 lvalp->Number = tp->value;
933 return tp->type;
934 }
935 }
936
937 /* Drop out any periods and try the timezone table again. */
938 for (i = 0, p = q = buff; *q; q++)
939 if (*q != '.')
940 *p++ = *q;
941 else
942 i++;
943 *p = '\0';
944 if (i)
945 for (tp = TimezoneTable; tp->name; tp++)
946 if (strcmp (buff, tp->name) == 0)
947 {
948 lvalp->Number = tp->value;
949 return tp->type;
950 }
951
952 return tID;
953 }
954
yylex(YYSTYPE * lvalp,void * parm)955 int yylex(YYSTYPE *lvalp, void *parm)
956 {
957 unsigned char c;
958 char *p;
959 char buff[20];
960 int Count;
961 int sign;
962 struct date_yy * date = (struct date_yy *)parm;
963
964 for (;;)
965 {
966 while (ISSPACE ((unsigned char) *date->yyInput))
967 date->yyInput++;
968
969 if (ISDIGIT (c = *date->yyInput) || c == '-' || c == '+')
970 {
971 if (c == '-' || c == '+')
972 {
973 sign = c == '-' ? -1 : 1;
974 if (!ISDIGIT (*++date->yyInput))
975 /* skip the '-' sign */
976 continue;
977 }
978 else
979 sign = 0;
980 for (lvalp->Number = 0; ISDIGIT (c = *date->yyInput++);)
981 lvalp->Number = 10 * lvalp->Number + c - '0';
982 date->yyInput--;
983 if (sign < 0)
984 lvalp->Number = -lvalp->Number;
985 /* Ignore ordinal suffixes on numbers */
986 c = *date->yyInput;
987 if (c == 's' || c == 'n' || c == 'r' || c == 't') {
988 c = *++date->yyInput;
989 if (c == 't' || c == 'd' || c == 'h') {
990 date->yyInput++;
991 } else {
992 date->yyInput--;
993 }
994 }
995 return sign ? tSNUMBER : tUNUMBER;
996 }
997 if (ISALPHA (c))
998 {
999 for (p = buff; (c = *date->yyInput++, ISALPHA (c)) || c == '.';)
1000 if (p < &buff[sizeof buff - 1])
1001 *p++ = c;
1002 *p = '\0';
1003 date->yyInput--;
1004 return LookupWord (lvalp, buff);
1005 }
1006 if (c != '(')
1007 return *date->yyInput++;
1008 Count = 0;
1009 do
1010 {
1011 c = *date->yyInput++;
1012 if (c == '\0')
1013 return c;
1014 if (c == '(')
1015 Count++;
1016 else if (c == ')')
1017 Count--;
1018 }
1019 while (Count > 0);
1020 }
1021 }
1022
1023 #define TM_YEAR_ORIGIN 1900
1024
1025 /* Yield A - B, measured in seconds. */
1026 static long
difftm(struct tm * a,struct tm * b)1027 difftm (struct tm *a, struct tm *b)
1028 {
1029 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
1030 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
1031 long days = (
1032 /* difference in day of year */
1033 a->tm_yday - b->tm_yday
1034 /* + intervening leap days */
1035 + ((ay >> 2) - (by >> 2))
1036 - (ay / 100 - by / 100)
1037 + ((ay / 100 >> 2) - (by / 100 >> 2))
1038 /* + difference in years * 365 */
1039 + (long) (ay - by) * 365
1040 );
1041 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1042 + (a->tm_min - b->tm_min))
1043 + (a->tm_sec - b->tm_sec));
1044 }
1045
raptor_parse_date(const char * p,time_t * now)1046 time_t raptor_parse_date(const char *p, time_t *now)
1047 {
1048 struct tm tm, tm0, *tmp;
1049 time_t Start;
1050 struct date_yy date;
1051
1052 date.yyInput = p;
1053 Start = now ? *now : time ((time_t *) NULL);
1054 tmp = localtime (&Start);
1055 if (!tmp)
1056 return -1;
1057 date.yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
1058 date.yyMonth = tmp->tm_mon + 1;
1059 date.yyDay = tmp->tm_mday;
1060 date.yyHour = tmp->tm_hour;
1061 date.yyMinutes = tmp->tm_min;
1062 date.yySeconds = tmp->tm_sec;
1063 tm.tm_isdst = tmp->tm_isdst;
1064 date.yyMeridian = MER24;
1065 date.yyRelSeconds = 0;
1066 date.yyRelMinutes = 0;
1067 date.yyRelHour = 0;
1068 date.yyRelDay = 0;
1069 date.yyRelMonth = 0;
1070 date.yyRelYear = 0;
1071 date.yyHaveDate = 0;
1072 date.yyHaveDay = 0;
1073 date.yyHaveRel = 0;
1074 date.yyHaveTime = 0;
1075 date.yyHaveZone = 0;
1076
1077 if (yyparse ((void *)&date)
1078 || date.yyHaveTime > 1 || date.yyHaveZone > 1
1079 || date.yyHaveDate > 1 || date.yyHaveDay > 1) {
1080 return -1;
1081 }
1082 tm.tm_year = ToYear (date.yyYear) - TM_YEAR_ORIGIN + date.yyRelYear;
1083 tm.tm_mon = date.yyMonth - 1 + date.yyRelMonth;
1084 tm.tm_mday = date.yyDay + date.yyRelDay;
1085 if (date.yyHaveTime || (date.yyHaveRel && !date.yyHaveDate && !date.yyHaveDay))
1086 {
1087 tm.tm_hour = ToHour (date.yyHour, date.yyMeridian);
1088 if (tm.tm_hour < 0)
1089 return -1;
1090 tm.tm_min = date.yyMinutes;
1091 tm.tm_sec = date.yySeconds;
1092 }
1093 else
1094 {
1095 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1096 }
1097 tm.tm_hour += date.yyRelHour;
1098 tm.tm_min += date.yyRelMinutes;
1099 tm.tm_sec += date.yyRelSeconds;
1100
1101 /* Let mktime deduce tm_isdst if we have an absolute timestamp,
1102 or if the relative timestamp mentions days, months, or years. */
1103 if (date.yyHaveDate | date.yyHaveDay | date.yyHaveTime | date.yyRelDay | date.yyRelMonth | date.yyRelYear)
1104 tm.tm_isdst = -1;
1105
1106 tm0 = tm;
1107
1108 Start = mktime (&tm);
1109
1110 if (Start == (time_t) -1)
1111 {
1112
1113 /* Guard against falsely reporting errors near the time_t boundaries
1114 when parsing times in other time zones. For example, if the min
1115 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
1116 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
1117 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
1118 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
1119 zone by 24 hours to compensate. This algorithm assumes that
1120 there is no DST transition within a day of the time_t boundaries. */
1121 if (date.yyHaveZone)
1122 {
1123 tm = tm0;
1124 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
1125 {
1126 tm.tm_mday++;
1127 date.yyTimezone -= 24 * 60;
1128 }
1129 else
1130 {
1131 tm.tm_mday--;
1132 date.yyTimezone += 24 * 60;
1133 }
1134 Start = mktime (&tm);
1135 }
1136
1137 if (Start == (time_t) -1)
1138 return Start;
1139 }
1140
1141 if (date.yyHaveDay && !date.yyHaveDate)
1142 {
1143 tm.tm_mday += ((date.yyDayNumber - tm.tm_wday + 7) % 7
1144 + 7 * (date.yyDayOrdinal - (0 < date.yyDayOrdinal)));
1145 Start = mktime (&tm);
1146 if (Start == (time_t) -1)
1147 return Start;
1148 }
1149
1150 if (date.yyHaveZone)
1151 {
1152 long delta;
1153 struct tm *gmt = gmtime (&Start);
1154 if (!gmt)
1155 return -1;
1156 delta = date.yyTimezone * 60L + difftm (&tm, gmt);
1157
1158 if ((Start + delta < Start) != (delta < 0))
1159 return -1; /* time_t overflow */
1160 Start += delta;
1161 }
1162
1163 return Start;
1164 }
1165