diff options
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 198 |
1 files changed, 153 insertions, 45 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index c28bda0aad..af3c700db7 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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 @@ finished: MSG_PUTS(_("\nCommand terminated\n")); } } - vim_free(argv); - vim_free(p_shcf_copy); error: if (!did_settmode) @@ -5329,11 +5420,28 @@ error: # 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) |