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