1/* 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2015 Derick Rethans 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25/* $Id$ */ 26 27#include "timelib.h" 28 29#include <stdio.h> 30#include <ctype.h> 31 32#ifdef HAVE_STDLIB_H 33#include <stdlib.h> 34#endif 35#ifdef HAVE_STRING_H 36#include <string.h> 37#else 38#include <strings.h> 39#endif 40 41#if defined(_MSC_VER) 42# define strtoll(s, f, b) _atoi64(s) 43#elif !defined(HAVE_STRTOLL) 44# if defined(HAVE_ATOLL) 45# define strtoll(s, f, b) atoll(s) 46# else 47# define strtoll(s, f, b) strtol(s, f, b) 48# endif 49#endif 50 51#define TIMELIB_UNSET -99999 52 53#define TIMELIB_SECOND 1 54#define TIMELIB_MINUTE 2 55#define TIMELIB_HOUR 3 56#define TIMELIB_DAY 4 57#define TIMELIB_MONTH 5 58#define TIMELIB_YEAR 6 59 60#define EOI 257 61 62#define TIMELIB_PERIOD 260 63#define TIMELIB_ISO_DATE 261 64#define TIMELIB_ERROR 999 65 66typedef unsigned char uchar; 67 68#define BSIZE 8192 69 70#define YYCTYPE uchar 71#define YYCURSOR cursor 72#define YYLIMIT s->lim 73#define YYMARKER s->ptr 74#define YYFILL(n) return EOI; 75 76#define RET(i) {s->cur = cursor; return i;} 77 78#define timelib_string_free timelib_free 79 80#define TIMELIB_INIT s->cur = cursor; str = timelib_string(s); ptr = str 81#define TIMELIB_DEINIT timelib_string_free(str) 82 83#ifdef DEBUG_PARSER 84#define DEBUG_OUTPUT(s) printf("%s\n", s); 85#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } } 86#else 87#define DEBUG_OUTPUT(s) 88#define YYDEBUG(s,c) 89#endif 90 91#include "timelib_structs.h" 92 93typedef struct Scanner { 94 int fd; 95 uchar *lim, *str, *ptr, *cur, *tok, *pos; 96 unsigned int line, len; 97 struct timelib_error_container *errors; 98 99 struct timelib_time *begin; 100 struct timelib_time *end; 101 struct timelib_rel_time *period; 102 int recurrences; 103 104 int have_period; 105 int have_recurrences; 106 int have_date; 107 int have_begin_date; 108 int have_end_date; 109} Scanner; 110 111static void add_warning(Scanner *s, char *error) 112{ 113 s->errors->warning_count++; 114 s->errors->warning_messages = timelib_realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message)); 115 s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0; 116 s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0; 117 s->errors->warning_messages[s->errors->warning_count - 1].message = timelib_strdup(error); 118} 119 120static void add_error(Scanner *s, char *error) 121{ 122 s->errors->error_count++; 123 s->errors->error_messages = timelib_realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message)); 124 s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0; 125 s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0; 126 s->errors->error_messages[s->errors->error_count - 1].message = timelib_strdup(error); 127} 128 129static char *timelib_string(Scanner *s) 130{ 131 char *tmp = timelib_calloc(1, s->cur - s->tok + 1); 132 memcpy(tmp, s->tok, s->cur - s->tok); 133 134 return tmp; 135} 136 137static timelib_sll timelib_get_nr(char **ptr, int max_length) 138{ 139 char *begin, *end, *str; 140 timelib_sll tmp_nr = TIMELIB_UNSET; 141 int len = 0; 142 143 while ((**ptr < '0') || (**ptr > '9')) { 144 if (**ptr == '\0') { 145 return TIMELIB_UNSET; 146 } 147 ++*ptr; 148 } 149 begin = *ptr; 150 while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) { 151 ++*ptr; 152 ++len; 153 } 154 end = *ptr; 155 str = timelib_calloc(1, end - begin + 1); 156 memcpy(str, begin, end - begin); 157 tmp_nr = strtoll(str, NULL, 10); 158 timelib_free(str); 159 return tmp_nr; 160} 161 162static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length) 163{ 164 timelib_ull dir = 1; 165 166 while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { 167 if (**ptr == '\0') { 168 return TIMELIB_UNSET; 169 } 170 ++*ptr; 171 } 172 173 while (**ptr == '+' || **ptr == '-') 174 { 175 if (**ptr == '-') { 176 dir *= -1; 177 } 178 ++*ptr; 179 } 180 return dir * timelib_get_nr(ptr, max_length); 181} 182 183static void timelib_eat_spaces(char **ptr) 184{ 185 while (**ptr == ' ' || **ptr == '\t') { 186 ++*ptr; 187 } 188} 189 190static void timelib_eat_until_separator(char **ptr) 191{ 192 while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) { 193 ++*ptr; 194 } 195} 196 197static timelib_long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb) 198{ 199 timelib_long retval = 0; 200 201 *tz_not_found = 0; 202 203 while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { 204 ++*ptr; 205 } 206 if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { 207 *ptr += 3; 208 } 209 if (**ptr == '+') { 210 ++*ptr; 211 t->is_localtime = 1; 212 t->zone_type = TIMELIB_ZONETYPE_OFFSET; 213 *tz_not_found = 0; 214 t->dst = 0; 215 216 retval = -1 * timelib_parse_tz_cor(ptr); 217 } else if (**ptr == '-') { 218 ++*ptr; 219 t->is_localtime = 1; 220 t->zone_type = TIMELIB_ZONETYPE_OFFSET; 221 *tz_not_found = 0; 222 t->dst = 0; 223 224 retval = timelib_parse_tz_cor(ptr); 225 } 226 while (**ptr == ')') { 227 ++*ptr; 228 } 229 return retval; 230} 231 232#define timelib_split_free(arg) { \ 233 int i; \ 234 for (i = 0; i < arg.c; i++) { \ 235 timelib_free(arg.v[i]); \ 236 } \ 237 if (arg.v) { \ 238 timelib_free(arg.v); \ 239 } \ 240} 241 242/* date parser's scan function too large for VC6 - VC7.x 243 drop the optimization solves the problem */ 244#ifdef PHP_WIN32 245#pragma optimize( "", off ) 246#endif 247static int scan(Scanner *s) 248{ 249 uchar *cursor = s->cur; 250 char *str, *ptr = NULL; 251 252std: 253 s->tok = cursor; 254 s->len = 0; 255/*!re2c 256 257/* */ 258any = [\000-\377]; 259number = [0-9]+; 260 261hour24lz = [01][0-9] | "2"[0-4]; 262minutelz = [0-5][0-9]; 263monthlz = "0" [1-9] | "1" [0-2]; 264monthlzz = "0" [0-9] | "1" [0-2]; 265daylz = "0" [1-9] | [1-2][0-9] | "3" [01]; 266daylzz = "0" [0-9] | [1-2][0-9] | "3" [01]; 267secondlz = minutelz; 268year4 = [0-9]{4}; 269weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3]; 270 271space = [ \t]+; 272datetimebasic = year4 monthlz daylz "T" hour24lz minutelz secondlz "Z"; 273datetimeextended = year4 "-" monthlz "-" daylz "T" hour24lz ':' minutelz ':' secondlz "Z"; 274period = "P" (number "Y")? (number "M")? (number "W")? (number "D")? ("T" (number "H")? (number "M")? (number "S")?)?; 275combinedrep = "P" year4 "-" monthlzz "-" daylzz "T" hour24lz ':' minutelz ':' secondlz; 276 277recurrences = "R" number; 278 279isoweekday = year4 "-"? "W" weekofyear "-"? [0-7]; 280isoweek = year4 "-"? "W" weekofyear; 281 282*/ 283 284/*!re2c 285 /* so that vim highlights correctly */ 286 recurrences 287 { 288 DEBUG_OUTPUT("recurrences"); 289 TIMELIB_INIT; 290 ptr++; 291 s->recurrences = timelib_get_unsigned_nr((char **) &ptr, 9); 292 TIMELIB_DEINIT; 293 s->have_recurrences = 1; 294 return TIMELIB_PERIOD; 295 } 296 297 datetimebasic| datetimeextended 298 { 299 timelib_time *current; 300 301 if (s->have_date || s->have_period) { 302 current = s->end; 303 s->have_end_date = 1; 304 } else { 305 current = s->begin; 306 s->have_begin_date = 1; 307 } 308 DEBUG_OUTPUT("datetimebasic | datetimeextended"); 309 TIMELIB_INIT; 310 current->y = timelib_get_nr((char **) &ptr, 4); 311 current->m = timelib_get_nr((char **) &ptr, 2); 312 current->d = timelib_get_nr((char **) &ptr, 2); 313 current->h = timelib_get_nr((char **) &ptr, 2); 314 current->i = timelib_get_nr((char **) &ptr, 2); 315 current->s = timelib_get_nr((char **) &ptr, 2); 316 s->have_date = 1; 317 TIMELIB_DEINIT; 318 return TIMELIB_ISO_DATE; 319 } 320 321 period 322 { 323 timelib_sll nr; 324 int in_time = 0; 325 DEBUG_OUTPUT("period"); 326 TIMELIB_INIT; 327 ptr++; 328 do { 329 if ( *ptr == 'T' ) { 330 in_time = 1; 331 ptr++; 332 } 333 if ( *ptr == '\0' ) { 334 add_error(s, "Missing expected time part"); 335 break; 336 } 337 338 nr = timelib_get_unsigned_nr((char **) &ptr, 12); 339 switch (*ptr) { 340 case 'Y': s->period->y = nr; break; 341 case 'W': s->period->d = nr * 7; break; 342 case 'D': s->period->d = nr; break; 343 case 'H': s->period->h = nr; break; 344 case 'S': s->period->s = nr; break; 345 case 'M': 346 if (in_time) { 347 s->period->i = nr; 348 } else { 349 s->period->m = nr; 350 } 351 break; 352 default: 353 add_error(s, "Undefined period specifier"); 354 break; 355 } 356 ptr++; 357 } while (!s->errors->error_count && *ptr); 358 s->have_period = 1; 359 TIMELIB_DEINIT; 360 return TIMELIB_PERIOD; 361 } 362 363 combinedrep 364 { 365 DEBUG_OUTPUT("combinedrep"); 366 TIMELIB_INIT; 367 s->period->y = timelib_get_unsigned_nr((char **) &ptr, 4); 368 ptr++; 369 s->period->m = timelib_get_unsigned_nr((char **) &ptr, 2); 370 ptr++; 371 s->period->d = timelib_get_unsigned_nr((char **) &ptr, 2); 372 ptr++; 373 s->period->h = timelib_get_unsigned_nr((char **) &ptr, 2); 374 ptr++; 375 s->period->i = timelib_get_unsigned_nr((char **) &ptr, 2); 376 ptr++; 377 s->period->s = timelib_get_unsigned_nr((char **) &ptr, 2); 378 s->have_period = 1; 379 TIMELIB_DEINIT; 380 return TIMELIB_PERIOD; 381 } 382 383 [ .,\t/] 384 { 385 goto std; 386 } 387 388 "\000"|"\n" 389 { 390 s->pos = cursor; s->line++; 391 goto std; 392 } 393 394 any 395 { 396 add_error(s, "Unexpected character"); 397 goto std; 398 } 399*/ 400} 401#ifdef PHP_WIN32 402#pragma optimize( "", on ) 403#endif 404 405/*!max:re2c */ 406 407void timelib_strtointerval(char *s, size_t len, 408 timelib_time **begin, timelib_time **end, 409 timelib_rel_time **period, int *recurrences, 410 struct timelib_error_container **errors) 411{ 412 Scanner in; 413 int t; 414 char *e = s + len - 1; 415 416 memset(&in, 0, sizeof(in)); 417 in.errors = timelib_malloc(sizeof(struct timelib_error_container)); 418 in.errors->warning_count = 0; 419 in.errors->warning_messages = NULL; 420 in.errors->error_count = 0; 421 in.errors->error_messages = NULL; 422 423 if (len > 0) { 424 while (isspace(*s) && s < e) { 425 s++; 426 } 427 while (isspace(*e) && e > s) { 428 e--; 429 } 430 } 431 if (e - s < 0) { 432 add_error(&in, "Empty string"); 433 if (errors) { 434 *errors = in.errors; 435 } else { 436 timelib_error_container_dtor(in.errors); 437 } 438 return; 439 } 440 e++; 441 442 /* init cursor */ 443 in.str = timelib_malloc((e - s) + YYMAXFILL); 444 memset(in.str, 0, (e - s) + YYMAXFILL); 445 memcpy(in.str, s, (e - s)); 446 in.lim = in.str + (e - s) + YYMAXFILL; 447 in.cur = in.str; 448 449 /* init value containers */ 450 in.begin = timelib_time_ctor(); 451 in.begin->y = TIMELIB_UNSET; 452 in.begin->d = TIMELIB_UNSET; 453 in.begin->m = TIMELIB_UNSET; 454 in.begin->h = TIMELIB_UNSET; 455 in.begin->i = TIMELIB_UNSET; 456 in.begin->s = TIMELIB_UNSET; 457 in.begin->f = 0; 458 in.begin->z = 0; 459 in.begin->dst = 0; 460 in.begin->is_localtime = 0; 461 in.begin->zone_type = TIMELIB_ZONETYPE_OFFSET; 462 463 in.end = timelib_time_ctor(); 464 in.end->y = TIMELIB_UNSET; 465 in.end->d = TIMELIB_UNSET; 466 in.end->m = TIMELIB_UNSET; 467 in.end->h = TIMELIB_UNSET; 468 in.end->i = TIMELIB_UNSET; 469 in.end->s = TIMELIB_UNSET; 470 in.end->f = 0; 471 in.end->z = 0; 472 in.end->dst = 0; 473 in.end->is_localtime = 0; 474 in.end->zone_type = TIMELIB_ZONETYPE_OFFSET; 475 476 in.period = timelib_rel_time_ctor(); 477 in.period->y = 0; 478 in.period->d = 0; 479 in.period->m = 0; 480 in.period->h = 0; 481 in.period->i = 0; 482 in.period->s = 0; 483 in.period->weekday = 0; 484 in.period->weekday_behavior = 0; 485 in.period->first_last_day_of = 0; 486 in.period->days = TIMELIB_UNSET; 487 488 in.recurrences = 1; 489 490 do { 491 t = scan(&in); 492#ifdef DEBUG_PARSER 493 printf("%d\n", t); 494#endif 495 } while(t != EOI); 496 497 timelib_free(in.str); 498 if (errors) { 499 *errors = in.errors; 500 } else { 501 timelib_error_container_dtor(in.errors); 502 } 503 if (in.have_begin_date) { 504 *begin = in.begin; 505 } else { 506 timelib_time_dtor(in.begin); 507 } 508 if (in.have_end_date) { 509 *end = in.end; 510 } else { 511 timelib_time_dtor(in.end); 512 } 513 if (in.have_period) { 514 *period = in.period; 515 } else { 516 timelib_rel_time_dtor(in.period); 517 } 518 if (in.have_recurrences) { 519 *recurrences = in.recurrences; 520 } 521} 522 523 524/* 525 * vim: syntax=c 526 */ 527