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