chiark / gitweb /
Treat a trailing backslash as an error
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 18 Dec 2014 22:51:38 +0000 (17:51 -0500)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 19 Dec 2014 00:26:21 +0000 (19:26 -0500)
Commit a2a5291b3f5 changed the parser to reject unfinished quoted
strings. Unfortunately it introduced an error where a trailing
backslash would case an infinite loop. Of course this must fixed, but
the question is what to to instead. Allowing trailing backslashes and
treating them as normal characters would be one option, but this seems
suboptimal. First, there would be inconsistency between handling of
quoting and of backslashes. Second, a trailing backslash is most
likely an error, at it seems better to point it out to the user than
to try to continue.

Updated rules:
ExecStart=/bin/echo \\ → OK, prints a backslash
ExecStart=/bin/echo \ → error
ExecStart=/bin/echo "x → error
ExecStart=/bin/echo "x"y → error

src/shared/util.c
src/test/test-strv.c
src/test/test-util.c

index 364f61885b00a1b3f9a076a993b982f74468ba7e..91cf6705ea104233b6ede6f8ef03c7f7084b57a9 100644 (file)
@@ -521,7 +521,7 @@ int safe_atod(const char *s, double *ret_d) {
 
 static size_t strcspn_escaped(const char *s, const char *reject) {
         bool escaped = false;
-        size_t n;
+        int n;
 
         for (n=0; s[n]; n++) {
                 if (escaped)
@@ -531,6 +531,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
                 else if (strchr(reject, s[n]))
                         break;
         }
+
         /* if s ends in \, return index of previous char */
         return n - escaped;
 }
@@ -566,6 +567,11 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
                 *state = current++ + *l + 2;
         } else if (quoted) {
                 *l = strcspn_escaped(current, separator);
+                if (current[*l] && !strchr(separator, current[*l])) {
+                        /* unfinished escape */
+                        *state = current;
+                        return NULL;
+                }
                 *state = current + *l;
         } else {
                 *l = strcspn(current, separator);
index 0b78086ea9a0b202204059d26589825fd9b5e3ec..f343eab7c63d3eec3f2676846f744ef82da3f99d 100644 (file)
@@ -520,6 +520,10 @@ int main(int argc, char *argv[]) {
         test_strv_unquote("  \"x'\"   ", STRV_MAKE("x'"));
         test_strv_unquote("a  '--b=c \"d e\"'", STRV_MAKE("a", "--b=c \"d e\""));
 
+        /* trailing backslashes */
+        test_strv_unquote("  x\\\\", STRV_MAKE("x\\"));
+        test_invalid_unquote("  x\\");
+
         test_invalid_unquote("a  --b='c \"d e\"''");
         test_invalid_unquote("a  --b='c \"d e\" '\"");
         test_invalid_unquote("a  --b='c \"d e\"garbage");
index bbf7512839c173e54f9195c5c1bee2fc41ad2efd..222af9a73a75c0e3ccdbaa8986d622140ebea412 100644 (file)
@@ -406,28 +406,12 @@ static void test_foreach_word(void) {
                 assert_se(strneq(expected[i++], word, l));
 }
 
-static void test_foreach_word_quoted(void) {
+static void check(const char *test, char** expected, bool trailing) {
         const char *word, *state;
         size_t l;
         int i = 0;
-        const char test[] = "test a b c 'd' e '' '' hhh '' '' \"a b c\"";
-        const char * const expected[] = {
-                "test",
-                "a",
-                "b",
-                "c",
-                "d",
-                "e",
-                "",
-                "",
-                "hhh",
-                "",
-                "",
-                "a b c",
-                NULL
-        };
 
-        printf("<%s>\n", test);
+        printf("<<<%s>>>\n", test);
         FOREACH_WORD_QUOTED(word, l, test, state) {
                 _cleanup_free_ char *t = NULL;
 
@@ -435,7 +419,34 @@ static void test_foreach_word_quoted(void) {
                 assert_se(strneq(expected[i++], word, l));
                 printf("<%s>\n", t);
         }
-        assert_se(isempty(state));
+        printf("<<<%s>>>\n", state);
+        assert(expected[i] == NULL);
+        assert_se(isempty(state) == !trailing);
+}
+
+static void test_foreach_word_quoted(void) {
+        check("test a b c 'd' e '' '' hhh '' '' \"a b c\"",
+              STRV_MAKE("test",
+                        "a",
+                        "b",
+                        "c",
+                        "d",
+                        "e",
+                        "",
+                        "",
+                        "hhh",
+                        "",
+                        "",
+                        "a b c"),
+              false);
+
+        check("test \"xxx",
+              STRV_MAKE("test"),
+              true);
+
+        check("test\\",
+              STRV_MAKE_EMPTY,
+              true);
 }
 
 static void test_default_term_for_tty(void) {