Subject: Re: bin/10116: vi somewhat easily confused by suspension
To: None <gnats-bugs@gnats.netbsd.org, tech-userlevel@netbsd.org>
From: Julian Coleman <J.D.Coleman@newcastle.ac.uk>
List: tech-userlevel
Date: 05/26/2000 14:47:33
>                                                              Is it worth
> importing nvi-1.79 (wasn't there mention of this a while back?)?  If not,
> the bits dealing with SA_ALTERNATE need to be back-ported in order to fix
> this (see also the comment in cl_attr() (cl/cl_funcs.c:91) in 1.79.

Hmm, no comments.  I've added the alternate screen handling from 1.79 to our
vi.  Patches appended.  One thing that I noticed is that running a command
from vi does not switch out of "keypad enable" mode.  For example, type :

  :!cat

and watch what the cursor keys generate.  Does this matter?  Also, perhaps it
would be nice to get the shell prompt back on a new line instead of :

  Press any key to continue [: to enter more ex commands]: aire:njdc bash$ 

when vi exits and I didn't press <return> at the prompt.

J

-- 
                    My other computer also runs NetBSD
                          http://www.netbsd.org/

 ---8<---------------------------- Cut here ---------------------------->8---

diff -ru /usr/src/usr.bin/vi/cl/cl_funcs.c ./cl/cl_funcs.c
--- /usr/src/usr.bin/vi/cl/cl_funcs.c	Tue Jan 13 14:54:43 1998
+++ ./cl/cl_funcs.c	Fri May 26 12:14:11 2000
@@ -85,10 +85,60 @@
 {
 	CL_PRIVATE *clp;
 
+	clp = CLP(sp);
+
 	switch (attribute) {
+	case SA_ALTERNATE:
+	/*
+	 * !!!
+	 * There's a major layering violation here.  The problem is that the
+	 * X11 xterm screen has what's known as an "alternate" screen.  Some
+	 * xterm termcap/terminfo entries include sequences to switch to/from
+	 * that alternate screen as part of the ti/te (smcup/rmcup) strings.
+	 * Vi runs in the alternate screen, so that you are returned to the
+	 * same screen contents on exit from vi that you had when you entered
+	 * vi.  Further, when you run :shell, or :!date or similar ex commands,
+	 * you also see the original screen contents.  This wasn't deliberate
+	 * on vi's part, it's just that it historically sent terminal init/end
+	 * sequences at those times, and the addition of the alternate screen
+	 * sequences to the strings changed the behavior of vi.  The problem
+	 * caused by this is that we don't want to switch back to the alternate
+	 * screen while getting a new command from the user, when the user is
+	 * continuing to enter ex commands, e.g.:
+	 *
+	 *	:!date				<<< switch to original screen
+	 *	[Hit return to continue]	<<< prompt user to continue
+	 *	:command			<<< get command from user
+	 *
+	 * Note that the :command input is a true vi input mode, e.g., input
+	 * maps and abbreviations are being done.  So, we need to be able to
+	 * switch back into the vi screen mode, without flashing the screen. 
+	 *
+	 * To make matters worse, the curses initscr() and endwin() calls will
+	 * do this automatically -- so, this attribute isn't as controlled by
+	 * the higher level screen as closely as one might like.
+	 */
+	if (on) {
+		if (clp->ti_te != TI_SENT) {
+			clp->ti_te = TI_SENT;
+			if (clp->smcup == NULL)
+				(void)cl_getcap(sp, "smcup", &clp->smcup);
+			if (clp->smcup != NULL)
+				(void)tputs(clp->smcup, 1, cl_putchar);
+		}
+	} else
+		if (clp->ti_te != TE_SENT) {
+			clp->ti_te = TE_SENT;
+			if (clp->rmcup == NULL)
+				(void)cl_getcap(sp, "rmcup", &clp->rmcup);
+			if (clp->rmcup != NULL)
+				(void)tputs(clp->rmcup, 1, cl_putchar);
+			(void)fflush(stdout);
+		}
+		(void)fflush(stdout);
+		break;
 	case SA_INVERSE:
 		if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
-			clp = CLP(sp);
 			if (clp->smso == NULL)
 				return (1);
 			if (on)
@@ -529,12 +579,7 @@
 	(void)keypad(stdscr, FALSE);
 
 #ifdef HAVE_BSD_CURSES
-	/* Send the terminal end sequence. */
-	if (clp->rmcup == NULL)
-		(void)cl_getcap(sp, "rmcup", &clp->rmcup);
-	if (clp->rmcup != NULL)
-		(void)tputs(clp->rmcup, 1, cl_putchar);
-	(void)fflush(stdout);
+	(void)cl_attr(sp, SA_ALTERNATE, 0);
 #else
 	(void)endwin();
 #endif
@@ -557,12 +602,7 @@
 	if (F_ISSET(gp, G_STDIN_TTY))
 		(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
 
-	/* Send the terminal initialization sequence. */
-	if (clp->smcup == NULL)
-		(void)cl_getcap(sp, "smcup", &clp->smcup);
-	if (clp->smcup != NULL)
-		(void)tputs(clp->smcup, 1, cl_putchar);
-	(void)fflush(stdout);
+	(void)cl_attr(sp, SA_ALTERNATE, 1);
 #endif
 	/* Put the cursor keys into application mode. */
 	(void)keypad(stdscr, TRUE);
diff -ru /usr/src/usr.bin/vi/cl/cl_screen.c ./cl/cl_screen.c
--- /usr/src/usr.bin/vi/cl/cl_screen.c	Tue Jan 13 14:54:57 1998
+++ ./cl/cl_screen.c	Fri May 26 11:44:16 2000
@@ -374,16 +374,6 @@
 err:		(void)cl_vi_end(sp->gp);
 		return (1);
 	}
-
-	/* If not already done, send the terminal initialization sequence. */
-	if (clp->ti_te == TE_SENT) {
-		clp->ti_te = TI_SENT;
-		if (clp->smcup == NULL)
-			(void)cl_getcap(sp, "smcup", &clp->smcup);
-		if (clp->smcup != NULL)
-			(void)tputs(clp->smcup, 1, cl_putchar);
-		(void)fflush(stdout);
-	}
 	return (0);
 }
 
@@ -498,15 +488,6 @@
 		return (1);
 	}
 
-	/* If not already done, send the terminal end sequence. */
-	if (clp->ti_te == TI_SENT) {
-		clp->ti_te = TE_SENT;
-		if (clp->rmcup == NULL)
-			(void)cl_getcap(sp, "rmcup", &clp->rmcup);
-		if (clp->rmcup != NULL)
-			(void)tputs(clp->rmcup, 1, cl_putchar);
-		(void)fflush(stdout);
-	}
 	return (0);
 }
 
diff -ru /usr/src/usr.bin/vi/common/gs.h ./common/gs.h
--- /usr/src/usr.bin/vi/common/gs.h	Tue Jan 13 14:55:41 1998
+++ ./common/gs.h	Fri May 26 11:46:15 2000
@@ -44,7 +44,7 @@
 typedef enum { EX_TERM_CE, EX_TERM_SCROLL } exadj_t;
 
 /* Screen attribute argument to scr_attr(). */
-typedef enum { SA_INVERSE } scr_attr_t;
+typedef enum { SA_ALTERNATE, SA_INVERSE } scr_attr_t;
 
 /* Key type argument to scr_keyval(). */
 typedef enum { KEY_VEOF, KEY_VERASE, KEY_VKILL, KEY_VWERASE } scr_keyval_t;
diff -ru /usr/src/usr.bin/vi/ex/ex_read.c ./ex/ex_read.c
--- /usr/src/usr.bin/vi/ex/ex_read.c	Tue Jan 13 15:01:11 1998
+++ ./ex/ex_read.c	Fri May 26 11:47:40 2000
@@ -162,6 +162,12 @@
 				ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON_F);
 				return (1);
 			}
+			/*
+			 * !!!
+			 * Historically, the read command doesn't switch to
+			 * the alternate X11 xterm screen, if doing a filter
+			 * read -- don't set SA_ALTERNATE.
+			 */
 			F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
 		}
 
diff -ru /usr/src/usr.bin/vi/ex/ex_shell.c ./ex/ex_shell.c
--- /usr/src/usr.bin/vi/ex/ex_shell.c	Tue Jan 13 15:01:26 1998
+++ ./ex/ex_shell.c	Fri May 26 12:07:19 2000
@@ -84,9 +84,12 @@
 	const char *msg;
 	int need_newline;
 {
+	GS *gp;
 	const char *name;
 	pid_t pid;
 
+	gp = sp->gp;
+
 	/* We'll need a shell. */
 	if (opts_empty(sp, O_SHELL, 0))
 		return (1);
@@ -97,6 +100,7 @@
 			ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON);
 			return (1);
 		}
+		(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
 		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
 	}
 
diff -ru /usr/src/usr.bin/vi/ex/ex_util.c ./ex/ex_util.c
--- /usr/src/usr.bin/vi/ex/ex_util.c	Tue Jan 13 15:01:42 1998
+++ ./ex/ex_util.c	Fri May 26 11:58:18 2000
@@ -154,8 +154,13 @@
 ex_init(sp)
 	SCR *sp;
 {
-	if (sp->gp->scr_screen(sp, SC_EX))
+	GS *gp;
+
+	gp = sp->gp;
+
+	if (gp->scr_screen(sp, SC_EX))
 		return (1);
+	(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
 
 	sp->rows = O_VAL(sp, O_LINES);
 	sp->cols = O_VAL(sp, O_COLUMNS);
diff -ru /usr/src/usr.bin/vi/vi/v_ex.c ./vi/v_ex.c
--- /usr/src/usr.bin/vi/vi/v_ex.c	Fri Jan  8 12:14:26 1999
+++ ./vi/v_ex.c	Fri May 26 12:01:05 2000
@@ -66,12 +66,17 @@
 	SCR *sp;
 	VICMD *vp;
 {
+	GS *gp;
+
+	gp = sp->gp;
+
 	/* Try and switch screens -- the screen may not permit it. */
 	if (sp->gp->scr_screen(sp, SC_EX)) {
 		msgq(sp, M_ERR,
 		    "207|The Q command requires the ex terminal interface");
 		return (1);
 	}
+	(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
 
 	/* Save the current cursor position. */
 	sp->frp->lno = sp->lno;
diff -ru /usr/src/usr.bin/vi/vi/vi.c ./vi/vi.c
--- /usr/src/usr.bin/vi/vi/vi.c	Tue Jan 13 15:03:17 1998
+++ ./vi/vi.c	Fri May 26 11:51:27 2000
@@ -89,6 +89,7 @@
 	/* Initialize the vi screen. */
 	if (v_init(sp))
 		return (1);
+	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
 
 	for (vip = VIP(sp), rval = 0;;) {
 		/* Resolve messages. */
diff -ru /usr/src/usr.bin/vi/vi/vs_msg.c ./vi/vs_msg.c
--- /usr/src/usr.bin/vi/vi/vs_msg.c	Tue Jan 13 15:03:38 1998
+++ ./vi/vs_msg.c	Fri May 26 11:51:48 2000
@@ -620,6 +620,9 @@
 	if (F_ISSET(vip, VIP_N_EX_REDRAW))
 		F_SET(sp, SC_SCR_REFORMAT);
 
+	/* Ex may have switched out of the alternate screen, return. */
+	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
+
 	/*
 	 * Whew.  We're finally back home, after what feels like years.
 	 * Kiss the ground.