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