1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
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#include <math.h>
26#include <assert.h>
27
28#ifdef HAVE_STDLIB_H
29#include <stdlib.h>
30#endif
31#ifdef HAVE_STRING_H
32#include <string.h>
33#else
34#include <strings.h>
35#endif
36
37#if defined(_MSC_VER)
38# define strtoll(s, f, b) _atoi64(s)
39#elif !defined(HAVE_STRTOLL)
40# if defined(HAVE_ATOLL)
41#  define strtoll(s, f, b) atoll(s)
42# else
43#  define strtoll(s, f, b) strtol(s, f, b)
44# endif
45#endif
46
47#define TIMELIB_UNSET   -99999
48
49#define TIMELIB_SECOND  1
50#define TIMELIB_MINUTE  2
51#define TIMELIB_HOUR    3
52#define TIMELIB_DAY     4
53#define TIMELIB_MONTH   5
54#define TIMELIB_YEAR    6
55#define TIMELIB_WEEKDAY 7
56#define TIMELIB_SPECIAL 8
57
58#define EOI      257
59#define TIME     258
60#define DATE     259
61
62#define TIMELIB_XMLRPC_SOAP    260
63#define TIMELIB_TIME12         261
64#define TIMELIB_TIME24         262
65#define TIMELIB_GNU_NOCOLON    263
66#define TIMELIB_GNU_NOCOLON_TZ 264
67#define TIMELIB_ISO_NOCOLON    265
68
69#define TIMELIB_AMERICAN       266
70#define TIMELIB_ISO_DATE       267
71#define TIMELIB_DATE_FULL      268
72#define TIMELIB_DATE_TEXT      269
73#define TIMELIB_DATE_NOCOLON   270
74#define TIMELIB_PG_YEARDAY     271
75#define TIMELIB_PG_TEXT        272
76#define TIMELIB_PG_REVERSE     273
77#define TIMELIB_CLF            274
78#define TIMELIB_DATE_NO_DAY    275
79#define TIMELIB_SHORTDATE_WITH_TIME 276
80#define TIMELIB_DATE_FULL_POINTED 277
81#define TIMELIB_TIME24_WITH_ZONE 278
82#define TIMELIB_ISO_WEEK       279
83#define TIMELIB_LF_DAY_OF_MONTH 280
84#define TIMELIB_WEEK_DAY_OF_MONTH 281
85
86#define TIMELIB_TIMEZONE       300
87#define TIMELIB_AGO            301
88
89#define TIMELIB_RELATIVE       310
90
91#define TIMELIB_ERROR          999
92
93/* Some compilers like AIX, defines uchar in sys/types.h */
94#undef uchar
95typedef unsigned char uchar;
96
97#define   BSIZE    8192
98
99#define   YYCTYPE      uchar
100#define   YYCURSOR     cursor
101#define   YYLIMIT      s->lim
102#define   YYMARKER     s->ptr
103#define   YYFILL(n)    return EOI;
104
105#define   RET(i)       {s->cur = cursor; return i;}
106
107#define timelib_string_free free
108
109#define TIMELIB_HAVE_TIME() { if (s->time->have_time) { add_error(s, "Double time specification"); timelib_string_free(str); return TIMELIB_ERROR; } else { s->time->have_time = 1; s->time->h = 0; s->time->i = 0; s->time->s = 0; s->time->f = 0; } }
110#define TIMELIB_UNHAVE_TIME() { s->time->have_time = 0; s->time->h = 0; s->time->i = 0; s->time->s = 0; s->time->f = 0; }
111#define TIMELIB_HAVE_DATE() { if (s->time->have_date) { add_error(s, "Double date specification"); timelib_string_free(str); return TIMELIB_ERROR; } else { s->time->have_date = 1; } }
112#define TIMELIB_UNHAVE_DATE() { s->time->have_date = 0; s->time->d = 0; s->time->m = 0; s->time->y = 0; }
113#define TIMELIB_HAVE_RELATIVE() { s->time->have_relative = 1; }
114#define TIMELIB_HAVE_WEEKDAY_RELATIVE() { s->time->have_relative = 1; s->time->relative.have_weekday_relative = 1; }
115#define TIMELIB_HAVE_SPECIAL_RELATIVE() { s->time->have_relative = 1; s->time->relative.have_special_relative = 1; }
116#define TIMELIB_HAVE_TZ() { s->cur = cursor; if (s->time->have_zone) { s->time->have_zone > 1 ? add_error(s, "Double timezone specification") : add_warning(s, "Double timezone specification"); timelib_string_free(str); s->time->have_zone++; return TIMELIB_ERROR; } else { s->time->have_zone++; } }
117
118#define TIMELIB_INIT  s->cur = cursor; str = timelib_string(s); ptr = str
119#define TIMELIB_DEINIT timelib_string_free(str)
120#define TIMELIB_ADJUST_RELATIVE_WEEKDAY() if (in->time.have_weekday_relative && (in.rel.d > 0)) { in.rel.d -= 7; }
121
122#define TIMELIB_PROCESS_YEAR(x, l) { \
123    if (((x) == TIMELIB_UNSET) || ((l) >= 4)) { \
124    /*  (x) = 0; */          \
125    } else if ((x) < 100) {  \
126        if ((x) < 70) {      \
127            (x) += 2000;     \
128        } else {             \
129            (x) += 1900;     \
130        }                    \
131    }                        \
132}
133
134#ifdef DEBUG_PARSER
135#define DEBUG_OUTPUT(s) printf("%s\n", s);
136#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } }
137#else
138#define DEBUG_OUTPUT(s)
139#define YYDEBUG(s,c)
140#endif
141
142#include "timelib_structs.h"
143
144typedef struct timelib_elems {
145    unsigned int   c; /* Number of elements */
146    char         **v; /* Values */
147} timelib_elems;
148
149typedef struct Scanner {
150    int           fd;
151    uchar        *lim, *str, *ptr, *cur, *tok, *pos;
152    unsigned int  line, len;
153    struct timelib_error_container *errors;
154
155    struct timelib_time *time;
156    const timelib_tzdb  *tzdb;
157} Scanner;
158
159typedef struct _timelib_lookup_table {
160    const char *name;
161    int         type;
162    int         value;
163} timelib_lookup_table;
164
165typedef struct _timelib_relunit {
166    const char *name;
167    int         unit;
168    int         multiplier;
169} timelib_relunit;
170
171/* The timezone table. */
172const static timelib_tz_lookup_table timelib_timezone_lookup[] = {
173#include "timezonemap.h"
174    { NULL, 0, 0, NULL },
175};
176
177const static timelib_tz_lookup_table timelib_timezone_fallbackmap[] = {
178#include "fallbackmap.h"
179    { NULL, 0, 0, NULL },
180};
181
182const static timelib_tz_lookup_table timelib_timezone_utc[] = {
183    { "utc", 0, 0, "UTC" },
184};
185
186static timelib_relunit const timelib_relunit_lookup[] = {
187    { "sec",         TIMELIB_SECOND,  1 },
188    { "secs",        TIMELIB_SECOND,  1 },
189    { "second",      TIMELIB_SECOND,  1 },
190    { "seconds",     TIMELIB_SECOND,  1 },
191    { "min",         TIMELIB_MINUTE,  1 },
192    { "mins",        TIMELIB_MINUTE,  1 },
193    { "minute",      TIMELIB_MINUTE,  1 },
194    { "minutes",     TIMELIB_MINUTE,  1 },
195    { "hour",        TIMELIB_HOUR,    1 },
196    { "hours",       TIMELIB_HOUR,    1 },
197    { "day",         TIMELIB_DAY,     1 },
198    { "days",        TIMELIB_DAY,     1 },
199    { "week",        TIMELIB_DAY,     7 },
200    { "weeks",       TIMELIB_DAY,     7 },
201    { "fortnight",   TIMELIB_DAY,    14 },
202    { "fortnights",  TIMELIB_DAY,    14 },
203    { "forthnight",  TIMELIB_DAY,    14 },
204    { "forthnights", TIMELIB_DAY,    14 },
205    { "month",       TIMELIB_MONTH,   1 },
206    { "months",      TIMELIB_MONTH,   1 },
207    { "year",        TIMELIB_YEAR,    1 },
208    { "years",       TIMELIB_YEAR,    1 },
209
210    { "monday",      TIMELIB_WEEKDAY, 1 },
211    { "mon",         TIMELIB_WEEKDAY, 1 },
212    { "tuesday",     TIMELIB_WEEKDAY, 2 },
213    { "tue",         TIMELIB_WEEKDAY, 2 },
214    { "wednesday",   TIMELIB_WEEKDAY, 3 },
215    { "wed",         TIMELIB_WEEKDAY, 3 },
216    { "thursday",    TIMELIB_WEEKDAY, 4 },
217    { "thu",         TIMELIB_WEEKDAY, 4 },
218    { "friday",      TIMELIB_WEEKDAY, 5 },
219    { "fri",         TIMELIB_WEEKDAY, 5 },
220    { "saturday",    TIMELIB_WEEKDAY, 6 },
221    { "sat",         TIMELIB_WEEKDAY, 6 },
222    { "sunday",      TIMELIB_WEEKDAY, 0 },
223    { "sun",         TIMELIB_WEEKDAY, 0 },
224
225    { "weekday",     TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY },
226    { "weekdays",    TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY },
227    { NULL,          0,          0 }
228};
229
230/* The relative text table. */
231static timelib_lookup_table const timelib_reltext_lookup[] = {
232    { "first",    0,  1 },
233    { "next",     0,  1 },
234    { "second",   0,  2 },
235    { "third",    0,  3 },
236    { "fourth",   0,  4 },
237    { "fifth",    0,  5 },
238    { "sixth",    0,  6 },
239    { "seventh",  0,  7 },
240    { "eight",    0,  8 },
241    { "eighth",   0,  8 },
242    { "ninth",    0,  9 },
243    { "tenth",    0, 10 },
244    { "eleventh", 0, 11 },
245    { "twelfth",  0, 12 },
246    { "last",     0, -1 },
247    { "previous", 0, -1 },
248    { "this",     1,  0 },
249    { NULL,       1,  0 }
250};
251
252/* The month table. */
253static timelib_lookup_table const timelib_month_lookup[] = {
254    { "jan",  0,  1 },
255    { "feb",  0,  2 },
256    { "mar",  0,  3 },
257    { "apr",  0,  4 },
258    { "may",  0,  5 },
259    { "jun",  0,  6 },
260    { "jul",  0,  7 },
261    { "aug",  0,  8 },
262    { "sep",  0,  9 },
263    { "sept", 0,  9 },
264    { "oct",  0, 10 },
265    { "nov",  0, 11 },
266    { "dec",  0, 12 },
267    { "i",    0,  1 },
268    { "ii",   0,  2 },
269    { "iii",  0,  3 },
270    { "iv",   0,  4 },
271    { "v",    0,  5 },
272    { "vi",   0,  6 },
273    { "vii",  0,  7 },
274    { "viii", 0,  8 },
275    { "ix",   0,  9 },
276    { "x",    0, 10 },
277    { "xi",   0, 11 },
278    { "xii",  0, 12 },
279
280    { "january",   0,  1 },
281    { "february",  0,  2 },
282    { "march",     0,  3 },
283    { "april",     0,  4 },
284    { "may",       0,  5 },
285    { "june",      0,  6 },
286    { "july",      0,  7 },
287    { "august",    0,  8 },
288    { "september", 0,  9 },
289    { "october",   0, 10 },
290    { "november",  0, 11 },
291    { "december",  0, 12 },
292    {  NULL,       0,  0 }
293};
294
295#if 0
296static char* timelib_ltrim(char *s)
297{
298    char *ptr = s;
299    while (ptr[0] == ' ' || ptr[0] == '\t') {
300        ptr++;
301    }
302    return ptr;
303}
304#endif
305
306#if 0
307uchar *fill(Scanner *s, uchar *cursor){
308    if(!s->eof){
309        unsigned int cnt = s->tok - s->bot;
310        if(cnt){
311            memcpy(s->bot, s->tok, s->lim - s->tok);
312            s->tok = s->bot;
313            s->ptr -= cnt;
314            cursor -= cnt;
315            s->pos -= cnt;
316            s->lim -= cnt;
317        }
318        if((s->top - s->lim) < BSIZE){
319            uchar *buf = (uchar*) malloc(((s->lim - s->bot) + BSIZE)*sizeof(uchar));
320            memcpy(buf, s->tok, s->lim - s->tok);
321            s->tok = buf;
322            s->ptr = &buf[s->ptr - s->bot];
323            cursor = &buf[cursor - s->bot];
324            s->pos = &buf[s->pos - s->bot];
325            s->lim = &buf[s->lim - s->bot];
326            s->top = &s->lim[BSIZE];
327            free(s->bot);
328            s->bot = buf;
329        }
330        if((cnt = read(s->fd, (char*) s->lim, BSIZE)) != BSIZE){
331            s->eof = &s->lim[cnt]; *(s->eof)++ = '\n';
332        }
333        s->lim += cnt;
334    }
335    return cursor;
336}
337#endif
338
339static void add_warning(Scanner *s, char *error)
340{
341    s->errors->warning_count++;
342    s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
343    s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0;
344    s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0;
345    s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
346}
347
348static void add_error(Scanner *s, char *error)
349{
350    s->errors->error_count++;
351    s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
352    s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0;
353    s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0;
354    s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
355}
356
357static void add_pbf_warning(Scanner *s, char *error, char *sptr, char *cptr)
358{
359    s->errors->warning_count++;
360    s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
361    s->errors->warning_messages[s->errors->warning_count - 1].position = cptr - sptr;
362    s->errors->warning_messages[s->errors->warning_count - 1].character = *cptr;
363    s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
364}
365
366static void add_pbf_error(Scanner *s, char *error, char *sptr, char *cptr)
367{
368    s->errors->error_count++;
369    s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
370    s->errors->error_messages[s->errors->error_count - 1].position = cptr - sptr;
371    s->errors->error_messages[s->errors->error_count - 1].character = *cptr;
372    s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
373}
374
375static timelib_sll timelib_meridian(char **ptr, timelib_sll h)
376{
377    timelib_sll retval = 0;
378
379    while (!strchr("AaPp", **ptr)) {
380        ++*ptr;
381    }
382    if (**ptr == 'a' || **ptr == 'A') {
383        if (h == 12) {
384            retval = -12;
385        }
386    } else if (h != 12) {
387        retval = 12;
388    }
389    ++*ptr;
390    if (**ptr == '.') {
391        *ptr += 3;
392    } else {
393        ++*ptr;
394    }
395    return retval;
396}
397
398static timelib_sll timelib_meridian_with_check(char **ptr, timelib_sll h)
399{
400    timelib_sll retval = 0;
401
402    while (**ptr && !strchr("AaPp", **ptr)) {
403        ++*ptr;
404    }
405    if(!**ptr) {
406        return TIMELIB_UNSET;
407    }
408    if (**ptr == 'a' || **ptr == 'A') {
409        if (h == 12) {
410            retval = -12;
411        }
412    } else if (h != 12) {
413        retval = 12;
414    }
415    ++*ptr;
416    if (**ptr == '.') {
417        ++*ptr;
418        if (**ptr != 'm' && **ptr != 'M') {
419            return TIMELIB_UNSET;
420        }
421        ++*ptr;
422        if (**ptr != '.' ) {
423            return TIMELIB_UNSET;
424        }
425        ++*ptr;
426    } else if (**ptr == 'm' || **ptr == 'M') {
427        ++*ptr;
428    } else {
429        return TIMELIB_UNSET;
430    }
431    return retval;
432}
433
434static char *timelib_string(Scanner *s)
435{
436    char *tmp = calloc(1, s->cur - s->tok + 1);
437    memcpy(tmp, s->tok, s->cur - s->tok);
438
439    return tmp;
440}
441
442static timelib_sll timelib_get_nr_ex(char **ptr, int max_length, int *scanned_length)
443{
444    char *begin, *end, *str;
445    timelib_sll tmp_nr = TIMELIB_UNSET;
446    int len = 0;
447
448    while ((**ptr < '0') || (**ptr > '9')) {
449        if (**ptr == '\0') {
450            return TIMELIB_UNSET;
451        }
452        ++*ptr;
453    }
454    begin = *ptr;
455    while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) {
456        ++*ptr;
457        ++len;
458    }
459    end = *ptr;
460    if (scanned_length) {
461        *scanned_length = end - begin;
462    }
463    str = calloc(1, end - begin + 1);
464    memcpy(str, begin, end - begin);
465    tmp_nr = strtoll(str, NULL, 10);
466    free(str);
467    return tmp_nr;
468}
469
470static timelib_sll timelib_get_nr(char **ptr, int max_length)
471{
472    return timelib_get_nr_ex(ptr, max_length, NULL);
473}
474
475static void timelib_skip_day_suffix(char **ptr)
476{
477    if (isspace(**ptr)) {
478        return;
479    }
480    if (!strncasecmp(*ptr, "nd", 2) || !strncasecmp(*ptr, "rd", 2) ||!strncasecmp(*ptr, "st", 2) || !strncasecmp(*ptr, "th", 2)) {
481        *ptr += 2;
482    }
483}
484
485static double timelib_get_frac_nr(char **ptr, int max_length)
486{
487    char *begin, *end, *str;
488    double tmp_nr = TIMELIB_UNSET;
489    int len = 0;
490
491    while ((**ptr != '.') && (**ptr != ':') && ((**ptr < '0') || (**ptr > '9'))) {
492        if (**ptr == '\0') {
493            return TIMELIB_UNSET;
494        }
495        ++*ptr;
496    }
497    begin = *ptr;
498    while (((**ptr == '.') || (**ptr == ':') || ((**ptr >= '0') && (**ptr <= '9'))) && len < max_length) {
499        ++*ptr;
500        ++len;
501    }
502    end = *ptr;
503    str = calloc(1, end - begin + 1);
504    memcpy(str, begin, end - begin);
505    if (str[0] == ':') {
506        str[0] = '.';
507    }
508    tmp_nr = strtod(str, NULL);
509    free(str);
510    return tmp_nr;
511}
512
513static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length)
514{
515    timelib_ull dir = 1;
516
517    while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) {
518        if (**ptr == '\0') {
519            return TIMELIB_UNSET;
520        }
521        ++*ptr;
522    }
523
524    while (**ptr == '+' || **ptr == '-')
525    {
526        if (**ptr == '-') {
527            dir *= -1;
528        }
529        ++*ptr;
530    }
531    return dir * timelib_get_nr(ptr, max_length);
532}
533
534static timelib_sll timelib_lookup_relative_text(char **ptr, int *behavior)
535{
536    char *word;
537    char *begin = *ptr, *end;
538    timelib_sll  value = 0;
539    const timelib_lookup_table *tp;
540
541    while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
542        ++*ptr;
543    }
544    end = *ptr;
545    word = calloc(1, end - begin + 1);
546    memcpy(word, begin, end - begin);
547
548    for (tp = timelib_reltext_lookup; tp->name; tp++) {
549        if (strcasecmp(word, tp->name) == 0) {
550            value = tp->value;
551            *behavior = tp->type;
552        }
553    }
554
555    free(word);
556    return value;
557}
558
559static timelib_sll timelib_get_relative_text(char **ptr, int *behavior)
560{
561    while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '/') {
562        ++*ptr;
563    }
564    return timelib_lookup_relative_text(ptr, behavior);
565}
566
567static long timelib_lookup_month(char **ptr)
568{
569    char *word;
570    char *begin = *ptr, *end;
571    long  value = 0;
572    const timelib_lookup_table *tp;
573
574    while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
575        ++*ptr;
576    }
577    end = *ptr;
578    word = calloc(1, end - begin + 1);
579    memcpy(word, begin, end - begin);
580
581    for (tp = timelib_month_lookup; tp->name; tp++) {
582        if (strcasecmp(word, tp->name) == 0) {
583            value = tp->value;
584        }
585    }
586
587    free(word);
588    return value;
589}
590
591static long timelib_get_month(char **ptr)
592{
593    while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '.' || **ptr == '/') {
594        ++*ptr;
595    }
596    return timelib_lookup_month(ptr);
597}
598
599static void timelib_eat_spaces(char **ptr)
600{
601    while (**ptr == ' ' || **ptr == '\t') {
602        ++*ptr;
603    }
604}
605
606static void timelib_eat_until_separator(char **ptr)
607{
608    ++*ptr;
609    while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) {
610        ++*ptr;
611    }
612}
613
614static const timelib_relunit* timelib_lookup_relunit(char **ptr)
615{
616    char *word;
617    char *begin = *ptr, *end;
618    const timelib_relunit *tp, *value = NULL;
619
620    while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t' && **ptr != ';' && **ptr != ':' &&
621           **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) {
622        ++*ptr;
623    }
624    end = *ptr;
625    word = calloc(1, end - begin + 1);
626    memcpy(word, begin, end - begin);
627
628    for (tp = timelib_relunit_lookup; tp->name; tp++) {
629        if (strcasecmp(word, tp->name) == 0) {
630            value = tp;
631            break;
632        }
633    }
634
635    free(word);
636    return value;
637}
638
639static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, Scanner *s)
640{
641    const timelib_relunit* relunit;
642
643    if (!(relunit = timelib_lookup_relunit(ptr))) {
644        return;
645    }
646
647    switch (relunit->unit) {
648        case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
649        case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
650        case TIMELIB_HOUR:   s->time->relative.h += amount * relunit->multiplier; break;
651        case TIMELIB_DAY:    s->time->relative.d += amount * relunit->multiplier; break;
652        case TIMELIB_MONTH:  s->time->relative.m += amount * relunit->multiplier; break;
653        case TIMELIB_YEAR:   s->time->relative.y += amount * relunit->multiplier; break;
654
655        case TIMELIB_WEEKDAY:
656            TIMELIB_HAVE_WEEKDAY_RELATIVE();
657            TIMELIB_UNHAVE_TIME();
658            s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7;
659            s->time->relative.weekday = relunit->multiplier;
660            s->time->relative.weekday_behavior = behavior;
661            break;
662
663        case TIMELIB_SPECIAL:
664            TIMELIB_HAVE_SPECIAL_RELATIVE();
665            TIMELIB_UNHAVE_TIME();
666            s->time->relative.special.type = relunit->multiplier;
667            s->time->relative.special.amount = amount;
668    }
669}
670
671const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffset, int isdst)
672{
673    int first_found = 0;
674    const timelib_tz_lookup_table  *tp, *first_found_elem = NULL;
675    const timelib_tz_lookup_table  *fmp;
676
677    if (strcasecmp("utc", word) == 0 || strcasecmp("gmt", word) == 0) {
678        return timelib_timezone_utc;
679    }
680
681    for (tp = timelib_timezone_lookup; tp->name; tp++) {
682        if (strcasecmp(word, tp->name) == 0) {
683            if (!first_found) {
684                first_found = 1;
685                first_found_elem = tp;
686                if (gmtoffset == -1) {
687                    return tp;
688                }
689            }
690            if (tp->gmtoffset == gmtoffset) {
691                return tp;
692            }
693        }
694    }
695    if (first_found) {
696        return first_found_elem;
697    }
698
699    for (tp = timelib_timezone_lookup; tp->name; tp++) {
700        if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) {
701            if (!first_found) {
702                first_found = 1;
703                first_found_elem = tp;
704                if (gmtoffset == -1) {
705                    return tp;
706                }
707            }
708            if (tp->gmtoffset == gmtoffset) {
709                return tp;
710            }
711        }
712    }
713    if (first_found) {
714        return first_found_elem;
715    }
716
717
718    /* Still didn't find anything, let's find the zone solely based on
719     * offset/isdst then */
720    for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) {
721        if ((fmp->gmtoffset * 60) == gmtoffset && fmp->type == isdst) {
722            return fmp;
723        }
724    }
725    return NULL;
726}
727
728static long timelib_lookup_zone(char **ptr, int *dst, char **tz_abbr, int *found)
729{
730    char *word;
731    char *begin = *ptr, *end;
732    long  value = 0;
733    const timelib_tz_lookup_table *tp;
734
735    while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
736        ++*ptr;
737    }
738    end = *ptr;
739    word = calloc(1, end - begin + 1);
740    memcpy(word, begin, end - begin);
741
742    if ((tp = zone_search(word, -1, 0))) {
743        value = -tp->gmtoffset / 60;
744        *dst = tp->type;
745        value += tp->type * 60;
746        *found = 1;
747    } else {
748        *found = 0;
749    }
750
751    *tz_abbr = word;
752    return value;
753}
754
755long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper)
756{
757    timelib_tzinfo *res;
758    long            retval = 0;
759
760    *tz_not_found = 0;
761
762    while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
763        ++*ptr;
764    }
765    if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
766        *ptr += 3;
767    }
768    if (**ptr == '+') {
769        ++*ptr;
770        t->is_localtime = 1;
771        t->zone_type = TIMELIB_ZONETYPE_OFFSET;
772        *tz_not_found = 0;
773        t->dst = 0;
774
775        retval = -1 * timelib_parse_tz_cor(ptr);
776    } else if (**ptr == '-') {
777        ++*ptr;
778        t->is_localtime = 1;
779        t->zone_type = TIMELIB_ZONETYPE_OFFSET;
780        *tz_not_found = 0;
781        t->dst = 0;
782
783        retval = timelib_parse_tz_cor(ptr);
784    } else {
785        int found = 0;
786        long offset;
787        char *tz_abbr;
788
789        t->is_localtime = 1;
790
791        offset = timelib_lookup_zone(ptr, dst, &tz_abbr, &found);
792        if (found) {
793            t->zone_type = TIMELIB_ZONETYPE_ABBR;
794        }
795#if 0
796        /* If we found a TimeZone identifier, use it */
797        if (tz_name) {
798            t->tz_info = timelib_parse_tzfile(tz_name);
799            t->zone_type = TIMELIB_ZONETYPE_ID;
800        }
801#endif
802        /* If we have a TimeZone identifier to start with, use it */
803        if (strstr(tz_abbr, "/") || strcmp(tz_abbr, "UTC") == 0) {
804            if ((res = tz_wrapper(tz_abbr, tzdb)) != NULL) {
805                t->tz_info = res;
806                t->zone_type = TIMELIB_ZONETYPE_ID;
807                found++;
808            }
809        }
810        if (found && t->zone_type != TIMELIB_ZONETYPE_ID) {
811            timelib_time_tz_abbr_update(t, tz_abbr);
812        }
813        free(tz_abbr);
814        *tz_not_found = (found == 0);
815        retval = offset;
816    }
817    while (**ptr == ')') {
818        ++*ptr;
819    }
820    return retval;
821}
822
823#define timelib_split_free(arg) {       \
824    int i;                         \
825    for (i = 0; i < arg.c; i++) {  \
826        free(arg.v[i]);            \
827    }                              \
828    if (arg.v) {                   \
829        free(arg.v);               \
830    }                              \
831}
832
833static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
834{
835    uchar *cursor = s->cur;
836    char *str, *ptr = NULL;
837
838std:
839    s->tok = cursor;
840    s->len = 0;
841/*!re2c
842any = [\000-\377];
843
844space = [ \t]+;
845frac = "."[0-9]+;
846
847ago = 'ago';
848
849hour24 = [01]?[0-9] | "2"[0-4];
850hour24lz = [01][0-9] | "2"[0-4];
851hour12 = "0"?[1-9] | "1"[0-2];
852minute = [0-5]?[0-9];
853minutelz = [0-5][0-9];
854second = minute | "60";
855secondlz = minutelz | "60";
856meridian = ([AaPp] "."? [Mm] "."?) [\000\t ];
857tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+;
858tzcorrection = "GMT"? [+-] hour24 ":"? minute?;
859
860daysuf = "st" | "nd" | "rd" | "th";
861
862month = "0"? [0-9] | "1"[0-2];
863day   = (([0-2]?[0-9]) | ("3"[01])) daysuf?;
864year  = [0-9]{1,4};
865year2 = [0-9]{2};
866year4 = [0-9]{4};
867year4withsign = [+-]? [0-9]{4};
868
869dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6];
870weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
871
872monthlz = "0" [0-9] | "1" [0-2];
873daylz   = "0" [0-9] | [1-2][0-9] | "3" [01];
874
875dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
876dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
877dayspecial = 'weekday' | 'weekdays';
878daytext = dayfull | dayabbr | dayspecial;
879
880monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
881monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec';
882monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII";
883monthtext = monthfull | monthabbr | monthroman;
884
885/* Time formats */
886timetiny12 = hour12 space? meridian;
887timeshort12 = hour12[:.]minutelz space? meridian;
888timelong12 = hour12[:.]minute[:.]secondlz space? meridian;
889
890timeshort24 = 't'? hour24[:.]minute;
891timelong24 =  't'? hour24[:.]minute[:.]second;
892iso8601long =  't'? hour24 [:.] minute [:.] second frac;
893
894/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */
895iso8601normtz =  't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz);
896/* iso8601longtz =  hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */
897
898gnunocolon       = 't'? hour24lz minutelz;
899/* gnunocolontz     = hour24lz minutelz space? (tzcorrection | tz); */
900iso8601nocolon   = 't'? hour24lz minutelz secondlz;
901/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */
902
903/* Date formats */
904americanshort    = month "/" day;
905american         = month "/" day "/" year;
906iso8601dateslash = year4 "/" monthlz "/" daylz "/"?;
907dateslash        = year4 "/" month "/" day;
908iso8601date4     = year4withsign "-" monthlz "-" daylz;
909iso8601date2     = year2 "-" monthlz "-" daylz;
910gnudateshorter   = year4 "-" month;
911gnudateshort     = year "-" month "-" day;
912pointeddate4     = day [.\t-] month [.-] year4;
913pointeddate2     = day [.\t] month "." year2;
914datefull         = day ([ \t.-])* monthtext ([ \t.-])* year;
915datenoday        = monthtext ([ .\t-])* year4;
916datenodayrev     = year4 ([ .\t-])* monthtext;
917datetextual      = monthtext ([ .\t-])* day [,.stndrh\t ]+ year;
918datenoyear       = monthtext ([ .\t-])* day [,.stndrh\t ]*;
919datenoyearrev    = day ([ .\t-])* monthtext;
920datenocolon      = year4 monthlz daylz;
921
922/* Special formats */
923soap             = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?;
924xmlrpc           = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz;
925xmlrpcnocolon    = year4 monthlz daylz 't' hour24 minutelz secondlz;
926wddx             = year4 "-" month "-" day "T" hour24 ":" minute ":" second;
927pgydotd          = year4 "."? dayofyear;
928pgtextshort      = monthabbr "-" daylz "-" year;
929pgtextreverse    = year "-" monthabbr "-" daylz;
930mssqltime        = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian;
931isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
932isoweek          = year4 "-"? "W" weekofyear;
933exif             = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz;
934firstdayof       = 'first day of';
935lastdayof        = 'last day of';
936backof           = 'back of ' hour24 space? meridian?;
937frontof          = 'front of ' hour24 space? meridian?;
938
939/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */
940clf              = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection;
941
942/* Timestamp format: @1126396800 */
943timestamp        = "@" "-"? [0-9]+;
944
945/* To fix some ambiguities */
946dateshortwithtimeshort12  = datenoyear timeshort12;
947dateshortwithtimelong12   = datenoyear timelong12;
948dateshortwithtimeshort  = datenoyear timeshort24;
949dateshortwithtimelong   = datenoyear timelong24;
950dateshortwithtimelongtz = datenoyear iso8601normtz;
951
952/*
953 * Relative regexps
954 */
955reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
956reltexttext = 'next'|'last'|'previous'|'this';
957reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
958
959relnumber = ([+-]*[ \t]*[0-9]+);
960relative = relnumber space? (reltextunit | 'week' );
961relativetext = (reltextnumber|reltexttext) space reltextunit;
962relativetextweek = reltexttext space 'week';
963
964weekdayof        = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of';
965
966*/
967
968/*!re2c
969    /* so that vim highlights correctly */
970    'yesterday'
971    {
972        DEBUG_OUTPUT("yesterday");
973        TIMELIB_INIT;
974        TIMELIB_HAVE_RELATIVE();
975        TIMELIB_UNHAVE_TIME();
976
977        s->time->relative.d = -1;
978        TIMELIB_DEINIT;
979        return TIMELIB_RELATIVE;
980    }
981
982    'now'
983    {
984        DEBUG_OUTPUT("now");
985        TIMELIB_INIT;
986
987        TIMELIB_DEINIT;
988        return TIMELIB_RELATIVE;
989    }
990
991    'noon'
992    {
993        DEBUG_OUTPUT("noon");
994        TIMELIB_INIT;
995        TIMELIB_UNHAVE_TIME();
996        TIMELIB_HAVE_TIME();
997        s->time->h = 12;
998
999        TIMELIB_DEINIT;
1000        return TIMELIB_RELATIVE;
1001    }
1002
1003    'midnight' | 'today'
1004    {
1005        DEBUG_OUTPUT("midnight | today");
1006        TIMELIB_INIT;
1007        TIMELIB_UNHAVE_TIME();
1008
1009        TIMELIB_DEINIT;
1010        return TIMELIB_RELATIVE;
1011    }
1012
1013    'tomorrow'
1014    {
1015        DEBUG_OUTPUT("tomorrow");
1016        TIMELIB_INIT;
1017        TIMELIB_HAVE_RELATIVE();
1018        TIMELIB_UNHAVE_TIME();
1019
1020        s->time->relative.d = 1;
1021        TIMELIB_DEINIT;
1022        return TIMELIB_RELATIVE;
1023    }
1024
1025    timestamp
1026    {
1027        timelib_ull i;
1028
1029        TIMELIB_INIT;
1030        TIMELIB_HAVE_RELATIVE();
1031        TIMELIB_UNHAVE_DATE();
1032        TIMELIB_UNHAVE_TIME();
1033        TIMELIB_HAVE_TZ();
1034
1035        i = timelib_get_unsigned_nr((char **) &ptr, 24);
1036        s->time->y = 1970;
1037        s->time->m = 1;
1038        s->time->d = 1;
1039        s->time->h = s->time->i = s->time->s = 0;
1040        s->time->f = 0.0;
1041        s->time->relative.s += i;
1042        s->time->is_localtime = 1;
1043        s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1044        s->time->z = 0;
1045        s->time->dst = 0;
1046
1047        TIMELIB_DEINIT;
1048        return TIMELIB_RELATIVE;
1049    }
1050
1051    firstdayof | lastdayof
1052    {
1053        DEBUG_OUTPUT("firstdayof | lastdayof");
1054        TIMELIB_INIT;
1055        TIMELIB_HAVE_RELATIVE();
1056
1057        /* skip "last day of" or "first day of" */
1058        if (*ptr == 'l') {
1059            s->time->relative.first_last_day_of = 2;
1060        } else {
1061            s->time->relative.first_last_day_of = 1;
1062        }
1063
1064        TIMELIB_DEINIT;
1065        return TIMELIB_LF_DAY_OF_MONTH;
1066    }
1067
1068    backof | frontof
1069    {
1070        DEBUG_OUTPUT("backof | frontof");
1071        TIMELIB_INIT;
1072        TIMELIB_UNHAVE_TIME();
1073        TIMELIB_HAVE_TIME();
1074
1075        if (*ptr == 'b') {
1076            s->time->h = timelib_get_nr((char **) &ptr, 2);
1077            s->time->i = 15;
1078        } else {
1079            s->time->h = timelib_get_nr((char **) &ptr, 2) - 1;
1080            s->time->i = 45;
1081        }
1082        if (*ptr != '\0' ) {
1083            timelib_eat_spaces((char **) &ptr);
1084            s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1085        }
1086
1087        TIMELIB_DEINIT;
1088        return TIMELIB_LF_DAY_OF_MONTH;
1089    }
1090
1091    weekdayof
1092    {
1093        timelib_sll i;
1094        int         behavior = 0;
1095        DEBUG_OUTPUT("weekdayof");
1096        TIMELIB_INIT;
1097        TIMELIB_HAVE_RELATIVE();
1098        TIMELIB_HAVE_SPECIAL_RELATIVE();
1099
1100        i = timelib_get_relative_text((char **) &ptr, &behavior);
1101        timelib_eat_spaces((char **) &ptr);
1102        if (i > 0) { /* first, second... etc */
1103            s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH;
1104            timelib_set_relative((char **) &ptr, i, 1, s);
1105        } else { /* last */
1106            s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH;
1107            timelib_set_relative((char **) &ptr, i, behavior, s);
1108        }
1109        TIMELIB_DEINIT;
1110        return TIMELIB_WEEK_DAY_OF_MONTH;
1111    }
1112
1113    timetiny12 | timeshort12 | timelong12
1114    {
1115        DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
1116        TIMELIB_INIT;
1117        TIMELIB_HAVE_TIME();
1118        s->time->h = timelib_get_nr((char **) &ptr, 2);
1119        if (*ptr == ':' || *ptr == '.') {
1120            s->time->i = timelib_get_nr((char **) &ptr, 2);
1121            if (*ptr == ':' || *ptr == '.') {
1122                s->time->s = timelib_get_nr((char **) &ptr, 2);
1123            }
1124        }
1125        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1126        TIMELIB_DEINIT;
1127        return TIMELIB_TIME12;
1128    }
1129
1130    mssqltime
1131    {
1132        DEBUG_OUTPUT("mssqltime");
1133        TIMELIB_INIT;
1134        TIMELIB_HAVE_TIME();
1135        s->time->h = timelib_get_nr((char **) &ptr, 2);
1136        s->time->i = timelib_get_nr((char **) &ptr, 2);
1137        if (*ptr == ':' || *ptr == '.') {
1138            s->time->s = timelib_get_nr((char **) &ptr, 2);
1139
1140            if (*ptr == ':' || *ptr == '.') {
1141                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1142            }
1143        }
1144        timelib_eat_spaces((char **) &ptr);
1145        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1146        TIMELIB_DEINIT;
1147        return TIMELIB_TIME24_WITH_ZONE;
1148    }
1149
1150    timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/
1151    {
1152        int tz_not_found;
1153        DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
1154        TIMELIB_INIT;
1155        TIMELIB_HAVE_TIME();
1156        s->time->h = timelib_get_nr((char **) &ptr, 2);
1157        s->time->i = timelib_get_nr((char **) &ptr, 2);
1158        if (*ptr == ':' || *ptr == '.') {
1159            s->time->s = timelib_get_nr((char **) &ptr, 2);
1160
1161            if (*ptr == '.') {
1162                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1163            }
1164        }
1165
1166        if (*ptr != '\0') {
1167            s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1168            if (tz_not_found) {
1169                add_error(s, "The timezone could not be found in the database");
1170            }
1171        }
1172        TIMELIB_DEINIT;
1173        return TIMELIB_TIME24_WITH_ZONE;
1174    }
1175
1176    gnunocolon
1177    {
1178        DEBUG_OUTPUT("gnunocolon");
1179        TIMELIB_INIT;
1180        switch (s->time->have_time) {
1181            case 0:
1182                s->time->h = timelib_get_nr((char **) &ptr, 2);
1183                s->time->i = timelib_get_nr((char **) &ptr, 2);
1184                s->time->s = 0;
1185                break;
1186            case 1:
1187                s->time->y = timelib_get_nr((char **) &ptr, 4);
1188                break;
1189            default:
1190                TIMELIB_DEINIT;
1191                add_error(s, "Double time specification");
1192                return TIMELIB_ERROR;
1193        }
1194        s->time->have_time++;
1195        TIMELIB_DEINIT;
1196        return TIMELIB_GNU_NOCOLON;
1197    }
1198/*
1199    gnunocolontz
1200    {
1201        DEBUG_OUTPUT("gnunocolontz");
1202        TIMELIB_INIT;
1203        switch (s->time->have_time) {
1204            case 0:
1205                s->time->h = timelib_get_nr((char **) &ptr, 2);
1206                s->time->i = timelib_get_nr((char **) &ptr, 2);
1207                s->time->s = 0;
1208                s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper);
1209                break;
1210            case 1:
1211                s->time->y = timelib_get_nr((char **) &ptr, 4);
1212                break;
1213            default:
1214                TIMELIB_DEINIT;
1215                return TIMELIB_ERROR;
1216        }
1217        s->time->have_time++;
1218        TIMELIB_DEINIT;
1219        return TIMELIB_GNU_NOCOLON_TZ;
1220    }
1221*/
1222    iso8601nocolon /*| iso8601nocolontz*/
1223    {
1224        int tz_not_found;
1225        DEBUG_OUTPUT("iso8601nocolon");
1226        TIMELIB_INIT;
1227        TIMELIB_HAVE_TIME();
1228        s->time->h = timelib_get_nr((char **) &ptr, 2);
1229        s->time->i = timelib_get_nr((char **) &ptr, 2);
1230        s->time->s = timelib_get_nr((char **) &ptr, 2);
1231
1232        if (*ptr != '\0') {
1233            s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1234            if (tz_not_found) {
1235                add_error(s, "The timezone could not be found in the database");
1236            }
1237        }
1238        TIMELIB_DEINIT;
1239        return TIMELIB_ISO_NOCOLON;
1240    }
1241
1242    americanshort | american
1243    {
1244        int length = 0;
1245        DEBUG_OUTPUT("americanshort | american");
1246        TIMELIB_INIT;
1247        TIMELIB_HAVE_DATE();
1248        s->time->m = timelib_get_nr((char **) &ptr, 2);
1249        s->time->d = timelib_get_nr((char **) &ptr, 2);
1250        if (*ptr == '/') {
1251            s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1252            TIMELIB_PROCESS_YEAR(s->time->y, length);
1253        }
1254        TIMELIB_DEINIT;
1255        return TIMELIB_AMERICAN;
1256    }
1257
1258    iso8601date4 | iso8601dateslash | dateslash
1259    {
1260        DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
1261        TIMELIB_INIT;
1262        TIMELIB_HAVE_DATE();
1263        s->time->y = timelib_get_unsigned_nr((char **) &ptr, 4);
1264        s->time->m = timelib_get_nr((char **) &ptr, 2);
1265        s->time->d = timelib_get_nr((char **) &ptr, 2);
1266        TIMELIB_DEINIT;
1267        return TIMELIB_ISO_DATE;
1268    }
1269
1270    iso8601date2
1271    {
1272        int length = 0;
1273        DEBUG_OUTPUT("iso8601date2");
1274        TIMELIB_INIT;
1275        TIMELIB_HAVE_DATE();
1276        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1277        s->time->m = timelib_get_nr((char **) &ptr, 2);
1278        s->time->d = timelib_get_nr((char **) &ptr, 2);
1279        TIMELIB_PROCESS_YEAR(s->time->y, length);
1280        TIMELIB_DEINIT;
1281        return TIMELIB_ISO_DATE;
1282    }
1283
1284    gnudateshorter
1285    {
1286        int length = 0;
1287        DEBUG_OUTPUT("gnudateshorter");
1288        TIMELIB_INIT;
1289        TIMELIB_HAVE_DATE();
1290        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1291        s->time->m = timelib_get_nr((char **) &ptr, 2);
1292        s->time->d = 1;
1293        TIMELIB_PROCESS_YEAR(s->time->y, length);
1294        TIMELIB_DEINIT;
1295        return TIMELIB_ISO_DATE;
1296    }
1297
1298    gnudateshort
1299    {
1300        int length = 0;
1301        DEBUG_OUTPUT("gnudateshort");
1302        TIMELIB_INIT;
1303        TIMELIB_HAVE_DATE();
1304        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1305        s->time->m = timelib_get_nr((char **) &ptr, 2);
1306        s->time->d = timelib_get_nr((char **) &ptr, 2);
1307        TIMELIB_PROCESS_YEAR(s->time->y, length);
1308        TIMELIB_DEINIT;
1309        return TIMELIB_ISO_DATE;
1310    }
1311
1312    datefull
1313    {
1314        int length = 0;
1315        DEBUG_OUTPUT("datefull");
1316        TIMELIB_INIT;
1317        TIMELIB_HAVE_DATE();
1318        s->time->d = timelib_get_nr((char **) &ptr, 2);
1319        timelib_skip_day_suffix((char **) &ptr);
1320        s->time->m = timelib_get_month((char **) &ptr);
1321        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1322        TIMELIB_PROCESS_YEAR(s->time->y, length);
1323        TIMELIB_DEINIT;
1324        return TIMELIB_DATE_FULL;
1325    }
1326
1327    pointeddate4
1328    {
1329        DEBUG_OUTPUT("pointed date YYYY");
1330        TIMELIB_INIT;
1331        TIMELIB_HAVE_DATE();
1332        s->time->d = timelib_get_nr((char **) &ptr, 2);
1333        s->time->m = timelib_get_nr((char **) &ptr, 2);
1334        s->time->y = timelib_get_nr((char **) &ptr, 4);
1335        TIMELIB_DEINIT;
1336        return TIMELIB_DATE_FULL_POINTED;
1337    }
1338
1339    pointeddate2
1340    {
1341        int length = 0;
1342        DEBUG_OUTPUT("pointed date YY");
1343        TIMELIB_INIT;
1344        TIMELIB_HAVE_DATE();
1345        s->time->d = timelib_get_nr((char **) &ptr, 2);
1346        s->time->m = timelib_get_nr((char **) &ptr, 2);
1347        s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length);
1348        TIMELIB_PROCESS_YEAR(s->time->y, length);
1349        TIMELIB_DEINIT;
1350        return TIMELIB_DATE_FULL_POINTED;
1351    }
1352
1353    datenoday
1354    {
1355        int length = 0;
1356        DEBUG_OUTPUT("datenoday");
1357        TIMELIB_INIT;
1358        TIMELIB_HAVE_DATE();
1359        s->time->m = timelib_get_month((char **) &ptr);
1360        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1361        s->time->d = 1;
1362        TIMELIB_PROCESS_YEAR(s->time->y, length);
1363        TIMELIB_DEINIT;
1364        return TIMELIB_DATE_NO_DAY;
1365    }
1366
1367    datenodayrev
1368    {
1369        int length = 0;
1370        DEBUG_OUTPUT("datenodayrev");
1371        TIMELIB_INIT;
1372        TIMELIB_HAVE_DATE();
1373        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1374        s->time->m = timelib_get_month((char **) &ptr);
1375        s->time->d = 1;
1376        TIMELIB_PROCESS_YEAR(s->time->y, length);
1377        TIMELIB_DEINIT;
1378        return TIMELIB_DATE_NO_DAY;
1379    }
1380
1381    datetextual | datenoyear
1382    {
1383        int length = 0;
1384        DEBUG_OUTPUT("datetextual | datenoyear");
1385        TIMELIB_INIT;
1386        TIMELIB_HAVE_DATE();
1387        s->time->m = timelib_get_month((char **) &ptr);
1388        s->time->d = timelib_get_nr((char **) &ptr, 2);
1389        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1390        TIMELIB_PROCESS_YEAR(s->time->y, length);
1391        TIMELIB_DEINIT;
1392        return TIMELIB_DATE_TEXT;
1393    }
1394
1395    datenoyearrev
1396    {
1397        DEBUG_OUTPUT("datenoyearrev");
1398        TIMELIB_INIT;
1399        TIMELIB_HAVE_DATE();
1400        s->time->d = timelib_get_nr((char **) &ptr, 2);
1401        timelib_skip_day_suffix((char **) &ptr);
1402        s->time->m = timelib_get_month((char **) &ptr);
1403        TIMELIB_DEINIT;
1404        return TIMELIB_DATE_TEXT;
1405    }
1406
1407    datenocolon
1408    {
1409        DEBUG_OUTPUT("datenocolon");
1410        TIMELIB_INIT;
1411        TIMELIB_HAVE_DATE();
1412        s->time->y = timelib_get_nr((char **) &ptr, 4);
1413        s->time->m = timelib_get_nr((char **) &ptr, 2);
1414        s->time->d = timelib_get_nr((char **) &ptr, 2);
1415        TIMELIB_DEINIT;
1416        return TIMELIB_DATE_NOCOLON;
1417    }
1418
1419    xmlrpc | xmlrpcnocolon | soap | wddx | exif
1420    {
1421        int tz_not_found;
1422        DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
1423        TIMELIB_INIT;
1424        TIMELIB_HAVE_TIME();
1425        TIMELIB_HAVE_DATE();
1426        s->time->y = timelib_get_nr((char **) &ptr, 4);
1427        s->time->m = timelib_get_nr((char **) &ptr, 2);
1428        s->time->d = timelib_get_nr((char **) &ptr, 2);
1429        s->time->h = timelib_get_nr((char **) &ptr, 2);
1430        s->time->i = timelib_get_nr((char **) &ptr, 2);
1431        s->time->s = timelib_get_nr((char **) &ptr, 2);
1432        if (*ptr == '.') {
1433            s->time->f = timelib_get_frac_nr((char **) &ptr, 9);
1434            if (*ptr) { /* timezone is optional */
1435                s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1436                if (tz_not_found) {
1437                    add_error(s, "The timezone could not be found in the database");
1438                }
1439            }
1440        }
1441        TIMELIB_DEINIT;
1442        return TIMELIB_XMLRPC_SOAP;
1443    }
1444
1445    pgydotd
1446    {
1447        int length = 0;
1448        DEBUG_OUTPUT("pgydotd");
1449        TIMELIB_INIT;
1450        TIMELIB_HAVE_DATE();
1451        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1452        s->time->d = timelib_get_nr((char **) &ptr, 3);
1453        s->time->m = 1;
1454        TIMELIB_PROCESS_YEAR(s->time->y, length);
1455        TIMELIB_DEINIT;
1456        return TIMELIB_PG_YEARDAY;
1457    }
1458
1459    isoweekday
1460    {
1461        timelib_sll w, d;
1462        DEBUG_OUTPUT("isoweekday");
1463        TIMELIB_INIT;
1464        TIMELIB_HAVE_DATE();
1465        TIMELIB_HAVE_RELATIVE();
1466
1467        s->time->y = timelib_get_nr((char **) &ptr, 4);
1468        w = timelib_get_nr((char **) &ptr, 2);
1469        d = timelib_get_nr((char **) &ptr, 1);
1470        s->time->m = 1;
1471        s->time->d = 1;
1472        s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1473
1474        TIMELIB_DEINIT;
1475        return TIMELIB_ISO_WEEK;
1476    }
1477
1478    isoweek
1479    {
1480        timelib_sll w, d;
1481        DEBUG_OUTPUT("isoweek");
1482        TIMELIB_INIT;
1483        TIMELIB_HAVE_DATE();
1484        TIMELIB_HAVE_RELATIVE();
1485
1486        s->time->y = timelib_get_nr((char **) &ptr, 4);
1487        w = timelib_get_nr((char **) &ptr, 2);
1488        d = 1;
1489        s->time->m = 1;
1490        s->time->d = 1;
1491        s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1492
1493        TIMELIB_DEINIT;
1494        return TIMELIB_ISO_WEEK;
1495    }
1496
1497    pgtextshort
1498    {
1499        int length = 0;
1500        DEBUG_OUTPUT("pgtextshort");
1501        TIMELIB_INIT;
1502        TIMELIB_HAVE_DATE();
1503        s->time->m = timelib_get_month((char **) &ptr);
1504        s->time->d = timelib_get_nr((char **) &ptr, 2);
1505        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1506        TIMELIB_PROCESS_YEAR(s->time->y, length);
1507        TIMELIB_DEINIT;
1508        return TIMELIB_PG_TEXT;
1509    }
1510
1511    pgtextreverse
1512    {
1513        int length = 0;
1514        DEBUG_OUTPUT("pgtextreverse");
1515        TIMELIB_INIT;
1516        TIMELIB_HAVE_DATE();
1517        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1518        s->time->m = timelib_get_month((char **) &ptr);
1519        s->time->d = timelib_get_nr((char **) &ptr, 2);
1520        TIMELIB_PROCESS_YEAR(s->time->y, length);
1521        TIMELIB_DEINIT;
1522        return TIMELIB_PG_TEXT;
1523    }
1524
1525    clf
1526    {
1527        int tz_not_found;
1528        DEBUG_OUTPUT("clf");
1529        TIMELIB_INIT;
1530        TIMELIB_HAVE_TIME();
1531        TIMELIB_HAVE_DATE();
1532        s->time->d = timelib_get_nr((char **) &ptr, 2);
1533        s->time->m = timelib_get_month((char **) &ptr);
1534        s->time->y = timelib_get_nr((char **) &ptr, 4);
1535        s->time->h = timelib_get_nr((char **) &ptr, 2);
1536        s->time->i = timelib_get_nr((char **) &ptr, 2);
1537        s->time->s = timelib_get_nr((char **) &ptr, 2);
1538        s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1539        if (tz_not_found) {
1540            add_error(s, "The timezone could not be found in the database");
1541        }
1542        TIMELIB_DEINIT;
1543        return TIMELIB_CLF;
1544    }
1545
1546    year4
1547    {
1548        DEBUG_OUTPUT("year4");
1549        TIMELIB_INIT;
1550        s->time->y = timelib_get_nr((char **) &ptr, 4);
1551        TIMELIB_DEINIT;
1552        return TIMELIB_CLF;
1553    }
1554
1555    ago
1556    {
1557        DEBUG_OUTPUT("ago");
1558        TIMELIB_INIT;
1559        s->time->relative.y = 0 - s->time->relative.y;
1560        s->time->relative.m = 0 - s->time->relative.m;
1561        s->time->relative.d = 0 - s->time->relative.d;
1562        s->time->relative.h = 0 - s->time->relative.h;
1563        s->time->relative.i = 0 - s->time->relative.i;
1564        s->time->relative.s = 0 - s->time->relative.s;
1565        s->time->relative.weekday = 0 - s->time->relative.weekday;
1566        if (s->time->relative.weekday == 0) {
1567            s->time->relative.weekday = -7;
1568        }
1569        if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) {
1570            s->time->relative.special.amount = 0 - s->time->relative.special.amount;
1571        }
1572        TIMELIB_DEINIT;
1573        return TIMELIB_AGO;
1574    }
1575
1576    daytext
1577    {
1578        const timelib_relunit* relunit;
1579        DEBUG_OUTPUT("daytext");
1580        TIMELIB_INIT;
1581        TIMELIB_HAVE_RELATIVE();
1582        TIMELIB_HAVE_WEEKDAY_RELATIVE();
1583        TIMELIB_UNHAVE_TIME();
1584        relunit = timelib_lookup_relunit((char**) &ptr);
1585        s->time->relative.weekday = relunit->multiplier;
1586        if (s->time->relative.weekday_behavior != 2) {
1587            s->time->relative.weekday_behavior = 1;
1588        }
1589
1590        TIMELIB_DEINIT;
1591        return TIMELIB_WEEKDAY;
1592    }
1593
1594    relativetextweek
1595    {
1596        timelib_sll i;
1597        int         behavior = 0;
1598        DEBUG_OUTPUT("relativetextweek");
1599        TIMELIB_INIT;
1600        TIMELIB_HAVE_RELATIVE();
1601
1602        while(*ptr) {
1603            i = timelib_get_relative_text((char **) &ptr, &behavior);
1604            timelib_eat_spaces((char **) &ptr);
1605            timelib_set_relative((char **) &ptr, i, behavior, s);
1606            s->time->relative.weekday_behavior = 2;
1607
1608            /* to handle the format weekday + last/this/next week */
1609            if (s->time->relative.have_weekday_relative == 0) {
1610                TIMELIB_HAVE_WEEKDAY_RELATIVE();
1611                s->time->relative.weekday = 1;
1612            }
1613        }
1614        TIMELIB_DEINIT;
1615        return TIMELIB_RELATIVE;
1616    }
1617
1618    relativetext
1619    {
1620        timelib_sll i;
1621        int         behavior = 0;
1622        DEBUG_OUTPUT("relativetext");
1623        TIMELIB_INIT;
1624        TIMELIB_HAVE_RELATIVE();
1625
1626        while(*ptr) {
1627            i = timelib_get_relative_text((char **) &ptr, &behavior);
1628            timelib_eat_spaces((char **) &ptr);
1629            timelib_set_relative((char **) &ptr, i, behavior, s);
1630        }
1631        TIMELIB_DEINIT;
1632        return TIMELIB_RELATIVE;
1633    }
1634
1635    monthfull | monthabbr
1636    {
1637        DEBUG_OUTPUT("monthtext");
1638        TIMELIB_INIT;
1639        TIMELIB_HAVE_DATE();
1640        s->time->m = timelib_lookup_month((char **) &ptr);
1641        TIMELIB_DEINIT;
1642        return TIMELIB_DATE_TEXT;
1643    }
1644
1645    tzcorrection | tz
1646    {
1647        int tz_not_found;
1648        DEBUG_OUTPUT("tzcorrection | tz");
1649        TIMELIB_INIT;
1650        TIMELIB_HAVE_TZ();
1651        s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1652        if (tz_not_found) {
1653            add_error(s, "The timezone could not be found in the database");
1654        }
1655        TIMELIB_DEINIT;
1656        return TIMELIB_TIMEZONE;
1657    }
1658
1659    dateshortwithtimeshort12 | dateshortwithtimelong12
1660    {
1661        DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
1662        TIMELIB_INIT;
1663        TIMELIB_HAVE_DATE();
1664        s->time->m = timelib_get_month((char **) &ptr);
1665        s->time->d = timelib_get_nr((char **) &ptr, 2);
1666
1667        TIMELIB_HAVE_TIME();
1668        s->time->h = timelib_get_nr((char **) &ptr, 2);
1669        s->time->i = timelib_get_nr((char **) &ptr, 2);
1670        if (*ptr == ':' || *ptr == '.') {
1671            s->time->s = timelib_get_nr((char **) &ptr, 2);
1672
1673            if (*ptr == '.') {
1674                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1675            }
1676        }
1677
1678        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1679        TIMELIB_DEINIT;
1680        return TIMELIB_SHORTDATE_WITH_TIME;
1681    }
1682
1683    dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz
1684    {
1685        int tz_not_found;
1686        DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
1687        TIMELIB_INIT;
1688        TIMELIB_HAVE_DATE();
1689        s->time->m = timelib_get_month((char **) &ptr);
1690        s->time->d = timelib_get_nr((char **) &ptr, 2);
1691
1692        TIMELIB_HAVE_TIME();
1693        s->time->h = timelib_get_nr((char **) &ptr, 2);
1694        s->time->i = timelib_get_nr((char **) &ptr, 2);
1695        if (*ptr == ':') {
1696            s->time->s = timelib_get_nr((char **) &ptr, 2);
1697
1698            if (*ptr == '.') {
1699                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1700            }
1701        }
1702
1703        if (*ptr != '\0') {
1704            s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1705            if (tz_not_found) {
1706                add_error(s, "The timezone could not be found in the database");
1707            }
1708        }
1709        TIMELIB_DEINIT;
1710        return TIMELIB_SHORTDATE_WITH_TIME;
1711    }
1712
1713    relative
1714    {
1715        timelib_ull i;
1716        DEBUG_OUTPUT("relative");
1717        TIMELIB_INIT;
1718        TIMELIB_HAVE_RELATIVE();
1719
1720        while(*ptr) {
1721            i = timelib_get_unsigned_nr((char **) &ptr, 24);
1722            timelib_eat_spaces((char **) &ptr);
1723            timelib_set_relative((char **) &ptr, i, 1, s);
1724        }
1725        TIMELIB_DEINIT;
1726        return TIMELIB_RELATIVE;
1727    }
1728
1729    [ .,\t]
1730    {
1731        goto std;
1732    }
1733
1734    "\000"|"\n"
1735    {
1736        s->pos = cursor; s->line++;
1737        goto std;
1738    }
1739
1740    any
1741    {
1742        add_error(s, "Unexpected character");
1743        goto std;
1744    }
1745*/
1746}
1747
1748/*!max:re2c */
1749
1750timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1751{
1752    Scanner in;
1753    int t;
1754    char *e = s + len - 1;
1755
1756    memset(&in, 0, sizeof(in));
1757    in.errors = malloc(sizeof(struct timelib_error_container));
1758    in.errors->warning_count = 0;
1759    in.errors->warning_messages = NULL;
1760    in.errors->error_count = 0;
1761    in.errors->error_messages = NULL;
1762
1763    if (len > 0) {
1764        while (isspace(*s) && s < e) {
1765            s++;
1766        }
1767        while (isspace(*e) && e > s) {
1768            e--;
1769        }
1770    }
1771    if (e - s < 0) {
1772        in.time = timelib_time_ctor();
1773        add_error(&in, "Empty string");
1774        if (errors) {
1775            *errors = in.errors;
1776        } else {
1777            timelib_error_container_dtor(in.errors);
1778        }
1779        in.time->y = in.time->d = in.time->m = in.time->h = in.time->i = in.time->s = in.time->f = in.time->dst = in.time->z = TIMELIB_UNSET;
1780        in.time->is_localtime = in.time->zone_type = 0;
1781        return in.time;
1782    }
1783    e++;
1784
1785    in.str = malloc((e - s) + YYMAXFILL);
1786    memset(in.str, 0, (e - s) + YYMAXFILL);
1787    memcpy(in.str, s, (e - s));
1788    in.lim = in.str + (e - s) + YYMAXFILL;
1789    in.cur = in.str;
1790    in.time = timelib_time_ctor();
1791    in.time->y = TIMELIB_UNSET;
1792    in.time->d = TIMELIB_UNSET;
1793    in.time->m = TIMELIB_UNSET;
1794    in.time->h = TIMELIB_UNSET;
1795    in.time->i = TIMELIB_UNSET;
1796    in.time->s = TIMELIB_UNSET;
1797    in.time->f = TIMELIB_UNSET;
1798    in.time->z = TIMELIB_UNSET;
1799    in.time->dst = TIMELIB_UNSET;
1800    in.tzdb = tzdb;
1801    in.time->is_localtime = 0;
1802    in.time->zone_type = 0;
1803    in.time->relative.days = TIMELIB_UNSET;
1804
1805    do {
1806        t = scan(&in, tz_get_wrapper);
1807#ifdef DEBUG_PARSER
1808        printf("%d\n", t);
1809#endif
1810    } while(t != EOI);
1811
1812    /* do funky checking whether the parsed time was valid time */
1813    if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) {
1814        add_warning(&in, "The parsed time was invalid");
1815    }
1816    /* do funky checking whether the parsed date was valid date */
1817    if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) {
1818        add_warning(&in, "The parsed date was invalid");
1819    }
1820
1821    free(in.str);
1822    if (errors) {
1823        *errors = in.errors;
1824    } else {
1825        timelib_error_container_dtor(in.errors);
1826    }
1827    return in.time;
1828}
1829
1830#define TIMELIB_CHECK_NUMBER                                           \
1831        if (strchr("0123456789", *ptr) == NULL)                        \
1832        {                                                              \
1833            add_pbf_error(s, "Unexpected data found.", string, begin); \
1834        }
1835
1836static void timelib_time_reset_fields(timelib_time *time)
1837{
1838    assert(time != NULL);
1839
1840    time->y = 1970;
1841    time->m = 1;
1842    time->d = 1;
1843    time->h = time->i = time->s = 0;
1844    time->f = 0.0;
1845    time->tz_info = NULL;
1846}
1847
1848static void timelib_time_reset_unset_fields(timelib_time *time)
1849{
1850    assert(time != NULL);
1851
1852    if (time->y == TIMELIB_UNSET ) time->y = 1970;
1853    if (time->m == TIMELIB_UNSET ) time->m = 1;
1854    if (time->d == TIMELIB_UNSET ) time->d = 1;
1855    if (time->h == TIMELIB_UNSET ) time->h = 0;
1856    if (time->i == TIMELIB_UNSET ) time->i = 0;
1857    if (time->s == TIMELIB_UNSET ) time->s = 0;
1858    if (time->f == TIMELIB_UNSET ) time->f = 0.0;
1859}
1860
1861timelib_time *timelib_parse_from_format(char *format, char *string, int len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1862{
1863    char       *fptr = format;
1864    char       *ptr = string;
1865    char       *begin;
1866    timelib_sll tmp;
1867    Scanner in;
1868    Scanner *s = &in;
1869    int allow_extra = 0;
1870
1871    memset(&in, 0, sizeof(in));
1872    in.errors = malloc(sizeof(struct timelib_error_container));
1873    in.errors->warning_count = 0;
1874    in.errors->warning_messages = NULL;
1875    in.errors->error_count = 0;
1876    in.errors->error_messages = NULL;
1877
1878    in.time = timelib_time_ctor();
1879    in.time->y = TIMELIB_UNSET;
1880    in.time->d = TIMELIB_UNSET;
1881    in.time->m = TIMELIB_UNSET;
1882    in.time->h = TIMELIB_UNSET;
1883    in.time->i = TIMELIB_UNSET;
1884    in.time->s = TIMELIB_UNSET;
1885    in.time->f = TIMELIB_UNSET;
1886    in.time->z = TIMELIB_UNSET;
1887    in.time->dst = TIMELIB_UNSET;
1888    in.tzdb = tzdb;
1889    in.time->is_localtime = 0;
1890    in.time->zone_type = 0;
1891
1892    /* Loop over the format string */
1893    while (*fptr && *ptr) {
1894        begin = ptr;
1895        switch (*fptr) {
1896            case 'D': /* three letter day */
1897            case 'l': /* full day */
1898                {
1899                    const timelib_relunit* tmprel = 0;
1900
1901                    tmprel = timelib_lookup_relunit((char **) &ptr);
1902                    if (!tmprel) {
1903                        add_pbf_error(s, "A textual day could not be found", string, begin);
1904                        break;
1905                    } else {
1906                        in.time->have_relative = 1;
1907                        in.time->relative.have_weekday_relative = 1;
1908                        in.time->relative.weekday = tmprel->multiplier;
1909                        in.time->relative.weekday_behavior = 1;
1910                    }
1911                }
1912                break;
1913            case 'd': /* two digit day, with leading zero */
1914            case 'j': /* two digit day, without leading zero */
1915                TIMELIB_CHECK_NUMBER;
1916                if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1917                    add_pbf_error(s, "A two digit day could not be found", string, begin);
1918                }
1919                break;
1920            case 'S': /* day suffix, ignored, nor checked */
1921                timelib_skip_day_suffix((char **) &ptr);
1922                break;
1923            case 'z': /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */
1924                TIMELIB_CHECK_NUMBER;
1925                if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) {
1926                    add_pbf_error(s, "A three digit day-of-year could not be found", string, begin);
1927                } else {
1928                    s->time->m = 1;
1929                    s->time->d = tmp + 1;
1930                    timelib_do_normalize(s->time);
1931                }
1932                break;
1933
1934            case 'm': /* two digit month, with leading zero */
1935            case 'n': /* two digit month, without leading zero */
1936                TIMELIB_CHECK_NUMBER;
1937                if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1938                    add_pbf_error(s, "A two digit month could not be found", string, begin);
1939                }
1940                break;
1941            case 'M': /* three letter month */
1942            case 'F': /* full month */
1943                tmp = timelib_lookup_month((char **) &ptr);
1944                if (!tmp) {
1945                    add_pbf_error(s, "A textual month could not be found", string, begin);
1946                } else {
1947                    s->time->m = tmp;
1948                }
1949                break;
1950            case 'y': /* two digit year */
1951                {
1952                    int length = 0;
1953                    TIMELIB_CHECK_NUMBER;
1954                    if ((s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length)) == TIMELIB_UNSET) {
1955                        add_pbf_error(s, "A two digit year could not be found", string, begin);
1956                    }
1957                    TIMELIB_PROCESS_YEAR(s->time->y, length);
1958                }
1959                break;
1960            case 'Y': /* four digit year */
1961                TIMELIB_CHECK_NUMBER;
1962                if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) {
1963                    add_pbf_error(s, "A four digit year could not be found", string, begin);
1964                }
1965                break;
1966            case 'g': /* two digit hour, with leading zero */
1967            case 'h': /* two digit hour, without leading zero */
1968                TIMELIB_CHECK_NUMBER;
1969                if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1970                    add_pbf_error(s, "A two digit hour could not be found", string, begin);
1971                }
1972                if (s->time->h > 12) {
1973                    add_pbf_error(s, "Hour can not be higher than 12", string, begin);
1974                }
1975                break;
1976            case 'G': /* two digit hour, with leading zero */
1977            case 'H': /* two digit hour, without leading zero */
1978                TIMELIB_CHECK_NUMBER;
1979                if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1980                    add_pbf_error(s, "A two digit hour could not be found", string, begin);
1981                }
1982                break;
1983            case 'a': /* am/pm/a.m./p.m. */
1984            case 'A': /* AM/PM/A.M./P.M. */
1985                if (s->time->h == TIMELIB_UNSET) {
1986                    add_pbf_error(s, "Meridian can only come after an hour has been found", string, begin);
1987                } else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) {
1988                    add_pbf_error(s, "A meridian could not be found", string, begin);
1989                } else {
1990                    s->time->h += tmp;
1991                }
1992                break;
1993            case 'i': /* two digit minute, with leading zero */
1994                {
1995                    int length;
1996                    timelib_sll min;
1997
1998                    TIMELIB_CHECK_NUMBER;
1999                    min = timelib_get_nr_ex((char **) &ptr, 2, &length);
2000                    if (min == TIMELIB_UNSET || length != 2) {
2001                        add_pbf_error(s, "A two digit minute could not be found", string, begin);
2002                    } else {
2003                        s->time->i = min;
2004                    }
2005                }
2006                break;
2007            case 's': /* two digit second, with leading zero */
2008                {
2009                    int length;
2010                    timelib_sll sec;
2011
2012                    TIMELIB_CHECK_NUMBER;
2013                    sec = timelib_get_nr_ex((char **) &ptr, 2, &length);
2014                    if (sec == TIMELIB_UNSET || length != 2) {
2015                        add_pbf_error(s, "A two digit second could not be found", string, begin);
2016                    } else {
2017                        s->time->s = sec;
2018                    }
2019                }
2020                break;
2021            case 'u': /* up to six digit millisecond */
2022                {
2023                    double f;
2024                    char *tptr;
2025
2026                    TIMELIB_CHECK_NUMBER;
2027                    tptr = ptr;
2028                    if ((f = timelib_get_nr((char **) &ptr, 6)) == TIMELIB_UNSET || (ptr - tptr < 1)) {
2029                        add_pbf_error(s, "A six digit millisecond could not be found", string, begin);
2030                    } else {
2031                        s->time->f = (f / pow(10, (ptr - tptr)));
2032                    }
2033                }
2034                break;
2035            case ' ': /* any sort of whitespace (' ' and \t) */
2036                timelib_eat_spaces((char **) &ptr);
2037                break;
2038            case 'U': /* epoch seconds */
2039                TIMELIB_CHECK_NUMBER;
2040                TIMELIB_HAVE_RELATIVE();
2041                tmp = timelib_get_unsigned_nr((char **) &ptr, 24);
2042                s->time->y = 1970;
2043                s->time->m = 1;
2044                s->time->d = 1;
2045                s->time->h = s->time->i = s->time->s = 0;
2046                s->time->f = 0.0;
2047                s->time->relative.s += tmp;
2048                s->time->is_localtime = 1;
2049                s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
2050                s->time->z = 0;
2051                s->time->dst = 0;
2052                break;
2053
2054            case 'e': /* timezone */
2055            case 'P': /* timezone */
2056            case 'T': /* timezone */
2057            case 'O': /* timezone */
2058                {
2059                    int tz_not_found;
2060                    s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
2061                    if (tz_not_found) {
2062                        add_pbf_error(s, "The timezone could not be found in the database", string, begin);
2063                    }
2064                }
2065                break;
2066
2067            case '#': /* separation symbol */
2068                if (*ptr == ';' || *ptr == ':' || *ptr == '/' || *ptr == '.' || *ptr == ',' || *ptr == '-' || *ptr == '(' || *ptr == ')') {
2069                    ++ptr;
2070                } else {
2071                    add_pbf_error(s, "The separation symbol ([;:/.,-]) could not be found", string, begin);
2072                }
2073                break;
2074
2075            case ';':
2076            case ':':
2077            case '/':
2078            case '.':
2079            case ',':
2080            case '-':
2081            case '(':
2082            case ')':
2083                if (*ptr == *fptr) {
2084                    ++ptr;
2085                } else {
2086                    add_pbf_error(s, "The separation symbol could not be found", string, begin);
2087                }
2088                break;
2089
2090            case '!': /* reset all fields to default */
2091                timelib_time_reset_fields(s->time);
2092                break; /* break intentionally not missing */
2093
2094            case '|': /* reset all fields to default when not set */
2095                timelib_time_reset_unset_fields(s->time);
2096                break; /* break intentionally not missing */
2097
2098            case '?': /* random char */
2099                ++ptr;
2100                break;
2101
2102            case '\\': /* escaped char */
2103                if(!fptr[1]) {
2104                    add_pbf_error(s, "Escaped character expected", string, begin);
2105                    break;
2106                }
2107                fptr++;
2108                if (*ptr == *fptr) {
2109                    ++ptr;
2110                } else {
2111                    add_pbf_error(s, "The escaped character could not be found", string, begin);
2112                }
2113                break;
2114
2115            case '*': /* random chars until a separator or number ([ \t.,:;/-0123456789]) */
2116                timelib_eat_until_separator((char **) &ptr);
2117                break;
2118
2119            case '+': /* allow extra chars in the format */
2120                allow_extra = 1;
2121                break;
2122
2123            default:
2124                if (*fptr != *ptr) {
2125                    add_pbf_error(s, "The format separator does not match", string, begin);
2126                }
2127                ptr++;
2128        }
2129        fptr++;
2130    }
2131    if (*ptr) {
2132        if (allow_extra) {
2133            add_pbf_warning(s, "Trailing data", string, ptr);
2134        } else {
2135            add_pbf_error(s, "Trailing data", string, ptr);
2136        }
2137    }
2138    /* ignore trailing +'s */
2139    while (*fptr == '+') {
2140        fptr++;
2141    }
2142    if (*fptr) {
2143        /* Trailing | and ! specifiers are valid. */
2144        int done = 0;
2145        while (*fptr && !done) {
2146            switch (*fptr++) {
2147                case '!': /* reset all fields to default */
2148                    timelib_time_reset_fields(s->time);
2149                    break;
2150
2151                case '|': /* reset all fields to default when not set */
2152                    timelib_time_reset_unset_fields(s->time);
2153                    break;
2154
2155                default:
2156                    add_pbf_error(s, "Data missing", string, ptr);
2157                    done = 1;
2158            }
2159        }
2160    }
2161
2162    /* clean up a bit */
2163    if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET) {
2164        if (s->time->h == TIMELIB_UNSET ) {
2165            s->time->h = 0;
2166        }
2167        if (s->time->i == TIMELIB_UNSET ) {
2168            s->time->i = 0;
2169        }
2170        if (s->time->s == TIMELIB_UNSET ) {
2171            s->time->s = 0;
2172        }
2173    }
2174
2175    /* do funky checking whether the parsed time was valid time */
2176    if (s->time->h != TIMELIB_UNSET && s->time->i != TIMELIB_UNSET &&
2177        s->time->s != TIMELIB_UNSET &&
2178        !timelib_valid_time( s->time->h, s->time->i, s->time->s)) {
2179        add_pbf_warning(s, "The parsed time was invalid", string, ptr);
2180    }
2181    /* do funky checking whether the parsed date was valid date */
2182    if (s->time->y != TIMELIB_UNSET && s->time->m != TIMELIB_UNSET &&
2183        s->time->d != TIMELIB_UNSET &&
2184        !timelib_valid_date( s->time->y, s->time->m, s->time->d)) {
2185        add_pbf_warning(s, "The parsed date was invalid", string, ptr);
2186    }
2187
2188    if (errors) {
2189        *errors = in.errors;
2190    } else {
2191        timelib_error_container_dtor(in.errors);
2192    }
2193    return in.time;
2194}
2195
2196void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
2197{
2198    if (!(options & TIMELIB_OVERRIDE_TIME) && parsed->have_date && !parsed->have_time) {
2199        parsed->h = 0;
2200        parsed->i = 0;
2201        parsed->s = 0;
2202        parsed->f = 0;
2203    }
2204    if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
2205    if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
2206    if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
2207    if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
2208    if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
2209    if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
2210    if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
2211    if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
2212    if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
2213
2214    if (!parsed->tz_abbr) {
2215        parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2216    }
2217    if (!parsed->tz_info) {
2218        parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL;
2219    }
2220    if (parsed->zone_type == 0 && now->zone_type != 0) {
2221        parsed->zone_type = now->zone_type;
2222/*      parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2223        parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL;
2224*/      parsed->is_localtime = 1;
2225    }
2226/*  timelib_dump_date(parsed, 2);
2227    timelib_dump_date(now, 2);
2228*/
2229}
2230
2231char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst)
2232{
2233    const timelib_tz_lookup_table *tp;
2234
2235    tp = zone_search(abbr, gmtoffset, isdst);
2236    if (tp) {
2237        return (tp->full_tz_name);
2238    } else {
2239        return NULL;
2240    }
2241}
2242
2243const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void)
2244{
2245    return timelib_timezone_lookup;
2246}
2247
2248#ifdef DEBUG_PARSER_STUB
2249int main(void)
2250{
2251    timelib_time time = timelib_strtotime("May 12");
2252
2253    printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
2254        time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
2255    if (time.have_relative) {
2256        printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
2257            time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
2258    }
2259    if (time.have_weekday_relative) {
2260        printf (" / %d", time.relative.weekday);
2261    }
2262    if (time.have_weeknr_day) {
2263        printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek);
2264    }
2265    return 0;
2266}
2267#endif
2268
2269/*
2270 * vim: syntax=c
2271 */
2272