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, &notfound);
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, &notfound);
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