Fix Windows command line quoting of backslash runs: a trailing backslash, or backslashes preceding a double quote, were not doubled, so CommandLineToArgvW in the child parsed them as escapes and mangled the argument list. Upstream PR: https://github.com/sheredom/subprocess.h/pull/101 Applied locally by scripts/sync_vendor.py until it is merged upstream. (the test/ changes from the PR are omitted, we only vendor subprocess.h) 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++] = '"';