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