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