aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Bracewell <jack.bracewell@unboxedconsulting.com>2016-03-29 13:47:30 +0100
committerJustin M. Keyes <justinkz@gmail.com>2017-03-17 17:47:33 +0100
commit2ea7bfc627e57bc1dfc1a86b66a4be5c7eed2bec (patch)
treeb0d34745e7573d285340fdf147d9afcf098fb096
parent0c1f7831649e92b6904ca580ee90acd6ba89d1a8 (diff)
downloadrneovim-2ea7bfc627e57bc1dfc1a86b66a4be5c7eed2bec.tar.gz
rneovim-2ea7bfc627e57bc1dfc1a86b66a4be5c7eed2bec.tar.bz2
rneovim-2ea7bfc627e57bc1dfc1a86b66a4be5c7eed2bec.zip
terminal: Support extra arguments in 'shell'. #4504
Tokenize p_sh if used as default in ex_terminal(). Previously p_sh was used as the first arg in a list when calling termopen(), this would try to call an untokenized version of shell, meaning if you had an argument in 'shell': set shell=/bin/bash\ --login the command would fail. Helped-by: oni-link <knil.ino@gmail.com> Closes #3999
-rw-r--r--src/nvim/ex_docmd.c44
-rw-r--r--src/nvim/os/shell.c7
-rw-r--r--test/functional/fixtures/shell-test.c16
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua36
4 files changed, 83 insertions, 20 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index c7cb875b88..1234f8e888 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9627,26 +9627,38 @@ static void ex_folddo(exarg_T *eap)
static void ex_terminal(exarg_T *eap)
{
- char *name = (char *)p_sh; // Default to 'shell' if {cmd} is not given.
- bool mustfree = false;
- char *lquote = "['";
- char *rquote = "']";
+ char ex_cmd[1024];
- if (*eap->arg != NUL) {
- name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");
- mustfree = true;
- lquote = rquote = "\"";
- }
+ if (*eap->arg != NUL) { // Run {cmd} in 'shell'.
+ char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");
+ snprintf(ex_cmd, sizeof(ex_cmd),
+ ":enew%s | call termopen(\"%s\") | startinsert",
+ eap->forceit ? "!" : "", name);
+ xfree(name);
+ } else { // No {cmd}: run the job with tokenized 'shell'.
+ if (*p_sh == NUL) {
+ EMSG(_(e_shellempty));
+ return;
+ }
- char ex_cmd[512];
- snprintf(ex_cmd, sizeof(ex_cmd),
- ":enew%s | call termopen(%s%s%s) | startinsert",
- eap->forceit==TRUE ? "!" : "", lquote, name, rquote);
- do_cmdline_cmd(ex_cmd);
+ char **argv = shell_build_argv(NULL, NULL);
+ char **p = argv;
+ char tempstring[512];
+ char shell_argv[512] = { 0 };
- if (mustfree) {
- xfree(name);
+ while (*p != NULL) {
+ snprintf(tempstring, sizeof(tempstring), ",\"%s\"", *p);
+ xstrlcat(shell_argv, tempstring, sizeof(shell_argv));
+ p++;
+ }
+ shell_free_argv(argv);
+
+ snprintf(ex_cmd, sizeof(ex_cmd),
+ ":enew%s | call termopen([%s]) | startinsert",
+ eap->forceit ? "!" : "", shell_argv + 1);
}
+
+ do_cmdline_cmd(ex_cmd);
}
/// Checks if `cmd` is "previewable" (i.e. supported by 'inccommand').
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index a5fd37ed5c..b449cc3d5a 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -39,12 +39,13 @@ typedef struct {
#endif
/// Builds the argument vector for running the user-configured 'shell' (p_sh)
-/// with an optional command prefixed by 'shellcmdflag' (p_shcf).
+/// with an optional command prefixed by 'shellcmdflag' (p_shcf). E.g.:
+///
+/// ["shell", "-extra_args", "-shellcmdflag", "command with spaces"]
///
/// @param cmd Command string, or NULL to run an interactive shell.
/// @param extra_args Extra arguments to the shell, or NULL.
-/// @return A newly allocated argument vector. It must be freed with
-/// `shell_free_argv` when no longer needed.
+/// @return Newly allocated argument vector. Must be freed with shell_free_argv.
char **shell_build_argv(const char *cmd, const char *extra_args)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC
{
diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c
index d9ec254aff..3f3976ece5 100644
--- a/test/functional/fixtures/shell-test.c
+++ b/test/functional/fixtures/shell-test.c
@@ -12,8 +12,12 @@ static void help(void)
puts(" shell-test");
puts(" shell-test EXE");
puts(" Prints \"ready $ \" to stderr.");
+ puts(" shell-test -t {prompt text}");
+ puts(" Prints \"{prompt text} $ \" to stderr.");
puts(" shell-test EXE \"prog args...\"");
puts(" Prints \"ready $ prog args...\\n\" to stderr.");
+ puts(" shell-test -t {prompt text} EXE \"prog args...\"");
+ puts(" Prints \"{prompt text} $ progs args...\" to stderr.");
puts(" shell-test REP {byte} \"line line line\"");
puts(" Prints \"{lnr}: line line line\\n\" to stdout {byte} times.");
puts(" I.e. for `shell-test REP ab \"test\"'");
@@ -30,7 +34,17 @@ int main(int argc, char **argv)
}
if (argc >= 2) {
- if (strcmp(argv[1], "EXE") == 0) {
+ if (strcmp(argv[1], "-t") == 0) {
+ if (argc < 3) {
+ fprintf(stderr,"Missing prompt text for -t option\n");
+ return 5;
+ } else {
+ fprintf(stderr, "%s $ ", argv[2]);
+ if (argc >= 5 && (strcmp(argv[3], "EXE") == 0)) {
+ fprintf(stderr, "%s\n", argv[4]);
+ }
+ }
+ } else if (strcmp(argv[1], "EXE") == 0) {
fprintf(stderr, "ready $ ");
if (argc >= 3) {
fprintf(stderr, "%s\n", argv[2]);
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 7a9d2a9b36..9bb683f25f 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -79,6 +79,30 @@ describe(':terminal (with fake shell)', function()
]])
end)
+ it("with no argument, and 'shell' is set to empty string", function()
+ nvim('set_option', 'shell', '')
+ terminal_with_fake_shell()
+ wait()
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ E91: 'shell' option is empty |
+ ]])
+ end)
+
+ it("with no argument, but 'shell' has arguments, acts like termopen()", function()
+ nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff')
+ terminal_with_fake_shell()
+ wait()
+ screen:expect([[
+ jeff $ |
+ [Process exited 0] |
+ |
+ -- TERMINAL -- |
+ ]])
+ end)
+
it('executes a given command through the shell', function()
terminal_with_fake_shell('echo hi')
wait()
@@ -90,6 +114,18 @@ describe(':terminal (with fake shell)', function()
]])
end)
+ it("executes a given command through the shell, when 'shell' has arguments", function()
+ nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff')
+ terminal_with_fake_shell('echo hi')
+ wait()
+ screen:expect([[
+ jeff $ echo hi |
+ |
+ [Process exited 0] |
+ -- TERMINAL -- |
+ ]])
+ end)
+
it('allows quotes and slashes', function()
terminal_with_fake_shell([[echo 'hello' \ "world"]])
wait()