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