1 /*
2 * Copyright 2015, alex at staticlibs.net
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * File: CronExprParser.cpp
19 * Author: alex
20 *
21 * Created on February 24, 2015, 9:35 AM
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <string.h>
30 #include <math.h>
31
32 #include "mega/mega_ccronexpr.h"
33
34 #define CRON_MAX_SECONDS 60
35 #define CRON_MAX_MINUTES 60
36 #define CRON_MAX_HOURS 24
37 #define CRON_MAX_DAYS_OF_WEEK 8
38 #define CRON_MAX_DAYS_OF_MONTH 32
39 #define CRON_MAX_MONTHS 12
40
41 #define CRON_CF_SECOND 0
42 #define CRON_CF_MINUTE 1
43 #define CRON_CF_HOUR_OF_DAY 2
44 #define CRON_CF_DAY_OF_WEEK 3
45 #define CRON_CF_DAY_OF_MONTH 4
46 #define CRON_CF_MONTH 5
47 #define CRON_CF_YEAR 6
48
49 #define CRON_CF_ARR_LEN 7
50
51 #define CRON_INVALID_INSTANT ((time_t) -1)
52
53 static const char* DAYS_ARR[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
54 #define CRON_DAYS_ARR_LEN 7
55 static const char* MONTHS_ARR[] = { "FOO", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
56 #define CRON_MONTHS_ARR_LEN 13
57
58 #define CRON_MAX_STR_LEN_TO_SPLIT 256
59 #define CRON_MAX_NUM_TO_SRING 1000000000
60 /* computes number of digits in decimal number */
61 #define CRON_NUM_OF_DIGITS(num) (abs(num) < 10 ? 1 : \
62 (abs(num) < 100 ? 2 : \
63 (abs(num) < 1000 ? 3 : \
64 (abs(num) < 10000 ? 4 : \
65 (abs(num) < 100000 ? 5 : \
66 (abs(num) < 1000000 ? 6 : \
67 (abs(num) < 10000000 ? 7 : \
68 (abs(num) < 100000000 ? 8 : \
69 (abs(num) < 1000000000 ? 9 : 10)))))))))
70
71 #ifndef _WIN32
72 struct tm *gmtime_r(const time_t *timep, struct tm *result);
73 struct tm *localtime_r(const time_t *timep, struct tm *result);
74 #endif
75
76 /* Defining 'cron_mktime' to use use UTC (default) or local time */
77 #ifndef CRON_USE_LOCAL_TIME
78
79 /* http://stackoverflow.com/a/22557778 */
80 #ifdef _WIN32
cron_mktime(struct tm * tm)81 time_t cron_mktime(struct tm* tm) {
82 return _mkgmtime(tm);
83 }
84 #else /* _WIN32 */
85 #ifndef ANDROID
86 /* can be hidden in time.h */
87 time_t timegm(struct tm* __tp);
88 #endif /* ANDROID */
cron_mktime(struct tm * tm)89 time_t cron_mktime(struct tm* tm) {
90 #if !defined(ANDROID) || defined(__LP64__)
91 return timegm(tm);
92 #else /* ANDROID */
93 /* https://github.com/adobe/chromium/blob/cfe5bf0b51b1f6b9fe239c2a3c2f2364da9967d7/base/os_compat_android.cc#L20 */
94 static const time_t kTimeMax = ~(1L << (sizeof (time_t) * CHAR_BIT - 1));
95 static const time_t kTimeMin = (1L << (sizeof (time_t) * CHAR_BIT - 1));
96 time64_t result = timegm64(tm);
97 if (result < kTimeMin || result > kTimeMax) return -1;
98 return result;
99 #endif /* ANDROID */
100 }
101 #endif /* _WIN32 */
102
103
104 #ifndef CRON_TEST_MALLOC
105 #define cronFree(x) free(x);
106 #define cronMalloc(x) malloc(x);
107 #else
108 void* cronMalloc(size_t n);
109 void cronFree(void* p);
110 #endif
111
cron_time(time_t * date,struct tm * out)112 struct tm* cron_time(time_t* date, struct tm* out) {
113 #ifdef __MINGW32__
114 (void)(out); /* To avoid unused warning */
115 return gmtime(date);
116 #else /* __MINGW32__ */
117 #ifdef _WIN32
118 errno_t err = gmtime_s(out, date);
119 return 0 == err ? out : NULL;
120 #else /* _WIN32 */
121 return gmtime_r(date, out);
122 #endif /* _WIN32 */
123 #endif /* __MINGW32__ */
124 }
125
126 #else /* CRON_USE_LOCAL_TIME */
127
cron_mktime(struct tm * tm)128 time_t cron_mktime(struct tm* tm) {
129 return mktime(tm);
130 }
131
cron_time(time_t * date,struct tm * out)132 struct tm* cron_time(time_t* date, struct tm* out) {
133 #ifdef _WIN32
134 errno_t err = localtime_s(out, date);
135 return 0 == err ? out : NULL;
136 #else /* _WIN32 */
137 return localtime_r(date, out);
138 #endif /* _WIN32 */
139 }
140
141 #endif /* CRON_USE_LOCAL_TIME */
142
cron_set_bit(uint8_t * rbyte,int idx)143 void cron_set_bit(uint8_t* rbyte, int idx) {
144 uint8_t j = (uint8_t) (idx / 8);
145 uint8_t k = (uint8_t) (idx % 8);
146
147 rbyte[j] |= (1 << k);
148 }
149
cron_del_bit(uint8_t * rbyte,int idx)150 void cron_del_bit(uint8_t* rbyte, int idx) {
151 uint8_t j = (uint8_t) (idx / 8);
152 uint8_t k = (uint8_t) (idx % 8);
153
154 rbyte[j] &= ~(1 << k);
155 }
156
cron_get_bit(const uint8_t * rbyte,int idx)157 uint8_t cron_get_bit(const uint8_t* rbyte, int idx) {
158 uint8_t j = (uint8_t) (idx / 8);
159 uint8_t k = (uint8_t) (idx % 8);
160
161 if (rbyte[j] & (1 << k)) {
162 return 1;
163 } else {
164 return 0;
165 }
166 }
167
free_splitted(char ** splitted,size_t len)168 static void free_splitted(char** splitted, size_t len) {
169 size_t i;
170 if (!splitted) return;
171 for (i = 0; i < len; i++) {
172 if (splitted[i]) {
173 cronFree(splitted[i]);
174 }
175 }
176 cronFree(splitted);
177 }
178
strdupl(const char * str,size_t len)179 static char* strdupl(const char* str, size_t len) {
180 if (!str) return NULL;
181 char* res = (char*) cronMalloc(len + 1);
182 if (!res) return NULL;
183 memset(res, 0, len + 1);
184 memcpy(res, str, len);
185 return res;
186 }
187
next_set_bit(const uint8_t * bits,unsigned int max,unsigned int from_index,int * notfound)188 static unsigned int next_set_bit(const uint8_t* bits, unsigned int max, unsigned int from_index, int* notfound) {
189 unsigned int i;
190 if (!bits) {
191 *notfound = 1;
192 return 0;
193 }
194 for (i = from_index; i < max; i++) {
195 if (cron_get_bit(bits, i)) return i;
196 }
197 *notfound = 1;
198 return 0;
199 }
200
push_to_fields_arr(int * arr,int fi)201 static void push_to_fields_arr(int* arr, int fi) {
202 int i;
203 if (!arr || -1 == fi) {
204 return;
205 }
206 for (i = 0; i < CRON_CF_ARR_LEN; i++) {
207 if (arr[i] == fi) return;
208 }
209 for (i = 0; i < CRON_CF_ARR_LEN; i++) {
210 if (-1 == arr[i]) {
211 arr[i] = fi;
212 return;
213 }
214 }
215 }
216
add_to_field(struct tm * calendar,int field,int val)217 static int add_to_field(struct tm* calendar, int field, int val) {
218 if (!calendar || -1 == field) {
219 return 1;
220 }
221 switch (field) {
222 case CRON_CF_SECOND:
223 calendar->tm_sec = calendar->tm_sec + val;
224 break;
225 case CRON_CF_MINUTE:
226 calendar->tm_min = calendar->tm_min + val;
227 break;
228 case CRON_CF_HOUR_OF_DAY:
229 calendar->tm_hour = calendar->tm_hour + val;
230 break;
231 case CRON_CF_DAY_OF_WEEK: /* mkgmtime ignores this field */
232 case CRON_CF_DAY_OF_MONTH:
233 calendar->tm_mday = calendar->tm_mday + val;
234 break;
235 case CRON_CF_MONTH:
236 calendar->tm_mon = calendar->tm_mon + val;
237 break;
238 case CRON_CF_YEAR:
239 calendar->tm_year = calendar->tm_year + val;
240 break;
241 default:
242 return 1; /* unknown field */
243 }
244 time_t res = cron_mktime(calendar);
245 if (CRON_INVALID_INSTANT == res) {
246 return 1;
247 }
248 return 0;
249 }
250
251 /**
252 * Reset the calendar setting all the fields provided to zero.
253 */
reset(struct tm * calendar,int field)254 static int reset(struct tm* calendar, int field) {
255 if (!calendar || -1 == field) {
256 return 1;
257 }
258 switch (field) {
259 case CRON_CF_SECOND:
260 calendar->tm_sec = 0;
261 break;
262 case CRON_CF_MINUTE:
263 calendar->tm_min = 0;
264 break;
265 case CRON_CF_HOUR_OF_DAY:
266 calendar->tm_hour = 0;
267 break;
268 case CRON_CF_DAY_OF_WEEK:
269 calendar->tm_wday = 0;
270 break;
271 case CRON_CF_DAY_OF_MONTH:
272 calendar->tm_mday = 1;
273 break;
274 case CRON_CF_MONTH:
275 calendar->tm_mon = 0;
276 break;
277 case CRON_CF_YEAR:
278 calendar->tm_year = 0;
279 break;
280 default:
281 return 1; /* unknown field */
282 }
283 time_t res = cron_mktime(calendar);
284 if (CRON_INVALID_INSTANT == res) {
285 return 1;
286 }
287 return 0;
288 }
289
reset_all(struct tm * calendar,int * fields)290 static int reset_all(struct tm* calendar, int* fields) {
291 int i;
292 int res = 0;
293 if (!calendar || !fields) {
294 return 1;
295 }
296 for (i = 0; i < CRON_CF_ARR_LEN; i++) {
297 if (-1 != fields[i]) {
298 res = reset(calendar, fields[i]);
299 if (0 != res) return res;
300 }
301 }
302 return 0;
303 }
304
set_field(struct tm * calendar,int field,int val)305 static int set_field(struct tm* calendar, int field, int val) {
306 if (!calendar || -1 == field) {
307 return 1;
308 }
309 switch (field) {
310 case CRON_CF_SECOND:
311 calendar->tm_sec = val;
312 break;
313 case CRON_CF_MINUTE:
314 calendar->tm_min = val;
315 break;
316 case CRON_CF_HOUR_OF_DAY:
317 calendar->tm_hour = val;
318 break;
319 case CRON_CF_DAY_OF_WEEK:
320 calendar->tm_wday = val;
321 break;
322 case CRON_CF_DAY_OF_MONTH:
323 calendar->tm_mday = val;
324 break;
325 case CRON_CF_MONTH:
326 calendar->tm_mon = val;
327 break;
328 case CRON_CF_YEAR:
329 calendar->tm_year = val;
330 break;
331 default:
332 return 1; /* unknown field */
333 }
334 time_t res = cron_mktime(calendar);
335 if (CRON_INVALID_INSTANT == res) {
336 return 1;
337 }
338 return 0;
339 }
340
341 /**
342 * Search the bits provided for the next set bit after the value provided,
343 * and reset the calendar.
344 */
find_next(const uint8_t * bits,unsigned int max,unsigned int value,struct tm * calendar,unsigned int field,unsigned int nextField,int * lower_orders,int * res_out)345 static unsigned int find_next(const uint8_t* bits, unsigned int max, unsigned int value, struct tm* calendar, unsigned int field, unsigned int nextField, int* lower_orders, int* res_out) {
346 int notfound = 0;
347 int err = 0;
348 unsigned int next_value = next_set_bit(bits, max, value, ¬found);
349 /* roll over if needed */
350 if (notfound) {
351 err = add_to_field(calendar, nextField, 1);
352 if (err) goto return_error;
353 err = reset(calendar, field);
354 if (err) goto return_error;
355 notfound = 0;
356 next_value = next_set_bit(bits, max, 0, ¬found);
357 }
358 if (notfound || next_value != value) {
359 err = set_field(calendar, field, next_value);
360 if (err) goto return_error;
361 err = reset_all(calendar, lower_orders);
362 if (err) goto return_error;
363 }
364 return next_value;
365
366 return_error:
367 *res_out = 1;
368 return 0;
369 }
370
find_next_day(struct tm * calendar,const uint8_t * days_of_month,unsigned int day_of_month,const uint8_t * days_of_week,unsigned int day_of_week,int * resets,int * res_out)371 static unsigned int find_next_day(struct tm* calendar, const uint8_t* days_of_month, unsigned int day_of_month, const uint8_t* days_of_week, unsigned int day_of_week, int* resets, int* res_out) {
372 int err;
373 unsigned int count = 0;
374 unsigned int max = 366;
375 while ((!cron_get_bit(days_of_month, day_of_month) || !cron_get_bit(days_of_week, day_of_week)) && count++ < max) {
376 err = add_to_field(calendar, CRON_CF_DAY_OF_MONTH, 1);
377
378 if (err) goto return_error;
379 day_of_month = calendar->tm_mday;
380 day_of_week = calendar->tm_wday;
381 reset_all(calendar, resets);
382 }
383 return day_of_month;
384
385 return_error:
386 *res_out = 1;
387 return 0;
388 }
389
do_next(const cron_expr * expr,struct tm * calendar,unsigned int dot)390 static int do_next(const cron_expr* expr, struct tm* calendar, unsigned int dot) {
391 int i;
392 int res = 0;
393 int* resets = NULL;
394 int* empty_list = NULL;
395 unsigned int second = 0;
396 unsigned int update_second = 0;
397 unsigned int minute = 0;
398 unsigned int update_minute = 0;
399 unsigned int hour = 0;
400 unsigned int update_hour = 0;
401 unsigned int day_of_week = 0;
402 unsigned int day_of_month = 0;
403 unsigned int update_day_of_month = 0;
404 unsigned int month = 0;
405 unsigned int update_month = 0;
406
407 resets = (int*) cronMalloc(CRON_CF_ARR_LEN * sizeof(int));
408 if (!resets) goto return_result;
409 empty_list = (int*) cronMalloc(CRON_CF_ARR_LEN * sizeof(int));
410 if (!empty_list) goto return_result;
411 for (i = 0; i < CRON_CF_ARR_LEN; i++) {
412 resets[i] = -1;
413 empty_list[i] = -1;
414 }
415
416 second = calendar->tm_sec;
417 update_second = find_next(expr->seconds, CRON_MAX_SECONDS, second, calendar, CRON_CF_SECOND, CRON_CF_MINUTE, empty_list, &res);
418 if (0 != res) goto return_result;
419 if (second == update_second) {
420 push_to_fields_arr(resets, CRON_CF_SECOND);
421 }
422
423 minute = calendar->tm_min;
424 update_minute = find_next(expr->minutes, CRON_MAX_MINUTES, minute, calendar, CRON_CF_MINUTE, CRON_CF_HOUR_OF_DAY, resets, &res);
425 if (0 != res) goto return_result;
426 if (minute == update_minute) {
427 push_to_fields_arr(resets, CRON_CF_MINUTE);
428 } else {
429 res = do_next(expr, calendar, dot);
430 if (0 != res) goto return_result;
431 }
432
433 hour = calendar->tm_hour;
434 update_hour = find_next(expr->hours, CRON_MAX_HOURS, hour, calendar, CRON_CF_HOUR_OF_DAY, CRON_CF_DAY_OF_WEEK, resets, &res);
435 if (0 != res) goto return_result;
436 if (hour == update_hour) {
437 push_to_fields_arr(resets, CRON_CF_HOUR_OF_DAY);
438 } else {
439 res = do_next(expr, calendar, dot);
440 if (0 != res) goto return_result;
441 }
442
443 day_of_week = calendar->tm_wday;
444 day_of_month = calendar->tm_mday;
445 update_day_of_month = find_next_day(calendar, expr->days_of_month, day_of_month, expr->days_of_week, day_of_week, resets, &res);
446 if (0 != res) goto return_result;
447 if (day_of_month == update_day_of_month) {
448 push_to_fields_arr(resets, CRON_CF_DAY_OF_MONTH);
449 } else {
450 res = do_next(expr, calendar, dot);
451 if (0 != res) goto return_result;
452 }
453
454 month = calendar->tm_mon; /*day already adds one if no day in same month is found*/
455 update_month = find_next(expr->months, CRON_MAX_MONTHS, month, calendar, CRON_CF_MONTH, CRON_CF_YEAR, resets, &res);
456 if (0 != res) goto return_result;
457 if (month != update_month) {
458 if (calendar->tm_year - dot > 4) {
459 res = -1;
460 goto return_result;
461 }
462 res = do_next(expr, calendar, dot);
463 if (0 != res) goto return_result;
464 }
465 goto return_result;
466
467 return_result:
468 if (!resets || !empty_list) {
469 res = -1;
470 }
471 if (resets) {
472 cronFree(resets);
473 }
474 if (empty_list) {
475 cronFree(empty_list);
476 }
477 return res;
478 }
479
to_upper(char * str)480 static int to_upper(char* str) {
481 if (!str) return 1;
482 int i;
483 for (i = 0; '\0' != str[i]; i++) {
484 str[i] = (char) toupper(str[i]);
485 }
486 return 0;
487 }
488
to_string(int num)489 static char* to_string(int num) {
490 if (abs(num) >= CRON_MAX_NUM_TO_SRING) return NULL;
491 char* str = (char*) cronMalloc(CRON_NUM_OF_DIGITS(num) + 1);
492 if (!str) return NULL;
493 int res = sprintf(str, "%d", num);
494 if (res < 0) return NULL;
495 return str;
496 }
497
str_replace(char * orig,const char * rep,const char * with)498 static char* str_replace(char *orig, const char *rep, const char *with) {
499 char *result; /* the return string */
500 char *ins; /* the next insert point */
501 char *tmp; /* varies */
502 size_t len_rep; /* length of rep */
503 size_t len_with; /* length of with */
504 size_t len_front; /* distance between rep and end of last rep */
505 int count; /* number of replacements */
506 if (!orig) return NULL;
507 if (!rep) rep = "";
508 if (!with) with = "";
509 len_rep = strlen(rep);
510 len_with = strlen(with);
511
512 ins = orig;
513 for (count = 0; NULL != (tmp = strstr(ins, rep)); ++count) {
514 ins = tmp + len_rep;
515 }
516
517 /* first time through the loop, all the variable are set correctly
518 from here on,
519 tmp points to the end of the result string
520 ins points to the next occurrence of rep in orig
521 orig points to the remainder of orig after "end of rep"
522 */
523 tmp = result = (char*) cronMalloc(strlen(orig) + (len_with - len_rep) * count + 1);
524 if (!result) return NULL;
525
526 while (count--) {
527 ins = strstr(orig, rep);
528 len_front = ins - orig;
529 tmp = strncpy(tmp, orig, len_front) + len_front;
530 tmp = strcpy(tmp, with) + len_with;
531 orig += len_front + len_rep; /* move to next "end of rep" */
532 }
533 strcpy(tmp, orig);
534 return result;
535 }
536
parse_uint(const char * str,int * errcode)537 static unsigned int parse_uint(const char* str, int* errcode) {
538 char* endptr;
539 errno = 0;
540 long int l = strtol(str, &endptr, 0);
541 if (errno == ERANGE || *endptr != '\0' || l < 0 || l > INT_MAX) {
542 *errcode = 1;
543 return 0;
544 } else {
545 *errcode = 0;
546 return (unsigned int) l;
547 }
548 }
549
split_str(const char * str,char del,size_t * len_out)550 static char** split_str(const char* str, char del, size_t* len_out) {
551 size_t i;
552 size_t stlen = 0;
553 size_t len = 0;
554 int accum = 0;
555 char* buf = NULL;
556 char** res = NULL;
557 size_t bi = 0;
558 size_t ri = 0;
559 char* tmp;
560
561 if (!str) goto return_error;
562 for (i = 0; '\0' != str[i]; i++) {
563 stlen += 1;
564 if (stlen >= CRON_MAX_STR_LEN_TO_SPLIT) goto return_error;
565 }
566
567 for (i = 0; i < stlen; i++) {
568 if (del == str[i]) {
569 if (accum > 0) {
570 len += 1;
571 accum = 0;
572 }
573 } else if (!isspace(str[i])) {
574 accum += 1;
575 }
576 }
577 /* tail */
578 if (accum > 0) {
579 len += 1;
580 }
581 if (0 == len) return NULL;
582
583 buf = (char*) cronMalloc(stlen + 1);
584 if (!buf) goto return_error;
585 memset(buf, 0, stlen + 1);
586 res = (char**) cronMalloc(len * sizeof(char*));
587 if (!res) goto return_error;
588
589 for (i = 0; i < stlen; i++) {
590 if (del == str[i]) {
591 if (bi > 0) {
592 tmp = strdupl(buf, bi);
593 if (!tmp) goto return_error;
594 res[ri++] = tmp;
595 memset(buf, 0, stlen + 1);
596 bi = 0;
597 }
598 } else if (!isspace(str[i])) {
599 buf[bi++] = str[i];
600 }
601 }
602 /* tail */
603 if (bi > 0) {
604 tmp = strdupl(buf, bi);
605 if (!tmp) goto return_error;
606 res[ri++] = tmp;
607 }
608 cronFree(buf);
609 *len_out = len;
610 return res;
611
612 return_error:
613 if (buf) {
614 cronFree(buf);
615 }
616 free_splitted(res, len);
617 *len_out = 0;
618 return NULL;
619 }
620
replace_ordinals(char * value,const char ** arr,size_t arr_len)621 static char* replace_ordinals(char* value, const char** arr, size_t arr_len) {
622 size_t i;
623 char* cur = value;
624 char* res = NULL;
625 int first = 1;
626 for (i = 0; i < arr_len; i++) {
627 char* strnum = to_string((int) i);
628 if (!strnum) {
629 if (!first) {
630 cronFree(cur);
631 }
632 return NULL;
633 }
634 res = str_replace(cur, arr[i], strnum);
635 cronFree(strnum);
636 if (!first) {
637 cronFree(cur);
638 }
639 if (!res) {
640 return NULL;
641 }
642 cur = res;
643 if (first) {
644 first = 0;
645 }
646 }
647 return res;
648 }
649
has_char(char * str,char ch)650 static int has_char(char* str, char ch) {
651 size_t i;
652 size_t len = 0;
653 if (!str) return 0;
654 len = strlen(str);
655 for (i = 0; i < len; i++) {
656 if (str[i] == ch) return 1;
657 }
658 return 0;
659 }
660
get_range(char * field,unsigned int min,unsigned int max,const char ** error)661 static unsigned int* get_range(char* field, unsigned int min, unsigned int max, const char** error) {
662
663 char** parts = NULL;
664 size_t len = 0;
665 unsigned int* res = (unsigned int*) cronMalloc(2 * sizeof(unsigned int));
666 if (!res) goto return_error;
667
668 res[0] = 0;
669 res[1] = 0;
670 if (1 == strlen(field) && '*' == field[0]) {
671 res[0] = min;
672 res[1] = max - 1;
673 } else if (!has_char(field, '-')) {
674 int err = 0;
675 unsigned int val = parse_uint(field, &err);
676 if (err) {
677 *error = "Unsigned integer parse error 1";
678 goto return_error;
679 }
680
681 res[0] = val;
682 res[1] = val;
683 } else {
684 parts = split_str(field, '-', &len);
685 if (0 == len || len > 2) {
686 *error = "Specified range has more than two fields";
687 goto return_error;
688 }
689 int err = 0;
690 res[0] = parse_uint(parts[0], &err);
691 if (err) {
692 *error = "Unsigned integer parse error 2";
693 goto return_error;
694 }
695 res[1] = parse_uint(parts[1], &err);
696 if (err) {
697 *error = "Unsigned integer parse error 3";
698 goto return_error;
699 }
700 }
701 if (res[0] >= max || res[1] >= max) {
702 *error = "Specified range exceeds maximum";
703 goto return_error;
704 }
705 if (res[0] < min || res[1] < min) {
706 *error = "Specified range is less than minimum";
707 goto return_error;
708 }
709
710 free_splitted(parts, len);
711 *error = NULL;
712 return res;
713
714 return_error:
715 free_splitted(parts, len);
716 if (res) {
717 cronFree(res);
718 }
719
720 return NULL;
721 }
722
set_number_hits(const char * value,uint8_t * target,unsigned int min,unsigned int max,const char ** error)723 void set_number_hits(const char* value, uint8_t* target, unsigned int min, unsigned int max, const char** error) {
724 size_t i;
725 unsigned int i1;
726 size_t len = 0;
727
728 char** fields = split_str(value, ',', &len);
729 if (!fields) {
730 *error = "Comma split error";
731 goto return_result;
732 }
733
734 for (i = 0; i < len; i++) {
735 if (!has_char(fields[i], '/')) {
736 /* Not an incrementer so it must be a range (possibly empty) */
737
738 unsigned int* range = get_range(fields[i], min, max, error);
739
740 if (*error) {
741 if (range) {
742 cronFree(range);
743 }
744 goto return_result;
745
746 }
747
748 for (i1 = range[0]; i1 <= range[1]; i1++) {
749 cron_set_bit(target, i1);
750
751 }
752 cronFree(range);
753
754 } else {
755 size_t len2 = 0;
756 char** split = split_str(fields[i], '/', &len2);
757 if (0 == len2 || len2 > 2) {
758 *error = "Incrementer has more than two fields";
759 free_splitted(split, len2);
760 goto return_result;
761 }
762 unsigned int* range = get_range(split[0], min, max, error);
763 if (*error) {
764 if (range) {
765 cronFree(range);
766 }
767 free_splitted(split, len2);
768 goto return_result;
769 }
770 if (!has_char(split[0], '-')) {
771 range[1] = max - 1;
772 }
773 int err = 0;
774 unsigned int delta = parse_uint(split[1], &err);
775 if (err) {
776 *error = "Unsigned integer parse error 4";
777 cronFree(range);
778 free_splitted(split, len2);
779 goto return_result;
780 }
781 for (i1 = range[0]; i1 <= range[1]; i1 += delta) {
782 cron_set_bit(target, i1);
783 }
784 free_splitted(split, len2);
785 cronFree(range);
786
787 }
788 }
789 goto return_result;
790
791 return_result:
792 free_splitted(fields, len);
793
794 }
795
set_months(char * value,uint8_t * targ,const char ** error)796 static void set_months(char* value, uint8_t* targ, const char** error) {
797 int err;
798 unsigned int i;
799 unsigned int max = 12;
800
801 char* replaced = NULL;
802
803 err = to_upper(value);
804 if (err) return;
805 replaced = replace_ordinals(value, MONTHS_ARR, CRON_MONTHS_ARR_LEN);
806 if (!replaced) return;
807
808 set_number_hits(replaced, targ, 1, max + 1, error);
809 cronFree(replaced);
810
811 /* ... and then rotate it to the front of the months */
812 for (i = 1; i <= max; i++) {
813 if (cron_get_bit(targ, i)) {
814 cron_set_bit(targ, i - 1);
815 cron_del_bit(targ, i);
816 }
817 }
818 }
819
set_days(char * field,uint8_t * targ,int max,const char ** error)820 static void set_days(char* field, uint8_t* targ, int max, const char** error) {
821 if (1 == strlen(field) && '?' == field[0]) {
822 field[0] = '*';
823 }
824 set_number_hits(field, targ, 0, max, error);
825 }
826
set_days_of_month(char * field,uint8_t * targ,const char ** error)827 static void set_days_of_month(char* field, uint8_t* targ, const char** error) {
828 /* Days of month start with 1 (in Cron and Calendar) so add one */
829 set_days(field, targ, CRON_MAX_DAYS_OF_MONTH, error);
830 /* ... and remove it from the front */
831 if (targ) {
832 cron_del_bit(targ, 0);
833 }
834
835 }
836
cron_parse_expr(const char * expression,cron_expr * target,const char ** error)837 void cron_parse_expr(const char* expression, cron_expr* target, const char** error) {
838 const char* err_local;
839 size_t len = 0;
840 char** fields = NULL;
841 char* days_replaced = NULL;
842 if (!error) {
843 error = &err_local;
844 }
845 *error = NULL;
846 if (!expression) {
847 *error = "Invalid NULL expression";
848 goto return_res;
849 }
850
851 fields = split_str(expression, ' ', &len);
852 if (len != 6) {
853 *error = "Invalid number of fields, expression must consist of 6 fields";
854 goto return_res;
855 }
856 set_number_hits(fields[0], target->seconds, 0, 60, error);
857 if (*error) goto return_res;
858 set_number_hits(fields[1], target->minutes, 0, 60, error);
859 if (*error) goto return_res;
860 set_number_hits(fields[2], target->hours, 0, 24, error);
861 if (*error) goto return_res;
862 to_upper(fields[5]);
863 days_replaced = replace_ordinals(fields[5], DAYS_ARR, CRON_DAYS_ARR_LEN);
864 set_days(days_replaced, target->days_of_week, 8, error);
865 cronFree(days_replaced);
866 if (*error) goto return_res;
867 if (cron_get_bit(target->days_of_week, 7)) {
868 /* Sunday can be represented as 0 or 7*/
869 cron_set_bit(target->days_of_week, 0);
870 cron_del_bit(target->days_of_week, 7);
871 }
872 set_days_of_month(fields[3], target->days_of_month, error);
873 if (*error) goto return_res;
874 set_months(fields[4], target->months, error);
875 if (*error) goto return_res;
876
877 goto return_res;
878
879 return_res:
880 free_splitted(fields, len);
881 }
882
cron_next(const cron_expr * expr,time_t date)883 time_t cron_next(const cron_expr* expr, time_t date) {
884 /*
885 The plan:
886
887 1 Round up to the next whole second
888
889 2 If seconds match move on, otherwise find the next match:
890 2.1 If next match is in the next minute then roll forwards
891
892 3 If minute matches move on, otherwise find the next match
893 3.1 If next match is in the next hour then roll forwards
894 3.2 Reset the seconds and go to 2
895
896 4 If hour matches move on, otherwise find the next match
897 4.1 If next match is in the next day then roll forwards,
898 4.2 Reset the minutes and seconds and go to 2
899
900 ...
901 */
902 if (!expr) return CRON_INVALID_INSTANT;
903 struct tm calval;
904 memset(&calval, 0, sizeof(struct tm));
905 struct tm* calendar = cron_time(&date, &calval);
906 if (!calendar) return CRON_INVALID_INSTANT;
907 time_t original = cron_mktime(calendar);
908 if (CRON_INVALID_INSTANT == original) return CRON_INVALID_INSTANT;
909
910 int res = do_next(expr, calendar, calendar->tm_year);
911 if (0 != res) return CRON_INVALID_INSTANT;
912
913 time_t calculated = cron_mktime(calendar);
914 if (CRON_INVALID_INSTANT == calculated) return CRON_INVALID_INSTANT;
915 if (calculated == original) {
916 /* We arrived at the original timestamp - round up to the next whole second and try again... */
917 res = add_to_field(calendar, CRON_CF_SECOND, 1);
918 if (0 != res) return CRON_INVALID_INSTANT;
919 int res = do_next(expr, calendar, calendar->tm_year);
920 if (0 != res) return CRON_INVALID_INSTANT;
921 }
922
923 return cron_mktime(calendar);
924 }
925