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