Skip to content

Commit 1356825

Browse files
committed
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.
1 parent 43cb626 commit 1356825

10 files changed

Lines changed: 416 additions & 164 deletions

File tree

runtime/doc/options.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3845,7 +3845,14 @@ A jump table for the options with a short description can be found at |Q_op|.
38453845
To avoid problems with flags that are added in the future, use the
38463846
"+=" and "-=" feature of ":set" |add-option-flags|.
38473847

3848-
Valid letters are as follows:
3848+
Valid characters are as follows:
3849+
*'go-!'*
3850+
'!' External commands are executed in a terminal window. Without
3851+
this flag the MS-Windows GUI will open a console window to
3852+
execute the command. The Unix GUI will simulate a dumb
3853+
terminal to list the command output.
3854+
The terminal window will be positioned at the bottom, and grow
3855+
upwards as needed.
38493856
*guioptions_a* *'go-a'*
38503857
'a' Autoselect: If present, then whenever VISUAL mode is started,
38513858
or the Visual area extended, Vim tries to become the owner of

src/channel.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5383,11 +5383,13 @@ job_check_ended(void)
53835383

53845384
/*
53855385
* Create a job and return it. Implements job_start().
5386+
* "argv_arg" is only for Unix.
5387+
* When "argv_arg" is NULL then "argvars" is used.
53865388
* The returned job has a refcount of one.
53875389
* Returns NULL when out of memory.
53885390
*/
53895391
job_T *
5390-
job_start(typval_T *argvars, jobopt_T *opt_arg)
5392+
job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg)
53915393
{
53925394
job_T *job;
53935395
char_u *cmd = NULL;
@@ -5474,6 +5476,13 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
54745476

54755477
job_set_options(job, &opt);
54765478

5479+
#ifdef USE_ARGV
5480+
if (argv_arg != NULL)
5481+
{
5482+
argv = argv_arg;
5483+
}
5484+
else
5485+
#endif
54775486
if (argvars[0].v_type == VAR_STRING)
54785487
{
54795488
/* Command is a string. */
@@ -5551,7 +5560,8 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
55515560

55525561
theend:
55535562
#ifdef USE_ARGV
5554-
vim_free(argv);
5563+
if (argv != argv_arg)
5564+
vim_free(argv);
55555565
#else
55565566
vim_free(ga.ga_data);
55575567
#endif

src/evalfunc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7032,7 +7032,7 @@ f_job_start(typval_T *argvars, typval_T *rettv)
70327032
rettv->v_type = VAR_JOB;
70337033
if (check_restricted() || check_secure())
70347034
return;
7035-
rettv->vval.v_job = job_start(argvars, NULL);
7035+
rettv->vval.v_job = job_start(argvars, NULL, NULL);
70367036
}
70377037

70387038
/*

src/option.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@
214214
#define SHM_ALL "rmfixlnwaWtToOsAIcqF" /* all possible flags for 'shm' */
215215

216216
/* characters for p_go: */
217+
#define GO_TERMINAL '!' /* use terminal for system commands */
217218
#define GO_ASEL 'a' /* autoselect */
218219
#define GO_ASELML 'A' /* autoselect modeless selection */
219220
#define GO_BOT 'b' /* use bottom scrollbar */
@@ -236,7 +237,7 @@
236237
#define GO_FOOTER 'F' /* add footer */
237238
#define GO_VERTICAL 'v' /* arrange dialog buttons vertically */
238239
#define GO_KEEPWINSIZE 'k' /* keep GUI window size */
239-
#define GO_ALL "aAbcefFghilmMprtTvk" /* all possible flags for 'go' */
240+
#define GO_ALL "!aAbcefFghilmMprtTvk" /* all possible flags for 'go' */
240241

241242
/* flags for 'comments' option */
242243
#define COM_NEST 'n' /* comments strings nest */

src/os_unix.c

Lines changed: 153 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4154,10 +4154,13 @@ wait4pid(pid_t child, waitstatus *status)
41544154
return wait_pid;
41554155
}
41564156

4157-
#if defined(FEAT_JOB_CHANNEL) || !defined(USE_SYSTEM) || defined(PROTO)
4157+
#if defined(FEAT_JOB_CHANNEL) \
4158+
|| !defined(USE_SYSTEM) \
4159+
|| (defined(FEAT_GUI) && defined(FEAT_TERMINAL)) \
4160+
|| defined(PROTO)
41584161
/*
41594162
* Parse "cmd" and put the white-separated parts in "argv".
4160-
* "argv" is an allocated array with "argc" entries.
4163+
* "argv" is an allocated array with "argc" entries and room for 4 more.
41614164
* Returns FAIL when out of memory.
41624165
*/
41634166
int
@@ -4359,8 +4362,121 @@ may_send_sigint(int c UNUSED, pid_t pid UNUSED, pid_t wpid UNUSED)
43594362
# endif
43604363
}
43614364

4362-
int
4363-
mch_call_shell(
4365+
#if !defined(USE_SYSTEM) || (defined(FEAT_GUI) && defined(FEAT_TERMINAL))
4366+
4367+
static int
4368+
build_argv(
4369+
char_u *cmd,
4370+
char ***argvp,
4371+
char_u **sh_tofree,
4372+
char_u **shcf_tofree)
4373+
{
4374+
char **argv = NULL;
4375+
int argc;
4376+
4377+
*sh_tofree = vim_strsave(p_sh);
4378+
if (*sh_tofree == NULL) /* out of memory */
4379+
return FAIL;
4380+
4381+
if (mch_parse_cmd(*sh_tofree, TRUE, &argv, &argc) == FAIL)
4382+
return FAIL;
4383+
*argvp = argv;
4384+
4385+
if (cmd != NULL)
4386+
{
4387+
char_u *s;
4388+
char_u *p;
4389+
4390+
if (extra_shell_arg != NULL)
4391+
argv[argc++] = (char *)extra_shell_arg;
4392+
4393+
/* Break 'shellcmdflag' into white separated parts. This doesn't
4394+
* handle quoted strings, they are very unlikely to appear. */
4395+
*shcf_tofree = alloc((unsigned)STRLEN(p_shcf) + 1);
4396+
if (*shcf_tofree == NULL) /* out of memory */
4397+
return FAIL;
4398+
s = *shcf_tofree;
4399+
p = p_shcf;
4400+
while (*p != NUL)
4401+
{
4402+
argv[argc++] = (char *)s;
4403+
while (*p && *p != ' ' && *p != TAB)
4404+
*s++ = *p++;
4405+
*s++ = NUL;
4406+
p = skipwhite(p);
4407+
}
4408+
4409+
argv[argc++] = (char *)cmd;
4410+
}
4411+
argv[argc] = NULL;
4412+
return OK;
4413+
}
4414+
#endif
4415+
4416+
#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4417+
/*
4418+
* Use a terminal window to run a shell command in.
4419+
*/
4420+
static int
4421+
mch_call_shell_terminal(
4422+
char_u *cmd,
4423+
int options UNUSED) /* SHELL_*, see vim.h */
4424+
{
4425+
jobopt_T opt;
4426+
char **argv = NULL;
4427+
char_u *tofree1 = NULL;
4428+
char_u *tofree2 = NULL;
4429+
int retval = -1;
4430+
buf_T *buf;
4431+
aco_save_T aco;
4432+
oparg_T oa; /* operator arguments */
4433+
4434+
if (build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
4435+
goto theend;
4436+
4437+
init_job_options(&opt);
4438+
ch_log(NULL, "starting terminal for system command '%s'", cmd);
4439+
buf = term_start(NULL, argv, &opt, TERM_START_SYSTEM);
4440+
4441+
/* Find a window to make "buf" curbuf. */
4442+
aucmd_prepbuf(&aco, buf);
4443+
4444+
clear_oparg(&oa);
4445+
while (term_use_loop())
4446+
{
4447+
if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
4448+
{
4449+
/* If terminal_loop() returns OK we got a key that is handled
4450+
* in Normal model. We don't do redrawing anyway. */
4451+
if (terminal_loop(TRUE) == OK)
4452+
normal_cmd(&oa, TRUE);
4453+
}
4454+
else
4455+
normal_cmd(&oa, TRUE);
4456+
}
4457+
retval = 0;
4458+
ch_log(NULL, "system command finished");
4459+
4460+
/* restore curwin/curbuf and a few other things */
4461+
aucmd_restbuf(&aco);
4462+
4463+
wait_return(TRUE);
4464+
do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
4465+
4466+
theend:
4467+
vim_free(argv);
4468+
vim_free(tofree1);
4469+
vim_free(tofree2);
4470+
return retval;
4471+
}
4472+
#endif
4473+
4474+
#ifdef USE_SYSTEM
4475+
/*
4476+
* Use system() to start the shell: simple but slow.
4477+
*/
4478+
static int
4479+
mch_call_shell_system(
43644480
char_u *cmd,
43654481
int options) /* SHELL_*, see vim.h */
43664482
{
@@ -4369,7 +4485,6 @@ mch_call_shell(
43694485
char *ofn = NULL;
43704486
#endif
43714487
int tmode = cur_tmode;
4372-
#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */
43734488
char_u *newcmd; /* only needed for unix */
43744489
int x;
43754490

@@ -4443,14 +4558,23 @@ mch_call_shell(
44434558
restore_clipboard();
44444559
# endif
44454560
return x;
4561+
}
44464562

4447-
#else /* USE_SYSTEM */ /* don't use system(), use fork()/exec() */
4563+
#else /* USE_SYSTEM */
44484564

44494565
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
44504566
127, some shells use that already */
44514567
# define OPEN_NULL_FAILED 123 /* Exit code if /dev/null can't be opened */
44524568

4453-
char_u *newcmd;
4569+
/*
4570+
* Don't use system(), use fork()/exec().
4571+
*/
4572+
static int
4573+
mch_call_shell_fork(
4574+
char_u *cmd,
4575+
int options) /* SHELL_*, see vim.h */
4576+
{
4577+
int tmode = cur_tmode;
44544578
pid_t pid;
44554579
pid_t wpid = 0;
44564580
pid_t wait_pid = 0;
@@ -4461,8 +4585,8 @@ mch_call_shell(
44614585
# endif
44624586
int retval = -1;
44634587
char **argv = NULL;
4464-
int argc;
4465-
char_u *p_shcf_copy = NULL;
4588+
char_u *tofree1 = NULL;
4589+
char_u *tofree2 = NULL;
44664590
int i;
44674591
char_u *p;
44684592
int pty_master_fd = -1; /* for pty's */
@@ -4474,44 +4598,13 @@ mch_call_shell(
44744598
int pipe_error = FALSE;
44754599
int did_settmode = FALSE; /* settmode(TMODE_RAW) called */
44764600

4477-
newcmd = vim_strsave(p_sh);
4478-
if (newcmd == NULL) /* out of memory */
4479-
goto error;
4480-
44814601
out_flush();
44824602
if (options & SHELL_COOKED)
44834603
settmode(TMODE_COOK); /* set to normal mode */
44844604

4485-
if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
4605+
if (build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
44864606
goto error;
44874607

4488-
if (cmd != NULL)
4489-
{
4490-
char_u *s;
4491-
4492-
if (extra_shell_arg != NULL)
4493-
argv[argc++] = (char *)extra_shell_arg;
4494-
4495-
/* Break 'shellcmdflag' into white separated parts. This doesn't
4496-
* handle quoted strings, they are very unlikely to appear. */
4497-
p_shcf_copy = alloc((unsigned)STRLEN(p_shcf) + 1);
4498-
if (p_shcf_copy == NULL) /* out of memory */
4499-
goto error;
4500-
s = p_shcf_copy;
4501-
p = p_shcf;
4502-
while (*p != NUL)
4503-
{
4504-
argv[argc++] = (char *)s;
4505-
while (*p && *p != ' ' && *p != TAB)
4506-
*s++ = *p++;
4507-
*s++ = NUL;
4508-
p = skipwhite(p);
4509-
}
4510-
4511-
argv[argc++] = (char *)cmd;
4512-
}
4513-
argv[argc] = NULL;
4514-
45154608
/*
45164609
* For the GUI, when writing the output into the buffer and when reading
45174610
* input from the buffer: Try using a pseudo-tty to get the stdin/stdout
@@ -5319,8 +5412,6 @@ mch_call_shell(
53195412
MSG_PUTS(_("\nCommand terminated\n"));
53205413
}
53215414
}
5322-
vim_free(argv);
5323-
vim_free(p_shcf_copy);
53245415

53255416
error:
53265417
if (!did_settmode)
@@ -5329,11 +5420,28 @@ mch_call_shell(
53295420
# ifdef FEAT_TITLE
53305421
resettitle();
53315422
# endif
5332-
vim_free(newcmd);
5423+
vim_free(argv);
5424+
vim_free(tofree1);
5425+
vim_free(tofree2);
53335426

53345427
return retval;
5335-
5428+
}
53365429
#endif /* USE_SYSTEM */
5430+
5431+
int
5432+
mch_call_shell(
5433+
char_u *cmd,
5434+
int options) /* SHELL_*, see vim.h */
5435+
{
5436+
#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
5437+
if (gui.in_use && vim_strchr(p_go, GO_TERMINAL) != NULL)
5438+
return mch_call_shell_terminal(cmd, options);
5439+
#endif
5440+
#ifdef USE_SYSTEM
5441+
return mch_call_shell_system(cmd, options);
5442+
#else
5443+
return mch_call_shell_fork(cmd, options);
5444+
#endif
53375445
}
53385446

53395447
#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)

src/proto/channel.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void job_set_options(job_T *job, jobopt_T *opt);
6666
void job_stop_on_exit(void);
6767
int has_pending_job(void);
6868
void job_check_ended(void);
69-
job_T *job_start(typval_T *argvars, jobopt_T *opt_arg);
69+
job_T *job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg);
7070
char *job_status(job_T *job);
7171
void job_info(job_T *job, dict_T *dict);
7272
int job_stop(job_T *job, typval_T *argvars, char *type);

src/proto/terminal.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/* terminal.c */
2+
void init_job_options(jobopt_T *opt);
3+
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
24
void ex_terminal(exarg_T *eap);
35
int term_write_session(FILE *fd, win_T *wp);
46
int term_should_restore(buf_T *buf);

0 commit comments

Comments
 (0)