patch 8.0.0761: options not set properly for a terminal buffer

Problem:    Options of a buffer for a terminal window are not set properly.
Solution:   Add "terminal" value for 'buftype'.  Make 'buftype' and
            'bufhidden' not depend on the quickfix feature.
            Also set the buffer name and show "running" or "finished" in the
            window title.
diff --git a/src/buffer.c b/src/buffer.c
index dbd4d3a..ae23db0 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -468,7 +468,6 @@
     int		del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
     int		wipe_buf = (action == DOBUF_WIPE);
 
-#ifdef FEAT_QUICKFIX
     /*
      * Force unloading or deleting when 'bufhidden' says so.
      * The caller must take care of NOT deleting/freeing when 'bufhidden' is
@@ -487,7 +486,6 @@
     }
     else if (buf->b_p_bh[0] == 'u')	/* 'bufhidden' == "unload" */
 	unload_buf = TRUE;
-#endif
 
 #ifdef FEAT_AUTOCMD
     /* Disallow deleting the buffer when it is locked (already being closed or
@@ -1982,16 +1980,14 @@
 	    return NULL;
 # endif
 #endif
-#ifdef FEAT_QUICKFIX
-# ifdef FEAT_AUTOCMD
+#ifdef FEAT_AUTOCMD
 	if (buf == curbuf)
-# endif
+#endif
 	{
 	    /* Make sure 'bufhidden' and 'buftype' are empty */
 	    clear_string_option(&buf->b_p_bh);
 	    clear_string_option(&buf->b_p_bt);
 	}
-#endif
     }
     if (buf != curbuf || curbuf == NULL)
     {
@@ -2165,10 +2161,8 @@
 	clear_string_option(&buf->b_p_fenc);
 #endif
 	clear_string_option(&buf->b_p_ff);
-#ifdef FEAT_QUICKFIX
 	clear_string_option(&buf->b_p_bh);
 	clear_string_option(&buf->b_p_bt);
-#endif
     }
 #ifdef FEAT_FIND_ID
     clear_string_option(&buf->b_p_def);
@@ -3668,9 +3662,21 @@
 		/* remove the file name */
 		p = gettail_sep(buf + off);
 		if (p == buf + off)
-		    /* must be a help buffer */
-		    vim_strncpy(buf + off, (char_u *)_("help"),
+		{
+		    char *txt;
+
+#ifdef FEAT_TERMINAL
+		    if (curbuf->b_term != NULL)
+			txt = term_job_running(curbuf)
+						? _("running") : _("finished");
+		    else
+#endif
+			txt = _("help");
+
+		    /* must be a help or terminal buffer */
+		    vim_strncpy(buf + off, (char_u *)txt,
 					   (size_t)(SPACE_FOR_DIR - off - 1));
+		}
 		else
 		    *p = NUL;
 
diff --git a/src/channel.c b/src/channel.c
index c79107c..9885dfe 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -4696,6 +4696,10 @@
 	 * not use "job" after this! */
 	job_free(job);
     }
+
+#ifdef FEAT_TERMINAL
+    term_job_ended(job);
+#endif
 }
 
 /*
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 8f427cf..506a808 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8548,7 +8548,7 @@
     {
 	if (*eap->arg == '-' || *eap->arg == '+')
 	    n += curwin->w_height;
-	else if (n == 0 && eap->arg[0] == NUL)	/* default is very wide */
+	else if (n == 0 && eap->arg[0] == NUL)	/* default is very high */
 	    n = 9999;
 	win_setheight_win((int)n, wp);
     }
@@ -11679,10 +11679,7 @@
      */
     if ((*flagp & SSOP_FOLDS)
 	    && wp->w_buffer->b_ffname != NULL
-# ifdef FEAT_QUICKFIX
-	    && (*wp->w_buffer->b_p_bt == NUL || wp->w_buffer->b_help)
-# endif
-	    )
+	    && (*wp->w_buffer->b_p_bt == NUL || wp->w_buffer->b_help))
     {
 	if (put_folds(fd, wp) == FAIL)
 	    return FAIL;
diff --git a/src/fileio.c b/src/fileio.c
index cac866c..750c08f 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6882,9 +6882,7 @@
      * this buffer. */
     if (buf->b_ffname == NULL
 	    || buf->b_ml.ml_mfp == NULL
-#if defined(FEAT_QUICKFIX)
 	    || *buf->b_p_bt != NUL
-#endif
 	    || buf->b_saving
 #ifdef FEAT_AUTOCMD
 	    || busy
diff --git a/src/option.c b/src/option.c
index 5247893..fa7eac4 100644
--- a/src/option.c
+++ b/src/option.c
@@ -287,10 +287,8 @@
 #ifdef FEAT_MBYTE
 static int	p_bomb;
 #endif
-#if defined(FEAT_QUICKFIX)
 static char_u	*p_bh;
 static char_u	*p_bt;
-#endif
 static int	p_bl;
 static int	p_ci;
 #ifdef FEAT_CINDENT
@@ -720,26 +718,16 @@
 #endif
 			    SCRIPTID_INIT},
     {"bufhidden",   "bh",   P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB,
-#if defined(FEAT_QUICKFIX)
 			    (char_u *)&p_bh, PV_BH,
 			    {(char_u *)"", (char_u *)0L}
-#else
-			    (char_u *)NULL, PV_NONE,
-			    {(char_u *)0L, (char_u *)0L}
-#endif
 			    SCRIPTID_INIT},
     {"buflisted",   "bl",   P_BOOL|P_VI_DEF|P_NOGLOB,
 			    (char_u *)&p_bl, PV_BL,
 			    {(char_u *)1L, (char_u *)0L}
 			    SCRIPTID_INIT},
     {"buftype",	    "bt",   P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB,
-#if defined(FEAT_QUICKFIX)
 			    (char_u *)&p_bt, PV_BT,
 			    {(char_u *)"", (char_u *)0L}
-#else
-			    (char_u *)NULL, PV_NONE,
-			    {(char_u *)0L, (char_u *)0L}
-#endif
 			    SCRIPTID_INIT},
     {"casemap",	    "cmp",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_MBYTE
@@ -3254,14 +3242,12 @@
 #ifdef FEAT_WINDOWS
 static char *(p_ead_values[]) = {"both", "ver", "hor", NULL};
 #endif
-#if defined(FEAT_QUICKFIX)
-# ifdef FEAT_AUTOCMD
-static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "acwrite", NULL};
-# else
-static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", NULL};
-# endif
-static char *(p_bufhidden_values[]) = {"hide", "unload", "delete", "wipe", NULL};
+#ifdef FEAT_AUTOCMD
+static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "terminal", "acwrite", NULL};
+#else
+static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "terminal", NULL};
 #endif
+static char *(p_bufhidden_values[]) = {"hide", "unload", "delete", "wipe", NULL};
 static char *(p_bs_values[]) = {"indent", "eol", "start", NULL};
 #ifdef FEAT_FOLDING
 static char *(p_fdm_values[]) = {"manual", "expr", "marker", "indent", "syntax",
@@ -5649,10 +5635,8 @@
     void
 check_buf_options(buf_T *buf)
 {
-#if defined(FEAT_QUICKFIX)
     check_string_option(&buf->b_p_bh);
     check_string_option(&buf->b_p_bt);
-#endif
 #ifdef FEAT_MBYTE
     check_string_option(&buf->b_p_fenc);
 #endif
@@ -7115,7 +7099,6 @@
     }
 #endif
 
-#ifdef FEAT_QUICKFIX
     /* When 'bufhidden' is set, check for valid value. */
     else if (gvarp == &p_bh)
     {
@@ -7130,20 +7113,19 @@
 	    errmsg = e_invarg;
 	else
 	{
-# ifdef FEAT_WINDOWS
+#ifdef FEAT_WINDOWS
 	    if (curwin->w_status_height)
 	    {
 		curwin->w_redr_status = TRUE;
 		redraw_later(VALID);
 	    }
-# endif
+#endif
 	    curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
-# ifdef FEAT_TITLE
+#ifdef FEAT_TITLE
 	    redraw_titles();
-# endif
+#endif
 	}
     }
-#endif
 
 #ifdef FEAT_STL_OPT
     /* 'statusline' or 'rulerformat' */
@@ -10722,10 +10704,8 @@
 #ifdef FEAT_MBYTE
 	case PV_BOMB:	return (char_u *)&(curbuf->b_p_bomb);
 #endif
-#if defined(FEAT_QUICKFIX)
 	case PV_BH:	return (char_u *)&(curbuf->b_p_bh);
 	case PV_BT:	return (char_u *)&(curbuf->b_p_bt);
-#endif
 	case PV_BL:	return (char_u *)&(curbuf->b_p_bl);
 	case PV_CI:	return (char_u *)&(curbuf->b_p_ci);
 #ifdef FEAT_CINDENT
@@ -11119,10 +11099,8 @@
 		}
 		if (buf->b_p_ff != NULL)
 		    buf->b_start_ffc = *buf->b_p_ff;
-#if defined(FEAT_QUICKFIX)
 		buf->b_p_bh = empty_option;
 		buf->b_p_bt = empty_option;
-#endif
 	    }
 	    else
 		free_buf_options(buf, FALSE);
@@ -11284,10 +11262,8 @@
 		did_isk = TRUE;
 		buf->b_p_ts = p_ts;
 		buf->b_help = FALSE;
-#ifdef FEAT_QUICKFIX
 		if (buf->b_p_bt[0] == 'h')
 		    clear_string_option(&buf->b_p_bt);
-#endif
 		buf->b_p_ma = p_ma;
 	    }
 	}
diff --git a/src/proto/quickfix.pro b/src/proto/quickfix.pro
index 1fc8b4a..a1a634e 100644
--- a/src/proto/quickfix.pro
+++ b/src/proto/quickfix.pro
@@ -12,11 +12,6 @@
 void ex_copen(exarg_T *eap);
 void ex_cbottom(exarg_T *eap);
 linenr_T qf_current_entry(win_T *wp);
-int bt_quickfix(buf_T *buf);
-int bt_nofile(buf_T *buf);
-int bt_dontwrite(buf_T *buf);
-int bt_dontwrite_msg(buf_T *buf);
-int buf_hide(buf_T *buf);
 int grep_internal(cmdidx_T cmdidx);
 void ex_make(exarg_T *eap);
 int qf_get_size(exarg_T *eap);
@@ -33,4 +28,10 @@
 void ex_cbuffer(exarg_T *eap);
 void ex_cexpr(exarg_T *eap);
 void ex_helpgrep(exarg_T *eap);
+int bt_quickfix(buf_T *buf);
+int bt_terminal(buf_T *buf);
+int bt_nofile(buf_T *buf);
+int bt_dontwrite(buf_T *buf);
+int bt_dontwrite_msg(buf_T *buf);
+int buf_hide(buf_T *buf);
 /* vim: set ft=c : */
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index 58eb77f..bdab890 100644
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -2,6 +2,8 @@
 void ex_terminal(exarg_T *eap);
 void free_terminal(term_T *term);
 void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel);
-void term_update_window(win_T *wp);
 void terminal_loop(void);
+void term_job_ended(job_T *job);
+int term_job_running(buf_T *buf);
+void term_update_window(win_T *wp);
 /* vim: set ft=c : */
diff --git a/src/quickfix.c b/src/quickfix.c
index 2adfc52..1d63237 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -2169,7 +2169,7 @@
 	     * quickfix window. */
 	    FOR_ALL_WINDOWS(usable_win_ptr)
 		if (usable_win_ptr->w_llist == ll_ref
-			&& usable_win_ptr->w_buffer->b_p_bt[0] != 'q')
+			&& !bt_quickfix(usable_win_ptr->w_buffer))
 		{
 		    usable_win = 1;
 		    break;
@@ -3450,64 +3450,6 @@
 #endif /* FEAT_WINDOWS */
 
 /*
- * Return TRUE if "buf" is the quickfix buffer.
- */
-    int
-bt_quickfix(buf_T *buf)
-{
-    return buf != NULL && buf->b_p_bt[0] == 'q';
-}
-
-/*
- * Return TRUE if "buf" is a "nofile" or "acwrite" buffer.
- * This means the buffer name is not a file name.
- */
-    int
-bt_nofile(buf_T *buf)
-{
-    return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
-	    || buf->b_p_bt[0] == 'a');
-}
-
-/*
- * Return TRUE if "buf" is a "nowrite" or "nofile" buffer.
- */
-    int
-bt_dontwrite(buf_T *buf)
-{
-    return buf != NULL && buf->b_p_bt[0] == 'n';
-}
-
-    int
-bt_dontwrite_msg(buf_T *buf)
-{
-    if (bt_dontwrite(buf))
-    {
-	EMSG(_("E382: Cannot write, 'buftype' option is set"));
-	return TRUE;
-    }
-    return FALSE;
-}
-
-/*
- * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
- * and 'bufhidden'.
- */
-    int
-buf_hide(buf_T *buf)
-{
-    /* 'bufhidden' overrules 'hidden' and ":hide", check it first */
-    switch (buf->b_p_bh[0])
-    {
-	case 'u':		    /* "unload" */
-	case 'w':		    /* "wipe" */
-	case 'd': return FALSE;	    /* "delete" */
-	case 'h': return TRUE;	    /* "hide" */
-    }
-    return (p_hid || cmdmod.hide);
-}
-
-/*
  * Return TRUE when using ":vimgrep" for ":grep".
  */
     int
@@ -5584,3 +5526,72 @@
 }
 
 #endif /* FEAT_QUICKFIX */
+
+/*
+ * Return TRUE if "buf" is the quickfix buffer.
+ */
+    int
+bt_quickfix(buf_T *buf)
+{
+    return buf != NULL && buf->b_p_bt[0] == 'q';
+}
+
+/*
+ * Return TRUE if "buf" is a terminal buffer.
+ */
+    int
+bt_terminal(buf_T *buf)
+{
+    return buf != NULL && buf->b_p_bt[0] == 't';
+}
+
+/*
+ * Return TRUE if "buf" is a "nofile", "acwrite" or "terminal" buffer.
+ * This means the buffer name is not a file name.
+ */
+    int
+bt_nofile(buf_T *buf)
+{
+    return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
+	    || buf->b_p_bt[0] == 'a'
+	    || buf->b_p_bt[0] == 't');
+}
+
+/*
+ * Return TRUE if "buf" is a "nowrite", "nofile" or "terminal" buffer.
+ */
+    int
+bt_dontwrite(buf_T *buf)
+{
+    return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->b_p_bt[0] == 't');
+}
+
+    int
+bt_dontwrite_msg(buf_T *buf)
+{
+    if (bt_dontwrite(buf))
+    {
+	EMSG(_("E382: Cannot write, 'buftype' option is set"));
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
+ * and 'bufhidden'.
+ */
+    int
+buf_hide(buf_T *buf)
+{
+    /* 'bufhidden' overrules 'hidden' and ":hide", check it first */
+    switch (buf->b_p_bh[0])
+    {
+	case 'u':		    /* "unload" */
+	case 'w':		    /* "wipe" */
+	case 'd': return FALSE;	    /* "delete" */
+	case 'h': return TRUE;	    /* "hide" */
+    }
+    return (p_hid || cmdmod.hide);
+}
+
diff --git a/src/structs.h b/src/structs.h
index 44df356..cfe72db 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2088,9 +2088,9 @@
 #ifdef FEAT_MBYTE
     int		b_p_bomb;	/* 'bomb' */
 #endif
-#ifdef FEAT_QUICKFIX
     char_u	*b_p_bh;	/* 'bufhidden' */
     char_u	*b_p_bt;	/* 'buftype' */
+#ifdef FEAT_QUICKFIX
 #define BUF_HAS_QF_ENTRY 1
 #define BUF_HAS_LL_ENTRY 2
     int		b_has_qf_entry;
diff --git a/src/terminal.c b/src/terminal.c
index 3f1a91d..f5a9fd1 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -33,9 +33,8 @@
  * while, if the terminal window is visible, the screen contents is drawn.
  *
  * TODO:
- * - cursor flickers when moving the cursor
- * - set buffer options to be scratch, hidden, nomodifiable, etc.
- * - set buffer name to command, add (1) to avoid duplicates.
+ * - do not store terminal buffer in viminfo
+ * - put terminal title in the statusline
  * - Add a scrollback buffer (contains lines to scroll off the top).
  *   Can use the buf_T lines, store attributes somewhere else?
  * - When the job ends:
@@ -53,7 +52,6 @@
  * - support minimal size when 'termsize' is empty?
  * - implement "term" for job_start(): more job options when starting a
  *   terminal.
- * - implement ":buf {term-buf-name}"
  * - implement term_list()			list of buffers with a terminal
  * - implement term_getsize(buf)
  * - implement term_setsize(buf)
@@ -192,7 +190,31 @@
     term->tl_next = first_term;
     first_term = term;
 
-    /* TODO: set buffer type, hidden, etc. */
+    if (buflist_findname(cmd) == NULL)
+	curbuf->b_ffname = vim_strsave(cmd);
+    else
+    {
+	int	i;
+	size_t	len = STRLEN(cmd) + 10;
+	char_u	*p = alloc(len);
+
+	for (i = 1; p != NULL; ++i)
+	{
+	    vim_snprintf((char *)p, len, "%s (%d)", cmd, i);
+	    if (buflist_findname(p) == NULL)
+	    {
+		curbuf->b_ffname = p;
+		break;
+	    }
+	}
+    }
+    curbuf->b_fname = curbuf->b_ffname;
+
+    /* Mark the buffer as changed, so that it's not easy to abandon the job. */
+    curbuf->b_changed = TRUE;
+    curbuf->b_p_ma = FALSE;
+    set_string_option_direct((char_u *)"buftype", -1,
+				  (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0);
 
     set_term_and_win_size(term);
 
@@ -490,6 +512,26 @@
     }
 }
 
+/*
+ * Called when a job has finished.
+ */
+    void
+term_job_ended(job_T *job)
+{
+    if (curbuf->b_term != NULL && curbuf->b_term->tl_job == job)
+	maketitle();
+}
+
+/*
+ * Return TRUE if the job for "buf" is still running.
+ */
+    int
+term_job_running(buf_T *buf)
+{
+    return buf->b_term != NULL && buf->b_term->tl_job != NULL
+	&& buf->b_term->tl_job->jv_status == JOB_STARTED;
+}
+
     static void
 position_cursor(win_T *wp, VTermPos *pos)
 {
@@ -850,13 +892,20 @@
     opt->jo_out_mode = MODE_RAW;
     opt->jo_err_mode = MODE_RAW;
     opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE;
+
     opt->jo_io[PART_OUT] = JIO_BUFFER;
     opt->jo_io[PART_ERR] = JIO_BUFFER;
-    opt->jo_set |= JO_OUT_IO + (JO_OUT_IO << (PART_ERR - PART_OUT));
+    opt->jo_set |= JO_OUT_IO + JO_ERR_IO;
+
+    opt->jo_modifiable[PART_OUT] = 0;
+    opt->jo_modifiable[PART_ERR] = 0;
+    opt->jo_set |= JO_OUT_MODIFIABLE + JO_ERR_MODIFIABLE;
+
     opt->jo_io_buf[PART_OUT] = curbuf->b_fnum;
     opt->jo_io_buf[PART_ERR] = curbuf->b_fnum;
     opt->jo_pty = TRUE;
-    opt->jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT));
+    opt->jo_set |= JO_OUT_BUF + JO_ERR_BUF;
+
     opt->jo_term_rows = rows;
     opt->jo_term_cols = cols;
 }
diff --git a/src/version.c b/src/version.c
index 00580c7..2d8079c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -770,6 +770,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    761,
+/**/
     760,
 /**/
     759,