1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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 timelib_long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb)
192{
193    timelib_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, size_t 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