fix(cron): caught some more bugs
ci/woodpecker/pr/build Pipeline is pending Details
ci/woodpecker/pr/docker Pipeline is pending Details
ci/woodpecker/pr/docs Pipeline is pending Details
ci/woodpecker/pr/lint Pipeline is pending Details
ci/woodpecker/pr/man Pipeline is pending Details
ci/woodpecker/pr/test Pipeline is pending Details

Jef Roosens 2023-01-15 19:03:33 +01:00
parent 6d1b4aadb6
commit 4f093c08a7
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 38 additions and 14 deletions

View File

@ -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 {

View File

@ -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;
}

View File

@ -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' }
}
}

View File

@ -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 }
}