aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorMatt Coffin <mcoffin13@gmail.com>2019-06-11 12:10:17 -0600
committerBrian Ashworth <bosrsf04@gmail.com>2019-06-11 14:40:36 -0400
commit2b5bf78fafdf027624ca88e1f703bc9e577f4690 (patch)
treeee24a2a3740563aecfab9fdc922eafebf1527c97 /common
parent3f77591b00a98dba4d2ddc5198e87f9106579ed1 (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.c56
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;
}