vs_msg.c revision 19305
1/*-
2 * Copyright (c) 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *	Keith Bostic.  All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char sccsid[] = "@(#)vs_msg.c	10.77 (Berkeley) 10/13/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <ctype.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "../common/common.h"
28#include "vi.h"
29
30typedef enum {
31	SCROLL_W,			/* User wait. */
32	SCROLL_W_EX,			/* User wait, or enter : to continue. */
33	SCROLL_W_QUIT			/* User wait, or enter q to quit. */
34					/*
35					 * SCROLL_W_QUIT has another semantic
36					 * -- only wait if the screen is full
37					 */
38} sw_t;
39
40static void	vs_divider __P((SCR *));
41static void	vs_msgsave __P((SCR *, mtype_t, char *, size_t));
42static void	vs_output __P((SCR *, mtype_t, const char *, int));
43static void	vs_scroll __P((SCR *, int *, sw_t));
44static void	vs_wait __P((SCR *, int *, sw_t));
45
46/*
47 * vs_busy --
48 *	Display, update or clear a busy message.
49 *
50 * This routine is the default editor interface for vi busy messages.  It
51 * implements a standard strategy of stealing lines from the bottom of the
52 * vi text screen.  Screens using an alternate method of displaying busy
53 * messages, e.g. X11 clock icons, should set their scr_busy function to the
54 * correct function before calling the main editor routine.
55 *
56 * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t));
57 */
58void
59vs_busy(sp, msg, btype)
60	SCR *sp;
61	const char *msg;
62	busy_t btype;
63{
64	GS *gp;
65	VI_PRIVATE *vip;
66	static const char flagc[] = "|/-\\";
67	struct timeval tv;
68	size_t len, notused;
69	const char *p;
70
71	/* Ex doesn't display busy messages. */
72	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
73		return;
74
75	gp = sp->gp;
76	vip = VIP(sp);
77
78	/*
79	 * Most of this routine is to deal with the screen sharing real estate
80	 * between the normal edit messages and the busy messages.  Logically,
81	 * all that's needed is something that puts up a message, periodically
82	 * updates it, and then goes away.
83	 */
84	switch (btype) {
85	case BUSY_ON:
86		++vip->busy_ref;
87		if (vip->totalcount != 0 || vip->busy_ref != 1)
88			break;
89
90		/* Initialize state for updates. */
91		vip->busy_ch = 0;
92		(void)gettimeofday(&vip->busy_tv, NULL);
93
94		/* Save the current cursor. */
95		(void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
96
97		/* Display the busy message. */
98		p = msg_cat(sp, msg, &len);
99		(void)gp->scr_move(sp, LASTLINE(sp), 0);
100		(void)gp->scr_addstr(sp, p, len);
101		(void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
102		(void)gp->scr_clrtoeol(sp);
103		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
104		break;
105	case BUSY_OFF:
106		if (vip->busy_ref == 0)
107			break;
108		--vip->busy_ref;
109
110		/*
111		 * If the line isn't in use for another purpose, clear it.
112		 * Always return to the original position.
113		 */
114		if (vip->totalcount == 0 && vip->busy_ref == 0) {
115			(void)gp->scr_move(sp, LASTLINE(sp), 0);
116			(void)gp->scr_clrtoeol(sp);
117		}
118		(void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
119		break;
120	case BUSY_UPDATE:
121		if (vip->totalcount != 0 || vip->busy_ref == 0)
122			break;
123
124		/* Update no more than every 1/8 of a second. */
125		(void)gettimeofday(&tv, NULL);
126		if (((tv.tv_sec - vip->busy_tv.tv_sec) * 1000000 +
127		    (tv.tv_usec - vip->busy_tv.tv_usec)) < 125000)
128			return;
129		vip->busy_tv = tv;
130
131		/* Display the update. */
132		if (vip->busy_ch == sizeof(flagc) - 1)
133			vip->busy_ch = 0;
134		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
135		(void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
136		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
137		break;
138	}
139	(void)gp->scr_refresh(sp, 0);
140}
141
142/*
143 * vs_home --
144 *	Home the cursor to the bottom row, left-most column.
145 *
146 * PUBLIC: void vs_home __P((SCR *));
147 */
148void
149vs_home(sp)
150	SCR *sp;
151{
152	(void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
153	(void)sp->gp->scr_refresh(sp, 0);
154}
155
156/*
157 * vs_update --
158 *	Update a command.
159 *
160 * PUBLIC: void vs_update __P((SCR *, const char *, const char *));
161 */
162void
163vs_update(sp, m1, m2)
164	SCR *sp;
165	const char *m1, *m2;
166{
167	GS *gp;
168	size_t len, mlen, oldx, oldy;
169
170	gp = sp->gp;
171
172	/*
173	 * This routine displays a message on the bottom line of the screen,
174	 * without updating any of the command structures that would keep it
175	 * there for any period of time, i.e. it is overwritten immediately.
176	 *
177	 * It's used by the ex read and ! commands when the user's command is
178	 * expanded, and by the ex substitution confirmation prompt.
179	 */
180	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
181		(void)ex_printf(sp,
182		    "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : m2);
183		(void)ex_fflush(sp);
184	}
185
186	/*
187	 * Save the cursor position, the substitute-with-confirmation code
188	 * will have already set it correctly.
189	 */
190	(void)gp->scr_cursor(sp, &oldy, &oldx);
191
192	/* Clear the bottom line. */
193	(void)gp->scr_move(sp, LASTLINE(sp), 0);
194	(void)gp->scr_clrtoeol(sp);
195
196	/*
197	 * XXX
198	 * Don't let long file names screw up the screen.
199	 */
200	if (m1 != NULL) {
201		mlen = len = strlen(m1);
202		if (len > sp->cols - 2)
203			mlen = len = sp->cols - 2;
204		(void)gp->scr_addstr(sp, m1, mlen);
205	} else
206		len = 0;
207	if (m2 != NULL) {
208		mlen = strlen(m2);
209		if (len + mlen > sp->cols - 2)
210			mlen = (sp->cols - 2) - len;
211		(void)gp->scr_addstr(sp, m2, mlen);
212	}
213
214	(void)gp->scr_move(sp, oldy, oldx);
215	(void)gp->scr_refresh(sp, 0);
216}
217
218/*
219 * vs_msg --
220 *	Display ex output or error messages for the screen.
221 *
222 * This routine is the default editor interface for all ex output, and all ex
223 * and vi error/informational messages.  It implements the standard strategy
224 * of stealing lines from the bottom of the vi text screen.  Screens using an
225 * alternate method of displaying messages, e.g. dialog boxes, should set their
226 * scr_msg function to the correct function before calling the editor.
227 *
228 * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t));
229 */
230void
231vs_msg(sp, mtype, line, len)
232	SCR *sp;
233	mtype_t mtype;
234	char *line;
235	size_t len;
236{
237	GS *gp;
238	VI_PRIVATE *vip;
239	size_t maxcols, oldx, oldy, padding;
240	const char *e, *s, *t;
241
242	gp = sp->gp;
243	vip = VIP(sp);
244
245	/*
246	 * Ring the bell if it's scheduled.
247	 *
248	 * XXX
249	 * Shouldn't we save this, too?
250	 */
251	if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
252		if (F_ISSET(sp, SC_SCR_VI)) {
253			F_CLR(gp, G_BELLSCHED);
254			(void)gp->scr_bell(sp);
255		} else
256			F_SET(gp, G_BELLSCHED);
257
258	/*
259	 * If vi is using the error line for text input, there's no screen
260	 * real-estate for the error message.  Nothing to do without some
261	 * information as to how important the error message is.
262	 */
263	if (F_ISSET(sp, SC_TINPUT_INFO))
264		return;
265
266	/*
267	 * Ex or ex controlled screen output.
268	 *
269	 * If output happens during startup, e.g., a .exrc file, we may be
270	 * in ex mode but haven't initialized the screen.  Initialize here,
271	 * and in this case, stay in ex mode.
272	 *
273	 * If the SC_SCR_EXWROTE bit is set, then we're switching back and
274	 * forth between ex and vi, but the screen is trashed and we have
275	 * to respect that.  Switch to ex mode long enough to put out the
276	 * message.
277	 *
278	 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
279	 * the screen, so previous opinions are ignored.
280	 */
281	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
282		if (!F_ISSET(sp, SC_SCR_EX))
283			if (F_ISSET(sp, SC_SCR_EXWROTE)) {
284				if (sp->gp->scr_screen(sp, SC_EX))
285					return;
286			} else
287				if (ex_init(sp))
288					return;
289
290		if (mtype == M_ERR)
291			(void)gp->scr_attr(sp, SA_INVERSE, 1);
292		(void)printf("%.*s", (int)len, line);
293		if (mtype == M_ERR)
294			(void)gp->scr_attr(sp, SA_INVERSE, 0);
295		(void)fflush(stdout);
296
297		F_CLR(sp, SC_EX_WAIT_NO);
298
299		if (!F_ISSET(sp, SC_SCR_EX))
300			(void)sp->gp->scr_screen(sp, SC_VI);
301		return;
302	}
303
304	/* If the vi screen isn't ready, save the message. */
305	if (!F_ISSET(sp, SC_SCR_VI)) {
306		(void)vs_msgsave(sp, mtype, line, len);
307		return;
308	}
309
310	/* Save the cursor position. */
311	(void)gp->scr_cursor(sp, &oldy, &oldx);
312
313	/* If it's an ex output message, just write it out. */
314	if (mtype == M_NONE) {
315		vs_output(sp, mtype, line, len);
316		goto ret;
317	}
318
319	/*
320	 * If it's a vi message, strip the trailing <newline> so we can
321	 * try and paste messages together.
322	 */
323	if (line[len - 1] == '\n')
324		--len;
325
326	/*
327	 * If a message won't fit on a single line, try to split on a <blank>.
328	 * If a subsequent message fits on the same line, write a separator
329	 * and output it.  Otherwise, put out a newline.
330	 *
331	 * Need up to two padding characters normally; a semi-colon and a
332	 * separating space.  If only a single line on the screen, add some
333	 * more for the trailing continuation message.
334	 *
335	 * XXX
336	 * Assume that periods and semi-colons take up a single column on the
337	 * screen.
338	 *
339	 * XXX
340	 * There are almost certainly pathological cases that will break this
341	 * code.
342	 */
343	if (IS_ONELINE(sp))
344		(void)msg_cmsg(sp, CMSG_CONT_S, &padding);
345	else
346		padding = 0;
347	padding += 2;
348
349	maxcols = sp->cols - 1;
350	if (vip->lcontinue != 0)
351		if (len + vip->lcontinue + padding > maxcols)
352			vs_output(sp, vip->mtype, ".\n", 2);
353		else  {
354			vs_output(sp, vip->mtype, ";", 1);
355			vs_output(sp, M_NONE, " ", 1);
356		}
357	vip->mtype = mtype;
358	for (s = line;; s = t) {
359		for (; len > 0 && isblank(*s); --len, ++s);
360		if (len == 0)
361			break;
362		if (len + vip->lcontinue > maxcols) {
363			for (e = s + (maxcols - vip->lcontinue);
364			    e > s && !isblank(*e); --e);
365			if (e == s)
366				 e = t = s + (maxcols - vip->lcontinue);
367			else
368				for (t = e; isblank(e[-1]); --e);
369		} else
370			e = t = s + len;
371
372		/*
373		 * If the message ends in a period, discard it, we want to
374		 * gang messages where possible.
375		 */
376		len -= t - s;
377		if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
378			--e;
379		vs_output(sp, mtype, s, e - s);
380
381		if (len != 0)
382			vs_output(sp, M_NONE, "\n", 1);
383
384		if (INTERRUPTED(sp))
385			break;
386	}
387
388ret:	(void)gp->scr_move(sp, oldy, oldx);
389	(void)gp->scr_refresh(sp, 0);
390}
391
392/*
393 * vs_output --
394 *	Output the text to the screen.
395 */
396static void
397vs_output(sp, mtype, line, llen)
398	SCR *sp;
399	mtype_t mtype;
400	const char *line;
401	int llen;
402{
403	CHAR_T *kp;
404	GS *gp;
405	VI_PRIVATE *vip;
406	size_t chlen, notused;
407	int ch, len, rlen, tlen;
408	const char *p, *t;
409	char *cbp, *ecbp, cbuf[128];
410
411	gp = sp->gp;
412	vip = VIP(sp);
413	for (p = line, rlen = llen; llen > 0;) {
414		/* Get the next physical line. */
415		if ((p = memchr(line, '\n', llen)) == NULL)
416			len = llen;
417		else
418			len = p - line;
419
420		/*
421		 * The max is sp->cols characters, and we may have already
422		 * written part of the line.
423		 */
424		if (len + vip->lcontinue > sp->cols)
425			len = sp->cols - vip->lcontinue;
426
427		/*
428		 * If the first line output, do nothing.  If the second line
429		 * output, draw the divider line.  If drew a full screen, we
430		 * remove the divider line.  If it's a continuation line, move
431		 * to the continuation point, else, move the screen up.
432		 */
433		if (vip->lcontinue == 0) {
434			if (!IS_ONELINE(sp)) {
435				if (vip->totalcount == 1) {
436					(void)gp->scr_move(sp,
437					    LASTLINE(sp) - 1, 0);
438					(void)gp->scr_clrtoeol(sp);
439					(void)vs_divider(sp);
440					F_SET(vip, VIP_DIVIDER);
441					++vip->totalcount;
442					++vip->linecount;
443				}
444				if (vip->totalcount == sp->t_maxrows &&
445				    F_ISSET(vip, VIP_DIVIDER)) {
446					--vip->totalcount;
447					--vip->linecount;
448					F_CLR(vip, VIP_DIVIDER);
449				}
450			}
451			if (vip->totalcount != 0)
452				vs_scroll(sp, NULL, SCROLL_W_QUIT);
453
454			(void)gp->scr_move(sp, LASTLINE(sp), 0);
455			++vip->totalcount;
456			++vip->linecount;
457
458			if (INTERRUPTED(sp))
459				break;
460		} else
461			(void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
462
463		/* Error messages are in inverse video. */
464		if (mtype == M_ERR)
465			(void)gp->scr_attr(sp, SA_INVERSE, 1);
466
467		/* Display the line, doing character translation. */
468#define	FLUSH {								\
469	*cbp = '\0';							\
470	(void)gp->scr_addstr(sp, cbuf, cbp - cbuf);			\
471	cbp = cbuf;							\
472}
473		ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
474		for (t = line, tlen = len; tlen--; ++t) {
475			ch = *t;
476			/*
477			 * Replace tabs with spaces, there are places in
478			 * ex that do column calculations without looking
479			 * at <tabs> -- and all routines that care about
480			 * <tabs> do their own expansions.  This catches
481			 * <tabs> in things like tag search strings.
482			 */
483			if (ch == '\t')
484				ch = ' ';
485			chlen = KEY_LEN(sp, ch);
486			if (cbp + chlen >= ecbp)
487				FLUSH;
488			for (kp = KEY_NAME(sp, ch); chlen--;)
489				*cbp++ = *kp++;
490		}
491		if (cbp > cbuf)
492			FLUSH;
493		if (mtype == M_ERR)
494			(void)gp->scr_attr(sp, SA_INVERSE, 0);
495
496		/* Clear the rest of the line. */
497		(void)gp->scr_clrtoeol(sp);
498
499		/* If we loop, it's a new line. */
500		vip->lcontinue = 0;
501
502		/* Reset for the next line. */
503		line += len;
504		llen -= len;
505		if (p != NULL) {
506			++line;
507			--llen;
508		}
509	}
510
511	/* Set up next continuation line. */
512	if (p == NULL)
513		gp->scr_cursor(sp, &notused, &vip->lcontinue);
514}
515
516/*
517 * vs_ex_resolve --
518 *	Deal with ex message output.
519 *
520 * This routine is called when exiting a colon command to resolve any ex
521 * output that may have occurred.
522 *
523 * PUBLIC: int vs_ex_resolve __P((SCR *, int *));
524 */
525int
526vs_ex_resolve(sp, continuep)
527	SCR *sp;
528	int *continuep;
529{
530	EVENT ev;
531	GS *gp;
532	VI_PRIVATE *vip;
533	sw_t wtype;
534
535	gp = sp->gp;
536	vip = VIP(sp);
537	*continuep = 0;
538
539	/* If we ran any ex command, we can't trust the cursor position. */
540	F_SET(vip, VIP_CUR_INVALID);
541
542	/* Terminate any partially written message. */
543	if (vip->lcontinue != 0) {
544		vs_output(sp, vip->mtype, ".", 1);
545		vip->lcontinue = 0;
546
547		vip->mtype = M_NONE;
548	}
549
550	/*
551	 * If we switched out of the vi screen into ex, switch back while we
552	 * figure out what to do with the screen and potentially get another
553	 * command to execute.
554	 *
555	 * If we didn't switch into ex, we're not required to wait, and less
556	 * than 2 lines of output, we can continue without waiting for the
557	 * wait.
558	 *
559	 * Note, all other code paths require waiting, so we leave the report
560	 * of modified lines until later, so that we won't wait for no other
561	 * reason than a threshold number of lines were modified.  This means
562	 * we display cumulative line modification reports for groups of ex
563	 * commands.  That seems right to me (well, at least not wrong).
564	 */
565	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
566		if (sp->gp->scr_screen(sp, SC_VI))
567			return (1);
568	} else
569		if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
570			F_CLR(sp, SC_EX_WAIT_NO);
571			return (0);
572		}
573
574	/* Clear the required wait flag, it's no longer needed. */
575	F_CLR(sp, SC_EX_WAIT_YES);
576
577	/*
578	 * Wait, unless explicitly told not to wait or the user interrupted
579	 * the command.  If the user is leaving the screen, for any reason,
580	 * they can't continue with further ex commands.
581	 */
582	if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
583		wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
584		    SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
585		if (F_ISSET(sp, SC_SCR_EXWROTE))
586			vs_wait(sp, continuep, wtype);
587		else
588			vs_scroll(sp, continuep, wtype);
589		if (*continuep)
590			return (0);
591	}
592
593	/* If ex wrote on the screen, refresh the screen image. */
594	if (F_ISSET(sp, SC_SCR_EXWROTE))
595		F_SET(vip, VIP_N_EX_PAINT);
596
597	/*
598	 * If we're not the bottom of the split screen stack, the screen
599	 * image itself is wrong, so redraw everything.
600	 */
601	if (sp->q.cqe_next != (void *)&sp->gp->dq)
602		F_SET(sp, SC_SCR_REDRAW);
603
604	/* If ex changed the underlying file, the map itself is wrong. */
605	if (F_ISSET(vip, VIP_N_EX_REDRAW))
606		F_SET(sp, SC_SCR_REFORMAT);
607
608	/* Ex may have switched out of the alternate screen, return. */
609	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
610
611	/*
612	 * Whew.  We're finally back home, after what feels like years.
613	 * Kiss the ground.
614	 */
615	F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
616
617	/*
618	 * We may need to repaint some of the screen, e.g.:
619	 *
620	 *	:set
621	 *	:!ls
622	 *
623	 * gives us a combination of some lines that are "wrong", and a need
624	 * for a full refresh.
625	 */
626	if (vip->totalcount > 1) {
627		/* Set up the redraw of the overwritten lines. */
628		ev.e_event = E_REPAINT;
629		ev.e_flno = vip->totalcount >=
630		    sp->rows ? 1 : sp->rows - vip->totalcount;
631		ev.e_tlno = sp->rows;
632
633		/* Reset the count of overwriting lines. */
634		vip->linecount = vip->lcontinue = vip->totalcount = 0;
635
636		/* Redraw. */
637		(void)vs_repaint(sp, &ev);
638	} else
639		/* Reset the count of overwriting lines. */
640		vip->linecount = vip->lcontinue = vip->totalcount = 0;
641
642	return (0);
643}
644
645/*
646 * vs_resolve --
647 *	Deal with message output.
648 *
649 * PUBLIC: int vs_resolve __P((SCR *, SCR *, int));
650 */
651int
652vs_resolve(sp, csp, forcewait)
653	SCR *sp, *csp;
654	int forcewait;
655{
656	EVENT ev;
657	GS *gp;
658	MSGS *mp;
659	VI_PRIVATE *vip;
660	size_t oldy, oldx;
661	int redraw;
662
663	/*
664	 * Vs_resolve is called from the main vi loop and the refresh function
665	 * to periodically ensure that the user has seen any messages that have
666	 * been displayed and that any status lines are correct.  The sp screen
667	 * is the screen we're checking, usually the current screen.  When it's
668	 * not, csp is the current screen, used for final cursor positioning.
669	 */
670	gp = sp->gp;
671	vip = VIP(sp);
672	if (csp == NULL)
673		csp = sp;
674
675	/* Save the cursor position. */
676	(void)gp->scr_cursor(csp, &oldy, &oldx);
677
678	/* Ring the bell if it's scheduled. */
679	if (F_ISSET(gp, G_BELLSCHED)) {
680		F_CLR(gp, G_BELLSCHED);
681		(void)gp->scr_bell(sp);
682	}
683
684	/* Display new file status line. */
685	if (F_ISSET(sp, SC_STATUS)) {
686		F_CLR(sp, SC_STATUS);
687		msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
688	}
689
690	/* Report on line modifications. */
691	mod_rpt(sp);
692
693	/*
694	 * Flush any saved messages.  If the screen isn't ready, refresh
695	 * it.  (A side-effect of screen refresh is that we can display
696	 * messages.)  Once this is done, don't trust the cursor.  That
697	 * extra refresh screwed the pooch.
698	 */
699	if (gp->msgq.lh_first != NULL) {
700		if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
701			return (1);
702		while ((mp = gp->msgq.lh_first) != NULL) {
703			gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
704			LIST_REMOVE(mp, q);
705			free(mp->buf);
706			free(mp);
707		}
708		F_SET(vip, VIP_CUR_INVALID);
709	}
710
711	switch (vip->totalcount) {
712	case 0:
713		redraw = 0;
714		break;
715	case 1:
716		/*
717		 * If we're switching screens, we have to wait for messages,
718		 * regardless.  If we don't wait, skip updating the modeline.
719		 */
720		if (forcewait)
721			vs_scroll(sp, NULL, SCROLL_W);
722		else
723			F_SET(vip, VIP_S_MODELINE);
724
725		redraw = 0;
726		break;
727	default:
728		/*
729		 * If >1 message line in use, prompt the user to continue and
730		 * repaint overwritten lines.
731		 */
732		vs_scroll(sp, NULL, SCROLL_W);
733
734		ev.e_event = E_REPAINT;
735		ev.e_flno = vip->totalcount >=
736		    sp->rows ? 1 : sp->rows - vip->totalcount;
737		ev.e_tlno = sp->rows;
738
739		redraw = 1;
740		break;
741	}
742
743	/* Reset the count of overwriting lines. */
744	vip->linecount = vip->lcontinue = vip->totalcount = 0;
745
746	/* Redraw. */
747	if (redraw)
748		(void)vs_repaint(sp, &ev);
749
750	/* Restore the cursor position. */
751	(void)gp->scr_move(csp, oldy, oldx);
752
753	return (0);
754}
755
756/*
757 * vs_scroll --
758 *	Scroll the screen for output.
759 */
760static void
761vs_scroll(sp, continuep, wtype)
762	SCR *sp;
763	int *continuep;
764	sw_t wtype;
765{
766	GS *gp;
767	VI_PRIVATE *vip;
768
769	gp = sp->gp;
770	vip = VIP(sp);
771	if (!IS_ONELINE(sp)) {
772		/*
773		 * Scroll the screen.  Instead of scrolling the entire screen,
774		 * delete the line above the first line output so preserve the
775		 * maximum amount of the screen.
776		 */
777		(void)gp->scr_move(sp, vip->totalcount <
778		    sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
779		(void)gp->scr_deleteln(sp);
780
781		/* If there are screens below us, push them back into place. */
782		if (sp->q.cqe_next != (void *)&sp->gp->dq) {
783			(void)gp->scr_move(sp, LASTLINE(sp), 0);
784			(void)gp->scr_insertln(sp);
785		}
786	}
787	if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
788		return;
789	vs_wait(sp, continuep, wtype);
790}
791
792/*
793 * vs_wait --
794 *	Prompt the user to continue.
795 */
796static void
797vs_wait(sp, continuep, wtype)
798	SCR *sp;
799	int *continuep;
800	sw_t wtype;
801{
802	EVENT ev;
803	VI_PRIVATE *vip;
804	const char *p;
805	GS *gp;
806	size_t len;
807
808	gp = sp->gp;
809	vip = VIP(sp);
810
811	(void)gp->scr_move(sp, LASTLINE(sp), 0);
812	if (IS_ONELINE(sp))
813		p = msg_cmsg(sp, CMSG_CONT_S, &len);
814	else
815		switch (wtype) {
816		case SCROLL_W_QUIT:
817			p = msg_cmsg(sp, CMSG_CONT_Q, &len);
818			break;
819		case SCROLL_W_EX:
820			p = msg_cmsg(sp, CMSG_CONT_EX, &len);
821			break;
822		case SCROLL_W:
823			p = msg_cmsg(sp, CMSG_CONT, &len);
824			break;
825		default:
826			abort();
827			/* NOTREACHED */
828		}
829	(void)gp->scr_addstr(sp, p, len);
830
831	++vip->totalcount;
832	vip->linecount = 0;
833
834	(void)gp->scr_clrtoeol(sp);
835	(void)gp->scr_refresh(sp, 0);
836
837	/* Get a single character from the terminal. */
838	if (continuep != NULL)
839		*continuep = 0;
840	for (;;) {
841		if (v_event_get(sp, &ev, 0, 0))
842			return;
843		if (ev.e_event == E_CHARACTER)
844			break;
845		if (ev.e_event == E_INTERRUPT) {
846			ev.e_c = CH_QUIT;
847			F_SET(gp, G_INTERRUPTED);
848			break;
849		}
850		(void)gp->scr_bell(sp);
851	}
852	switch (wtype) {
853	case SCROLL_W_QUIT:
854		if (ev.e_c == CH_QUIT)
855			F_SET(gp, G_INTERRUPTED);
856		break;
857	case SCROLL_W_EX:
858		if (ev.e_c == ':' && continuep != NULL)
859			*continuep = 1;
860		break;
861	case SCROLL_W:
862		break;
863	}
864}
865
866/*
867 * vs_divider --
868 *	Draw a dividing line between the screen and the output.
869 */
870static void
871vs_divider(sp)
872	SCR *sp;
873{
874	GS *gp;
875	size_t len;
876
877#define	DIVIDESTR	"+=+=+=+=+=+=+=+"
878	len =
879	    sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
880	gp = sp->gp;
881	(void)gp->scr_attr(sp, SA_INVERSE, 1);
882	(void)gp->scr_addstr(sp, DIVIDESTR, len);
883	(void)gp->scr_attr(sp, SA_INVERSE, 0);
884}
885
886/*
887 * vs_msgsave --
888 *	Save a message for later display.
889 */
890static void
891vs_msgsave(sp, mt, p, len)
892	SCR *sp;
893	mtype_t mt;
894	char *p;
895	size_t len;
896{
897	GS *gp;
898	MSGS *mp_c, *mp_n;
899
900	/*
901	 * We have to handle messages before we have any place to put them.
902	 * If there's no screen support yet, allocate a msg structure, copy
903	 * in the message, and queue it on the global structure.  If we can't
904	 * allocate memory here, we're genuinely screwed, dump the message
905	 * to stderr in the (probably) vain hope that someone will see it.
906	 */
907	CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
908	MALLOC_GOTO(sp, mp_n->buf, char *, len);
909
910	memmove(mp_n->buf, p, len);
911	mp_n->len = len;
912	mp_n->mtype = mt;
913
914	gp = sp->gp;
915	if ((mp_c = gp->msgq.lh_first) == NULL) {
916		LIST_INSERT_HEAD(&gp->msgq, mp_n, q);
917	} else {
918		for (; mp_c->q.le_next != NULL; mp_c = mp_c->q.le_next);
919		LIST_INSERT_AFTER(mp_c, mp_n, q);
920	}
921	return;
922
923alloc_err:
924	if (mp_n != NULL)
925		free(mp_n);
926	(void)fprintf(stderr, "%.*s\n", (int)len, p);
927}
928