Home | History | Annotate | Line # | Download | only in vi
vs_msg.c revision 1.4
      1 /*	$NetBSD: vs_msg.c,v 1.4 2013/11/26 16:48:01 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 (vip == NULL)
    665 		return 0;
    666 	if (csp == NULL)
    667 		csp = sp;
    668 
    669 	/* Save the cursor position. */
    670 	(void)gp->scr_cursor(csp, &oldy, &oldx);
    671 
    672 	/* Ring the bell if it's scheduled. */
    673 	if (F_ISSET(gp, G_BELLSCHED)) {
    674 		F_CLR(gp, G_BELLSCHED);
    675 		(void)gp->scr_bell(sp);
    676 	}
    677 
    678 	/* Display new file status line. */
    679 	if (F_ISSET(sp, SC_STATUS)) {
    680 		F_CLR(sp, SC_STATUS);
    681 		msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
    682 	}
    683 
    684 	/* Report on line modifications. */
    685 	mod_rpt(sp);
    686 
    687 	/*
    688 	 * Flush any saved messages.  If the screen isn't ready, refresh
    689 	 * it.  (A side-effect of screen refresh is that we can display
    690 	 * messages.)  Once this is done, don't trust the cursor.  That
    691 	 * extra refresh screwed the pooch.
    692 	 */
    693 	if (!LIST_EMPTY(&gp->msgq)) {
    694 		if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
    695 			return (1);
    696 		while ((mp = LIST_FIRST(&gp->msgq)) != NULL) {
    697 			wp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
    698 			LIST_REMOVE(mp, q);
    699 			free(mp->buf);
    700 			free(mp);
    701 		}
    702 		F_SET(vip, VIP_CUR_INVALID);
    703 	}
    704 
    705 	switch (vip->totalcount) {
    706 	case 0:
    707 		redraw = 0;
    708 		break;
    709 	case 1:
    710 		/*
    711 		 * If we're switching screens, we have to wait for messages,
    712 		 * regardless.  If we don't wait, skip updating the modeline.
    713 		 */
    714 		if (forcewait)
    715 			vs_scroll(sp, NULL, SCROLL_W);
    716 		else
    717 			F_SET(vip, VIP_S_MODELINE);
    718 
    719 		redraw = 0;
    720 		break;
    721 	default:
    722 		/*
    723 		 * If >1 message line in use, prompt the user to continue and
    724 		 * repaint overwritten lines.
    725 		 */
    726 		vs_scroll(sp, NULL, SCROLL_W);
    727 
    728 		ev.e_event = E_REPAINT;
    729 		ev.e_flno = vip->totalcount >=
    730 		    sp->rows ? 1 : sp->rows - vip->totalcount;
    731 		ev.e_tlno = sp->rows;
    732 
    733 		redraw = 1;
    734 		break;
    735 	}
    736 
    737 	/* Reset the count of overwriting lines. */
    738 	vip->linecount = vip->lcontinue = vip->totalcount = 0;
    739 
    740 	/* Redraw. */
    741 	if (redraw)
    742 		(void)v_erepaint(sp, &ev);
    743 
    744 	/* Restore the cursor position. */
    745 	(void)gp->scr_move(csp, oldy, oldx);
    746 
    747 	return (0);
    748 }
    749 
    750 /*
    751  * vs_scroll --
    752  *	Scroll the screen for output.
    753  */
    754 static void
    755 vs_scroll(SCR *sp, int *continuep, sw_t wtype)
    756 {
    757 	GS *gp;
    758 	VI_PRIVATE *vip;
    759 
    760 	gp = sp->gp;
    761 	vip = VIP(sp);
    762 	if (!IS_ONELINE(sp)) {
    763 		/*
    764 		 * Scroll the screen.  Instead of scrolling the entire screen,
    765 		 * delete the line above the first line output so preserve the
    766 		 * maximum amount of the screen.
    767 		 */
    768 		(void)gp->scr_move(sp, vip->totalcount <
    769 		    sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
    770 		(void)gp->scr_deleteln(sp);
    771 
    772 		/* If there are screens below us, push them back into place. */
    773 		if (TAILQ_NEXT(sp, q) != NULL) {
    774 			(void)gp->scr_move(sp, LASTLINE(sp), 0);
    775 			(void)gp->scr_insertln(sp);
    776 		}
    777 	}
    778 	if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
    779 		return;
    780 	vs_wait(sp, continuep, wtype);
    781 }
    782 
    783 /*
    784  * vs_wait --
    785  *	Prompt the user to continue.
    786  */
    787 static void
    788 vs_wait(SCR *sp, int *continuep, sw_t wtype)
    789 {
    790 	EVENT ev;
    791 	VI_PRIVATE *vip;
    792 	const char *p;
    793 	GS *gp;
    794 	size_t len;
    795 
    796 	gp = sp->gp;
    797 	vip = VIP(sp);
    798 
    799 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
    800 	if (IS_ONELINE(sp))
    801 		p = msg_cmsg(sp, CMSG_CONT_S, &len);
    802 	else
    803 		switch (wtype) {
    804 		case SCROLL_W_QUIT:
    805 			p = msg_cmsg(sp, CMSG_CONT_Q, &len);
    806 			break;
    807 		case SCROLL_W_EX:
    808 			p = msg_cmsg(sp, CMSG_CONT_EX, &len);
    809 			break;
    810 		case SCROLL_W:
    811 			p = msg_cmsg(sp, CMSG_CONT, &len);
    812 			break;
    813 		default:
    814 			abort();
    815 			/* NOTREACHED */
    816 		}
    817 	(void)gp->scr_addstr(sp, p, len);
    818 
    819 	++vip->totalcount;
    820 	vip->linecount = 0;
    821 
    822 	(void)gp->scr_clrtoeol(sp);
    823 	(void)gp->scr_refresh(sp, 0);
    824 
    825 	/* Get a single character from the terminal. */
    826 	if (continuep != NULL)
    827 		*continuep = 0;
    828 	for (;;) {
    829 		if (v_event_get(sp, &ev, 0, 0))
    830 			return;
    831 		if (ev.e_event == E_CHARACTER)
    832 			break;
    833 		if (ev.e_event == E_INTERRUPT) {
    834 			ev.e_c = CH_QUIT;
    835 			F_SET(gp, G_INTERRUPTED);
    836 			break;
    837 		}
    838 		(void)gp->scr_bell(sp);
    839 	}
    840 	switch (wtype) {
    841 	case SCROLL_W_QUIT:
    842 		if (ev.e_c == CH_QUIT)
    843 			F_SET(gp, G_INTERRUPTED);
    844 		break;
    845 	case SCROLL_W_EX:
    846 		if (ev.e_c == ':' && continuep != NULL)
    847 			*continuep = 1;
    848 		break;
    849 	case SCROLL_W:
    850 		break;
    851 	}
    852 }
    853 
    854 /*
    855  * vs_divider --
    856  *	Draw a dividing line between the screen and the output.
    857  */
    858 static void
    859 vs_divider(SCR *sp)
    860 {
    861 	GS *gp;
    862 	size_t len;
    863 
    864 #define	DIVIDESTR	"+=+=+=+=+=+=+=+"
    865 	len =
    866 	    sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
    867 	gp = sp->gp;
    868 	(void)gp->scr_attr(sp, SA_INVERSE, 1);
    869 	(void)gp->scr_addstr(sp, DIVIDESTR, len);
    870 	(void)gp->scr_attr(sp, SA_INVERSE, 0);
    871 }
    872 
    873 /*
    874  * vs_msgsave --
    875  *	Save a message for later display.
    876  */
    877 static void
    878 vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len)
    879 {
    880 	GS *gp;
    881 	MSGS *mp_c, *mp_n;
    882 
    883 	/*
    884 	 * We have to handle messages before we have any place to put them.
    885 	 * If there's no screen support yet, allocate a msg structure, copy
    886 	 * in the message, and queue it on the global structure.  If we can't
    887 	 * allocate memory here, we're genuinely screwed, dump the message
    888 	 * to stderr in the (probably) vain hope that someone will see it.
    889 	 */
    890 	CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
    891 	MALLOC_GOTO(sp, mp_n->buf, char *, len);
    892 
    893 	memmove(mp_n->buf, p, len);
    894 	mp_n->len = len;
    895 	mp_n->mtype = mt;
    896 
    897 	gp = sp->gp;
    898 	if ((mp_c = LIST_FIRST(&gp->msgq)) == NULL) {
    899 		LIST_INSERT_HEAD(&gp->msgq, mp_n, q);
    900 	} else {
    901 		while (LIST_NEXT(mp_c, q) != NULL)
    902 			mp_c = LIST_NEXT(mp_c, q);
    903 		LIST_INSERT_AFTER(mp_c, mp_n, q);
    904 	}
    905 	return;
    906 
    907 alloc_err:
    908 	if (mp_n != NULL)
    909 		free(mp_n);
    910 	(void)fprintf(stderr, "%.*s\n", (int)len, p);
    911 }
    912