]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
util: New flag UNQUOTE_UNESCAPE_RELAX for unquote_first_word
authorFilipe Brandenburger <filbranden@google.com>
Wed, 3 Jun 2015 04:08:24 +0000 (21:08 -0700)
committerFilipe Brandenburger <filbranden@google.com>
Wed, 17 Jun 2015 18:12:11 +0000 (11:12 -0700)
The new flag UNQUOTE_UNESCAPE_RELAX preserves unrecognized escape
sequences verbatim in unquote_first_word, either when it's a trailing
backslash (similar to UNQUOTE_RELAX, but in this case keep the extra
backslash in the output) or in the middle of a sequence string.

Add unit test cases to ensure the new flag works as expected and to
prevent regressions from being introduced.

Tested with a follow up commit converting config_parse_exec() to start
using unquote_first_word, in which case this flags makes it possible to
preserve unrecognized escape sequences.

Relevant bug: https://bugs.freedesktop.org/show_bug.cgi?id=90794

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

index e0d12201537bdc5787f544dae2a73e82dfdce64f..46a48c4d60cf07302500bc5138fbf0cec71d880a 100644 (file)
@@ -5246,21 +5246,39 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                 case SINGLE_QUOTE_ESCAPE:
                 case DOUBLE_QUOTE_ESCAPE:
                 case VALUE_ESCAPE:
+                        if (!GREEDY_REALLOC(s, allocated, sz+7))
+                                return -ENOMEM;
+
                         if (c == 0) {
+                                if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
+                                    (state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
+                                        /* If we find an unquoted trailing backslash and we're in
+                                         * UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
+                                         * output.
+                                         *
+                                         * Unbalanced quotes will only be allowed in UNQUOTE_RELAX
+                                         * mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them.
+                                         */
+                                        s[sz++] = '\\';
+                                        goto finish;
+                                }
                                 if (flags & UNQUOTE_RELAX)
                                         goto finish;
                                 return -EINVAL;
                         }
 
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
-                                return -ENOMEM;
-
                         if (flags & UNQUOTE_CUNESCAPE) {
                                 uint32_t u;
 
                                 r = cunescape_one(*p, (size_t) -1, &c, &u);
-                                if (r < 0)
+                                if (r < 0) {
+                                        if (flags & UNQUOTE_CUNESCAPE_RELAX) {
+                                                s[sz++] = '\\';
+                                                s[sz++] = c;
+                                                goto end_escape;
+                                        }
                                         return -EINVAL;
+                                }
 
                                 (*p) += r - 1;
 
@@ -5271,6 +5289,7 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                         } else
                                 s[sz++] = c;
 
+end_escape:
                         state = (state == SINGLE_QUOTE_ESCAPE) ? SINGLE_QUOTE :
                                 (state == DOUBLE_QUOTE_ESCAPE) ? DOUBLE_QUOTE :
                                 VALUE;
index 7aca46d777884a3bb9afdac779ec64d750916eef..748f22f1a2eba3d609230e595b6b1f3314a9ca8a 100644 (file)
@@ -839,8 +839,9 @@ int is_dir(const char *path, bool follow);
 int is_device_node(const char *path);
 
 typedef enum UnquoteFlags {
-        UNQUOTE_RELAX     = 1,
-        UNQUOTE_CUNESCAPE = 2,
+        UNQUOTE_RELAX           = 1,
+        UNQUOTE_CUNESCAPE       = 2,
+        UNQUOTE_CUNESCAPE_RELAX = 4,
 } UnquoteFlags;
 
 int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
index ed8db4511502cff9a2f59562977acf5885c0be0f..b3e79cd6f55328d22754810f7ed7fa65d9ebb80f 100644 (file)
@@ -1304,6 +1304,100 @@ static void test_unquote_first_word(void) {
         assert_se(streq(t, "pi\360\237\222\251le"));
         free(t);
         assert_se(p == original + 32);
+
+        p = original = "fooo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "fooo"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "fooo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "fooo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "fooo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "foo"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "foo\\"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "foo\\"));
+        free(t);
+        assert_se(p == original + 5);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\ bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "\\w+@\\K[\\d.]+";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) == -EINVAL);
+        assert_se(p == original + 1);
+
+        p = original = "\\w+@\\K[\\d.]+";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+        free(t);
+        assert_se(p == original + 12);
+
+        p = original = "\\w+\\b";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "\\w+\b"));
+        free(t);
+        assert_se(p == original + 5);
 }
 
 static void test_unquote_many_words(void) {