diff --git a/src/cron/c/expression.h b/src/cron/c/expression.h index 4b40a96..3a5a530 100644 --- a/src/cron/c/expression.h +++ b/src/cron/c/expression.h @@ -11,7 +11,8 @@ typedef enum cron_parse_error { cron_parse_invalid_expression = 1, cron_parse_invalid_number = 2, cron_parse_out_of_range = 3, - cron_parse_too_many_parts = 4 + cron_parse_too_many_parts = 4, + cron_parse_not_enough_parts = 5 } cron_parse_error; typedef struct cron_expression { diff --git a/src/cron/c/parse.c b/src/cron/c/parse.c index bb54191..10df78a 100644 --- a/src/cron/c/parse.c +++ b/src/cron/c/parse.c @@ -112,7 +112,7 @@ cron_parse_error ce_parse_part(uint64_t *out, char *s, uint8_t min, *out = 0; char *next; - enum cron_parse_error res; + cron_parse_error res; while ((next = strchr(s, ',')) != NULL) { next[0] = '\0'; @@ -180,13 +180,12 @@ uint8_t bf_to_nums(uint8_t **out, uint64_t bf, uint8_t min, uint8_t max) { /* * Parse a cron expression string into a cron_expression struct. */ -enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) { +cron_parse_error ce_parse_expression(cron_expression *out, char *s) { // The parsing functions modify the input string in-place s = strdup(s); char *orig_s = s; - enum cron_parse_error res = cron_parse_ok; - uint64_t bfs[max_parts]; + cron_parse_error res = cron_parse_ok; // First we divide the input string into its parts, divided by spaces. // Each part is delimited by a NULL byte. @@ -207,23 +206,19 @@ enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) { next[0] = '\0'; parts[part_count] = s; + part_count++; + // Skip multiple spaces offset = 1; while (next[offset] == ' ') { offset++; } s = next + offset; - - part_count++; } - // The loop exited because we already have 4 parts, yet there's still at - // least one more part that follows. - if (next != NULL) { - res = cron_parse_too_many_parts; - goto end; - } else if (s[0] != '\0') { - // There's one more excessive trailing part + // Each iteration of the loop skips all trailing spaces. This means that, if + // s[0] isn't '\0', there's still another part before the end of the string. + if (s[0] != '\0') { if (part_count == max_parts) { res = cron_parse_too_many_parts; goto end; @@ -233,6 +228,11 @@ enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) { part_count++; } + if (part_count < min_parts) { + res = cron_parse_not_enough_parts; + goto end; + } + // We now parse the parts in reverse. This is because the month part // determines the maximum value of the day part. @@ -268,6 +268,8 @@ enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) { res = ce_parse_part(&bit_field, parts[2], min[2], max_day_value); if (res != cron_parse_ok) { + free(out->months); + goto end; } @@ -285,6 +287,9 @@ enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) { res = ce_parse_part(&bit_field, parts[1], min[1], max[1]); if (res != cron_parse_ok) { + free(out->months); + free(out->days); + goto end; } @@ -296,6 +301,10 @@ enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) { res = ce_parse_part(&bit_field, parts[0], min[0], max[0]); if (res != cron_parse_ok) { + free(out->months); + free(out->days); + free(out->hours); + goto end; } diff --git a/src/cron/expression.c.v b/src/cron/expression.c.v index 0217ca3..217b687 100644 --- a/src/cron/expression.c.v +++ b/src/cron/expression.c.v @@ -73,6 +73,7 @@ enum ParseError as u8 { invalid_number = 2 out_of_range = 3 too_many_parts = 4 + not_enough_parts = 5 } // str returns the string representation of a ParseError. @@ -83,6 +84,7 @@ fn (e ParseError) str() string { .invalid_number { 'Invalid number' } .out_of_range { 'Out of range' } .too_many_parts { 'Too many parts' } + .not_enough_parts { 'Not enough parts' } } } diff --git a/src/cron/parse_test.v b/src/cron/parse_test.v index a44dffd..a143c73 100644 --- a/src/cron/parse_test.v +++ b/src/cron/parse_test.v @@ -24,6 +24,14 @@ fn test_not_allowed() { res = false parse_expression('0 /5') or { res = true } assert res + + res = false + parse_expression('0 ') or { res = true } + assert res + + res = false + parse_expression('0') or { res = true } + assert res } fn test_leading_star() { @@ -43,3 +51,7 @@ fn test_auto_extend() ! { assert ce1 == ce2 && ce2 == ce3 } + +fn test_four() { + parse_expression('0 1 2 3 ') or { assert false } +}