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