mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
win: libuv_process_spawn(): special-case cmd.exe
Disable CommandLineToArgvW-standard quoting for cmd.exe. libuv assumes spawned processes follow the convention expected by CommandLineToArgvW(). But cmd.exe is non-conformant, so for cmd.exe: - With system([]), the caller has full control (and responsibility) to quote arguments correctly. - With system(''), shell* options are used. libuv quoting is disabled if argv[0] is: - cmd.exe - cmd - $COMSPEC resolving to a path with filename cmd.exe Closes #6329 References #6387
This commit is contained in:
parent
799443c994
commit
f3cc843755
@ -4852,16 +4852,18 @@ jobstart({cmd}[, {opts}]) {Nvim} *jobstart()*
|
|||||||
Spawns {cmd} as a job. If {cmd} is a |List| it is run
|
Spawns {cmd} as a job. If {cmd} is a |List| it is run
|
||||||
directly. If {cmd} is a |String| it is processed like this: >
|
directly. If {cmd} is a |String| it is processed like this: >
|
||||||
:call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
|
:call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
|
||||||
< NOTE: This only shows the idea; see |shell-unquoting| before
|
< (Only shows the idea; see |shell-unquoting| for full details.)
|
||||||
constructing lists with 'shell' or 'shellcmdflag'.
|
|
||||||
|
|
||||||
NOTE: On Windows if {cmd} is a List, cmd[0] must be a valid
|
NOTE: on Windows if {cmd} is a List:
|
||||||
executable (.exe, .com). If the executable is in $PATH it can
|
- cmd[0] must be executable. If it is in $PATH it can be
|
||||||
be called by name, with or without an extension: >
|
called by name, with or without an extension: >
|
||||||
:call jobstart(['ping', 'neovim.io'])
|
:call jobstart(['ping', 'neovim.io'])
|
||||||
< If it is a path (not a name), it must include the extension: >
|
< If it is a path (not a name), extension is required: >
|
||||||
:call jobstart(['System32\ping.exe', 'neovim.io'])
|
:call jobstart(['System32\ping.exe', 'neovim.io'])
|
||||||
<
|
< - {cmd} is quoted per the convention expected by
|
||||||
|
CommandLineToArgvW https://msdn.microsoft.com/bb776391
|
||||||
|
unless the first argument is some form of "cmd.exe".
|
||||||
|
|
||||||
{opts} is a dictionary with these keys:
|
{opts} is a dictionary with these keys:
|
||||||
on_stdout: stdout event handler (function name or |Funcref|)
|
on_stdout: stdout event handler (function name or |Funcref|)
|
||||||
on_stderr: stderr event handler (function name or |Funcref|)
|
on_stderr: stderr event handler (function name or |Funcref|)
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include "nvim/event/process.h"
|
#include "nvim/event/process.h"
|
||||||
#include "nvim/event/libuv_process.h"
|
#include "nvim/event/libuv_process.h"
|
||||||
#include "nvim/log.h"
|
#include "nvim/log.h"
|
||||||
|
#include "nvim/path.h"
|
||||||
|
#include "nvim/os/os.h"
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "event/libuv_process.c.generated.h"
|
# include "event/libuv_process.c.generated.h"
|
||||||
@ -24,6 +26,26 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
|||||||
if (proc->detach) {
|
if (proc->detach) {
|
||||||
uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
|
uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
|
||||||
}
|
}
|
||||||
|
#ifdef WIN32
|
||||||
|
// libuv assumes spawned processes follow the convention from
|
||||||
|
// CommandLineToArgvW(), cmd.exe does not. Disable quoting since it will
|
||||||
|
// result in unexpected behaviour, the caller is left with the responsibility
|
||||||
|
// to quote arguments accordingly. system('') has shell* options for this.
|
||||||
|
//
|
||||||
|
// Disable quoting for cmd, cmd.exe and $COMSPEC with a cmd.exe filename
|
||||||
|
bool is_cmd = STRICMP(proc->argv[0], "cmd.exe") == 0
|
||||||
|
|| STRICMP(proc->argv[0], "cmd") == 0;
|
||||||
|
if (!is_cmd) {
|
||||||
|
const char_u *comspec = (char_u *)os_getenv("COMSPEC");
|
||||||
|
const char_u *comspecshell = path_tail((char_u *)proc->argv[0]);
|
||||||
|
is_cmd = comspec != NULL && STRICMP(proc->argv[0], comspec) == 0
|
||||||
|
&& STRICMP("cmd.exe", (char *)comspecshell) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_cmd) {
|
||||||
|
uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
uvproc->uvopts.exit_cb = exit_cb;
|
uvproc->uvopts.exit_cb = exit_cb;
|
||||||
uvproc->uvopts.cwd = proc->cwd;
|
uvproc->uvopts.cwd = proc->cwd;
|
||||||
uvproc->uvopts.env = NULL;
|
uvproc->uvopts.env = NULL;
|
||||||
|
@ -129,15 +129,39 @@ describe('system()', function()
|
|||||||
screen:detach()
|
screen:detach()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('escapes inner double quotes #6329', function()
|
|
||||||
if helpers.os_name() == 'windows' then
|
if helpers.os_name() == 'windows' then
|
||||||
-- In Windows cmd.exe's echo prints the quotes
|
it('with the default cmd.exe shell', function()
|
||||||
eq('""\n', eval([[system('echo ""')]]))
|
eq('""\n', eval([[system('echo ""')]]))
|
||||||
|
eq('"a b"\n', eval([[system('echo "a b"')]]))
|
||||||
|
-- TODO: consistent with Vim, but it should be fixed
|
||||||
|
eq('a & echo b\n', eval([[system('echo a & echo b')]]))
|
||||||
|
eval([[system('cd "C:\Program Files"')]])
|
||||||
|
eq(0, eval('v:shell_error'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('with set shell="cmd"', function()
|
||||||
|
helpers.source('let &shell="cmd"')
|
||||||
|
eq('"a b"\n', eval([[system('echo "a b"')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works with cmd.exe from $COMSPEC', function()
|
||||||
|
local comspecshell = eval("fnamemodify($COMSPEC, ':t')")
|
||||||
|
if comspecshell == 'cmd.exe' then
|
||||||
|
helpers.source('let &shell=$COMSPEC')
|
||||||
|
eq('"a b"\n', eval([[system('echo "a b"')]]))
|
||||||
else
|
else
|
||||||
eq('\n', eval([[system('echo ""')]]))
|
pending('$COMSPEC is not cmd.exe ' .. comspecshell)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('works with powershell', function()
|
||||||
|
helpers.set_shell_powershell()
|
||||||
|
eq('a\nb\n', eval([[system('echo a b')]]))
|
||||||
|
eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]]))
|
||||||
|
eq('a b\n', eval([[system('echo "a b"')]]))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
it('`echo` and waits for its return', function()
|
it('`echo` and waits for its return', function()
|
||||||
feed(':call system("echo")<cr>')
|
feed(':call system("echo")<cr>')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
|
Loading…
Reference in New Issue
Block a user