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.
diff --git a/src/os_unix.c b/src/os_unix.c
index c28bda0..af3c700 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4154,10 +4154,13 @@
     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 @@
 # 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 @@
     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 @@
     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 @@
 # 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 @@
     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 @@
 		MSG_PUTS(_("\nCommand terminated\n"));
 	}
     }
-    vim_free(argv);
-    vim_free(p_shcf_copy);
 
 error:
     if (!did_settmode)
@@ -5329,11 +5420,28 @@
 # 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)