xref: /PHP-5.5/ext/date/lib/parse_iso_intervals.re (revision cdd2b8a7)
1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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
105static void add_warning(Scanner *s, char *error)
106{
107	s->errors->warning_count++;
108	s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
109	s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0;
110	s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0;
111	s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
112}
113
114static void add_error(Scanner *s, char *error)
115{
116	s->errors->error_count++;
117	s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
118	s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0;
119	s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0;
120	s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
121}
122
123static char *timelib_string(Scanner *s)
124{
125	char *tmp = calloc(1, s->cur - s->tok + 1);
126	memcpy(tmp, s->tok, s->cur - s->tok);
127
128	return tmp;
129}
130
131static timelib_sll timelib_get_nr(char **ptr, int max_length)
132{
133	char *begin, *end, *str;
134	timelib_sll tmp_nr = TIMELIB_UNSET;
135	int len = 0;
136
137	while ((**ptr < '0') || (**ptr > '9')) {
138		if (**ptr == '\0') {
139			return TIMELIB_UNSET;
140		}
141		++*ptr;
142	}
143	begin = *ptr;
144	while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) {
145		++*ptr;
146		++len;
147	}
148	end = *ptr;
149	str = calloc(1, end - begin + 1);
150	memcpy(str, begin, end - begin);
151	tmp_nr = strtoll(str, NULL, 10);
152	free(str);
153	return tmp_nr;
154}
155
156static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length)
157{
158	timelib_ull dir = 1;
159
160	while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) {
161		if (**ptr == '\0') {
162			return TIMELIB_UNSET;
163		}
164		++*ptr;
165	}
166
167	while (**ptr == '+' || **ptr == '-')
168	{
169		if (**ptr == '-') {
170			dir *= -1;
171		}
172		++*ptr;
173	}
174	return dir * timelib_get_nr(ptr, max_length);
175}
176
177static void timelib_eat_spaces(char **ptr)
178{
179	while (**ptr == ' ' || **ptr == '\t') {
180		++*ptr;
181	}
182}
183
184static void timelib_eat_until_separator(char **ptr)
185{
186	while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) {
187		++*ptr;
188	}
189}
190
191static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb)
192{
193	long retval = 0;
194
195	*tz_not_found = 0;
196
197	while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
198		++*ptr;
199	}
200	if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
201		*ptr += 3;
202	}
203	if (**ptr == '+') {
204		++*ptr;
205		t->is_localtime = 1;
206		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
207		*tz_not_found = 0;
208		t->dst = 0;
209
210		retval = -1 * timelib_parse_tz_cor(ptr);
211	} else if (**ptr == '-') {
212		++*ptr;
213		t->is_localtime = 1;
214		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
215		*tz_not_found = 0;
216		t->dst = 0;
217
218		retval = timelib_parse_tz_cor(ptr);
219	}
220	while (**ptr == ')') {
221		++*ptr;
222	}
223	return retval;
224}
225
226#define timelib_split_free(arg) {       \
227	int i;                         \
228	for (i = 0; i < arg.c; i++) {  \
229		free(arg.v[i]);            \
230	}                              \
231	if (arg.v) {                   \
232		free(arg.v);               \
233	}                              \
234}
235
236/* date parser's scan function too large for VC6 - VC7.x
237   drop the optimization solves the problem */
238#ifdef PHP_WIN32
239#pragma optimize( "", off )
240#endif
241static int scan(Scanner *s)
242{
243	uchar *cursor = s->cur;
244	char *str, *ptr = NULL;
245
246std:
247	s->tok = cursor;
248	s->len = 0;
249/*!re2c
250
251/* */
252any = [\000-\377];
253number = [0-9]+;
254
255hour24lz = [01][0-9] | "2"[0-4];
256minutelz = [0-5][0-9];
257monthlz = "0" [1-9] | "1" [0-2];
258monthlzz = "0" [0-9] | "1" [0-2];
259daylz   = "0" [1-9] | [1-2][0-9] | "3" [01];
260daylzz  = "0" [0-9] | [1-2][0-9] | "3" [01];
261secondlz = minutelz;
262year4 = [0-9]{4};
263weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
264
265space = [ \t]+;
266datetimebasic  = year4 monthlz daylz "T" hour24lz minutelz secondlz "Z";
267datetimeextended  = year4 "-" monthlz "-" daylz "T" hour24lz ':' minutelz ':' secondlz "Z";
268period   = "P" (number "Y")? (number "M")? (number "W")? (number "D")? ("T" (number "H")? (number "M")? (number "S")?)?;
269combinedrep = "P" year4 "-" monthlzz "-" daylzz "T" hour24lz ':' minutelz ':' secondlz;
270
271recurrences = "R" number;
272
273isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
274isoweek          = year4 "-"? "W" weekofyear;
275
276*/
277
278/*!re2c
279	/* so that vim highlights correctly */
280	recurrences
281	{
282		DEBUG_OUTPUT("recurrences");
283		TIMELIB_INIT;
284		ptr++;
285		s->recurrences = timelib_get_unsigned_nr((char **) &ptr, 9);
286		TIMELIB_DEINIT;
287		s->have_recurrences = 1;
288		return TIMELIB_PERIOD;
289	}
290
291	datetimebasic| datetimeextended
292	{
293		timelib_time *current;
294
295		if (s->have_date || s->have_period) {
296			current = s->end;
297			s->have_end_date = 1;
298		} else {
299			current = s->begin;
300			s->have_begin_date = 1;
301		}
302		DEBUG_OUTPUT("datetimebasic | datetimeextended");
303		TIMELIB_INIT;
304		current->y = timelib_get_nr((char **) &ptr, 4);
305		current->m = timelib_get_nr((char **) &ptr, 2);
306		current->d = timelib_get_nr((char **) &ptr, 2);
307		current->h = timelib_get_nr((char **) &ptr, 2);
308		current->i = timelib_get_nr((char **) &ptr, 2);
309		current->s = timelib_get_nr((char **) &ptr, 2);
310		s->have_date = 1;
311		TIMELIB_DEINIT;
312		return TIMELIB_ISO_DATE;
313	}
314
315	period
316	{
317		timelib_sll nr;
318		int         in_time = 0;
319		DEBUG_OUTPUT("period");
320		TIMELIB_INIT;
321		ptr++;
322		do {
323			if ( *ptr == 'T' ) {
324				in_time = 1;
325				ptr++;
326			}
327			if ( *ptr == '\0' ) {
328				add_error(s, "Missing expected time part");
329				break;
330			}
331
332			nr = timelib_get_unsigned_nr((char **) &ptr, 12);
333			switch (*ptr) {
334				case 'Y': s->period->y = nr; break;
335				case 'W': s->period->d = nr * 7; break;
336				case 'D': s->period->d = nr; break;
337				case 'H': s->period->h = nr; break;
338				case 'S': s->period->s = nr; break;
339				case 'M':
340					if (in_time) {
341						s->period->i = nr;
342					} else {
343						s->period->m = nr;
344					}
345					break;
346				default:
347					add_error(s, "Undefined period specifier");
348					break;
349			}
350			ptr++;
351		} while (!s->errors->error_count && *ptr);
352		s->have_period = 1;
353		TIMELIB_DEINIT;
354		return TIMELIB_PERIOD;
355	}
356
357	combinedrep
358	{
359		DEBUG_OUTPUT("combinedrep");
360		TIMELIB_INIT;
361		s->period->y = timelib_get_unsigned_nr((char **) &ptr, 4);
362		ptr++;
363		s->period->m = timelib_get_unsigned_nr((char **) &ptr, 2);
364		ptr++;
365		s->period->d = timelib_get_unsigned_nr((char **) &ptr, 2);
366		ptr++;
367		s->period->h = timelib_get_unsigned_nr((char **) &ptr, 2);
368		ptr++;
369		s->period->i = timelib_get_unsigned_nr((char **) &ptr, 2);
370		ptr++;
371		s->period->s = timelib_get_unsigned_nr((char **) &ptr, 2);
372		s->have_period = 1;
373		TIMELIB_DEINIT;
374		return TIMELIB_PERIOD;
375	}
376
377	[ .,\t/]
378	{
379		goto std;
380	}
381
382	"\000"|"\n"
383	{
384		s->pos = cursor; s->line++;
385		goto std;
386	}
387
388	any
389	{
390		add_error(s, "Unexpected character");
391		goto std;
392	}
393*/
394}
395#ifdef PHP_WIN32
396#pragma optimize( "", on )
397#endif
398
399/*!max:re2c */
400
401void timelib_strtointerval(char *s, int len,
402                           timelib_time **begin, timelib_time **end,
403						   timelib_rel_time **period, int *recurrences,
404						   struct timelib_error_container **errors)
405{
406	Scanner in;
407	int t;
408	char *e = s + len - 1;
409
410	memset(&in, 0, sizeof(in));
411	in.errors = malloc(sizeof(struct timelib_error_container));
412	in.errors->warning_count = 0;
413	in.errors->warning_messages = NULL;
414	in.errors->error_count = 0;
415	in.errors->error_messages = NULL;
416
417	if (len > 0) {
418		while (isspace(*s) && s < e) {
419			s++;
420		}
421		while (isspace(*e) && e > s) {
422			e--;
423		}
424	}
425	if (e - s < 0) {
426		add_error(&in, "Empty string");
427		if (errors) {
428			*errors = in.errors;
429		} else {
430			timelib_error_container_dtor(in.errors);
431		}
432		return;
433	}
434	e++;
435
436	/* init cursor */
437	in.str = malloc((e - s) + YYMAXFILL);
438	memset(in.str, 0, (e - s) + YYMAXFILL);
439	memcpy(in.str, s, (e - s));
440	in.lim = in.str + (e - s) + YYMAXFILL;
441	in.cur = in.str;
442
443	/* init value containers */
444	in.begin = timelib_time_ctor();
445	in.begin->y = TIMELIB_UNSET;
446	in.begin->d = TIMELIB_UNSET;
447	in.begin->m = TIMELIB_UNSET;
448	in.begin->h = TIMELIB_UNSET;
449	in.begin->i = TIMELIB_UNSET;
450	in.begin->s = TIMELIB_UNSET;
451	in.begin->f = 0;
452	in.begin->z = 0;
453	in.begin->dst = 0;
454	in.begin->is_localtime = 0;
455	in.begin->zone_type = TIMELIB_ZONETYPE_OFFSET;
456
457	in.end = timelib_time_ctor();
458	in.end->y = TIMELIB_UNSET;
459	in.end->d = TIMELIB_UNSET;
460	in.end->m = TIMELIB_UNSET;
461	in.end->h = TIMELIB_UNSET;
462	in.end->i = TIMELIB_UNSET;
463	in.end->s = TIMELIB_UNSET;
464	in.end->f = 0;
465	in.end->z = 0;
466	in.end->dst = 0;
467	in.end->is_localtime = 0;
468	in.end->zone_type = TIMELIB_ZONETYPE_OFFSET;
469
470	in.period = timelib_rel_time_ctor();
471	in.period->y = 0;
472	in.period->d = 0;
473	in.period->m = 0;
474	in.period->h = 0;
475	in.period->i = 0;
476	in.period->s = 0;
477	in.period->weekday = 0;
478	in.period->weekday_behavior = 0;
479	in.period->first_last_day_of = 0;
480	in.period->days = TIMELIB_UNSET;
481
482	in.recurrences = 1;
483
484	do {
485		t = scan(&in);
486#ifdef DEBUG_PARSER
487		printf("%d\n", t);
488#endif
489	} while(t != EOI);
490
491	free(in.str);
492	if (errors) {
493		*errors = in.errors;
494	} else {
495		timelib_error_container_dtor(in.errors);
496	}
497	if (in.have_begin_date) {
498		*begin = in.begin;
499	} else {
500		timelib_time_dtor(in.begin);
501	}
502	if (in.have_end_date) {
503		*end   = in.end;
504	} else {
505		timelib_time_dtor(in.end);
506	}
507	if (in.have_period) {
508		*period = in.period;
509	} else {
510		timelib_rel_time_dtor(in.period);
511	}
512	if (in.have_recurrences) {
513		*recurrences = in.recurrences;
514	}
515}
516
517
518/*
519 * vim: syntax=c
520 */
521