diff options
author | Matt Coffin <mcoffin13@gmail.com> | 2019-06-11 12:10:17 -0600 |
---|---|---|
committer | Brian Ashworth <bosrsf04@gmail.com> | 2019-06-11 14:40:36 -0400 |
commit | 2b5bf78fafdf027624ca88e1f703bc9e577f4690 (patch) | |
tree | ee24a2a3740563aecfab9fdc922eafebf1527c97 /common | |
parent | 3f77591b00a98dba4d2ddc5198e87f9106579ed1 (diff) |
Fix segfaults caused by faulty command parsing
This patch fixes faulty command parsing introduced by
f0f5de9a9e87ca1f0d74e7cbf82ffceba51ffbe6. When that commit allowed
criteria reset on ';' delimeters in commands lists, it failed to account
for its inner ','-parsing loop eating threw the entire rest of the
string.
This patch refactors argsep to use a list of multiple separators, and
(optionally) return the separator that it matched against in this
iteration via a pointer. This allows it to hint at the command parser
which separator was used at the end of the last command, allowing it to
trigger a potential secondary read of the criteria.
Fixes #4239
Diffstat (limited to 'common')
-rw-r--r-- | common/stringop.c | 56 |
1 files changed, 40 insertions, 16 deletions
diff --git a/common/stringop.c b/common/stringop.c index dea152cc..ac7df296 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -251,37 +251,61 @@ char *join_args(char **argv, int argc) { return res; } -char *argsep(char **stringp, const char *delim) { +static inline char *argsep_next_interesting(const char *src, const char *delim) { + char *special = strpbrk(src, "\"'\\"); + char *next_delim = strpbrk(src, delim); + if (!special) { + return next_delim; + } + if (!next_delim) { + return special; + } + return (next_delim < special) ? next_delim : special; +} + +char *argsep(char **stringp, const char *delim, char *matched) { char *start = *stringp; char *end = start; bool in_string = false; bool in_char = false; bool escaped = false; - while (1) { - if (*end == '"' && !in_char && !escaped) { + char *interesting = NULL; + + while ((interesting = argsep_next_interesting(end, delim))) { + if (escaped && interesting != end) { + escaped = false; + } + if (*interesting == '"' && !in_char && !escaped) { in_string = !in_string; - } else if (*end == '\'' && !in_string && !escaped) { + end = interesting + 1; + } else if (*interesting == '\'' && !in_string && !escaped) { in_char = !in_char; - } else if (*end == '\\') { + end = interesting + 1; + } else if (*interesting == '\\') { escaped = !escaped; - } else if (*end == '\0') { - *stringp = NULL; - break; - } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { + end = interesting + 1; + } else if (!in_string && !in_char && !escaped) { + // We must have matched a separator + end = interesting; + if (matched) { + *matched = *end; + } if (end - start) { *(end++) = 0; - *stringp = end + strspn(end, delim);; - if (!**stringp) *stringp = NULL; + *stringp = end; break; } else { - ++start; - end = start; + end = ++start; } + } else { + end++; } - if (*end != '\\') { - escaped = false; + } + if (!interesting) { + *stringp = NULL; + if (matched) { + *matched = '\0'; } - ++end; } return start; } |