summaryrefslogtreecommitdiffstats
path: root/src/os_unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/os_unix.c')
-rw-r--r--src/os_unix.c198
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)