]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
basic/extract-word: add EXTRACT_UNESCAPE_SEPARATORS mode
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 3 Aug 2020 17:06:16 +0000 (19:06 +0200)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 5 Aug 2020 20:29:13 +0000 (21:29 +0100)
This allows separators to be escaped, for example to allow
"a\:b:c", to be treated as "a:b", "c" with ":" as the separator.

src/basic/extract-word.c
src/basic/extract-word.h
src/test/test-extract-word.c

index ac9bf6099d87f62a78f9d9f9c7d01475db97121e..1a53da334a23588bdb65ae8a9a51d5de3ce97872 100644 (file)
@@ -86,25 +86,30 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
                                 return -EINVAL;
                         }
 
-                        if (flags & EXTRACT_CUNESCAPE) {
+                        if (flags & (EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS)) {
                                 bool eight_bit = false;
                                 char32_t u;
 
-                                r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false);
-                                if (r < 0) {
-                                        if (flags & EXTRACT_CUNESCAPE_RELAX) {
-                                                s[sz++] = '\\';
-                                                s[sz++] = c;
-                                        } else
-                                                return -EINVAL;
-                                } else {
+                                if ((flags & EXTRACT_CUNESCAPE) &&
+                                    (r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false)) >= 0) {
+                                        /* A valid escaped sequence */
+                                        assert(r >= 1);
+
                                         (*p) += r - 1;
 
                                         if (eight_bit)
                                                 s[sz++] = u;
                                         else
                                                 sz += utf8_encode_unichar(s + sz, u);
-                                }
+                                } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) &&
+                                           strchr(separators, **p))
+                                        /* An escaped separator char */
+                                        s[sz++] = c;
+                                else if (flags & EXTRACT_CUNESCAPE_RELAX) {
+                                        s[sz++] = '\\';
+                                        s[sz++] = c;
+                                } else
+                                        return -EINVAL;
                         } else
                                 s[sz++] = c;
 
index e2d433893ac89acceeb0bfc0a4a5c92fe7f049a8..f028577c40887e91622e32edb0e518fc51b968e3 100644 (file)
@@ -7,9 +7,10 @@ typedef enum ExtractFlags {
         EXTRACT_RELAX                    = 1 << 0,
         EXTRACT_CUNESCAPE                = 1 << 1,
         EXTRACT_CUNESCAPE_RELAX          = 1 << 2,
-        EXTRACT_UNQUOTE                  = 1 << 3,
-        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4,
-        EXTRACT_RETAIN_ESCAPE            = 1 << 5,
+        EXTRACT_UNESCAPE_SEPARATORS      = 1 << 3,
+        EXTRACT_UNQUOTE                  = 1 << 4,
+        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5,
+        EXTRACT_RETAIN_ESCAPE            = 1 << 6,
 } ExtractFlags;
 
 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
index 43ad1b7d82f99be64fda31d8975667ebdc31338f..c71e4d32bfc8c826734a9943b4bac411cfe93d6f 100644 (file)
@@ -341,6 +341,46 @@ static void test_extract_first_word(void) {
         assert_se(streq(t, "foo\\xbar"));
         free(t);
         assert_se(p == NULL);
+
+        p = "\\:";
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
+        assert_se(streq(t, ":"));
+        free(t);
+        assert_se(p == NULL);
+
+        p = "a\\:b";
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
+        assert_se(streq(t, "a:b"));
+        free(t);
+        assert_se(p == NULL);
+
+        p = "a\\ b:c";
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
+        assert_se(streq(t, "a b"));
+        free(t);
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
+        assert_se(streq(t, "c"));
+        free(t);
+        assert_se(p == NULL);
+
+        p = "\\:";
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
+
+        p = "a\\:b";
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == 1);
+        assert_se(streq(t, "b"));
+        free(t);
+
+        p = "a\\ b:c";
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == -EINVAL);
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == 1);
+        assert_se(streq(t, "b"));
+        free(t);
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == 1);
+        assert_se(streq(t, "c"));
+        free(t);
+        assert_se(p == NULL);
 }
 
 static void test_extract_first_word_and_warn(void) {