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