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