Permalink
Browse files

patch 8.0.1609: shell commands in the GUI use a dumb terminal

Problem:    Shell commands in the GUI use a dumb terminal.
Solution:   Add the "!" flag to 'guioptions' to execute system commands in a
            special terminal window.  Only for Unix now.
  • Loading branch information...
brammool committed Mar 16, 2018
1 parent 43cb626 commit 135682517bc378cfdb63fe3a6e3553935f69f6ce
Showing with 416 additions and 164 deletions.
  1. +8 −1 runtime/doc/options.txt
  2. +12 −2 src/channel.c
  3. +1 −1 src/evalfunc.c
  4. +2 −1 src/option.h
  5. +153 −45 src/os_unix.c
  6. +1 −1 src/proto/channel.pro
  7. +2 −0 src/proto/terminal.pro
  8. +230 −113 src/terminal.c
  9. +2 −0 src/version.c
  10. +5 −0 src/vim.h
@@ -3845,7 +3845,14 @@ A jump table for the options with a short description can be found at |Q_op|.
To avoid problems with flags that are added in the future, use the
"+=" and "-=" feature of ":set" |add-option-flags|.
Valid letters are as follows:
Valid characters are as follows:
*'go-!'*
'!' External commands are executed in a terminal window. Without
this flag the MS-Windows GUI will open a console window to
execute the command. The Unix GUI will simulate a dumb
terminal to list the command output.
The terminal window will be positioned at the bottom, and grow
upwards as needed.
*guioptions_a* *'go-a'*
'a' Autoselect: If present, then whenever VISUAL mode is started,
or the Visual area extended, Vim tries to become the owner of
@@ -5383,11 +5383,13 @@ job_check_ended(void)
/*
* Create a job and return it. Implements job_start().
* "argv_arg" is only for Unix.
* When "argv_arg" is NULL then "argvars" is used.
* The returned job has a refcount of one.
* Returns NULL when out of memory.
*/
job_T *
job_start(typval_T *argvars, jobopt_T *opt_arg)
job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg)
{
job_T *job;
char_u *cmd = NULL;
@@ -5474,6 +5476,13 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
job_set_options(job, &opt);
#ifdef USE_ARGV
if (argv_arg != NULL)
{
argv = argv_arg;
}
else
#endif
if (argvars[0].v_type == VAR_STRING)
{
/* Command is a string. */
@@ -5551,7 +5560,8 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
theend:
#ifdef USE_ARGV
vim_free(argv);
if (argv != argv_arg)
vim_free(argv);
#else
vim_free(ga.ga_data);
#endif
@@ -7032,7 +7032,7 @@ f_job_start(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_JOB;
if (check_restricted() || check_secure())
return;
rettv->vval.v_job = job_start(argvars, NULL);
rettv->vval.v_job = job_start(argvars, NULL, NULL);
}
/*
@@ -214,6 +214,7 @@
#define SHM_ALL "rmfixlnwaWtToOsAIcqF" /* all possible flags for 'shm' */
/* characters for p_go: */
#define GO_TERMINAL '!' /* use terminal for system commands */
#define GO_ASEL 'a' /* autoselect */
#define GO_ASELML 'A' /* autoselect modeless selection */
#define GO_BOT 'b' /* use bottom scrollbar */
@@ -236,7 +237,7 @@
#define GO_FOOTER 'F' /* add footer */
#define GO_VERTICAL 'v' /* arrange dialog buttons vertically */
#define GO_KEEPWINSIZE 'k' /* keep GUI window size */
#define GO_ALL "aAbcefFghilmMprtTvk" /* all possible flags for 'go' */
#define GO_ALL "!aAbcefFghilmMprtTvk" /* all possible flags for 'go' */
/* flags for 'comments' option */
#define COM_NEST 'n' /* comments strings nest */
@@ -4154,10 +4154,13 @@ wait4pid(pid_t child, waitstatus *status)
return wait_pid;
}
#if defined(FEAT_JOB_CHANNEL) || !defined(USE_SYSTEM) || defined(PROTO)
#if defined(FEAT_JOB_CHANNEL) \
|| !defined(USE_SYSTEM) \
|| (defined(FEAT_GUI) && defined(FEAT_TERMINAL)) \
|| defined(PROTO)
/*
* Parse "cmd" and put the white-separated parts in "argv".
* "argv" is an allocated array with "argc" entries.
* "argv" is an allocated array with "argc" entries and room for 4 more.
* Returns FAIL when out of memory.
*/
int
@@ -4359,8 +4362,121 @@ may_send_sigint(int c UNUSED, pid_t pid UNUSED, pid_t wpid UNUSED)
# endif
}
int
mch_call_shell(
#if !defined(USE_SYSTEM) || (defined(FEAT_GUI) && defined(FEAT_TERMINAL))
static int
build_argv(
char_u *cmd,
char ***argvp,
char_u **sh_tofree,
char_u **shcf_tofree)
{
char **argv = NULL;
int argc;
*sh_tofree = vim_strsave(p_sh);
if (*sh_tofree == NULL) /* out of memory */
return FAIL;
if (mch_parse_cmd(*sh_tofree, TRUE, &argv, &argc) == FAIL)
return FAIL;
*argvp = argv;
if (cmd != NULL)
{
char_u *s;
char_u *p;
if (extra_shell_arg != NULL)
argv[argc++] = (char *)extra_shell_arg;
/* Break 'shellcmdflag' into white separated parts. This doesn't
* handle quoted strings, they are very unlikely to appear. */
*shcf_tofree = alloc((unsigned)STRLEN(p_shcf) + 1);
if (*shcf_tofree == NULL) /* out of memory */
return FAIL;
s = *shcf_tofree;
p = p_shcf;
while (*p != NUL)
{
argv[argc++] = (char *)s;
while (*p && *p != ' ' && *p != TAB)
*s++ = *p++;
*s++ = NUL;
p = skipwhite(p);
}
argv[argc++] = (char *)cmd;
}
argv[argc] = NULL;
return OK;
}
#endif
#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
/*
* Use a terminal window to run a shell command in.
*/
static int
mch_call_shell_terminal(
char_u *cmd,
int options UNUSED) /* SHELL_*, see vim.h */
{
jobopt_T opt;
char **argv = NULL;
char_u *tofree1 = NULL;
char_u *tofree2 = NULL;
int retval = -1;
buf_T *buf;
aco_save_T aco;
oparg_T oa; /* operator arguments */
if (build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
goto theend;
init_job_options(&opt);
ch_log(NULL, "starting terminal for system command '%s'", cmd);
buf = term_start(NULL, argv, &opt, TERM_START_SYSTEM);
/* Find a window to make "buf" curbuf. */
aucmd_prepbuf(&aco, buf);
clear_oparg(&oa);
while (term_use_loop())
{
if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
{
/* If terminal_loop() returns OK we got a key that is handled
* in Normal model. We don't do redrawing anyway. */
if (terminal_loop(TRUE) == OK)
normal_cmd(&oa, TRUE);
}
else
normal_cmd(&oa, TRUE);
}
retval = 0;
ch_log(NULL, "system command finished");
/* restore curwin/curbuf and a few other things */
aucmd_restbuf(&aco);
wait_return(TRUE);
do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
theend:
vim_free(argv);
vim_free(tofree1);
vim_free(tofree2);
return retval;
}
#endif
#ifdef USE_SYSTEM
/*
* Use system() to start the shell: simple but slow.
*/
static int
mch_call_shell_system(
char_u *cmd,
int options) /* SHELL_*, see vim.h */
{
@@ -4369,7 +4485,6 @@ mch_call_shell(
char *ofn = NULL;
#endif
int tmode = cur_tmode;
#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */
char_u *newcmd; /* only needed for unix */
int x;
@@ -4443,14 +4558,23 @@ mch_call_shell(
restore_clipboard();
# endif
return x;
}
#else /* USE_SYSTEM */ /* don't use system(), use fork()/exec() */
#else /* USE_SYSTEM */
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
127, some shells use that already */
# define OPEN_NULL_FAILED 123 /* Exit code if /dev/null can't be opened */
char_u *newcmd;
/*
* Don't use system(), use fork()/exec().
*/
static int
mch_call_shell_fork(
char_u *cmd,
int options) /* SHELL_*, see vim.h */
{
int tmode = cur_tmode;
pid_t pid;
pid_t wpid = 0;
pid_t wait_pid = 0;
@@ -4461,8 +4585,8 @@ mch_call_shell(
# endif
int retval = -1;
char **argv = NULL;
int argc;
char_u *p_shcf_copy = NULL;
char_u *tofree1 = NULL;
char_u *tofree2 = NULL;
int i;
char_u *p;
int pty_master_fd = -1; /* for pty's */
@@ -4474,44 +4598,13 @@ mch_call_shell(
int pipe_error = FALSE;
int did_settmode = FALSE; /* settmode(TMODE_RAW) called */
newcmd = vim_strsave(p_sh);
if (newcmd == NULL) /* out of memory */
goto error;
out_flush();
if (options & SHELL_COOKED)
settmode(TMODE_COOK); /* set to normal mode */
if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
if (build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
goto error;
if (cmd != NULL)
{
char_u *s;
if (extra_shell_arg != NULL)
argv[argc++] = (char *)extra_shell_arg;
/* Break 'shellcmdflag' into white separated parts. This doesn't
* handle quoted strings, they are very unlikely to appear. */
p_shcf_copy = alloc((unsigned)STRLEN(p_shcf) + 1);
if (p_shcf_copy == NULL) /* out of memory */
goto error;
s = p_shcf_copy;
p = p_shcf;
while (*p != NUL)
{
argv[argc++] = (char *)s;
while (*p && *p != ' ' && *p != TAB)
*s++ = *p++;
*s++ = NUL;
p = skipwhite(p);
}
argv[argc++] = (char *)cmd;
}
argv[argc] = NULL;
/*
* For the GUI, when writing the output into the buffer and when reading
* input from the buffer: Try using a pseudo-tty to get the stdin/stdout
@@ -5319,8 +5412,6 @@ mch_call_shell(
MSG_PUTS(_("\nCommand terminated\n"));
}
}
vim_free(argv);
vim_free(p_shcf_copy);
error:
if (!did_settmode)
@@ -5329,11 +5420,28 @@ mch_call_shell(
# ifdef FEAT_TITLE
resettitle();
# endif
vim_free(newcmd);
vim_free(argv);
vim_free(tofree1);
vim_free(tofree2);
return retval;
}
#endif /* USE_SYSTEM */
int
mch_call_shell(
char_u *cmd,
int options) /* SHELL_*, see vim.h */
{
#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
if (gui.in_use && vim_strchr(p_go, GO_TERMINAL) != NULL)
return mch_call_shell_terminal(cmd, options);
#endif
#ifdef USE_SYSTEM
return mch_call_shell_system(cmd, options);
#else
return mch_call_shell_fork(cmd, options);
#endif
}
#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
@@ -66,7 +66,7 @@ void job_set_options(job_T *job, jobopt_T *opt);
void job_stop_on_exit(void);
int has_pending_job(void);
void job_check_ended(void);
job_T *job_start(typval_T *argvars, jobopt_T *opt_arg);
job_T *job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg);
char *job_status(job_T *job);
void job_info(job_T *job, dict_T *dict);
int job_stop(job_T *job, typval_T *argvars, char *type);
@@ -1,4 +1,6 @@
/* terminal.c */
void init_job_options(jobopt_T *opt);
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
void ex_terminal(exarg_T *eap);
int term_write_session(FILE *fd, win_T *wp);
int term_should_restore(buf_T *buf);
Oops, something went wrong.

0 comments on commit 1356825

Please sign in to comment.