xref: /netbsd-src/external/bsd/nvi/dist/cl/cl_screen.c (revision 6798bdb86d03ad4512598e1546baa70065279f27)
1*6798bdb8Smlelstv /*	$NetBSD: cl_screen.c,v 1.6 2017/09/01 07:21:01 mlelstv Exp $ */
2dbd550edSchristos /*-
3dbd550edSchristos  * Copyright (c) 1993, 1994
4dbd550edSchristos  *	The Regents of the University of California.  All rights reserved.
5dbd550edSchristos  * Copyright (c) 1993, 1994, 1995, 1996
6dbd550edSchristos  *	Keith Bostic.  All rights reserved.
7dbd550edSchristos  *
8dbd550edSchristos  * See the LICENSE file for redistribution information.
9dbd550edSchristos  */
10dbd550edSchristos 
11dbd550edSchristos #include "config.h"
12dbd550edSchristos 
132f698edbSchristos #include <sys/cdefs.h>
142f698edbSchristos #if 0
15dbd550edSchristos #ifndef lint
16dbd550edSchristos static const char sccsid[] = "Id: cl_screen.c,v 10.56 2002/05/03 19:59:44 skimo Exp  (Berkeley) Date: 2002/05/03 19:59:44 ";
17dbd550edSchristos #endif /* not lint */
182f698edbSchristos #else
19*6798bdb8Smlelstv __RCSID("$NetBSD: cl_screen.c,v 1.6 2017/09/01 07:21:01 mlelstv Exp $");
202f698edbSchristos #endif
21dbd550edSchristos 
22dbd550edSchristos #include <sys/types.h>
23dbd550edSchristos #include <sys/queue.h>
24dbd550edSchristos 
25dbd550edSchristos #include <bitstring.h>
26dbd550edSchristos #include <errno.h>
27dbd550edSchristos #include <signal.h>
28dbd550edSchristos #include <stdio.h>
29dbd550edSchristos #include <stdlib.h>
30dbd550edSchristos #include <string.h>
31dbd550edSchristos #include <termios.h>
32dbd550edSchristos #include <unistd.h>
33dbd550edSchristos 
34dbd550edSchristos #include "../common/common.h"
35dbd550edSchristos #include "cl.h"
36dbd550edSchristos 
37d4ec6dffSchristos #ifndef BLOCK_SIGNALS
38d4ec6dffSchristos extern sigset_t __sigblockset;
39d4ec6dffSchristos #endif
40d4ec6dffSchristos 
41dbd550edSchristos static int	cl_ex_end __P((GS *));
42dbd550edSchristos static int	cl_ex_init __P((SCR *));
43dbd550edSchristos static void	cl_freecap __P((CL_PRIVATE *));
44dbd550edSchristos static int	cl_vi_end __P((GS *));
45dbd550edSchristos static int	cl_vi_init __P((SCR *));
468d01a27eSchristos static int	cl_putenv __P((SCR *, const char *, const char *, u_long));
47dbd550edSchristos 
48dbd550edSchristos /*
49dbd550edSchristos  * cl_screen --
50dbd550edSchristos  *	Switch screen types.
51dbd550edSchristos  *
52dbd550edSchristos  * PUBLIC: int cl_screen __P((SCR *, u_int32_t));
53dbd550edSchristos  */
54dbd550edSchristos int
cl_screen(SCR * sp,u_int32_t flags)55dbd550edSchristos cl_screen(SCR *sp, u_int32_t flags)
56dbd550edSchristos {
57dbd550edSchristos 	CL_PRIVATE *clp;
58dbd550edSchristos 	WINDOW *win;
59dbd550edSchristos 	GS *gp;
60d4ec6dffSchristos 	int ret, error;
61d4ec6dffSchristos 	sigset_t oset;
62dbd550edSchristos 
63dbd550edSchristos 	gp = sp->gp;
64dbd550edSchristos 	clp = CLP(sp);
65dbd550edSchristos 	win = CLSP(sp) ? CLSP(sp) : stdscr;
66dbd550edSchristos 
67d4ec6dffSchristos 	ret = 0;
68d4ec6dffSchristos 
69d4ec6dffSchristos 	/*
70d4ec6dffSchristos 	 * During initialization of the screen, block signals to make sure that
71d4ec6dffSchristos 	 * curses/terminfo routines are not interrupted.
72d4ec6dffSchristos 	 */
73d4ec6dffSchristos 	error = sigprocmask(SIG_BLOCK, &__sigblockset, &oset);
74d4ec6dffSchristos 
75dbd550edSchristos 	/* See if the current information is incorrect. */
76dbd550edSchristos 	if (F_ISSET(gp, G_SRESTART)) {
77dbd550edSchristos 		if (CLSP(sp)) {
78dbd550edSchristos 		    delwin(CLSP(sp));
79dbd550edSchristos 		    sp->cl_private = NULL;
80dbd550edSchristos 		}
81d4ec6dffSchristos 		if (cl_quit(gp)) {
82d4ec6dffSchristos 			ret = 1;
83d4ec6dffSchristos 			goto end;
84d4ec6dffSchristos 		}
85dbd550edSchristos 		F_CLR(gp, G_SRESTART);
86dbd550edSchristos 	}
87dbd550edSchristos 
88dbd550edSchristos 	/* See if we're already in the right mode. */
89dbd550edSchristos 	if ((LF_ISSET(SC_EX) && F_ISSET(sp, SC_SCR_EX)) ||
90dbd550edSchristos 	    (LF_ISSET(SC_VI) && F_ISSET(sp, SC_SCR_VI)))
91d4ec6dffSchristos 		goto end;
92dbd550edSchristos 
93dbd550edSchristos 	/*
94dbd550edSchristos 	 * Fake leaving ex mode.
95dbd550edSchristos 	 *
96dbd550edSchristos 	 * We don't actually exit ex or vi mode unless forced (e.g. by a window
97dbd550edSchristos 	 * size change).  This is because many curses implementations can't be
98dbd550edSchristos 	 * called twice in a single program.  Plus, it's faster.  If the editor
99dbd550edSchristos 	 * "leaves" vi to enter ex, when it exits ex we'll just fall back into
100dbd550edSchristos 	 * vi.
101dbd550edSchristos 	 */
102dbd550edSchristos 	if (F_ISSET(sp, SC_SCR_EX))
103dbd550edSchristos 		F_CLR(sp, SC_SCR_EX);
104dbd550edSchristos 
105dbd550edSchristos 	/*
106dbd550edSchristos 	 * Fake leaving vi mode.
107dbd550edSchristos 	 *
108dbd550edSchristos 	 * Clear out the rest of the screen if we're in the middle of a split
109dbd550edSchristos 	 * screen.  Move to the last line in the current screen -- this makes
110dbd550edSchristos 	 * terminal scrolling happen naturally.  Note: *don't* move past the
111dbd550edSchristos 	 * end of the screen, as there are ex commands (e.g., :read ! cat file)
112dbd550edSchristos 	 * that don't want to.  Don't clear the info line, its contents may be
113dbd550edSchristos 	 * valid, e.g. :file|append.
114dbd550edSchristos 	 */
115dbd550edSchristos 	if (F_ISSET(sp, SC_SCR_VI)) {
116dbd550edSchristos 		F_CLR(sp, SC_SCR_VI);
117dbd550edSchristos 
118d89691f9Schristos 		if (TAILQ_NEXT(sp, q) != NULL) {
119dbd550edSchristos 			(void)wmove(win, RLNO(sp, sp->rows), 0);
120dbd550edSchristos 			wclrtobot(win);
121dbd550edSchristos 		}
122dbd550edSchristos 		(void)wmove(win, RLNO(sp, sp->rows) - 1, 0);
123dbd550edSchristos 		wrefresh(win);
124dbd550edSchristos 	}
125dbd550edSchristos 
126dbd550edSchristos 	/* Enter the requested mode. */
127dbd550edSchristos 	if (LF_ISSET(SC_EX)) {
128d4ec6dffSchristos 		if (cl_ex_init(sp)) {
129d4ec6dffSchristos 			ret = 1;
130d4ec6dffSchristos 			goto end;
131d4ec6dffSchristos 		}
132dbd550edSchristos 		F_SET(clp, CL_IN_EX | CL_SCR_EX_INIT);
133dbd550edSchristos 
134dbd550edSchristos 		/*
135dbd550edSchristos 		 * If doing an ex screen for ex mode, move to the last line
136dbd550edSchristos 		 * on the screen.
137dbd550edSchristos 		 */
138dbd550edSchristos 		if (F_ISSET(sp, SC_EX) && clp->cup != NULL)
139dbd550edSchristos 			tputs(tgoto(clp->cup,
140dbd550edSchristos 			    0, O_VAL(sp, O_LINES) - 1), 1, cl_putchar);
141dbd550edSchristos 	} else {
142d4ec6dffSchristos 		if (cl_vi_init(sp)) {
143d4ec6dffSchristos 			ret = 1;
144d4ec6dffSchristos 			goto end;
145d4ec6dffSchristos 		}
146dbd550edSchristos 		F_CLR(clp, CL_IN_EX);
147dbd550edSchristos 		F_SET(clp, CL_SCR_VI_INIT);
148dbd550edSchristos 	}
149d4ec6dffSchristos end:
150d4ec6dffSchristos 	/* Unblock signals. */
151d4ec6dffSchristos 	if (error == 0)
152d4ec6dffSchristos 		(void)sigprocmask(SIG_SETMASK, &oset, NULL);
153d4ec6dffSchristos 	return ret;
154dbd550edSchristos }
155dbd550edSchristos 
156dbd550edSchristos /*
157dbd550edSchristos  * cl_quit --
158dbd550edSchristos  *	Shutdown the screens.
159dbd550edSchristos  *
160dbd550edSchristos  * PUBLIC: int cl_quit __P((GS *));
161dbd550edSchristos  */
162dbd550edSchristos int
cl_quit(GS * gp)163dbd550edSchristos cl_quit(GS *gp)
164dbd550edSchristos {
165dbd550edSchristos 	CL_PRIVATE *clp;
166dbd550edSchristos 	int rval;
167dbd550edSchristos 
168dbd550edSchristos 	rval = 0;
169dbd550edSchristos 	clp = GCLP(gp);
170dbd550edSchristos 
171dbd550edSchristos 	/*
172dbd550edSchristos 	 * If we weren't really running, ignore it.  This happens if the
173dbd550edSchristos 	 * screen changes size before we've called curses.
174dbd550edSchristos 	 */
175dbd550edSchristos 	if (!F_ISSET(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT))
176dbd550edSchristos 		return (0);
177dbd550edSchristos 
178dbd550edSchristos 	/* Clean up the terminal mappings. */
179dbd550edSchristos 	if (cl_term_end(gp))
180dbd550edSchristos 		rval = 1;
181dbd550edSchristos 
182dbd550edSchristos 	/* Really leave vi mode. */
183dbd550edSchristos 	if (F_ISSET(clp, CL_STDIN_TTY) &&
184dbd550edSchristos 	    F_ISSET(clp, CL_SCR_VI_INIT) && cl_vi_end(gp))
185dbd550edSchristos 		rval = 1;
186dbd550edSchristos 
187dbd550edSchristos 	/* Really leave ex mode. */
188dbd550edSchristos 	if (F_ISSET(clp, CL_STDIN_TTY) &&
189dbd550edSchristos 	    F_ISSET(clp, CL_SCR_EX_INIT) && cl_ex_end(gp))
190dbd550edSchristos 		rval = 1;
191dbd550edSchristos 
192dbd550edSchristos 	/*
193dbd550edSchristos 	 * If we were running ex when we quit, or we're using an implementation
194dbd550edSchristos 	 * of curses where endwin() doesn't get this right, restore the original
195dbd550edSchristos 	 * terminal modes.
196dbd550edSchristos 	 *
197dbd550edSchristos 	 * XXX
198dbd550edSchristos 	 * We always do this because it's too hard to figure out what curses
199dbd550edSchristos 	 * implementations get it wrong.  It may discard type-ahead characters
200dbd550edSchristos 	 * from the tty queue.
201dbd550edSchristos 	 */
202dbd550edSchristos 	(void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
203dbd550edSchristos 
204dbd550edSchristos 	F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
205dbd550edSchristos 	return (rval);
206dbd550edSchristos }
207dbd550edSchristos 
208dbd550edSchristos /*
209dbd550edSchristos  * cl_vi_init --
210dbd550edSchristos  *	Initialize the curses vi screen.
211dbd550edSchristos  */
212dbd550edSchristos static int
cl_vi_init(SCR * sp)213dbd550edSchristos cl_vi_init(SCR *sp)
214dbd550edSchristos {
215dbd550edSchristos 	CL_PRIVATE *clp;
2168d01a27eSchristos 	char *o_cols, *o_lines, *o_term;
2178d01a27eSchristos 	const char *ttype;
218dbd550edSchristos 
219dbd550edSchristos 	clp = CLP(sp);
220dbd550edSchristos 
221dbd550edSchristos 	/* If already initialized, just set the terminal modes. */
222dbd550edSchristos 	if (F_ISSET(clp, CL_SCR_VI_INIT))
223dbd550edSchristos 		goto fast;
224dbd550edSchristos 
225dbd550edSchristos 	/* Curses vi always reads from (and writes to) a terminal. */
226dbd550edSchristos 	if (!F_ISSET(clp, CL_STDIN_TTY) || !isatty(STDOUT_FILENO)) {
227dbd550edSchristos 		msgq(sp, M_ERR,
228dbd550edSchristos 		    "016|Vi's standard input and output must be a terminal");
229dbd550edSchristos 		return (1);
230dbd550edSchristos 	}
231dbd550edSchristos 
232dbd550edSchristos 	/* We'll need a terminal type. */
233dbd550edSchristos 	if (opts_empty(sp, O_TERM, 0))
234dbd550edSchristos 		return (1);
235dbd550edSchristos 	ttype = O_STR(sp, O_TERM);
236dbd550edSchristos 
237dbd550edSchristos 	/*
238dbd550edSchristos 	 * XXX
239dbd550edSchristos 	 * Changing the row/column and terminal values is done by putting them
240dbd550edSchristos 	 * into the environment, which is then read by curses.  What this loses
241dbd550edSchristos 	 * in ugliness, it makes up for in stupidity.  We can't simply put the
242dbd550edSchristos 	 * values into the environment ourselves, because in the presence of a
243dbd550edSchristos 	 * kernel mechanism for returning the window size, entering values into
244dbd550edSchristos 	 * the environment will screw up future screen resizing events, e.g. if
245dbd550edSchristos 	 * the user enters a :shell command and then resizes their window.  So,
246dbd550edSchristos 	 * if they weren't already in the environment, we make sure to delete
247dbd550edSchristos 	 * them immediately after setting them.
248dbd550edSchristos 	 *
249dbd550edSchristos 	 * XXX
250dbd550edSchristos 	 * Putting the TERM variable into the environment is necessary, even
251dbd550edSchristos 	 * though we're using newterm() here.  We may be using initscr() as
252dbd550edSchristos 	 * the underlying function.
253dbd550edSchristos 	 */
254dbd550edSchristos 	o_term = getenv("TERM");
255dbd550edSchristos 	cl_putenv(sp, "TERM", ttype, 0);
256dbd550edSchristos 	o_lines = getenv("LINES");
257dbd550edSchristos 	cl_putenv(sp, "LINES", NULL, (u_long)O_VAL(sp, O_LINES));
258dbd550edSchristos 	o_cols = getenv("COLUMNS");
259dbd550edSchristos 	cl_putenv(sp, "COLUMNS", NULL, (u_long)O_VAL(sp, O_COLUMNS));
260dbd550edSchristos 
261d4ec6dffSchristos 	/* Delete cur_term if exists. */
262d4ec6dffSchristos 	if (F_ISSET(clp, CL_SETUPTERM)) {
263d4ec6dffSchristos 		if (del_curterm(cur_term))
264d4ec6dffSchristos 			return (1);
265d4ec6dffSchristos 		F_CLR(clp, CL_SETUPTERM);
266d4ec6dffSchristos 	}
267d4ec6dffSchristos 
268dbd550edSchristos 	/*
269dbd550edSchristos 	 * XXX
270dbd550edSchristos 	 * The SunOS initscr() can't be called twice.  Don't even think about
271dbd550edSchristos 	 * using it.  It fails in subtle ways (e.g. select(2) on fileno(stdin)
272dbd550edSchristos 	 * stops working).  (The SVID notes that applications should only call
273dbd550edSchristos 	 * initscr() once.)
274dbd550edSchristos 	 *
275dbd550edSchristos 	 * XXX
276dbd550edSchristos 	 * The HP/UX newterm doesn't support the NULL first argument, so we
277dbd550edSchristos 	 * have to specify the terminal type.
278dbd550edSchristos 	 */
279dbd550edSchristos 	errno = 0;
280d4ec6dffSchristos 	if ((clp->screen = newterm(__UNCONST(ttype), stdout, stdin)) == NULL) {
281dbd550edSchristos 		if (errno)
282dbd550edSchristos 			msgq(sp, M_SYSERR, "%s", ttype);
283dbd550edSchristos 		else
284dbd550edSchristos 			msgq(sp, M_ERR, "%s: unknown terminal type", ttype);
285dbd550edSchristos 		return (1);
286dbd550edSchristos 	}
287dbd550edSchristos 
288dbd550edSchristos 	if (o_term == NULL)
289dbd550edSchristos 		cl_unsetenv(sp, "TERM");
290dbd550edSchristos 	if (o_lines == NULL)
291dbd550edSchristos 		cl_unsetenv(sp, "LINES");
292dbd550edSchristos 	if (o_cols == NULL)
293dbd550edSchristos 		cl_unsetenv(sp, "COLUMNS");
294dbd550edSchristos 
295dbd550edSchristos 	/*
296dbd550edSchristos 	 * XXX
297dbd550edSchristos 	 * Someone got let out alone without adult supervision -- the SunOS
298dbd550edSchristos 	 * newterm resets the signal handlers.  There's a race, but it's not
299dbd550edSchristos 	 * worth closing.
300dbd550edSchristos 	 */
301dbd550edSchristos 	(void)sig_init(sp->gp, sp);
302dbd550edSchristos 
303dbd550edSchristos 	/*
304dbd550edSchristos 	 * We use raw mode.  What we want is 8-bit clean, however, signals
305dbd550edSchristos 	 * and flow control should continue to work.  Admittedly, it sounds
306dbd550edSchristos 	 * like cbreak, but it isn't.  Using cbreak() can get you additional
307dbd550edSchristos 	 * things like IEXTEN, which turns on flags like DISCARD and LNEXT.
308dbd550edSchristos 	 *
309dbd550edSchristos 	 * !!!
310dbd550edSchristos 	 * If raw isn't turning off echo and newlines, something's wrong.
311dbd550edSchristos 	 * However, it shouldn't hurt.
312dbd550edSchristos 	 */
313dbd550edSchristos 	noecho();			/* No character echo. */
314dbd550edSchristos 	nonl();				/* No CR/NL translation. */
315dbd550edSchristos 	raw();				/* 8-bit clean. */
316dbd550edSchristos 	idlok(stdscr, 1);		/* Use hardware insert/delete line. */
317dbd550edSchristos 
318dbd550edSchristos 	/* Put the cursor keys into application mode. */
319dbd550edSchristos 	(void)keypad(stdscr, TRUE);
320dbd550edSchristos 
321dbd550edSchristos 	/*
322dbd550edSchristos 	 * XXX
323dbd550edSchristos 	 * The screen TI sequence just got sent.  See the comment in
324dbd550edSchristos 	 * cl_funcs.c:cl_attr().
325dbd550edSchristos 	 */
326dbd550edSchristos 	clp->ti_te = TI_SENT;
327dbd550edSchristos 
328dbd550edSchristos 	/*
329dbd550edSchristos 	 * XXX
330dbd550edSchristos 	 * Historic implementations of curses handled SIGTSTP signals
331dbd550edSchristos 	 * in one of three ways.  They either:
332dbd550edSchristos 	 *
333dbd550edSchristos 	 *	1: Set their own handler, regardless.
334dbd550edSchristos 	 *	2: Did not set a handler if a handler was already installed.
335dbd550edSchristos 	 *	3: Set their own handler, but then called any previously set
336dbd550edSchristos 	 *	   handler after completing their own cleanup.
337dbd550edSchristos 	 *
338dbd550edSchristos 	 * We don't try and figure out which behavior is in place, we force
339dbd550edSchristos 	 * it to SIG_DFL after initializing the curses interface, which means
340dbd550edSchristos 	 * that curses isn't going to take the signal.  Since curses isn't
341dbd550edSchristos 	 * reentrant (i.e., the whole curses SIGTSTP interface is a fantasy),
342dbd550edSchristos 	 * we're doing The Right Thing.
343dbd550edSchristos 	 */
344dbd550edSchristos 	(void)signal(SIGTSTP, SIG_DFL);
345dbd550edSchristos 
346dbd550edSchristos 	/*
347dbd550edSchristos 	 * If flow control was on, turn it back on.  Turn signals on.  ISIG
348dbd550edSchristos 	 * turns on VINTR, VQUIT, VDSUSP and VSUSP.   The main curses code
349dbd550edSchristos 	 * already installed a handler for VINTR.  We're going to disable the
350dbd550edSchristos 	 * other three.
351dbd550edSchristos 	 *
352dbd550edSchristos 	 * XXX
353dbd550edSchristos 	 * We want to use ^Y as a vi scrolling command.  If the user has the
354dbd550edSchristos 	 * DSUSP character set to ^Y (common practice) clean it up.  As it's
355dbd550edSchristos 	 * equally possible that the user has VDSUSP set to 'a', we disable
356dbd550edSchristos 	 * it regardless.  It doesn't make much sense to suspend vi at read,
357dbd550edSchristos 	 * so I don't think anyone will care.  Alternatively, we could look
358dbd550edSchristos 	 * it up in the table of legal command characters and turn it off if
359dbd550edSchristos 	 * it matches one.  VDSUSP wasn't in POSIX 1003.1-1990, so we test for
360dbd550edSchristos 	 * it.
361dbd550edSchristos 	 *
362dbd550edSchristos 	 * XXX
363dbd550edSchristos 	 * We don't check to see if the user had signals enabled originally.
364dbd550edSchristos 	 * If they didn't, it's unclear what we're supposed to do here, but
365dbd550edSchristos 	 * it's also pretty unlikely.
366dbd550edSchristos 	 */
367dbd550edSchristos 	if (tcgetattr(STDIN_FILENO, &clp->vi_enter)) {
368dbd550edSchristos 		msgq(sp, M_SYSERR, "tcgetattr");
369dbd550edSchristos 		goto err;
370dbd550edSchristos 	}
371dbd550edSchristos 	if (clp->orig.c_iflag & IXON)
372dbd550edSchristos 		clp->vi_enter.c_iflag |= IXON;
373dbd550edSchristos 	if (clp->orig.c_iflag & IXOFF)
374dbd550edSchristos 		clp->vi_enter.c_iflag |= IXOFF;
375dbd550edSchristos 
376dbd550edSchristos 	clp->vi_enter.c_lflag |= ISIG;
377dbd550edSchristos #ifdef VDSUSP
378dbd550edSchristos 	clp->vi_enter.c_cc[VDSUSP] = _POSIX_VDISABLE;
379dbd550edSchristos #endif
380dbd550edSchristos 	clp->vi_enter.c_cc[VQUIT] = _POSIX_VDISABLE;
381dbd550edSchristos 	clp->vi_enter.c_cc[VSUSP] = _POSIX_VDISABLE;
382dbd550edSchristos 
383dbd550edSchristos 	/*
384dbd550edSchristos 	 * XXX
385dbd550edSchristos 	 * OSF/1 doesn't turn off the <discard>, <literal-next> or <status>
386dbd550edSchristos 	 * characters when curses switches into raw mode.  It should be OK
387dbd550edSchristos 	 * to do it explicitly for everyone.
388dbd550edSchristos 	 */
389dbd550edSchristos #ifdef VDISCARD
390dbd550edSchristos 	clp->vi_enter.c_cc[VDISCARD] = _POSIX_VDISABLE;
391dbd550edSchristos #endif
392dbd550edSchristos #ifdef VLNEXT
393dbd550edSchristos 	clp->vi_enter.c_cc[VLNEXT] = _POSIX_VDISABLE;
394dbd550edSchristos #endif
395dbd550edSchristos #ifdef VSTATUS
396dbd550edSchristos 	clp->vi_enter.c_cc[VSTATUS] = _POSIX_VDISABLE;
397dbd550edSchristos #endif
398dbd550edSchristos 
399dbd550edSchristos 	/* Initialize terminal based information. */
400dbd550edSchristos 	if (cl_term_init(sp))
401dbd550edSchristos 		goto err;
402dbd550edSchristos 
403dbd550edSchristos fast:	/* Set the terminal modes. */
404dbd550edSchristos 	if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &clp->vi_enter)) {
405dbd550edSchristos 		msgq(sp, M_SYSERR, "tcsetattr");
406dbd550edSchristos err:		(void)cl_vi_end(sp->gp);
407dbd550edSchristos 		return (1);
408dbd550edSchristos 	}
409dbd550edSchristos 	return (0);
410dbd550edSchristos }
411dbd550edSchristos 
412dbd550edSchristos /*
413dbd550edSchristos  * cl_vi_end --
414dbd550edSchristos  *	Shutdown the vi screen.
415dbd550edSchristos  */
416dbd550edSchristos static int
cl_vi_end(GS * gp)417dbd550edSchristos cl_vi_end(GS *gp)
418dbd550edSchristos {
419dbd550edSchristos 	CL_PRIVATE *clp;
420dbd550edSchristos 
421dbd550edSchristos 	clp = GCLP(gp);
422dbd550edSchristos 
423dbd550edSchristos 	/* Restore the cursor keys to normal mode. */
424dbd550edSchristos 	(void)keypad(stdscr, FALSE);
425dbd550edSchristos 
426dbd550edSchristos 	/*
427dbd550edSchristos 	 * If we were running vi when we quit, scroll the screen up a single
428dbd550edSchristos 	 * line so we don't lose any information.
429dbd550edSchristos 	 *
430dbd550edSchristos 	 * Move to the bottom of the window (some endwin implementations don't
431dbd550edSchristos 	 * do this for you).
432dbd550edSchristos 	 */
433*6798bdb8Smlelstv 	if (!F_ISSET(clp, CL_IN_EX) && !F_ISSET(gp, G_SRESTART)) {
434dbd550edSchristos 		(void)move(0, 0);
435dbd550edSchristos 		(void)deleteln();
436dbd550edSchristos 		(void)move(LINES - 1, 0);
437dbd550edSchristos 		(void)refresh();
438dbd550edSchristos 	}
439dbd550edSchristos 
440dbd550edSchristos 	cl_freecap(clp);
441dbd550edSchristos 
442dbd550edSchristos 	/* End curses window. */
443dbd550edSchristos 	(void)endwin();
444dbd550edSchristos 
445d4ec6dffSchristos 	/* Delete curses screen. */
446d4ec6dffSchristos 	delscreen(clp->screen);
447d4ec6dffSchristos 
448dbd550edSchristos 	/*
449dbd550edSchristos 	 * XXX
450dbd550edSchristos 	 * The screen TE sequence just got sent.  See the comment in
451dbd550edSchristos 	 * cl_funcs.c:cl_attr().
452dbd550edSchristos 	 */
453dbd550edSchristos 	clp->ti_te = TE_SENT;
454dbd550edSchristos 
455dbd550edSchristos 	return (0);
456dbd550edSchristos }
457dbd550edSchristos 
458dbd550edSchristos /*
459dbd550edSchristos  * cl_ex_init --
460dbd550edSchristos  *	Initialize the ex screen.
461dbd550edSchristos  */
462dbd550edSchristos static int
cl_ex_init(SCR * sp)463dbd550edSchristos cl_ex_init(SCR *sp)
464dbd550edSchristos {
465dbd550edSchristos 	CL_PRIVATE *clp;
466d4ec6dffSchristos 	int error;
467d4ec6dffSchristos 	const char *ttype;
468dbd550edSchristos 
469dbd550edSchristos 	clp = CLP(sp);
470dbd550edSchristos 
471dbd550edSchristos 	/* If already initialized, just set the terminal modes. */
472dbd550edSchristos 	if (F_ISSET(clp, CL_SCR_EX_INIT))
473dbd550edSchristos 		goto fast;
474dbd550edSchristos 
475dbd550edSchristos 	/* If not reading from a file, we're done. */
476dbd550edSchristos 	if (!F_ISSET(clp, CL_STDIN_TTY))
477dbd550edSchristos 		return (0);
478dbd550edSchristos 
479d4ec6dffSchristos 	if (F_ISSET(clp, CL_CHANGE_TERM)) {
480d4ec6dffSchristos 		if (F_ISSET(clp, CL_SETUPTERM) && del_curterm(cur_term))
481d4ec6dffSchristos 			return (1);
482d4ec6dffSchristos 		F_CLR(clp, CL_SETUPTERM | CL_CHANGE_TERM);
483d4ec6dffSchristos 	}
484d4ec6dffSchristos 
485d4ec6dffSchristos 	if (!F_ISSET(clp, CL_SETUPTERM)) {
486d4ec6dffSchristos 		/* We'll need a terminal type. */
487d4ec6dffSchristos 		if (opts_empty(sp, O_TERM, 0))
488d4ec6dffSchristos 			return (1);
489d4ec6dffSchristos 		ttype = O_STR(sp, O_TERM);
490d4ec6dffSchristos 		(void)setupterm(ttype, STDOUT_FILENO, &error);
491d4ec6dffSchristos 		if (error == 0 || error == -1)
492d4ec6dffSchristos 			return (1);
493d4ec6dffSchristos 	}
494d4ec6dffSchristos 
495dbd550edSchristos 	/* Get the ex termcap/terminfo strings. */
496dbd550edSchristos 	(void)cl_getcap(sp, "cup", &clp->cup);
497dbd550edSchristos 	(void)cl_getcap(sp, "smso", &clp->smso);
498dbd550edSchristos 	(void)cl_getcap(sp, "rmso", &clp->rmso);
499dbd550edSchristos 	(void)cl_getcap(sp, "el", &clp->el);
500dbd550edSchristos 	(void)cl_getcap(sp, "cuu1", &clp->cuu1);
501dbd550edSchristos 
502dbd550edSchristos 	/* Enter_standout_mode and exit_standout_mode are paired. */
503dbd550edSchristos 	if (clp->smso == NULL || clp->rmso == NULL) {
504dbd550edSchristos 		if (clp->smso != NULL) {
505dbd550edSchristos 			free(clp->smso);
506dbd550edSchristos 			clp->smso = NULL;
507dbd550edSchristos 		}
508dbd550edSchristos 		if (clp->rmso != NULL) {
509dbd550edSchristos 			free(clp->rmso);
510dbd550edSchristos 			clp->rmso = NULL;
511dbd550edSchristos 		}
512dbd550edSchristos 	}
513dbd550edSchristos 
514dbd550edSchristos 	/*
515dbd550edSchristos 	 * Turn on canonical mode, with normal input and output processing.
516dbd550edSchristos 	 * Start with the original terminal settings as the user probably
517dbd550edSchristos 	 * had them (including any local extensions) set correctly for the
518dbd550edSchristos 	 * current terminal.
519dbd550edSchristos 	 *
520dbd550edSchristos 	 * !!!
521dbd550edSchristos 	 * We can't get everything that we need portably; for example, ONLCR,
522dbd550edSchristos 	 * mapping <newline> to <carriage-return> on output isn't required
523dbd550edSchristos 	 * by POSIX 1003.1b-1993.  If this turns out to be a problem, then
524dbd550edSchristos 	 * we'll either have to play some games on the mapping, or we'll have
525dbd550edSchristos 	 * to make all ex printf's output \r\n instead of \n.
526dbd550edSchristos 	 */
527dbd550edSchristos 	clp->ex_enter = clp->orig;
528dbd550edSchristos 	clp->ex_enter.c_lflag  |= ECHO | ECHOE | ECHOK | ICANON | IEXTEN | ISIG;
529dbd550edSchristos #ifdef ECHOCTL
530dbd550edSchristos 	clp->ex_enter.c_lflag |= ECHOCTL;
531dbd550edSchristos #endif
532dbd550edSchristos #ifdef ECHOKE
533dbd550edSchristos 	clp->ex_enter.c_lflag |= ECHOKE;
534dbd550edSchristos #endif
535dbd550edSchristos 	clp->ex_enter.c_iflag |= ICRNL;
536dbd550edSchristos 	clp->ex_enter.c_oflag |= OPOST;
537dbd550edSchristos #ifdef ONLCR
538dbd550edSchristos 	clp->ex_enter.c_oflag |= ONLCR;
539dbd550edSchristos #endif
540dbd550edSchristos 
541dbd550edSchristos fast:	if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->ex_enter)) {
542dbd550edSchristos 		if (errno == EINTR)
543dbd550edSchristos 			goto fast;
544dbd550edSchristos 		msgq(sp, M_SYSERR, "tcsetattr");
545dbd550edSchristos 		return (1);
546dbd550edSchristos 	}
547dbd550edSchristos 	return (0);
548dbd550edSchristos }
549dbd550edSchristos 
550dbd550edSchristos /*
551dbd550edSchristos  * cl_ex_end --
552dbd550edSchristos  *	Shutdown the ex screen.
553dbd550edSchristos  */
554dbd550edSchristos static int
cl_ex_end(GS * gp)555dbd550edSchristos cl_ex_end(GS *gp)
556dbd550edSchristos {
557dbd550edSchristos 	CL_PRIVATE *clp;
558dbd550edSchristos 
559dbd550edSchristos 	clp = GCLP(gp);
560dbd550edSchristos 
561dbd550edSchristos 	cl_freecap(clp);
562dbd550edSchristos 
563dbd550edSchristos 	return (0);
564dbd550edSchristos }
565dbd550edSchristos 
566dbd550edSchristos /*
567dbd550edSchristos  * cl_getcap --
568dbd550edSchristos  *	Retrieve termcap/terminfo strings.
569dbd550edSchristos  *
5708d01a27eSchristos  * PUBLIC: int cl_getcap __P((SCR *, const char *, char **));
571dbd550edSchristos  */
572dbd550edSchristos int
cl_getcap(SCR * sp,const char * name,char ** elementp)5738d01a27eSchristos cl_getcap(SCR *sp, const char *name, char **elementp)
574dbd550edSchristos {
575dbd550edSchristos 	size_t len;
576dbd550edSchristos 	char *t;
577dbd550edSchristos 
578dbd550edSchristos 	if ((t = tigetstr(name)) != NULL &&
579dbd550edSchristos 	    t != (char *)-1 && (len = strlen(t)) != 0) {
580dbd550edSchristos 		MALLOC_RET(sp, *elementp, char *, len + 1);
581dbd550edSchristos 		memmove(*elementp, t, len + 1);
582dbd550edSchristos 	}
583dbd550edSchristos 	return (0);
584dbd550edSchristos }
585dbd550edSchristos 
586dbd550edSchristos /*
587dbd550edSchristos  * cl_freecap --
588dbd550edSchristos  *	Free any allocated termcap/terminfo strings.
589dbd550edSchristos  */
590dbd550edSchristos static void
cl_freecap(CL_PRIVATE * clp)591dbd550edSchristos cl_freecap(CL_PRIVATE *clp)
592dbd550edSchristos {
593dbd550edSchristos 	if (clp->el != NULL) {
594dbd550edSchristos 		free(clp->el);
595dbd550edSchristos 		clp->el = NULL;
596dbd550edSchristos 	}
597dbd550edSchristos 	if (clp->cup != NULL) {
598dbd550edSchristos 		free(clp->cup);
599dbd550edSchristos 		clp->cup = NULL;
600dbd550edSchristos 	}
601dbd550edSchristos 	if (clp->cuu1 != NULL) {
602dbd550edSchristos 		free(clp->cuu1);
603dbd550edSchristos 		clp->cuu1 = NULL;
604dbd550edSchristos 	}
605dbd550edSchristos 	if (clp->rmso != NULL) {
606dbd550edSchristos 		free(clp->rmso);
607dbd550edSchristos 		clp->rmso = NULL;
608dbd550edSchristos 	}
609dbd550edSchristos 	if (clp->smso != NULL) {
610dbd550edSchristos 		free(clp->smso);
611dbd550edSchristos 		clp->smso = NULL;
612dbd550edSchristos 	}
613dbd550edSchristos }
614dbd550edSchristos 
615dbd550edSchristos /*
616dbd550edSchristos  * cl_putenv --
617dbd550edSchristos  *	Put a value into the environment.
618dbd550edSchristos  */
619dbd550edSchristos static int
cl_putenv(SCR * sp,const char * name,const char * str,u_long value)6208d01a27eSchristos cl_putenv(SCR *sp, const char *name, const char *str, u_long value)
621dbd550edSchristos {
622dbd550edSchristos 	char buf[40];
623dbd550edSchristos 
624dbd550edSchristos 	if (str == NULL) {
625dbd550edSchristos 		(void)snprintf(buf, sizeof(buf), "%lu", value);
626dbd550edSchristos 		return (cl_setenv(sp, name, buf));
627dbd550edSchristos 	} else
628dbd550edSchristos 		return (cl_setenv(sp, name, str));
629dbd550edSchristos }
630