From 56beef8defcb5944be1ee523400b382119dbe8fc Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Wed, 15 Apr 2015 22:12:00 -0700 Subject: Fix escaping issues in open-process on Windows. This can now execute programs with spaces in their path names. Arguments can contain embedded double quotes, and other characters that are special to cmd.exe, like &, |, (, ), ^ and others. * stream.c (string_extend_count, win_escape_cmd): New static functions. (win_escape_arg): Fix and extend escaping scheme to cover not only the argument processing scheme implemented by programs which use Microsoft's C library, but also to smuggle the command line through cmd.exe. (win_make_cmdline): Use win_escape_cmd to wrap the command. Escape the quotes which are placed around arguments, so cmd.exe doesn't interpret them, which will cause it to suppress its processing of the caret escapes. --- ChangeLog | 17 +++++++++++++++++ stream.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index e2d223b0..b06f0dab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2015-04-15 Kaz Kylheku + + Fix escaping issues in open-process on Windows. + + This can now execute programs with spaces in their path names. + Arguments can contain embedded double quotes, and other characters + that are special to cmd.exe, like &, |, (, ), ^ and others. + + * stream.c (string_extend_count, win_escape_cmd): New static functions. + (win_escape_arg): Fix and extend escaping scheme to cover not only + the argument processing scheme implemented by programs which use Microsoft's + C library, but also to smuggle the command line through cmd.exe. + (win_make_cmdline): Use win_escape_cmd to wrap the command. + Escape the quotes which are placed around arguments, so cmd.exe + doesn't interpret them, which will cause it to suppress its processing + of the caret escapes. + 2015-04-15 Kaz Kylheku Allow quasiquotes in braces and quasiliterals, and quotes in braces. diff --git a/stream.c b/stream.c index ef91a07d..7187749a 100644 --- a/stream.c +++ b/stream.c @@ -2452,6 +2452,33 @@ val open_process(val name, val mode_str, val args) } #else +static void string_extend_count(int count, val out, val tail) +{ + int i; + for (i = 0; i < count; i++) + string_extend(out, tail); +} + +static val win_escape_cmd(val str) +{ + const wchar_t *s; + val out = string(L""); + + for (s = c_str(str); *s; s++) { + switch (*s) { + case ' ': case '\t': + string_extend(out, lit("\"")); + string_extend(out, chr(*s)); + string_extend(out, lit("\"")); + break; + default: + string_extend(out, chr(*s)); + } + } + + return out; +} + static val win_escape_arg(val str) { int bscount = 0, i; @@ -2461,17 +2488,25 @@ static val win_escape_arg(val str) for (s = c_str(str); *s; s++) { switch (*s) { case '"': - for (i = 0; i < bscount; i++) - string_extend(out, lit("\\\\")); - string_extend(out, lit("\\\"")); + string_extend_count(bscount, out, lit("\\\\")); + string_extend(out, lit("\\^\"")); bscount = 0; break; case '\\': bscount++; break; + case '^': case '%': case '!': + case '\n': case '&': case '|': + case '<': case '>': + case '(': case ')': + for (i = 0; i < bscount; i++) + string_extend_count(bscount, out, lit("\\")); + string_extend(out, chr('^')); + string_extend(out, chr(*s)); + break; default: for (i = 0; i < bscount; i++) - string_extend(out, lit("\\")); + string_extend_count(bscount, out, lit("\\")); string_extend(out, chr(*s)); bscount = 0; break; @@ -2488,16 +2523,16 @@ static val win_make_cmdline(val args) { val out = string(L""); - string_extend(out, pop(&args)); - string_extend(out, lit(" ")); + string_extend(out, win_escape_cmd(pop(&args))); + string_extend(out, chr(' ')); - for (; args; args = cdr(args)) { - string_extend(out, lit("\"")); - string_extend(out, win_escape_arg(car(args))); - if (cdr(args)) - string_extend(out, lit("\" ")); + while (args) { + string_extend(out, lit("^\"")); + string_extend(out, win_escape_arg(pop(&args))); + if (args) + string_extend(out, lit("^\" ")); else - string_extend(out, lit("\"")); + string_extend(out, lit("^\"")); } return out; -- cgit v1.2.3