strv: add strv_split_colon_pairs function

Given a string in the format 'one:two three four:five', returns a string
vector with each word. If the second element of the tuple is not
present, an empty string is returned in its place, so that the vector
can be processed in pairs.

[zjs: use EXTRACT_UNESCAPE_SEPARATORS instead of EXTRACT_CUNESCAPE_RELAX.
This way we do escaping exactly once and in normal strict mode.]
This commit is contained in:
Luca Boccassi 2020-07-07 17:12:48 +01:00
parent 1e198efcdb
commit a082edd53a
3 changed files with 87 additions and 0 deletions

View file

@ -353,6 +353,58 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
return (int) n;
}
int strv_split_colon_pairs(char ***t, const char *s) {
_cleanup_strv_free_ char **l = NULL;
size_t n = 0, allocated = 0;
int r;
assert(t);
assert(s);
for (;;) {
_cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
break;
const char *p = tuple;
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
&first, &second, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
/* Enforce that at most 2 colon-separated words are contained in each group */
if (!isempty(p))
return -EINVAL;
second_or_empty = strdup(strempty(second));
if (!second_or_empty)
return -ENOMEM;
if (!GREEDY_REALLOC(l, allocated, n + 3))
return -ENOMEM;
l[n++] = TAKE_PTR(first);
l[n++] = TAKE_PTR(second_or_empty);
l[n] = NULL;
}
if (!l) {
l = new0(char*, 1);
if (!l)
return -ENOMEM;
}
*t = TAKE_PTR(l);
return (int) n;
}
char *strv_join_prefix(char * const *l, const char *separator, const char *prefix) {
char * const *s;
char *r, *e;

View file

@ -80,6 +80,11 @@ char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
/* Given a string containing white-space separated tuples of words themselves separated by ':',
* returns a vector of strings. If the second element in a tuple is missing, the corresponding
* string in the vector is an empty string. */
int strv_split_colon_pairs(char ***t, const char *s);
char *strv_join_prefix(char * const *l, const char *separator, const char *prefix);
static inline char *strv_join(char * const *l, const char *separator) {
return strv_join_prefix(l, separator, NULL);

View file

@ -407,6 +407,35 @@ static void test_strv_split_extract(void) {
assert_se(streq_ptr(l[5], NULL));
}
static void test_strv_split_colon_pairs(void) {
_cleanup_strv_free_ char **l = NULL;
const char *str = "one:two three four:five six seven:eight\\:nine ten\\:eleven\\\\",
*str_inval="one:two three:four:five";
int r;
log_info("/* %s */", __func__);
r = strv_split_colon_pairs(&l, str);
assert_se(r == (int) strv_length(l));
assert_se(r == 12);
assert_se(streq_ptr(l[0], "one"));
assert_se(streq_ptr(l[1], "two"));
assert_se(streq_ptr(l[2], "three"));
assert_se(streq_ptr(l[3], ""));
assert_se(streq_ptr(l[4], "four"));
assert_se(streq_ptr(l[5], "five"));
assert_se(streq_ptr(l[6], "six"));
assert_se(streq_ptr(l[7], ""));
assert_se(streq_ptr(l[8], "seven"));
assert_se(streq_ptr(l[9], "eight:nine"));
assert_se(streq_ptr(l[10], "ten:eleven\\"));
assert_se(streq_ptr(l[11], ""));
assert_se(streq_ptr(l[12], NULL));
r = strv_split_colon_pairs(&l, str_inval);
assert_se(r == -EINVAL);
}
static void test_strv_split_newlines(void) {
unsigned i = 0;
char **s;
@ -998,6 +1027,7 @@ int main(int argc, char *argv[]) {
test_strv_split();
test_strv_split_empty();
test_strv_split_extract();
test_strv_split_colon_pairs();
test_strv_split_newlines();
test_strv_split_nulstr();
test_strv_parse_nulstr();