1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Derick Rethans <derick@derickrethans.nl> | 16 +----------------------------------------------------------------------+ 17 */ 18 19/* $Id$ */ 20 21#include "timelib.h" 22 23#include <stdio.h> 24#include <ctype.h> 25 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 105#define HOUR(a) (int)(a * 60) 106 107static void add_warning(Scanner *s, char *error) 108{ 109 s->errors->warning_count++; 110 s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message)); 111 s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0; 112 s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0; 113 s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error); 114} 115 116static void add_error(Scanner *s, char *error) 117{ 118 s->errors->error_count++; 119 s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message)); 120 s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0; 121 s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0; 122 s->errors->error_messages[s->errors->error_count - 1].message = strdup(error); 123} 124 125static char *timelib_string(Scanner *s) 126{ 127 char *tmp = calloc(1, s->cur - s->tok + 1); 128 memcpy(tmp, s->tok, s->cur - s->tok); 129 130 return tmp; 131} 132 133static timelib_sll timelib_get_nr(char **ptr, int max_length) 134{ 135 char *begin, *end, *str; 136 timelib_sll tmp_nr = TIMELIB_UNSET; 137 int len = 0; 138 139 while ((**ptr < '0') || (**ptr > '9')) { 140 if (**ptr == '\0') { 141 return TIMELIB_UNSET; 142 } 143 ++*ptr; 144 } 145 begin = *ptr; 146 while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) { 147 ++*ptr; 148 ++len; 149 } 150 end = *ptr; 151 str = calloc(1, end - begin + 1); 152 memcpy(str, begin, end - begin); 153 tmp_nr = strtoll(str, NULL, 10); 154 free(str); 155 return tmp_nr; 156} 157 158static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length) 159{ 160 timelib_ull dir = 1; 161 162 while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { 163 if (**ptr == '\0') { 164 return TIMELIB_UNSET; 165 } 166 ++*ptr; 167 } 168 169 while (**ptr == '+' || **ptr == '-') 170 { 171 if (**ptr == '-') { 172 dir *= -1; 173 } 174 ++*ptr; 175 } 176 return dir * timelib_get_nr(ptr, max_length); 177} 178 179static long timelib_parse_tz_cor(char **ptr) 180{ 181 char *begin = *ptr, *end; 182 long tmp; 183 184 while (isdigit(**ptr) || **ptr == ':') { 185 ++*ptr; 186 } 187 end = *ptr; 188 switch (end - begin) { 189 case 1: 190 case 2: 191 return HOUR(strtol(begin, NULL, 10)); 192 break; 193 case 3: 194 case 4: 195 if (begin[1] == ':') { 196 tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 2, NULL, 10); 197 return tmp; 198 } else if (begin[2] == ':') { 199 tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10); 200 return tmp; 201 } else { 202 tmp = strtol(begin, NULL, 10); 203 return HOUR(tmp / 100) + tmp % 100; 204 } 205 case 5: 206 tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10); 207 return tmp; 208 } 209 return 0; 210} 211 212static void timelib_eat_spaces(char **ptr) 213{ 214 while (**ptr == ' ' || **ptr == '\t') { 215 ++*ptr; 216 } 217} 218 219static void timelib_eat_until_separator(char **ptr) 220{ 221 while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) { 222 ++*ptr; 223 } 224} 225 226static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb) 227{ 228 long retval = 0; 229 230 *tz_not_found = 0; 231 232 while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { 233 ++*ptr; 234 } 235 if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { 236 *ptr += 3; 237 } 238 if (**ptr == '+') { 239 ++*ptr; 240 t->is_localtime = 1; 241 t->zone_type = TIMELIB_ZONETYPE_OFFSET; 242 *tz_not_found = 0; 243 t->dst = 0; 244 245 retval = -1 * timelib_parse_tz_cor(ptr); 246 } else if (**ptr == '-') { 247 ++*ptr; 248 t->is_localtime = 1; 249 t->zone_type = TIMELIB_ZONETYPE_OFFSET; 250 *tz_not_found = 0; 251 t->dst = 0; 252 253 retval = timelib_parse_tz_cor(ptr); 254 } 255 while (**ptr == ')') { 256 ++*ptr; 257 } 258 return retval; 259} 260 261#define timelib_split_free(arg) { \ 262 int i; \ 263 for (i = 0; i < arg.c; i++) { \ 264 free(arg.v[i]); \ 265 } \ 266 if (arg.v) { \ 267 free(arg.v); \ 268 } \ 269} 270 271/* date parser's scan function too large for VC6 - VC7.x 272 drop the optimization solves the problem */ 273#ifdef PHP_WIN32 274#pragma optimize( "", off ) 275#endif 276static int scan(Scanner *s) 277{ 278 uchar *cursor = s->cur; 279 char *str, *ptr = NULL; 280 281std: 282 s->tok = cursor; 283 s->len = 0; 284/*!re2c 285 286/* */ 287any = [\000-\377]; 288number = [0-9]+; 289 290hour24lz = [01][0-9] | "2"[0-4]; 291minutelz = [0-5][0-9]; 292monthlz = "0" [1-9] | "1" [0-2]; 293monthlzz = "0" [0-9] | "1" [0-2]; 294daylz = "0" [1-9] | [1-2][0-9] | "3" [01]; 295daylzz = "0" [0-9] | [1-2][0-9] | "3" [01]; 296secondlz = minutelz; 297year4 = [0-9]{4}; 298weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3]; 299 300space = [ \t]+; 301datetimebasic = year4 monthlz daylz "T" hour24lz minutelz secondlz "Z"; 302datetimeextended = year4 "-" monthlz "-" daylz "T" hour24lz ':' minutelz ':' secondlz "Z"; 303period = "P" (number "Y")? (number "M")? (number "W")? (number "D")? ("T" (number "H")? (number "M")? (number "S")?)?; 304combinedrep = "P" year4 "-" monthlzz "-" daylzz "T" hour24lz ':' minutelz ':' secondlz; 305 306recurrences = "R" number; 307 308isoweekday = year4 "-"? "W" weekofyear "-"? [0-7]; 309isoweek = year4 "-"? "W" weekofyear; 310 311*/ 312 313/*!re2c 314 /* so that vim highlights correctly */ 315 recurrences 316 { 317 DEBUG_OUTPUT("recurrences"); 318 TIMELIB_INIT; 319 ptr++; 320 s->recurrences = timelib_get_unsigned_nr((char **) &ptr, 9); 321 TIMELIB_DEINIT; 322 s->have_recurrences = 1; 323 return TIMELIB_PERIOD; 324 } 325 326 datetimebasic| datetimeextended 327 { 328 timelib_time *current; 329 330 if (s->have_date || s->have_period) { 331 current = s->end; 332 s->have_end_date = 1; 333 } else { 334 current = s->begin; 335 s->have_begin_date = 1; 336 } 337 DEBUG_OUTPUT("datetimebasic | datetimeextended"); 338 TIMELIB_INIT; 339 current->y = timelib_get_nr((char **) &ptr, 4); 340 current->m = timelib_get_nr((char **) &ptr, 2); 341 current->d = timelib_get_nr((char **) &ptr, 2); 342 current->h = timelib_get_nr((char **) &ptr, 2); 343 current->i = timelib_get_nr((char **) &ptr, 2); 344 current->s = timelib_get_nr((char **) &ptr, 2); 345 s->have_date = 1; 346 TIMELIB_DEINIT; 347 return TIMELIB_ISO_DATE; 348 } 349 350 period 351 { 352 timelib_sll nr; 353 int in_time = 0; 354 DEBUG_OUTPUT("period"); 355 TIMELIB_INIT; 356 ptr++; 357 do { 358 if ( *ptr == 'T' ) { 359 in_time = 1; 360 ptr++; 361 } 362 if ( *ptr == '\0' ) { 363 add_error(s, "Missing expected time part"); 364 break; 365 } 366 367 nr = timelib_get_unsigned_nr((char **) &ptr, 12); 368 switch (*ptr) { 369 case 'Y': s->period->y = nr; break; 370 case 'W': s->period->d = nr * 7; break; 371 case 'D': s->period->d = nr; break; 372 case 'H': s->period->h = nr; break; 373 case 'S': s->period->s = nr; break; 374 case 'M': 375 if (in_time) { 376 s->period->i = nr; 377 } else { 378 s->period->m = nr; 379 } 380 break; 381 default: 382 add_error(s, "Undefined period specifier"); 383 break; 384 } 385 ptr++; 386 } while (!s->errors->error_count && *ptr); 387 s->have_period = 1; 388 TIMELIB_DEINIT; 389 return TIMELIB_PERIOD; 390 } 391 392 combinedrep 393 { 394 DEBUG_OUTPUT("combinedrep"); 395 TIMELIB_INIT; 396 s->period->y = timelib_get_unsigned_nr((char **) &ptr, 4); 397 ptr++; 398 s->period->m = timelib_get_unsigned_nr((char **) &ptr, 2); 399 ptr++; 400 s->period->d = timelib_get_unsigned_nr((char **) &ptr, 2); 401 ptr++; 402 s->period->h = timelib_get_unsigned_nr((char **) &ptr, 2); 403 ptr++; 404 s->period->i = timelib_get_unsigned_nr((char **) &ptr, 2); 405 ptr++; 406 s->period->s = timelib_get_unsigned_nr((char **) &ptr, 2); 407 s->have_period = 1; 408 TIMELIB_DEINIT; 409 return TIMELIB_PERIOD; 410 } 411 412 [ .,\t/] 413 { 414 goto std; 415 } 416 417 "\000"|"\n" 418 { 419 s->pos = cursor; s->line++; 420 goto std; 421 } 422 423 any 424 { 425 add_error(s, "Unexpected character"); 426 goto std; 427 } 428*/ 429} 430#ifdef PHP_WIN32 431#pragma optimize( "", on ) 432#endif 433 434/*!max:re2c */ 435 436void timelib_strtointerval(char *s, int len, 437 timelib_time **begin, timelib_time **end, 438 timelib_rel_time **period, int *recurrences, 439 struct timelib_error_container **errors) 440{ 441 Scanner in; 442 int t; 443 char *e = s + len - 1; 444 445 memset(&in, 0, sizeof(in)); 446 in.errors = malloc(sizeof(struct timelib_error_container)); 447 in.errors->warning_count = 0; 448 in.errors->warning_messages = NULL; 449 in.errors->error_count = 0; 450 in.errors->error_messages = NULL; 451 452 if (len > 0) { 453 while (isspace(*s) && s < e) { 454 s++; 455 } 456 while (isspace(*e) && e > s) { 457 e--; 458 } 459 } 460 if (e - s < 0) { 461 add_error(&in, "Empty string"); 462 if (errors) { 463 *errors = in.errors; 464 } else { 465 timelib_error_container_dtor(in.errors); 466 } 467 return; 468 } 469 e++; 470 471 /* init cursor */ 472 in.str = malloc((e - s) + YYMAXFILL); 473 memset(in.str, 0, (e - s) + YYMAXFILL); 474 memcpy(in.str, s, (e - s)); 475 in.lim = in.str + (e - s) + YYMAXFILL; 476 in.cur = in.str; 477 478 /* init value containers */ 479 in.begin = timelib_time_ctor(); 480 in.begin->y = TIMELIB_UNSET; 481 in.begin->d = TIMELIB_UNSET; 482 in.begin->m = TIMELIB_UNSET; 483 in.begin->h = TIMELIB_UNSET; 484 in.begin->i = TIMELIB_UNSET; 485 in.begin->s = TIMELIB_UNSET; 486 in.begin->f = 0; 487 in.begin->z = 0; 488 in.begin->dst = 0; 489 in.begin->is_localtime = 0; 490 in.begin->zone_type = TIMELIB_ZONETYPE_OFFSET; 491 492 in.end = timelib_time_ctor(); 493 in.end->y = TIMELIB_UNSET; 494 in.end->d = TIMELIB_UNSET; 495 in.end->m = TIMELIB_UNSET; 496 in.end->h = TIMELIB_UNSET; 497 in.end->i = TIMELIB_UNSET; 498 in.end->s = TIMELIB_UNSET; 499 in.end->f = 0; 500 in.end->z = 0; 501 in.end->dst = 0; 502 in.end->is_localtime = 0; 503 in.end->zone_type = TIMELIB_ZONETYPE_OFFSET; 504 505 in.period = timelib_rel_time_ctor(); 506 in.period->y = 0; 507 in.period->d = 0; 508 in.period->m = 0; 509 in.period->h = 0; 510 in.period->i = 0; 511 in.period->s = 0; 512 in.period->weekday = 0; 513 in.period->weekday_behavior = 0; 514 in.period->first_last_day_of = 0; 515 in.period->days = TIMELIB_UNSET; 516 517 in.recurrences = 1; 518 519 do { 520 t = scan(&in); 521#ifdef DEBUG_PARSER 522 printf("%d\n", t); 523#endif 524 } while(t != EOI); 525 526 free(in.str); 527 if (errors) { 528 *errors = in.errors; 529 } else { 530 timelib_error_container_dtor(in.errors); 531 } 532 if (in.have_begin_date) { 533 *begin = in.begin; 534 } else { 535 timelib_time_dtor(in.begin); 536 } 537 if (in.have_end_date) { 538 *end = in.end; 539 } else { 540 timelib_time_dtor(in.end); 541 } 542 if (in.have_period) { 543 *period = in.period; 544 } else { 545 timelib_rel_time_dtor(in.period); 546 } 547 if (in.have_recurrences) { 548 *recurrences = in.recurrences; 549 } 550} 551 552 553/* 554 * vim: syntax=c 555 */ 556