# HG changeset patch # User Bram Moolenaar # Date 1570270504 -7200 # Node ID 85160a3649b937d457ca3c59517d8af93cb32611 # Parent 7aed836f50f7ef91b51efda744543a6cc1228d71 patch 8.1.2115: MS-Windows: shell commands fail if &shell contains a space Commit: https://github.com/vim/vim/commit/2efc44b3f0b6bd8307cb281af095e08e15ab1c24 Author: Bram Moolenaar Date: Sat Oct 5 12:09:32 2019 +0200 patch 8.1.2115: MS-Windows: shell commands fail if &shell contains a space Problem: MS-Windows: shell commands fail if &shell contains a space. Solution: Use quotes instead of escaping. (closes https://github.com/vim/vim/issues/4920) diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -102,7 +102,26 @@ set_init_1(int clean_arg) || ((p = (char_u *)default_shell()) != NULL && *p != NUL) #endif ) +#if defined(MSWIN) + { + // For MS-Windows put the path in quotes instead of escaping spaces. + char_u *cmd; + size_t len; + + if (vim_strchr(p, ' ') != NULL) + { + len = STRLEN(p) + 3; // two quotes and a trailing NUL + cmd = alloc(len); + vim_snprintf((char *)cmd, len, "\"%s\"", p); + set_string_default("sh", cmd); + vim_free(cmd); + } + else + set_string_default("sh", p); + } +#else set_string_default_esc("sh", p, TRUE); +#endif #ifdef FEAT_WILDIGN /* diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -4490,8 +4490,25 @@ mch_system_c(char *cmd, int options UNUS { int ret; WCHAR *wcmd; - - wcmd = enc_to_utf16((char_u *)cmd, NULL); + char_u *buf; + size_t len; + + // If the command starts and ends with double quotes, enclose the command + // in parentheses. + len = STRLEN(cmd); + if (len >= 2 && cmd[0] == '"' && cmd[len - 1] == '"') + { + len += 3; + buf = alloc(len); + if (buf == NULL) + return -1; + vim_snprintf((char *)buf, len, "(%s)", cmd); + wcmd = enc_to_utf16(buf, NULL); + free(buf); + } + else + wcmd = enc_to_utf16((char_u *)cmd, NULL); + if (wcmd == NULL) return -1; diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -574,11 +574,17 @@ func Test_set_shell() quit! [CODE] - let $SHELL = '/bin/with space/sh' + if has('win32') + let $SHELL = 'C:\with space\cmd.exe' + let expected = '"C:\with space\cmd.exe"' + else + let $SHELL = '/bin/with space/sh' + let expected = '/bin/with\ space/sh' + endif + if RunVimPiped([], after, '', '') let lines = readfile('Xtestout') - " MS-Windows adds a space after the word - call assert_equal('/bin/with\ space/sh', lines[0]) + call assert_equal(expected, lines[0]) endif call delete('Xtestout') endfunc diff --git a/src/testdir/test_system.vim b/src/testdir/test_system.vim --- a/src/testdir/test_system.vim +++ b/src/testdir/test_system.vim @@ -1,6 +1,7 @@ " Tests for system() and systemlist() source shared.vim +source check.vim func Test_System() if !has('win32') @@ -112,3 +113,53 @@ func Test_system_exmode() let a = system(GetVimCommand() . cmd) call assert_notequal(0, v:shell_error) endfunc + +func Test_system_with_shell_quote() + CheckMSWindows + + call mkdir('Xdir with spaces', 'p') + call system('copy "%COMSPEC%" "Xdir with spaces\cmd.exe"') + + let shell_save = &shell + let shellxquote_save = &shellxquote + try + " Set 'shell' always needs noshellslash. + let shellslash_save = &shellslash + set noshellslash + let shell_tests = [ + \ expand('$COMSPEC'), + \ '"' . fnamemodify('Xdir with spaces\cmd.exe', ':p') . '"', + \] + let &shellslash = shellslash_save + + let sxq_tests = ['', '(', '"'] + + " Matrix tests: 'shell' * 'shellxquote' + for shell in shell_tests + let &shell = shell + for sxq in sxq_tests + let &shellxquote = sxq + + let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote) + + try + let out = 'echo 123'->system() + catch + call assert_report(printf('%s: %s', msg, v:exception)) + continue + endtry + + " On Windows we may get a trailing space and CR. + if out != "123 \n" + call assert_equal("123\n", out, msg) + endif + + endfor + endfor + + finally + let &shell = shell_save + let &shellxquote = shellxquote_save + call delete('Xdir with spaces', 'rf') + endtry +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2115, +/**/ 2114, /**/ 2113, diff --git a/src/vimrun.c b/src/vimrun.c --- a/src/vimrun.c +++ b/src/vimrun.c @@ -27,6 +27,8 @@ main(void) { const wchar_t *p; + wchar_t *cmd; + size_t cmdlen; int retval; int inquote = 0; int silent = 0; @@ -63,16 +65,36 @@ main(void) ++p; } - /* Print the command, including quotes and redirection. */ + // Print the command, including quotes and redirection. hstdout = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsoleW(hstdout, p, wcslen(p), &written, NULL); WriteConsoleW(hstdout, L"\r\n", 2, &written, NULL); + // If the command starts and ends with double quotes, + // Enclose the command in parentheses. + cmd = NULL; + cmdlen = wcslen(p); + if (cmdlen >= 2 && p[0] == L'"' && p[cmdlen - 1] == L'"') + { + cmdlen += 3; + cmd = (wchar_t *)malloc(cmdlen * sizeof(wchar_t)); + if (cmd == NULL) + { + perror("vimrun malloc(): "); + return -1; + } + _snwprintf(cmd, cmdlen, L"(%s)", p); + p = cmd; + } + /* * Do it! */ retval = _wsystem(p); + if (cmd) + free(cmd); + if (retval == -1) perror("vimrun system(): "); else if (retval != 0)