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* abbr_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    /* Still didn't find anything, let's find the zone solely based on
700     * offset/isdst then */
701    for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) {
702        if ((fmp->gmtoffset * 60) == gmtoffset && fmp->type == isdst) {
703            return fmp;
704        }
705    }
706    return NULL;
707}
708
709static long timelib_lookup_abbr(char **ptr, int *dst, char **tz_abbr, int *found)
710{
711    char *word;
712    char *begin = *ptr, *end;
713    long  value = 0;
714    const timelib_tz_lookup_table *tp;
715
716    while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
717        ++*ptr;
718    }
719    end = *ptr;
720    word = calloc(1, end - begin + 1);
721    memcpy(word, begin, end - begin);
722
723    if ((tp = abbr_search(word, -1, 0))) {
724        value = -tp->gmtoffset / 60;
725        *dst = tp->type;
726        value += tp->type * 60;
727        *found = 1;
728    } else {
729        *found = 0;
730    }
731
732    *tz_abbr = word;
733    return value;
734}
735
736long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper)
737{
738    timelib_tzinfo *res;
739    long            retval = 0;
740
741    *tz_not_found = 0;
742
743    while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
744        ++*ptr;
745    }
746    if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
747        *ptr += 3;
748    }
749    if (**ptr == '+') {
750        ++*ptr;
751        t->is_localtime = 1;
752        t->zone_type = TIMELIB_ZONETYPE_OFFSET;
753        *tz_not_found = 0;
754        t->dst = 0;
755
756        retval = -1 * timelib_parse_tz_cor(ptr);
757    } else if (**ptr == '-') {
758        ++*ptr;
759        t->is_localtime = 1;
760        t->zone_type = TIMELIB_ZONETYPE_OFFSET;
761        *tz_not_found = 0;
762        t->dst = 0;
763
764        retval = timelib_parse_tz_cor(ptr);
765    } else {
766        int found = 0;
767        long offset = 0;
768        char *tz_abbr;
769
770        t->is_localtime = 1;
771
772        /* First, we lookup by abbreviation only */
773        offset = timelib_lookup_abbr(ptr, dst, &tz_abbr, &found);
774        if (found) {
775            t->zone_type = TIMELIB_ZONETYPE_ABBR;
776            timelib_time_tz_abbr_update(t, tz_abbr);
777        }
778
779        /* Otherwise, we look if we have a TimeZone identifier */
780        if (!found || strcmp("UTC", tz_abbr) == 0) {
781            if ((res = tz_wrapper(tz_abbr, tzdb)) != NULL) {
782                t->tz_info = res;
783                t->zone_type = TIMELIB_ZONETYPE_ID;
784                found++;
785            }
786        }
787        free(tz_abbr);
788        *tz_not_found = (found == 0);
789        retval = offset;
790    }
791    while (**ptr == ')') {
792        ++*ptr;
793    }
794    return retval;
795}
796
797#define timelib_split_free(arg) {       \
798    int i;                         \
799    for (i = 0; i < arg.c; i++) {  \
800        free(arg.v[i]);            \
801    }                              \
802    if (arg.v) {                   \
803        free(arg.v);               \
804    }                              \
805}
806
807static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
808{
809    uchar *cursor = s->cur;
810    char *str, *ptr = NULL;
811
812std:
813    s->tok = cursor;
814    s->len = 0;
815/*!re2c
816any = [\000-\377];
817
818space = [ \t]+;
819frac = "."[0-9]+;
820
821ago = 'ago';
822
823hour24 = [01]?[0-9] | "2"[0-4];
824hour24lz = [01][0-9] | "2"[0-4];
825hour12 = "0"?[1-9] | "1"[0-2];
826minute = [0-5]?[0-9];
827minutelz = [0-5][0-9];
828second = minute | "60";
829secondlz = minutelz | "60";
830meridian = ([AaPp] "."? [Mm] "."?) [\000\t ];
831tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+;
832tzcorrection = "GMT"? [+-] hour24 ":"? minute?;
833
834daysuf = "st" | "nd" | "rd" | "th";
835
836month = "0"? [0-9] | "1"[0-2];
837day   = (([0-2]?[0-9]) | ("3"[01])) daysuf?;
838year  = [0-9]{1,4};
839year2 = [0-9]{2};
840year4 = [0-9]{4};
841year4withsign = [+-]? [0-9]{4};
842
843dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6];
844weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
845
846monthlz = "0" [0-9] | "1" [0-2];
847daylz   = "0" [0-9] | [1-2][0-9] | "3" [01];
848
849dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
850dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
851dayspecial = 'weekday' | 'weekdays';
852daytext = dayfull | dayabbr | dayspecial;
853
854monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
855monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec';
856monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII";
857monthtext = monthfull | monthabbr | monthroman;
858
859/* Time formats */
860timetiny12 = hour12 space? meridian;
861timeshort12 = hour12[:.]minutelz space? meridian;
862timelong12 = hour12[:.]minute[:.]secondlz space? meridian;
863
864timeshort24 = 't'? hour24[:.]minute;
865timelong24 =  't'? hour24[:.]minute[:.]second;
866iso8601long =  't'? hour24 [:.] minute [:.] second frac;
867
868/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */
869iso8601normtz =  't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz);
870/* iso8601longtz =  hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */
871
872gnunocolon       = 't'? hour24lz minutelz;
873/* gnunocolontz     = hour24lz minutelz space? (tzcorrection | tz); */
874iso8601nocolon   = 't'? hour24lz minutelz secondlz;
875/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */
876
877/* Date formats */
878americanshort    = month "/" day;
879american         = month "/" day "/" year;
880iso8601dateslash = year4 "/" monthlz "/" daylz "/"?;
881dateslash        = year4 "/" month "/" day;
882iso8601date4     = year4withsign "-" monthlz "-" daylz;
883iso8601date2     = year2 "-" monthlz "-" daylz;
884gnudateshorter   = year4 "-" month;
885gnudateshort     = year "-" month "-" day;
886pointeddate4     = day [.\t-] month [.-] year4;
887pointeddate2     = day [.\t] month "." year2;
888datefull         = day ([ \t.-])* monthtext ([ \t.-])* year;
889datenoday        = monthtext ([ .\t-])* year4;
890datenodayrev     = year4 ([ .\t-])* monthtext;
891datetextual      = monthtext ([ .\t-])* day [,.stndrh\t ]+ year;
892datenoyear       = monthtext ([ .\t-])* day [,.stndrh\t ]*;
893datenoyearrev    = day ([ .\t-])* monthtext;
894datenocolon      = year4 monthlz daylz;
895
896/* Special formats */
897soap             = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?;
898xmlrpc           = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz;
899xmlrpcnocolon    = year4 monthlz daylz 't' hour24 minutelz secondlz;
900wddx             = year4 "-" month "-" day "T" hour24 ":" minute ":" second;
901pgydotd          = year4 "."? dayofyear;
902pgtextshort      = monthabbr "-" daylz "-" year;
903pgtextreverse    = year "-" monthabbr "-" daylz;
904mssqltime        = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian;
905isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
906isoweek          = year4 "-"? "W" weekofyear;
907exif             = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz;
908firstdayof       = 'first day of';
909lastdayof        = 'last day of';
910backof           = 'back of ' hour24 space? meridian?;
911frontof          = 'front of ' hour24 space? meridian?;
912
913/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */
914clf              = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection;
915
916/* Timestamp format: @1126396800 */
917timestamp        = "@" "-"? [0-9]+;
918
919/* To fix some ambiguities */
920dateshortwithtimeshort12  = datenoyear timeshort12;
921dateshortwithtimelong12   = datenoyear timelong12;
922dateshortwithtimeshort  = datenoyear timeshort24;
923dateshortwithtimelong   = datenoyear timelong24;
924dateshortwithtimelongtz = datenoyear iso8601normtz;
925
926/*
927 * Relative regexps
928 */
929reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
930reltexttext = 'next'|'last'|'previous'|'this';
931reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
932
933relnumber = ([+-]*[ \t]*[0-9]+);
934relative = relnumber space? (reltextunit | 'week' );
935relativetext = (reltextnumber|reltexttext) space reltextunit;
936relativetextweek = reltexttext space 'week';
937
938weekdayof        = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of';
939
940*/
941
942/*!re2c
943    /* so that vim highlights correctly */
944    'yesterday'
945    {
946        DEBUG_OUTPUT("yesterday");
947        TIMELIB_INIT;
948        TIMELIB_HAVE_RELATIVE();
949        TIMELIB_UNHAVE_TIME();
950
951        s->time->relative.d = -1;
952        TIMELIB_DEINIT;
953        return TIMELIB_RELATIVE;
954    }
955
956    'now'
957    {
958        DEBUG_OUTPUT("now");
959        TIMELIB_INIT;
960
961        TIMELIB_DEINIT;
962        return TIMELIB_RELATIVE;
963    }
964
965    'noon'
966    {
967        DEBUG_OUTPUT("noon");
968        TIMELIB_INIT;
969        TIMELIB_UNHAVE_TIME();
970        TIMELIB_HAVE_TIME();
971        s->time->h = 12;
972
973        TIMELIB_DEINIT;
974        return TIMELIB_RELATIVE;
975    }
976
977    'midnight' | 'today'
978    {
979        DEBUG_OUTPUT("midnight | today");
980        TIMELIB_INIT;
981        TIMELIB_UNHAVE_TIME();
982
983        TIMELIB_DEINIT;
984        return TIMELIB_RELATIVE;
985    }
986
987    'tomorrow'
988    {
989        DEBUG_OUTPUT("tomorrow");
990        TIMELIB_INIT;
991        TIMELIB_HAVE_RELATIVE();
992        TIMELIB_UNHAVE_TIME();
993
994        s->time->relative.d = 1;
995        TIMELIB_DEINIT;
996        return TIMELIB_RELATIVE;
997    }
998
999    timestamp
1000    {
1001        timelib_ull i;
1002
1003        TIMELIB_INIT;
1004        TIMELIB_HAVE_RELATIVE();
1005        TIMELIB_UNHAVE_DATE();
1006        TIMELIB_UNHAVE_TIME();
1007        TIMELIB_HAVE_TZ();
1008
1009        i = timelib_get_unsigned_nr((char **) &ptr, 24);
1010        s->time->y = 1970;
1011        s->time->m = 1;
1012        s->time->d = 1;
1013        s->time->h = s->time->i = s->time->s = 0;
1014        s->time->f = 0.0;
1015        s->time->relative.s += i;
1016        s->time->is_localtime = 1;
1017        s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1018        s->time->z = 0;
1019        s->time->dst = 0;
1020
1021        TIMELIB_DEINIT;
1022        return TIMELIB_RELATIVE;
1023    }
1024
1025    firstdayof | lastdayof
1026    {
1027        DEBUG_OUTPUT("firstdayof | lastdayof");
1028        TIMELIB_INIT;
1029        TIMELIB_HAVE_RELATIVE();
1030
1031        /* skip "last day of" or "first day of" */
1032        if (*ptr == 'l' || *ptr == 'L') {
1033            s->time->relative.first_last_day_of = 2;
1034        } else {
1035            s->time->relative.first_last_day_of = 1;
1036        }
1037
1038        TIMELIB_DEINIT;
1039        return TIMELIB_LF_DAY_OF_MONTH;
1040    }
1041
1042    backof | frontof
1043    {
1044        DEBUG_OUTPUT("backof | frontof");
1045        TIMELIB_INIT;
1046        TIMELIB_UNHAVE_TIME();
1047        TIMELIB_HAVE_TIME();
1048
1049        if (*ptr == 'b') {
1050            s->time->h = timelib_get_nr((char **) &ptr, 2);
1051            s->time->i = 15;
1052        } else {
1053            s->time->h = timelib_get_nr((char **) &ptr, 2) - 1;
1054            s->time->i = 45;
1055        }
1056        if (*ptr != '\0' ) {
1057            timelib_eat_spaces((char **) &ptr);
1058            s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1059        }
1060
1061        TIMELIB_DEINIT;
1062        return TIMELIB_LF_DAY_OF_MONTH;
1063    }
1064
1065    weekdayof
1066    {
1067        timelib_sll i;
1068        int         behavior = 0;
1069        DEBUG_OUTPUT("weekdayof");
1070        TIMELIB_INIT;
1071        TIMELIB_HAVE_RELATIVE();
1072        TIMELIB_HAVE_SPECIAL_RELATIVE();
1073
1074        i = timelib_get_relative_text((char **) &ptr, &behavior);
1075        timelib_eat_spaces((char **) &ptr);
1076        if (i > 0) { /* first, second... etc */
1077            s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH;
1078            timelib_set_relative((char **) &ptr, i, 1, s);
1079        } else { /* last */
1080            s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH;
1081            timelib_set_relative((char **) &ptr, i, behavior, s);
1082        }
1083        TIMELIB_DEINIT;
1084        return TIMELIB_WEEK_DAY_OF_MONTH;
1085    }
1086
1087    timetiny12 | timeshort12 | timelong12
1088    {
1089        DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
1090        TIMELIB_INIT;
1091        TIMELIB_HAVE_TIME();
1092        s->time->h = timelib_get_nr((char **) &ptr, 2);
1093        if (*ptr == ':' || *ptr == '.') {
1094            s->time->i = timelib_get_nr((char **) &ptr, 2);
1095            if (*ptr == ':' || *ptr == '.') {
1096                s->time->s = timelib_get_nr((char **) &ptr, 2);
1097            }
1098        }
1099        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1100        TIMELIB_DEINIT;
1101        return TIMELIB_TIME12;
1102    }
1103
1104    mssqltime
1105    {
1106        DEBUG_OUTPUT("mssqltime");
1107        TIMELIB_INIT;
1108        TIMELIB_HAVE_TIME();
1109        s->time->h = timelib_get_nr((char **) &ptr, 2);
1110        s->time->i = timelib_get_nr((char **) &ptr, 2);
1111        if (*ptr == ':' || *ptr == '.') {
1112            s->time->s = timelib_get_nr((char **) &ptr, 2);
1113
1114            if (*ptr == ':' || *ptr == '.') {
1115                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1116            }
1117        }
1118        timelib_eat_spaces((char **) &ptr);
1119        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1120        TIMELIB_DEINIT;
1121        return TIMELIB_TIME24_WITH_ZONE;
1122    }
1123
1124    timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/
1125    {
1126        int tz_not_found;
1127        DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
1128        TIMELIB_INIT;
1129        TIMELIB_HAVE_TIME();
1130        s->time->h = timelib_get_nr((char **) &ptr, 2);
1131        s->time->i = timelib_get_nr((char **) &ptr, 2);
1132        if (*ptr == ':' || *ptr == '.') {
1133            s->time->s = timelib_get_nr((char **) &ptr, 2);
1134
1135            if (*ptr == '.') {
1136                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1137            }
1138        }
1139
1140        if (*ptr != '\0') {
1141            s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1142            if (tz_not_found) {
1143                add_error(s, "The timezone could not be found in the database");
1144            }
1145        }
1146        TIMELIB_DEINIT;
1147        return TIMELIB_TIME24_WITH_ZONE;
1148    }
1149
1150    gnunocolon
1151    {
1152        DEBUG_OUTPUT("gnunocolon");
1153        TIMELIB_INIT;
1154        switch (s->time->have_time) {
1155            case 0:
1156                s->time->h = timelib_get_nr((char **) &ptr, 2);
1157                s->time->i = timelib_get_nr((char **) &ptr, 2);
1158                s->time->s = 0;
1159                break;
1160            case 1:
1161                s->time->y = timelib_get_nr((char **) &ptr, 4);
1162                break;
1163            default:
1164                TIMELIB_DEINIT;
1165                add_error(s, "Double time specification");
1166                return TIMELIB_ERROR;
1167        }
1168        s->time->have_time++;
1169        TIMELIB_DEINIT;
1170        return TIMELIB_GNU_NOCOLON;
1171    }
1172/*
1173    gnunocolontz
1174    {
1175        DEBUG_OUTPUT("gnunocolontz");
1176        TIMELIB_INIT;
1177        switch (s->time->have_time) {
1178            case 0:
1179                s->time->h = timelib_get_nr((char **) &ptr, 2);
1180                s->time->i = timelib_get_nr((char **) &ptr, 2);
1181                s->time->s = 0;
1182                s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper);
1183                break;
1184            case 1:
1185                s->time->y = timelib_get_nr((char **) &ptr, 4);
1186                break;
1187            default:
1188                TIMELIB_DEINIT;
1189                return TIMELIB_ERROR;
1190        }
1191        s->time->have_time++;
1192        TIMELIB_DEINIT;
1193        return TIMELIB_GNU_NOCOLON_TZ;
1194    }
1195*/
1196    iso8601nocolon /*| iso8601nocolontz*/
1197    {
1198        int tz_not_found;
1199        DEBUG_OUTPUT("iso8601nocolon");
1200        TIMELIB_INIT;
1201        TIMELIB_HAVE_TIME();
1202        s->time->h = timelib_get_nr((char **) &ptr, 2);
1203        s->time->i = timelib_get_nr((char **) &ptr, 2);
1204        s->time->s = timelib_get_nr((char **) &ptr, 2);
1205
1206        if (*ptr != '\0') {
1207            s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1208            if (tz_not_found) {
1209                add_error(s, "The timezone could not be found in the database");
1210            }
1211        }
1212        TIMELIB_DEINIT;
1213        return TIMELIB_ISO_NOCOLON;
1214    }
1215
1216    americanshort | american
1217    {
1218        int length = 0;
1219        DEBUG_OUTPUT("americanshort | american");
1220        TIMELIB_INIT;
1221        TIMELIB_HAVE_DATE();
1222        s->time->m = timelib_get_nr((char **) &ptr, 2);
1223        s->time->d = timelib_get_nr((char **) &ptr, 2);
1224        if (*ptr == '/') {
1225            s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1226            TIMELIB_PROCESS_YEAR(s->time->y, length);
1227        }
1228        TIMELIB_DEINIT;
1229        return TIMELIB_AMERICAN;
1230    }
1231
1232    iso8601date4 | iso8601dateslash | dateslash
1233    {
1234        DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
1235        TIMELIB_INIT;
1236        TIMELIB_HAVE_DATE();
1237        s->time->y = timelib_get_unsigned_nr((char **) &ptr, 4);
1238        s->time->m = timelib_get_nr((char **) &ptr, 2);
1239        s->time->d = timelib_get_nr((char **) &ptr, 2);
1240        TIMELIB_DEINIT;
1241        return TIMELIB_ISO_DATE;
1242    }
1243
1244    iso8601date2
1245    {
1246        int length = 0;
1247        DEBUG_OUTPUT("iso8601date2");
1248        TIMELIB_INIT;
1249        TIMELIB_HAVE_DATE();
1250        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1251        s->time->m = timelib_get_nr((char **) &ptr, 2);
1252        s->time->d = timelib_get_nr((char **) &ptr, 2);
1253        TIMELIB_PROCESS_YEAR(s->time->y, length);
1254        TIMELIB_DEINIT;
1255        return TIMELIB_ISO_DATE;
1256    }
1257
1258    gnudateshorter
1259    {
1260        int length = 0;
1261        DEBUG_OUTPUT("gnudateshorter");
1262        TIMELIB_INIT;
1263        TIMELIB_HAVE_DATE();
1264        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1265        s->time->m = timelib_get_nr((char **) &ptr, 2);
1266        s->time->d = 1;
1267        TIMELIB_PROCESS_YEAR(s->time->y, length);
1268        TIMELIB_DEINIT;
1269        return TIMELIB_ISO_DATE;
1270    }
1271
1272    gnudateshort
1273    {
1274        int length = 0;
1275        DEBUG_OUTPUT("gnudateshort");
1276        TIMELIB_INIT;
1277        TIMELIB_HAVE_DATE();
1278        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1279        s->time->m = timelib_get_nr((char **) &ptr, 2);
1280        s->time->d = timelib_get_nr((char **) &ptr, 2);
1281        TIMELIB_PROCESS_YEAR(s->time->y, length);
1282        TIMELIB_DEINIT;
1283        return TIMELIB_ISO_DATE;
1284    }
1285
1286    datefull
1287    {
1288        int length = 0;
1289        DEBUG_OUTPUT("datefull");
1290        TIMELIB_INIT;
1291        TIMELIB_HAVE_DATE();
1292        s->time->d = timelib_get_nr((char **) &ptr, 2);
1293        timelib_skip_day_suffix((char **) &ptr);
1294        s->time->m = timelib_get_month((char **) &ptr);
1295        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1296        TIMELIB_PROCESS_YEAR(s->time->y, length);
1297        TIMELIB_DEINIT;
1298        return TIMELIB_DATE_FULL;
1299    }
1300
1301    pointeddate4
1302    {
1303        DEBUG_OUTPUT("pointed date YYYY");
1304        TIMELIB_INIT;
1305        TIMELIB_HAVE_DATE();
1306        s->time->d = timelib_get_nr((char **) &ptr, 2);
1307        s->time->m = timelib_get_nr((char **) &ptr, 2);
1308        s->time->y = timelib_get_nr((char **) &ptr, 4);
1309        TIMELIB_DEINIT;
1310        return TIMELIB_DATE_FULL_POINTED;
1311    }
1312
1313    pointeddate2
1314    {
1315        int length = 0;
1316        DEBUG_OUTPUT("pointed date YY");
1317        TIMELIB_INIT;
1318        TIMELIB_HAVE_DATE();
1319        s->time->d = timelib_get_nr((char **) &ptr, 2);
1320        s->time->m = timelib_get_nr((char **) &ptr, 2);
1321        s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length);
1322        TIMELIB_PROCESS_YEAR(s->time->y, length);
1323        TIMELIB_DEINIT;
1324        return TIMELIB_DATE_FULL_POINTED;
1325    }
1326
1327    datenoday
1328    {
1329        int length = 0;
1330        DEBUG_OUTPUT("datenoday");
1331        TIMELIB_INIT;
1332        TIMELIB_HAVE_DATE();
1333        s->time->m = timelib_get_month((char **) &ptr);
1334        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1335        s->time->d = 1;
1336        TIMELIB_PROCESS_YEAR(s->time->y, length);
1337        TIMELIB_DEINIT;
1338        return TIMELIB_DATE_NO_DAY;
1339    }
1340
1341    datenodayrev
1342    {
1343        int length = 0;
1344        DEBUG_OUTPUT("datenodayrev");
1345        TIMELIB_INIT;
1346        TIMELIB_HAVE_DATE();
1347        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1348        s->time->m = timelib_get_month((char **) &ptr);
1349        s->time->d = 1;
1350        TIMELIB_PROCESS_YEAR(s->time->y, length);
1351        TIMELIB_DEINIT;
1352        return TIMELIB_DATE_NO_DAY;
1353    }
1354
1355    datetextual | datenoyear
1356    {
1357        int length = 0;
1358        DEBUG_OUTPUT("datetextual | datenoyear");
1359        TIMELIB_INIT;
1360        TIMELIB_HAVE_DATE();
1361        s->time->m = timelib_get_month((char **) &ptr);
1362        s->time->d = timelib_get_nr((char **) &ptr, 2);
1363        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1364        TIMELIB_PROCESS_YEAR(s->time->y, length);
1365        TIMELIB_DEINIT;
1366        return TIMELIB_DATE_TEXT;
1367    }
1368
1369    datenoyearrev
1370    {
1371        DEBUG_OUTPUT("datenoyearrev");
1372        TIMELIB_INIT;
1373        TIMELIB_HAVE_DATE();
1374        s->time->d = timelib_get_nr((char **) &ptr, 2);
1375        timelib_skip_day_suffix((char **) &ptr);
1376        s->time->m = timelib_get_month((char **) &ptr);
1377        TIMELIB_DEINIT;
1378        return TIMELIB_DATE_TEXT;
1379    }
1380
1381    datenocolon
1382    {
1383        DEBUG_OUTPUT("datenocolon");
1384        TIMELIB_INIT;
1385        TIMELIB_HAVE_DATE();
1386        s->time->y = timelib_get_nr((char **) &ptr, 4);
1387        s->time->m = timelib_get_nr((char **) &ptr, 2);
1388        s->time->d = timelib_get_nr((char **) &ptr, 2);
1389        TIMELIB_DEINIT;
1390        return TIMELIB_DATE_NOCOLON;
1391    }
1392
1393    xmlrpc | xmlrpcnocolon | soap | wddx | exif
1394    {
1395        int tz_not_found;
1396        DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
1397        TIMELIB_INIT;
1398        TIMELIB_HAVE_TIME();
1399        TIMELIB_HAVE_DATE();
1400        s->time->y = timelib_get_nr((char **) &ptr, 4);
1401        s->time->m = timelib_get_nr((char **) &ptr, 2);
1402        s->time->d = timelib_get_nr((char **) &ptr, 2);
1403        s->time->h = timelib_get_nr((char **) &ptr, 2);
1404        s->time->i = timelib_get_nr((char **) &ptr, 2);
1405        s->time->s = timelib_get_nr((char **) &ptr, 2);
1406        if (*ptr == '.') {
1407            s->time->f = timelib_get_frac_nr((char **) &ptr, 9);
1408            if (*ptr) { /* timezone is optional */
1409                s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1410                if (tz_not_found) {
1411                    add_error(s, "The timezone could not be found in the database");
1412                }
1413            }
1414        }
1415        TIMELIB_DEINIT;
1416        return TIMELIB_XMLRPC_SOAP;
1417    }
1418
1419    pgydotd
1420    {
1421        int length = 0;
1422        DEBUG_OUTPUT("pgydotd");
1423        TIMELIB_INIT;
1424        TIMELIB_HAVE_DATE();
1425        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1426        s->time->d = timelib_get_nr((char **) &ptr, 3);
1427        s->time->m = 1;
1428        TIMELIB_PROCESS_YEAR(s->time->y, length);
1429        TIMELIB_DEINIT;
1430        return TIMELIB_PG_YEARDAY;
1431    }
1432
1433    isoweekday
1434    {
1435        timelib_sll w, d;
1436        DEBUG_OUTPUT("isoweekday");
1437        TIMELIB_INIT;
1438        TIMELIB_HAVE_DATE();
1439        TIMELIB_HAVE_RELATIVE();
1440
1441        s->time->y = timelib_get_nr((char **) &ptr, 4);
1442        w = timelib_get_nr((char **) &ptr, 2);
1443        d = timelib_get_nr((char **) &ptr, 1);
1444        s->time->m = 1;
1445        s->time->d = 1;
1446        s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1447
1448        TIMELIB_DEINIT;
1449        return TIMELIB_ISO_WEEK;
1450    }
1451
1452    isoweek
1453    {
1454        timelib_sll w, d;
1455        DEBUG_OUTPUT("isoweek");
1456        TIMELIB_INIT;
1457        TIMELIB_HAVE_DATE();
1458        TIMELIB_HAVE_RELATIVE();
1459
1460        s->time->y = timelib_get_nr((char **) &ptr, 4);
1461        w = timelib_get_nr((char **) &ptr, 2);
1462        d = 1;
1463        s->time->m = 1;
1464        s->time->d = 1;
1465        s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1466
1467        TIMELIB_DEINIT;
1468        return TIMELIB_ISO_WEEK;
1469    }
1470
1471    pgtextshort
1472    {
1473        int length = 0;
1474        DEBUG_OUTPUT("pgtextshort");
1475        TIMELIB_INIT;
1476        TIMELIB_HAVE_DATE();
1477        s->time->m = timelib_get_month((char **) &ptr);
1478        s->time->d = timelib_get_nr((char **) &ptr, 2);
1479        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1480        TIMELIB_PROCESS_YEAR(s->time->y, length);
1481        TIMELIB_DEINIT;
1482        return TIMELIB_PG_TEXT;
1483    }
1484
1485    pgtextreverse
1486    {
1487        int length = 0;
1488        DEBUG_OUTPUT("pgtextreverse");
1489        TIMELIB_INIT;
1490        TIMELIB_HAVE_DATE();
1491        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1492        s->time->m = timelib_get_month((char **) &ptr);
1493        s->time->d = timelib_get_nr((char **) &ptr, 2);
1494        TIMELIB_PROCESS_YEAR(s->time->y, length);
1495        TIMELIB_DEINIT;
1496        return TIMELIB_PG_TEXT;
1497    }
1498
1499    clf
1500    {
1501        int tz_not_found;
1502        DEBUG_OUTPUT("clf");
1503        TIMELIB_INIT;
1504        TIMELIB_HAVE_TIME();
1505        TIMELIB_HAVE_DATE();
1506        s->time->d = timelib_get_nr((char **) &ptr, 2);
1507        s->time->m = timelib_get_month((char **) &ptr);
1508        s->time->y = timelib_get_nr((char **) &ptr, 4);
1509        s->time->h = timelib_get_nr((char **) &ptr, 2);
1510        s->time->i = timelib_get_nr((char **) &ptr, 2);
1511        s->time->s = timelib_get_nr((char **) &ptr, 2);
1512        s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1513        if (tz_not_found) {
1514            add_error(s, "The timezone could not be found in the database");
1515        }
1516        TIMELIB_DEINIT;
1517        return TIMELIB_CLF;
1518    }
1519
1520    year4
1521    {
1522        DEBUG_OUTPUT("year4");
1523        TIMELIB_INIT;
1524        s->time->y = timelib_get_nr((char **) &ptr, 4);
1525        TIMELIB_DEINIT;
1526        return TIMELIB_CLF;
1527    }
1528
1529    ago
1530    {
1531        DEBUG_OUTPUT("ago");
1532        TIMELIB_INIT;
1533        s->time->relative.y = 0 - s->time->relative.y;
1534        s->time->relative.m = 0 - s->time->relative.m;
1535        s->time->relative.d = 0 - s->time->relative.d;
1536        s->time->relative.h = 0 - s->time->relative.h;
1537        s->time->relative.i = 0 - s->time->relative.i;
1538        s->time->relative.s = 0 - s->time->relative.s;
1539        s->time->relative.weekday = 0 - s->time->relative.weekday;
1540        if (s->time->relative.weekday == 0) {
1541            s->time->relative.weekday = -7;
1542        }
1543        if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) {
1544            s->time->relative.special.amount = 0 - s->time->relative.special.amount;
1545        }
1546        TIMELIB_DEINIT;
1547        return TIMELIB_AGO;
1548    }
1549
1550    daytext
1551    {
1552        const timelib_relunit* relunit;
1553        DEBUG_OUTPUT("daytext");
1554        TIMELIB_INIT;
1555        TIMELIB_HAVE_RELATIVE();
1556        TIMELIB_HAVE_WEEKDAY_RELATIVE();
1557        TIMELIB_UNHAVE_TIME();
1558        relunit = timelib_lookup_relunit((char**) &ptr);
1559        s->time->relative.weekday = relunit->multiplier;
1560        if (s->time->relative.weekday_behavior != 2) {
1561            s->time->relative.weekday_behavior = 1;
1562        }
1563
1564        TIMELIB_DEINIT;
1565        return TIMELIB_WEEKDAY;
1566    }
1567
1568    relativetextweek
1569    {
1570        timelib_sll i;
1571        int         behavior = 0;
1572        DEBUG_OUTPUT("relativetextweek");
1573        TIMELIB_INIT;
1574        TIMELIB_HAVE_RELATIVE();
1575
1576        while(*ptr) {
1577            i = timelib_get_relative_text((char **) &ptr, &behavior);
1578            timelib_eat_spaces((char **) &ptr);
1579            timelib_set_relative((char **) &ptr, i, behavior, s);
1580            s->time->relative.weekday_behavior = 2;
1581
1582            /* to handle the format weekday + last/this/next week */
1583            if (s->time->relative.have_weekday_relative == 0) {
1584                TIMELIB_HAVE_WEEKDAY_RELATIVE();
1585                s->time->relative.weekday = 1;
1586            }
1587        }
1588        TIMELIB_DEINIT;
1589        return TIMELIB_RELATIVE;
1590    }
1591
1592    relativetext
1593    {
1594        timelib_sll i;
1595        int         behavior = 0;
1596        DEBUG_OUTPUT("relativetext");
1597        TIMELIB_INIT;
1598        TIMELIB_HAVE_RELATIVE();
1599
1600        while(*ptr) {
1601            i = timelib_get_relative_text((char **) &ptr, &behavior);
1602            timelib_eat_spaces((char **) &ptr);
1603            timelib_set_relative((char **) &ptr, i, behavior, s);
1604        }
1605        TIMELIB_DEINIT;
1606        return TIMELIB_RELATIVE;
1607    }
1608
1609    monthfull | monthabbr
1610    {
1611        DEBUG_OUTPUT("monthtext");
1612        TIMELIB_INIT;
1613        TIMELIB_HAVE_DATE();
1614        s->time->m = timelib_lookup_month((char **) &ptr);
1615        TIMELIB_DEINIT;
1616        return TIMELIB_DATE_TEXT;
1617    }
1618
1619    tzcorrection | tz
1620    {
1621        int tz_not_found;
1622        DEBUG_OUTPUT("tzcorrection | tz");
1623        TIMELIB_INIT;
1624        TIMELIB_HAVE_TZ();
1625        s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1626        if (tz_not_found) {
1627            add_error(s, "The timezone could not be found in the database");
1628        }
1629        TIMELIB_DEINIT;
1630        return TIMELIB_TIMEZONE;
1631    }
1632
1633    dateshortwithtimeshort12 | dateshortwithtimelong12
1634    {
1635        DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
1636        TIMELIB_INIT;
1637        TIMELIB_HAVE_DATE();
1638        s->time->m = timelib_get_month((char **) &ptr);
1639        s->time->d = timelib_get_nr((char **) &ptr, 2);
1640
1641        TIMELIB_HAVE_TIME();
1642        s->time->h = timelib_get_nr((char **) &ptr, 2);
1643        s->time->i = timelib_get_nr((char **) &ptr, 2);
1644        if (*ptr == ':' || *ptr == '.') {
1645            s->time->s = timelib_get_nr((char **) &ptr, 2);
1646
1647            if (*ptr == '.') {
1648                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1649            }
1650        }
1651
1652        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1653        TIMELIB_DEINIT;
1654        return TIMELIB_SHORTDATE_WITH_TIME;
1655    }
1656
1657    dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz
1658    {
1659        int tz_not_found;
1660        DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
1661        TIMELIB_INIT;
1662        TIMELIB_HAVE_DATE();
1663        s->time->m = timelib_get_month((char **) &ptr);
1664        s->time->d = timelib_get_nr((char **) &ptr, 2);
1665
1666        TIMELIB_HAVE_TIME();
1667        s->time->h = timelib_get_nr((char **) &ptr, 2);
1668        s->time->i = timelib_get_nr((char **) &ptr, 2);
1669        if (*ptr == ':') {
1670            s->time->s = timelib_get_nr((char **) &ptr, 2);
1671
1672            if (*ptr == '.') {
1673                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1674            }
1675        }
1676
1677        if (*ptr != '\0') {
1678            s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1679            if (tz_not_found) {
1680                add_error(s, "The timezone could not be found in the database");
1681            }
1682        }
1683        TIMELIB_DEINIT;
1684        return TIMELIB_SHORTDATE_WITH_TIME;
1685    }
1686
1687    relative
1688    {
1689        timelib_ull i;
1690        DEBUG_OUTPUT("relative");
1691        TIMELIB_INIT;
1692        TIMELIB_HAVE_RELATIVE();
1693
1694        while(*ptr) {
1695            i = timelib_get_unsigned_nr((char **) &ptr, 24);
1696            timelib_eat_spaces((char **) &ptr);
1697            timelib_set_relative((char **) &ptr, i, 1, s);
1698        }
1699        TIMELIB_DEINIT;
1700        return TIMELIB_RELATIVE;
1701    }
1702
1703    [ .,\t]
1704    {
1705        goto std;
1706    }
1707
1708    "\000"|"\n"
1709    {
1710        s->pos = cursor; s->line++;
1711        goto std;
1712    }
1713
1714    any
1715    {
1716        add_error(s, "Unexpected character");
1717        goto std;
1718    }
1719*/
1720}
1721
1722/*!max:re2c */
1723
1724timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1725{
1726    Scanner in;
1727    int t;
1728    char *e = s + len - 1;
1729
1730    memset(&in, 0, sizeof(in));
1731    in.errors = malloc(sizeof(struct timelib_error_container));
1732    in.errors->warning_count = 0;
1733    in.errors->warning_messages = NULL;
1734    in.errors->error_count = 0;
1735    in.errors->error_messages = NULL;
1736
1737    if (len > 0) {
1738        while (isspace(*s) && s < e) {
1739            s++;
1740        }
1741        while (isspace(*e) && e > s) {
1742            e--;
1743        }
1744    }
1745    if (e - s < 0) {
1746        in.time = timelib_time_ctor();
1747        add_error(&in, "Empty string");
1748        if (errors) {
1749            *errors = in.errors;
1750        } else {
1751            timelib_error_container_dtor(in.errors);
1752        }
1753        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;
1754        in.time->is_localtime = in.time->zone_type = 0;
1755        return in.time;
1756    }
1757    e++;
1758
1759    in.str = malloc((e - s) + YYMAXFILL);
1760    memset(in.str, 0, (e - s) + YYMAXFILL);
1761    memcpy(in.str, s, (e - s));
1762    in.lim = in.str + (e - s) + YYMAXFILL;
1763    in.cur = in.str;
1764    in.time = timelib_time_ctor();
1765    in.time->y = TIMELIB_UNSET;
1766    in.time->d = TIMELIB_UNSET;
1767    in.time->m = TIMELIB_UNSET;
1768    in.time->h = TIMELIB_UNSET;
1769    in.time->i = TIMELIB_UNSET;
1770    in.time->s = TIMELIB_UNSET;
1771    in.time->f = TIMELIB_UNSET;
1772    in.time->z = TIMELIB_UNSET;
1773    in.time->dst = TIMELIB_UNSET;
1774    in.tzdb = tzdb;
1775    in.time->is_localtime = 0;
1776    in.time->zone_type = 0;
1777    in.time->relative.days = TIMELIB_UNSET;
1778
1779    do {
1780        t = scan(&in, tz_get_wrapper);
1781#ifdef DEBUG_PARSER
1782        printf("%d\n", t);
1783#endif
1784    } while(t != EOI);
1785
1786    /* do funky checking whether the parsed time was valid time */
1787    if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) {
1788        add_warning(&in, "The parsed time was invalid");
1789    }
1790    /* do funky checking whether the parsed date was valid date */
1791    if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) {
1792        add_warning(&in, "The parsed date was invalid");
1793    }
1794
1795    free(in.str);
1796    if (errors) {
1797        *errors = in.errors;
1798    } else {
1799        timelib_error_container_dtor(in.errors);
1800    }
1801    return in.time;
1802}
1803
1804#define TIMELIB_CHECK_NUMBER                                           \
1805        if (strchr("0123456789", *ptr) == NULL)                        \
1806        {                                                              \
1807            add_pbf_error(s, "Unexpected data found.", string, begin); \
1808        }
1809
1810static void timelib_time_reset_fields(timelib_time *time)
1811{
1812    assert(time != NULL);
1813
1814    time->y = 1970;
1815    time->m = 1;
1816    time->d = 1;
1817    time->h = time->i = time->s = 0;
1818    time->f = 0.0;
1819    time->tz_info = NULL;
1820}
1821
1822static void timelib_time_reset_unset_fields(timelib_time *time)
1823{
1824    assert(time != NULL);
1825
1826    if (time->y == TIMELIB_UNSET ) time->y = 1970;
1827    if (time->m == TIMELIB_UNSET ) time->m = 1;
1828    if (time->d == TIMELIB_UNSET ) time->d = 1;
1829    if (time->h == TIMELIB_UNSET ) time->h = 0;
1830    if (time->i == TIMELIB_UNSET ) time->i = 0;
1831    if (time->s == TIMELIB_UNSET ) time->s = 0;
1832    if (time->f == TIMELIB_UNSET ) time->f = 0.0;
1833}
1834
1835timelib_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)
1836{
1837    char       *fptr = format;
1838    char       *ptr = string;
1839    char       *begin;
1840    timelib_sll tmp;
1841    Scanner in;
1842    Scanner *s = &in;
1843    int allow_extra = 0;
1844
1845    memset(&in, 0, sizeof(in));
1846    in.errors = malloc(sizeof(struct timelib_error_container));
1847    in.errors->warning_count = 0;
1848    in.errors->warning_messages = NULL;
1849    in.errors->error_count = 0;
1850    in.errors->error_messages = NULL;
1851
1852    in.time = timelib_time_ctor();
1853    in.time->y = TIMELIB_UNSET;
1854    in.time->d = TIMELIB_UNSET;
1855    in.time->m = TIMELIB_UNSET;
1856    in.time->h = TIMELIB_UNSET;
1857    in.time->i = TIMELIB_UNSET;
1858    in.time->s = TIMELIB_UNSET;
1859    in.time->f = TIMELIB_UNSET;
1860    in.time->z = TIMELIB_UNSET;
1861    in.time->dst = TIMELIB_UNSET;
1862    in.tzdb = tzdb;
1863    in.time->is_localtime = 0;
1864    in.time->zone_type = 0;
1865
1866    /* Loop over the format string */
1867    while (*fptr && *ptr) {
1868        begin = ptr;
1869        switch (*fptr) {
1870            case 'D': /* three letter day */
1871            case 'l': /* full day */
1872                {
1873                    const timelib_relunit* tmprel = 0;
1874
1875                    tmprel = timelib_lookup_relunit((char **) &ptr);
1876                    if (!tmprel) {
1877                        add_pbf_error(s, "A textual day could not be found", string, begin);
1878                        break;
1879                    } else {
1880                        in.time->have_relative = 1;
1881                        in.time->relative.have_weekday_relative = 1;
1882                        in.time->relative.weekday = tmprel->multiplier;
1883                        in.time->relative.weekday_behavior = 1;
1884                    }
1885                }
1886                break;
1887            case 'd': /* two digit day, with leading zero */
1888            case 'j': /* two digit day, without leading zero */
1889                TIMELIB_CHECK_NUMBER;
1890                if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1891                    add_pbf_error(s, "A two digit day could not be found", string, begin);
1892                }
1893                break;
1894            case 'S': /* day suffix, ignored, nor checked */
1895                timelib_skip_day_suffix((char **) &ptr);
1896                break;
1897            case 'z': /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */
1898                TIMELIB_CHECK_NUMBER;
1899                if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) {
1900                    add_pbf_error(s, "A three digit day-of-year could not be found", string, begin);
1901                } else {
1902                    s->time->m = 1;
1903                    s->time->d = tmp + 1;
1904                    timelib_do_normalize(s->time);
1905                }
1906                break;
1907
1908            case 'm': /* two digit month, with leading zero */
1909            case 'n': /* two digit month, without leading zero */
1910                TIMELIB_CHECK_NUMBER;
1911                if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1912                    add_pbf_error(s, "A two digit month could not be found", string, begin);
1913                }
1914                break;
1915            case 'M': /* three letter month */
1916            case 'F': /* full month */
1917                tmp = timelib_lookup_month((char **) &ptr);
1918                if (!tmp) {
1919                    add_pbf_error(s, "A textual month could not be found", string, begin);
1920                } else {
1921                    s->time->m = tmp;
1922                }
1923                break;
1924            case 'y': /* two digit year */
1925                {
1926                    int length = 0;
1927                    TIMELIB_CHECK_NUMBER;
1928                    if ((s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length)) == TIMELIB_UNSET) {
1929                        add_pbf_error(s, "A two digit year could not be found", string, begin);
1930                    }
1931                    TIMELIB_PROCESS_YEAR(s->time->y, length);
1932                }
1933                break;
1934            case 'Y': /* four digit year */
1935                TIMELIB_CHECK_NUMBER;
1936                if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) {
1937                    add_pbf_error(s, "A four digit year could not be found", string, begin);
1938                }
1939                break;
1940            case 'g': /* two digit hour, with leading zero */
1941            case 'h': /* two digit hour, without leading zero */
1942                TIMELIB_CHECK_NUMBER;
1943                if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1944                    add_pbf_error(s, "A two digit hour could not be found", string, begin);
1945                }
1946                if (s->time->h > 12) {
1947                    add_pbf_error(s, "Hour can not be higher than 12", string, begin);
1948                }
1949                break;
1950            case 'G': /* two digit hour, with leading zero */
1951            case 'H': /* two digit hour, without leading zero */
1952                TIMELIB_CHECK_NUMBER;
1953                if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1954                    add_pbf_error(s, "A two digit hour could not be found", string, begin);
1955                }
1956                break;
1957            case 'a': /* am/pm/a.m./p.m. */
1958            case 'A': /* AM/PM/A.M./P.M. */
1959                if (s->time->h == TIMELIB_UNSET) {
1960                    add_pbf_error(s, "Meridian can only come after an hour has been found", string, begin);
1961                } else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) {
1962                    add_pbf_error(s, "A meridian could not be found", string, begin);
1963                } else {
1964                    s->time->h += tmp;
1965                }
1966                break;
1967            case 'i': /* two digit minute, with leading zero */
1968                {
1969                    int length;
1970                    timelib_sll min;
1971
1972                    TIMELIB_CHECK_NUMBER;
1973                    min = timelib_get_nr_ex((char **) &ptr, 2, &length);
1974                    if (min == TIMELIB_UNSET || length != 2) {
1975                        add_pbf_error(s, "A two digit minute could not be found", string, begin);
1976                    } else {
1977                        s->time->i = min;
1978                    }
1979                }
1980                break;
1981            case 's': /* two digit second, with leading zero */
1982                {
1983                    int length;
1984                    timelib_sll sec;
1985
1986                    TIMELIB_CHECK_NUMBER;
1987                    sec = timelib_get_nr_ex((char **) &ptr, 2, &length);
1988                    if (sec == TIMELIB_UNSET || length != 2) {
1989                        add_pbf_error(s, "A two digit second could not be found", string, begin);
1990                    } else {
1991                        s->time->s = sec;
1992                    }
1993                }
1994                break;
1995            case 'u': /* up to six digit millisecond */
1996                {
1997                    double f;
1998                    char *tptr;
1999
2000                    TIMELIB_CHECK_NUMBER;
2001                    tptr = ptr;
2002                    if ((f = timelib_get_nr((char **) &ptr, 6)) == TIMELIB_UNSET || (ptr - tptr < 1)) {
2003                        add_pbf_error(s, "A six digit millisecond could not be found", string, begin);
2004                    } else {
2005                        s->time->f = (f / pow(10, (ptr - tptr)));
2006                    }
2007                }
2008                break;
2009            case ' ': /* any sort of whitespace (' ' and \t) */
2010                timelib_eat_spaces((char **) &ptr);
2011                break;
2012            case 'U': /* epoch seconds */
2013                TIMELIB_CHECK_NUMBER;
2014                TIMELIB_HAVE_RELATIVE();
2015                tmp = timelib_get_unsigned_nr((char **) &ptr, 24);
2016                s->time->y = 1970;
2017                s->time->m = 1;
2018                s->time->d = 1;
2019                s->time->h = s->time->i = s->time->s = 0;
2020                s->time->f = 0.0;
2021                s->time->relative.s += tmp;
2022                s->time->is_localtime = 1;
2023                s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
2024                s->time->z = 0;
2025                s->time->dst = 0;
2026                break;
2027
2028            case 'e': /* timezone */
2029            case 'P': /* timezone */
2030            case 'T': /* timezone */
2031            case 'O': /* timezone */
2032                {
2033                    int tz_not_found;
2034                    s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
2035                    if (tz_not_found) {
2036                        add_pbf_error(s, "The timezone could not be found in the database", string, begin);
2037                    }
2038                }
2039                break;
2040
2041            case '#': /* separation symbol */
2042                if (*ptr == ';' || *ptr == ':' || *ptr == '/' || *ptr == '.' || *ptr == ',' || *ptr == '-' || *ptr == '(' || *ptr == ')') {
2043                    ++ptr;
2044                } else {
2045                    add_pbf_error(s, "The separation symbol ([;:/.,-]) could not be found", string, begin);
2046                }
2047                break;
2048
2049            case ';':
2050            case ':':
2051            case '/':
2052            case '.':
2053            case ',':
2054            case '-':
2055            case '(':
2056            case ')':
2057                if (*ptr == *fptr) {
2058                    ++ptr;
2059                } else {
2060                    add_pbf_error(s, "The separation symbol could not be found", string, begin);
2061                }
2062                break;
2063
2064            case '!': /* reset all fields to default */
2065                timelib_time_reset_fields(s->time);
2066                break; /* break intentionally not missing */
2067
2068            case '|': /* reset all fields to default when not set */
2069                timelib_time_reset_unset_fields(s->time);
2070                break; /* break intentionally not missing */
2071
2072            case '?': /* random char */
2073                ++ptr;
2074                break;
2075
2076            case '\\': /* escaped char */
2077                if(!fptr[1]) {
2078                    add_pbf_error(s, "Escaped character expected", string, begin);
2079                    break;
2080                }
2081                fptr++;
2082                if (*ptr == *fptr) {
2083                    ++ptr;
2084                } else {
2085                    add_pbf_error(s, "The escaped character could not be found", string, begin);
2086                }
2087                break;
2088
2089            case '*': /* random chars until a separator or number ([ \t.,:;/-0123456789]) */
2090                timelib_eat_until_separator((char **) &ptr);
2091                break;
2092
2093            case '+': /* allow extra chars in the format */
2094                allow_extra = 1;
2095                break;
2096
2097            default:
2098                if (*fptr != *ptr) {
2099                    add_pbf_error(s, "The format separator does not match", string, begin);
2100                }
2101                ptr++;
2102        }
2103        fptr++;
2104    }
2105    if (*ptr) {
2106        if (allow_extra) {
2107            add_pbf_warning(s, "Trailing data", string, ptr);
2108        } else {
2109            add_pbf_error(s, "Trailing data", string, ptr);
2110        }
2111    }
2112    /* ignore trailing +'s */
2113    while (*fptr == '+') {
2114        fptr++;
2115    }
2116    if (*fptr) {
2117        /* Trailing | and ! specifiers are valid. */
2118        int done = 0;
2119        while (*fptr && !done) {
2120            switch (*fptr++) {
2121                case '!': /* reset all fields to default */
2122                    timelib_time_reset_fields(s->time);
2123                    break;
2124
2125                case '|': /* reset all fields to default when not set */
2126                    timelib_time_reset_unset_fields(s->time);
2127                    break;
2128
2129                default:
2130                    add_pbf_error(s, "Data missing", string, ptr);
2131                    done = 1;
2132            }
2133        }
2134    }
2135
2136    /* clean up a bit */
2137    if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET) {
2138        if (s->time->h == TIMELIB_UNSET ) {
2139            s->time->h = 0;
2140        }
2141        if (s->time->i == TIMELIB_UNSET ) {
2142            s->time->i = 0;
2143        }
2144        if (s->time->s == TIMELIB_UNSET ) {
2145            s->time->s = 0;
2146        }
2147    }
2148
2149    /* do funky checking whether the parsed time was valid time */
2150    if (s->time->h != TIMELIB_UNSET && s->time->i != TIMELIB_UNSET &&
2151        s->time->s != TIMELIB_UNSET &&
2152        !timelib_valid_time( s->time->h, s->time->i, s->time->s)) {
2153        add_pbf_warning(s, "The parsed time was invalid", string, ptr);
2154    }
2155    /* do funky checking whether the parsed date was valid date */
2156    if (s->time->y != TIMELIB_UNSET && s->time->m != TIMELIB_UNSET &&
2157        s->time->d != TIMELIB_UNSET &&
2158        !timelib_valid_date( s->time->y, s->time->m, s->time->d)) {
2159        add_pbf_warning(s, "The parsed date was invalid", string, ptr);
2160    }
2161
2162    if (errors) {
2163        *errors = in.errors;
2164    } else {
2165        timelib_error_container_dtor(in.errors);
2166    }
2167    return in.time;
2168}
2169
2170void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
2171{
2172    if (!(options & TIMELIB_OVERRIDE_TIME) && parsed->have_date && !parsed->have_time) {
2173        parsed->h = 0;
2174        parsed->i = 0;
2175        parsed->s = 0;
2176        parsed->f = 0;
2177    }
2178    if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
2179    if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
2180    if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
2181    if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
2182    if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
2183    if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
2184    if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
2185    if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
2186    if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
2187
2188    if (!parsed->tz_abbr) {
2189        parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2190    }
2191    if (!parsed->tz_info) {
2192        parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL;
2193    }
2194    if (parsed->zone_type == 0 && now->zone_type != 0) {
2195        parsed->zone_type = now->zone_type;
2196/*      parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2197        parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL;
2198*/      parsed->is_localtime = 1;
2199    }
2200/*  timelib_dump_date(parsed, 2);
2201    timelib_dump_date(now, 2);
2202*/
2203}
2204
2205char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst)
2206{
2207    const timelib_tz_lookup_table *tp;
2208
2209    tp = abbr_search(abbr, gmtoffset, isdst);
2210    if (tp) {
2211        return (tp->full_tz_name);
2212    } else {
2213        return NULL;
2214    }
2215}
2216
2217const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void)
2218{
2219    return timelib_timezone_lookup;
2220}
2221
2222#ifdef DEBUG_PARSER_STUB
2223int main(void)
2224{
2225    timelib_time time = timelib_strtotime("May 12");
2226
2227    printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
2228        time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
2229    if (time.have_relative) {
2230        printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
2231            time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
2232    }
2233    if (time.have_weekday_relative) {
2234        printf (" / %d", time.relative.weekday);
2235    }
2236    if (time.have_weeknr_day) {
2237        printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek);
2238    }
2239    return 0;
2240}
2241#endif
2242
2243/*
2244 * vim: syntax=c
2245 */
2246