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