xref: /PHP-5.3/ext/date/lib/parse_iso_intervals.re (revision a0bb3fd6)
1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Derick Rethans <derick@derickrethans.nl>                    |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#include "timelib.h"
22
23#include <stdio.h>
24#include <ctype.h>
25
26#ifdef HAVE_STDLIB_H
27#include <stdlib.h>
28#endif
29#ifdef HAVE_STRING_H
30#include <string.h>
31#else
32#include <strings.h>
33#endif
34
35#if defined(_MSC_VER)
36# define strtoll(s, f, b) _atoi64(s)
37#elif !defined(HAVE_STRTOLL)
38# if defined(HAVE_ATOLL)
39#  define strtoll(s, f, b) atoll(s)
40# else
41#  define strtoll(s, f, b) strtol(s, f, b)
42# endif
43#endif
44
45#define TIMELIB_UNSET   -99999
46
47#define TIMELIB_SECOND  1
48#define TIMELIB_MINUTE  2
49#define TIMELIB_HOUR    3
50#define TIMELIB_DAY     4
51#define TIMELIB_MONTH   5
52#define TIMELIB_YEAR    6
53
54#define EOI      257
55
56#define TIMELIB_PERIOD  260
57#define TIMELIB_ISO_DATE 261
58#define TIMELIB_ERROR   999
59
60typedef unsigned char uchar;
61
62#define   BSIZE	   8192
63
64#define   YYCTYPE      uchar
65#define   YYCURSOR     cursor
66#define   YYLIMIT      s->lim
67#define   YYMARKER     s->ptr
68#define   YYFILL(n)    return EOI;
69
70#define   RET(i)       {s->cur = cursor; return i;}
71
72#define timelib_string_free free
73
74#define TIMELIB_INIT  s->cur = cursor; str = timelib_string(s); ptr = str
75#define TIMELIB_DEINIT timelib_string_free(str)
76
77#ifdef DEBUG_PARSER
78#define DEBUG_OUTPUT(s) printf("%s\n", s);
79#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } }
80#else
81#define DEBUG_OUTPUT(s)
82#define YYDEBUG(s,c)
83#endif
84
85#include "timelib_structs.h"
86
87typedef struct Scanner {
88	int           fd;
89	uchar        *lim, *str, *ptr, *cur, *tok, *pos;
90	unsigned int  line, len;
91	struct timelib_error_container *errors;
92
93	struct timelib_time     *begin;
94	struct timelib_time     *end;
95	struct timelib_rel_time *period;
96	int                      recurrences;
97
98	int have_period;
99	int have_recurrences;
100	int have_date;
101	int have_begin_date;
102	int have_end_date;
103} Scanner;
104
105#define HOUR(a) (int)(a * 60)
106
107static void add_warning(Scanner *s, char *error)
108{
109	s->errors->warning_count++;
110	s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
111	s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0;
112	s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0;
113	s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
114}
115
116static void add_error(Scanner *s, char *error)
117{
118	s->errors->error_count++;
119	s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
120	s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0;
121	s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0;
122	s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
123}
124
125static char *timelib_string(Scanner *s)
126{
127	char *tmp = calloc(1, s->cur - s->tok + 1);
128	memcpy(tmp, s->tok, s->cur - s->tok);
129
130	return tmp;
131}
132
133static timelib_sll timelib_get_nr(char **ptr, int max_length)
134{
135	char *begin, *end, *str;
136	timelib_sll tmp_nr = TIMELIB_UNSET;
137	int len = 0;
138
139	while ((**ptr < '0') || (**ptr > '9')) {
140		if (**ptr == '\0') {
141			return TIMELIB_UNSET;
142		}
143		++*ptr;
144	}
145	begin = *ptr;
146	while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) {
147		++*ptr;
148		++len;
149	}
150	end = *ptr;
151	str = calloc(1, end - begin + 1);
152	memcpy(str, begin, end - begin);
153	tmp_nr = strtoll(str, NULL, 10);
154	free(str);
155	return tmp_nr;
156}
157
158static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length)
159{
160	timelib_ull dir = 1;
161
162	while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) {
163		if (**ptr == '\0') {
164			return TIMELIB_UNSET;
165		}
166		++*ptr;
167	}
168
169	while (**ptr == '+' || **ptr == '-')
170	{
171		if (**ptr == '-') {
172			dir *= -1;
173		}
174		++*ptr;
175	}
176	return dir * timelib_get_nr(ptr, max_length);
177}
178
179static long timelib_parse_tz_cor(char **ptr)
180{
181	char *begin = *ptr, *end;
182	long  tmp;
183
184	while (isdigit(**ptr) || **ptr == ':') {
185		++*ptr;
186	}
187	end = *ptr;
188	switch (end - begin) {
189		case 1:
190		case 2:
191			return HOUR(strtol(begin, NULL, 10));
192			break;
193		case 3:
194		case 4:
195			if (begin[1] == ':') {
196				tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 2, NULL, 10);
197				return tmp;
198			} else if (begin[2] == ':') {
199				tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10);
200				return tmp;
201			} else {
202				tmp = strtol(begin, NULL, 10);
203				return HOUR(tmp / 100) + tmp % 100;
204			}
205		case 5:
206			tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10);
207			return tmp;
208	}
209	return 0;
210}
211
212static void timelib_eat_spaces(char **ptr)
213{
214	while (**ptr == ' ' || **ptr == '\t') {
215		++*ptr;
216	}
217}
218
219static void timelib_eat_until_separator(char **ptr)
220{
221	while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) {
222		++*ptr;
223	}
224}
225
226static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb)
227{
228	long retval = 0;
229
230	*tz_not_found = 0;
231
232	while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
233		++*ptr;
234	}
235	if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
236		*ptr += 3;
237	}
238	if (**ptr == '+') {
239		++*ptr;
240		t->is_localtime = 1;
241		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
242		*tz_not_found = 0;
243		t->dst = 0;
244
245		retval = -1 * timelib_parse_tz_cor(ptr);
246	} else if (**ptr == '-') {
247		++*ptr;
248		t->is_localtime = 1;
249		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
250		*tz_not_found = 0;
251		t->dst = 0;
252
253		retval = timelib_parse_tz_cor(ptr);
254	}
255	while (**ptr == ')') {
256		++*ptr;
257	}
258	return retval;
259}
260
261#define timelib_split_free(arg) {       \
262	int i;                         \
263	for (i = 0; i < arg.c; i++) {  \
264		free(arg.v[i]);            \
265	}                              \
266	if (arg.v) {                   \
267		free(arg.v);               \
268	}                              \
269}
270
271/* date parser's scan function too large for VC6 - VC7.x
272   drop the optimization solves the problem */
273#ifdef PHP_WIN32
274#pragma optimize( "", off )
275#endif
276static int scan(Scanner *s)
277{
278	uchar *cursor = s->cur;
279	char *str, *ptr = NULL;
280
281std:
282	s->tok = cursor;
283	s->len = 0;
284/*!re2c
285
286/* */
287any = [\000-\377];
288number = [0-9]+;
289
290hour24lz = [01][0-9] | "2"[0-4];
291minutelz = [0-5][0-9];
292monthlz = "0" [1-9] | "1" [0-2];
293monthlzz = "0" [0-9] | "1" [0-2];
294daylz   = "0" [1-9] | [1-2][0-9] | "3" [01];
295daylzz  = "0" [0-9] | [1-2][0-9] | "3" [01];
296secondlz = minutelz;
297year4 = [0-9]{4};
298weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
299
300space = [ \t]+;
301datetimebasic  = year4 monthlz daylz "T" hour24lz minutelz secondlz "Z";
302datetimeextended  = year4 "-" monthlz "-" daylz "T" hour24lz ':' minutelz ':' secondlz "Z";
303period   = "P" (number "Y")? (number "M")? (number "W")? (number "D")? ("T" (number "H")? (number "M")? (number "S")?)?;
304combinedrep = "P" year4 "-" monthlzz "-" daylzz "T" hour24lz ':' minutelz ':' secondlz;
305
306recurrences = "R" number;
307
308isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
309isoweek          = year4 "-"? "W" weekofyear;
310
311*/
312
313/*!re2c
314	/* so that vim highlights correctly */
315	recurrences
316	{
317		DEBUG_OUTPUT("recurrences");
318		TIMELIB_INIT;
319		ptr++;
320		s->recurrences = timelib_get_unsigned_nr((char **) &ptr, 9);
321		TIMELIB_DEINIT;
322		s->have_recurrences = 1;
323		return TIMELIB_PERIOD;
324	}
325
326	datetimebasic| datetimeextended
327	{
328		timelib_time *current;
329
330		if (s->have_date || s->have_period) {
331			current = s->end;
332			s->have_end_date = 1;
333		} else {
334			current = s->begin;
335			s->have_begin_date = 1;
336		}
337		DEBUG_OUTPUT("datetimebasic | datetimeextended");
338		TIMELIB_INIT;
339		current->y = timelib_get_nr((char **) &ptr, 4);
340		current->m = timelib_get_nr((char **) &ptr, 2);
341		current->d = timelib_get_nr((char **) &ptr, 2);
342		current->h = timelib_get_nr((char **) &ptr, 2);
343		current->i = timelib_get_nr((char **) &ptr, 2);
344		current->s = timelib_get_nr((char **) &ptr, 2);
345		s->have_date = 1;
346		TIMELIB_DEINIT;
347		return TIMELIB_ISO_DATE;
348	}
349
350	period
351	{
352		timelib_sll nr;
353		int         in_time = 0;
354		DEBUG_OUTPUT("period");
355		TIMELIB_INIT;
356		ptr++;
357		do {
358			if ( *ptr == 'T' ) {
359				in_time = 1;
360				ptr++;
361			}
362			if ( *ptr == '\0' ) {
363				add_error(s, "Missing expected time part");
364				break;
365			}
366
367			nr = timelib_get_unsigned_nr((char **) &ptr, 12);
368			switch (*ptr) {
369				case 'Y': s->period->y = nr; break;
370				case 'W': s->period->d = nr * 7; break;
371				case 'D': s->period->d = nr; break;
372				case 'H': s->period->h = nr; break;
373				case 'S': s->period->s = nr; break;
374				case 'M':
375					if (in_time) {
376						s->period->i = nr;
377					} else {
378						s->period->m = nr;
379					}
380					break;
381				default:
382					add_error(s, "Undefined period specifier");
383					break;
384			}
385			ptr++;
386		} while (!s->errors->error_count && *ptr);
387		s->have_period = 1;
388		TIMELIB_DEINIT;
389		return TIMELIB_PERIOD;
390	}
391
392	combinedrep
393	{
394		DEBUG_OUTPUT("combinedrep");
395		TIMELIB_INIT;
396		s->period->y = timelib_get_unsigned_nr((char **) &ptr, 4);
397		ptr++;
398		s->period->m = timelib_get_unsigned_nr((char **) &ptr, 2);
399		ptr++;
400		s->period->d = timelib_get_unsigned_nr((char **) &ptr, 2);
401		ptr++;
402		s->period->h = timelib_get_unsigned_nr((char **) &ptr, 2);
403		ptr++;
404		s->period->i = timelib_get_unsigned_nr((char **) &ptr, 2);
405		ptr++;
406		s->period->s = timelib_get_unsigned_nr((char **) &ptr, 2);
407		s->have_period = 1;
408		TIMELIB_DEINIT;
409		return TIMELIB_PERIOD;
410	}
411
412	[ .,\t/]
413	{
414		goto std;
415	}
416
417	"\000"|"\n"
418	{
419		s->pos = cursor; s->line++;
420		goto std;
421	}
422
423	any
424	{
425		add_error(s, "Unexpected character");
426		goto std;
427	}
428*/
429}
430#ifdef PHP_WIN32
431#pragma optimize( "", on )
432#endif
433
434/*!max:re2c */
435
436void timelib_strtointerval(char *s, int len,
437                           timelib_time **begin, timelib_time **end,
438						   timelib_rel_time **period, int *recurrences,
439						   struct timelib_error_container **errors)
440{
441	Scanner in;
442	int t;
443	char *e = s + len - 1;
444
445	memset(&in, 0, sizeof(in));
446	in.errors = malloc(sizeof(struct timelib_error_container));
447	in.errors->warning_count = 0;
448	in.errors->warning_messages = NULL;
449	in.errors->error_count = 0;
450	in.errors->error_messages = NULL;
451
452	if (len > 0) {
453		while (isspace(*s) && s < e) {
454			s++;
455		}
456		while (isspace(*e) && e > s) {
457			e--;
458		}
459	}
460	if (e - s < 0) {
461		add_error(&in, "Empty string");
462		if (errors) {
463			*errors = in.errors;
464		} else {
465			timelib_error_container_dtor(in.errors);
466		}
467		return;
468	}
469	e++;
470
471	/* init cursor */
472	in.str = malloc((e - s) + YYMAXFILL);
473	memset(in.str, 0, (e - s) + YYMAXFILL);
474	memcpy(in.str, s, (e - s));
475	in.lim = in.str + (e - s) + YYMAXFILL;
476	in.cur = in.str;
477
478	/* init value containers */
479	in.begin = timelib_time_ctor();
480	in.begin->y = TIMELIB_UNSET;
481	in.begin->d = TIMELIB_UNSET;
482	in.begin->m = TIMELIB_UNSET;
483	in.begin->h = TIMELIB_UNSET;
484	in.begin->i = TIMELIB_UNSET;
485	in.begin->s = TIMELIB_UNSET;
486	in.begin->f = 0;
487	in.begin->z = 0;
488	in.begin->dst = 0;
489	in.begin->is_localtime = 0;
490	in.begin->zone_type = TIMELIB_ZONETYPE_OFFSET;
491
492	in.end = timelib_time_ctor();
493	in.end->y = TIMELIB_UNSET;
494	in.end->d = TIMELIB_UNSET;
495	in.end->m = TIMELIB_UNSET;
496	in.end->h = TIMELIB_UNSET;
497	in.end->i = TIMELIB_UNSET;
498	in.end->s = TIMELIB_UNSET;
499	in.end->f = 0;
500	in.end->z = 0;
501	in.end->dst = 0;
502	in.end->is_localtime = 0;
503	in.end->zone_type = TIMELIB_ZONETYPE_OFFSET;
504
505	in.period = timelib_rel_time_ctor();
506	in.period->y = 0;
507	in.period->d = 0;
508	in.period->m = 0;
509	in.period->h = 0;
510	in.period->i = 0;
511	in.period->s = 0;
512	in.period->weekday = 0;
513	in.period->weekday_behavior = 0;
514	in.period->first_last_day_of = 0;
515	in.period->days = TIMELIB_UNSET;
516
517	in.recurrences = 1;
518
519	do {
520		t = scan(&in);
521#ifdef DEBUG_PARSER
522		printf("%d\n", t);
523#endif
524	} while(t != EOI);
525
526	free(in.str);
527	if (errors) {
528		*errors = in.errors;
529	} else {
530		timelib_error_container_dtor(in.errors);
531	}
532	if (in.have_begin_date) {
533		*begin = in.begin;
534	} else {
535		timelib_time_dtor(in.begin);
536	}
537	if (in.have_end_date) {
538		*end   = in.end;
539	} else {
540		timelib_time_dtor(in.end);
541	}
542	if (in.have_period) {
543		*period = in.period;
544	} else {
545		timelib_rel_time_dtor(in.period);
546	}
547	if (in.have_recurrences) {
548		*recurrences = in.recurrences;
549	}
550}
551
552
553/*
554 * vim: syntax=c
555 */
556