]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
strv: add strv_split_colon_pairs function
authorLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 7 Jul 2020 16:12:48 +0000 (17:12 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 5 Aug 2020 20:29:13 +0000 (21:29 +0100)
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.]

src/basic/strv.c
src/basic/strv.h
src/test/test-strv.c

index 858e1e62ecc3bc5ed6c585d5f8ab1795174e7a2f..a172ca2fe9174b99b019e6e9fa0d08b86288277d 100644 (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;
index 2ad927bce5163df4e0beebc11d96c0e14ac68411..e57dfff69bee78fad10d4740e682212378e28bf8 100644 (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);
index cba5441d4b3c0e0334da10449196b12200022679..fda5948f4998ead8142f4284b9f1cb0977eab33c 100644 (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();