diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2018-09-21 09:20:04 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-21 09:20:04 +0200 |
commit | ecdd2df88ab52ed6e39057e2a8fb9eabfbb90bd5 (patch) | |
tree | a6072625d7c97b943c4ed5cbfe3d258bd111c23f | |
parent | ad6bbe44683cf936ec2268ee89e8e1c22f538ecc (diff) | |
download | rneovim-ecdd2df88ab52ed6e39057e2a8fb9eabfbb90bd5.tar.gz rneovim-ecdd2df88ab52ed6e39057e2a8fb9eabfbb90bd5.tar.bz2 rneovim-ecdd2df88ab52ed6e39057e2a8fb9eabfbb90bd5.zip |
shell/logging: Fix E730 with verbose system({List}) #9009
ref https://github.com/neovim/neovim/issues/9001#issuecomment-421843790
Steps to reproduce:
:set verbose=9
:call system(['echo'])
E730: using List as a String
-rw-r--r-- | src/nvim/eval.c | 7 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 10 | ||||
-rw-r--r-- | src/nvim/misc1.c | 2 | ||||
-rw-r--r-- | src/nvim/os/shell.c | 38 | ||||
-rw-r--r-- | test/functional/eval/system_spec.lua | 22 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 2 | ||||
-rw-r--r-- | test/unit/os/shell_spec.lua | 31 |
7 files changed, 82 insertions, 30 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d0077ecc9..44560792f0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16471,13 +16471,12 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } if (p_verbose > 3) { - char buf[NUMBUFLEN]; - const char * cmd = tv_get_string_buf(argvars, buf); - + char *cmdstr = shell_argv_to_str(argv); verbose_enter_scroll(); - smsg(_("Calling shell to execute: \"%s\""), cmd); + smsg(_("Executing command: \"%s\""), cmdstr); msg_puts("\n\n"); verbose_leave_scroll(); + xfree(cmdstr); } if (do_profiling == PROF_YES) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b151357196..55dcfcc198 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2632,7 +2632,7 @@ static const char *const str_errors[] = { #undef FUNC_ERROR -/// Check that given value is a string or can be converted to it +/// Check that given value is a VimL String or can be "cast" to it. /// /// Error messages are compatible with tv_get_string_chk() previously used for /// the same purpose. @@ -2805,7 +2805,7 @@ float_T tv_get_float(const typval_T *const tv) return 0; } -/// Get the string value of a VimL object +/// Get the string value of a "stringish" VimL object. /// /// @param[in] tv Object to get value of. /// @param buf Buffer used to hold numbers and special variables converted to @@ -2847,7 +2847,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) return NULL; } -/// Get the string value of a VimL object +/// Get the string value of a "stringish" VimL object. /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use @@ -2866,7 +2866,7 @@ const char *tv_get_string_chk(const typval_T *const tv) return tv_get_string_buf_chk(tv, mybuf); } -/// Get the string value of a VimL object +/// Get the string value of a "stringish" VimL object. /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use @@ -2888,7 +2888,7 @@ const char *tv_get_string(const typval_T *const tv) return tv_get_string_buf((typval_T *)tv, mybuf); } -/// Get the string value of a VimL object +/// Get the string value of a "stringish" VimL object. /// /// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 7a32b3c00a..2e7fe4d7a9 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2718,7 +2718,7 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) if (p_verbose > 3) { verbose_enter(); - smsg(_("Calling shell to execute: \"%s\""), cmd == NULL ? p_sh : cmd); + smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd); msg_putchar('\n'); verbose_leave(); } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index aa4e7d307b..19d199f4d5 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -80,21 +80,53 @@ char **shell_build_argv(const char *cmd, const char *extra_args) void shell_free_argv(char **argv) { char **p = argv; - if (p == NULL) { // Nothing was allocated, return return; } - while (*p != NULL) { // Free each argument xfree(*p); p++; } - xfree(argv); } +/// Joins shell arguments from `argv` into a new string. +/// If the result is too long it is truncated with ellipsis ("..."). +/// +/// @returns[allocated] `argv` joined to a string. +char *shell_argv_to_str(char **const argv) + FUNC_ATTR_NONNULL_ALL +{ + size_t n = 0; + char **p = argv; + char *rv = xcalloc(256, sizeof(*rv)); + const size_t maxsize = (256 * sizeof(*rv)); + if (*p == NULL) { + return rv; + } + while (*p != NULL) { + xstrlcat(rv, "'", maxsize); + xstrlcat(rv, *p, maxsize); + n = xstrlcat(rv, "' ", maxsize); + if (n >= maxsize) { + break; + } + p++; + } + if (n < maxsize) { + rv[n - 1] = '\0'; + } else { + // Command too long, show ellipsis: "/bin/bash 'foo' 'bar'..." + rv[maxsize - 4] = '.'; + rv[maxsize - 3] = '.'; + rv[maxsize - 2] = '.'; + rv[maxsize - 1] = '\0'; + } + return rv; +} + /// Calls the user-configured 'shell' (p_sh) for running a command or wildcard /// expansion. /// diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 0d84f47b65..5cbf34365b 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -203,23 +203,13 @@ describe('system()', function() end) it('prints verbose information', function() + screen:try_resize(72, 14) feed(':4verbose echo system("echo hi")<cr>') - screen:expect([[ - | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - | - Calling shell to execute: "echo hi" | - | - hi | - | - Press ENTER or type command to continue^ | - ]]) + if iswin() then + screen:expect{any=[[Executing command: "'cmd.exe' '/s' '/c' '"echo hi"'"]]} + else + screen:expect{any=[[Executing command: "'/[^']*sh' '%-c' 'echo hi'"]]} + end feed('<cr>') end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 364b65c581..3831968f5b 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -243,7 +243,7 @@ local ext_keys = { -- nothing is ignored. -- condition: Function asserting some arbitrary condition. Return value is -- ignored, throw an error (use eq() or similar) to signal failure. --- any: A string that should be present on any line of the screen. +-- any: Lua pattern string expected to match a screen line. -- mode: Expected mode as signaled by "mode_change" event -- -- The following keys should be used to expect the state of various ext_ diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index 37274502de..a73fc8e47e 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -25,6 +25,7 @@ describe('shell functions', function() local res = cimported.shell_build_argv( cmd and to_cstr(cmd), extra_args and to_cstr(extra_args)) + -- `res` is zero-indexed (C pointer, not Lua table)! local argc = 0 local ret = {} -- Explicitly free everything, so if it is not in allocated memory it will @@ -38,6 +39,26 @@ describe('shell functions', function() return ret end + local function shell_argv_to_str(argv_table) + -- C string array (char **). + local argv = (argv_table + and ffi.new("char*[?]", #argv_table+1) + or NULL) + + local argc = 1 + while argv_table ~= nil and argv_table[argc] ~= nil do + -- `argv` is zero-indexed (C pointer, not Lua table)! + argv[argc - 1] = to_cstr(argv_table[argc]) + argc = argc + 1 + end + if argv_table ~= nil then + argv[argc - 1] = NULL + end + + local res = cimported.shell_argv_to_str(argv) + return ffi.string(res) + end + local function os_system(cmd, input) local input_or = input and to_cstr(input) or NULL local input_len = (input ~= nil) and string.len(input) or 0 @@ -151,4 +172,14 @@ describe('shell functions', function() eq(nil, argv[3]) end) end) + + itp('shell_argv_to_str', function() + eq('', shell_argv_to_str({ nil })) + eq("''", shell_argv_to_str({ '' })) + eq("'foo' '' 'bar'", shell_argv_to_str({ 'foo', '', 'bar' })) + eq("'/bin/sh' '-c' 'abc def'", shell_argv_to_str({'/bin/sh', '-c', 'abc def'})) + eq("'abc def' 'ghi jkl'", shell_argv_to_str({'abc def', 'ghi jkl'})) + eq("'/bin/sh' '-c' 'abc def' '"..('x'):rep(225).."...", + shell_argv_to_str({'/bin/sh', '-c', 'abc def', ('x'):rep(999)})) + end) end) |