diff --git a/subprocess.h b/subprocess.h index 5e80902..b06ad4d 100644 --- a/subprocess.h +++ b/subprocess.h @@ -653,6 +653,7 @@ int subprocess_create_ex(const char *const commandLine[], int options, int wide_len; int i, j; int need_quoting; + subprocess_size_t bs_run; unsigned long flags = 0; unsigned long last_error = 0; int result = subprocess_error_unknown; @@ -906,25 +907,29 @@ int subprocess_create_ex(const char *const commandLine[], int options, len++; // Quote the argument if it has a space in it - if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL || - commandLine[i][0] == SUBPROCESS_NULL) + need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL || + commandLine[i][0] == SUBPROCESS_NULL; + if (need_quoting) len += 2; + bs_run = 0; for (j = 0; '\0' != commandLine[i][j]; j++) { - switch (commandLine[i][j]) { - default: - break; - case '\\': - if (commandLine[i][j + 1] == '"') { - len++; - } + len++; - break; - case '"': - len++; - break; + if ('\\' == commandLine[i][j]) { + bs_run++; + } else { + if ('"' == commandLine[i][j]) { + // Duplicate the preceding run and escape the quote. + len += bs_run + 1; + } + bs_run = 0; } - len++; + } + + if (need_quoting) { + // Duplicate trailing slashes before the generated closing quote. + len += bs_run; } } @@ -949,22 +954,29 @@ int subprocess_create_ex(const char *const commandLine[], int options, commandLineCombined[len++] = '"'; } - for (j = 0; '\0' != commandLine[i][j]; j++) { - switch (commandLine[i][j]) { - default: - break; - case '\\': - if (commandLine[i][j + 1] == '"') { - commandLineCombined[len++] = '\\'; - } + for (j = 0; '\0' != commandLine[i][j];) { + bs_run = 0; + while ('\\' == commandLine[i][j]) { + bs_run++; + j++; + } + + if ('"' == commandLine[i][j]) { + // 2n + 1 slashes preserve n slashes and escape the quote. + bs_run = (bs_run * 2) + 1; + } else if ('\0' == commandLine[i][j] && need_quoting) { + // 2n slashes preserve n slashes before the closing quote. + bs_run *= 2; + } - break; - case '"': + while (bs_run > 0) { commandLineCombined[len++] = '\\'; - break; + bs_run--; } - commandLineCombined[len++] = commandLine[i][j]; + if ('\0' != commandLine[i][j]) { + commandLineCombined[len++] = commandLine[i][j++]; + } } if (need_quoting) { commandLineCombined[len++] = '"'; diff --git a/test/process_return_special_argv.c b/test/process_return_special_argv.c index c60907b..c921c0b 100644 --- a/test/process_return_special_argv.c +++ b/test/process_return_special_argv.c @@ -26,8 +26,16 @@ #include int main(int argc, const char *const argv[]) { unsigned int res = 0; + if (argc != 9) { + return 0; + } res |= ((strcmp(argv[1], "foo\nbar") == 0) << 0); res |= ((strcmp(argv[2], "\"baz\"") == 0) << 1); res |= ((strcmp(argv[3], "faz\\\"faz") == 0) << 2); + res |= ((strcmp(argv[4], "a b\\") == 0) << 3); + res |= ((strcmp(argv[5], "foo\\\\\"bar") == 0) << 4); + res |= ((strcmp(argv[6], "a b\\\\") == 0) << 5); + res |= ((strcmp(argv[7], "a \\\\\"b") == 0) << 6); + res |= ((strcmp(argv[8], "after") == 0) << 7); return (int)res; -} \ No newline at end of file +} diff --git a/test/test_shared.h b/test/test_shared.h index a694ed6..4a57e14 100644 --- a/test/test_shared.h +++ b/test/test_shared.h @@ -411,14 +411,22 @@ SUBPROCESS_TEST(create, subprocess_stderr_argv) { SUBPROCESS_TEST(create, subprocess_return_special_argv) { const char *const commandLine[] = {"./process_return_special_argv", - "foo\nbar", "\"baz\"", "faz\\\"faz", 0}; + "foo\nbar", + "\"baz\"", + "faz\\\"faz", + "a b\\", + "foo\\\\\"bar", + "a b\\\\", + "a \\\\\"b", + "after", + 0}; struct subprocess_s process; int ret = -1; ASSERT_EQ(0, subprocess_create(commandLine, 0, &process)); ASSERT_EQ(0, subprocess_join(&process, &ret)); - ASSERT_EQ(7, ret); // 0b111 + ASSERT_EQ(255, ret); // 0b11111111 ASSERT_EQ(0, subprocess_destroy(&process)); }