changeset 18241:85160a3649b9 v8.1.2115

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 <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Sat, 05 Oct 2019 12:15:04 +0200
parents 7aed836f50f7
children 0d7e8480d1c5
files src/option.c src/os_win32.c src/testdir/test_startup.vim src/testdir/test_system.vim src/version.c src/vimrun.c
diffstat 6 files changed, 123 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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
     /*
--- 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;
 
--- 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
--- 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
--- 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,
--- 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)