parse_iso_intervals.re 10 KB

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