strv: introduce 'relax' mode to strv_split_full()

If SPLIT_RELAX is specified, then it accepts unfinished quotes or
missing separator after right quote.
This commit is contained in:
Yu Watanabe 2018-09-26 22:17:40 +09:00
parent 2c3a11d86e
commit 8059aa9c92
4 changed files with 24 additions and 14 deletions

View File

@ -128,7 +128,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
} }
/* Split a string into words. */ /* Split a string into words. */
const char* split(const char **state, size_t *l, const char *separator, bool quoted) { const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) {
const char *current; const char *current;
current = *state; current = *state;
@ -144,20 +144,24 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
return NULL; return NULL;
} }
if (quoted && strchr("\'\"", *current)) { if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) {
char quotechars[2] = {*current, '\0'}; char quotechars[2] = {*current, '\0'};
*l = strcspn_escaped(current + 1, quotechars); *l = strcspn_escaped(current + 1, quotechars);
if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) { (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */ /* right quote missing or garbage at the end */
if (flags & SPLIT_RELAX) {
*state = current + *l + 1 + (current[*l + 1] != '\0');
return current + 1;
}
*state = current; *state = current;
return NULL; return NULL;
} }
*state = current++ + *l + 2; *state = current++ + *l + 2;
} else if (quoted) { } else if (flags & SPLIT_QUOTES) {
*l = strcspn_escaped(current, separator); *l = strcspn_escaped(current, separator);
if (current[*l] && !strchr(separator, current[*l])) { if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
/* unfinished escape */ /* unfinished escape */
*state = current; *state = current;
return NULL; return NULL;

View File

@ -81,16 +81,21 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_; char *first_word(const char *s, const char *word) _pure_;
const char* split(const char **state, size_t *l, const char *separator, bool quoted); typedef enum SplitFlags {
SPLIT_QUOTES = 0x01 << 0,
SPLIT_RELAX = 0x01 << 1,
} SplitFlags;
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
#define FOREACH_WORD(word, length, s, state) \ #define FOREACH_WORD(word, length, s, state) \
_FOREACH_WORD(word, length, s, WHITESPACE, false, state) _FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
_FOREACH_WORD(word, length, s, separator, false, state) _FOREACH_WORD(word, length, s, separator, 0, state)
#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ #define _FOREACH_WORD(word, length, s, separator, flags, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
char *strappend(const char *s, const char *suffix); char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length); char *strnappend(const char *s, const char *suffix, size_t length);

View File

@ -245,7 +245,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
return 0; return 0;
} }
char **strv_split_full(const char *s, const char *separator, bool quoted) { char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
const char *word, *state; const char *word, *state;
size_t l; size_t l;
size_t n, i; size_t n, i;
@ -261,7 +261,7 @@ char **strv_split_full(const char *s, const char *separator, bool quoted) {
return new0(char*, 1); return new0(char*, 1);
n = 0; n = 0;
_FOREACH_WORD(word, l, s, separator, quoted, state) _FOREACH_WORD(word, l, s, separator, flags, state)
n++; n++;
r = new(char*, n+1); r = new(char*, n+1);
@ -269,7 +269,7 @@ char **strv_split_full(const char *s, const char *separator, bool quoted) {
return NULL; return NULL;
i = 0; i = 0;
_FOREACH_WORD(word, l, s, separator, quoted, state) { _FOREACH_WORD(word, l, s, separator, flags, state) {
r[i] = strndup(word, l); r[i] = strndup(word, l);
if (!r[i]) { if (!r[i]) {
strv_free(r); strv_free(r);

View File

@ -9,6 +9,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "extract-word.h" #include "extract-word.h"
#include "macro.h" #include "macro.h"
#include "string-util.h"
#include "util.h" #include "util.h"
char *strv_find(char **l, const char *name) _pure_; char *strv_find(char **l, const char *name) _pure_;
@ -66,9 +67,9 @@ static inline bool strv_isempty(char * const *l) {
return !l || !*l; return !l || !*l;
} }
char **strv_split_full(const char *s, const char *separator, bool quoted); char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
static inline char **strv_split(const char *s, const char *separator) { static inline char **strv_split(const char *s, const char *separator) {
return strv_split_full(s, separator, false); return strv_split_full(s, separator, 0);
} }
char **strv_split_newlines(const char *s); char **strv_split_newlines(const char *s);