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