131921Sbrian/*-
231921Sbrian * Copyright (c) 1993, 1994
331921Sbrian *	The Regents of the University of California.  All rights reserved.
431921Sbrian * Copyright (c) 1992, 1993, 1994, 1995, 1996
531921Sbrian *	Keith Bostic.  All rights reserved.
631921Sbrian *
731921Sbrian * See the LICENSE file for redistribution information.
831921Sbrian */
931921Sbrian
1031921Sbrian#include "config.h"
1131921Sbrian
1231921Sbrian#ifndef lint
1331921Sbrianstatic const char sccsid[] = "$Id: vs_msg.c,v 10.88 2013/03/19 09:59:03 zy Exp $";
1431921Sbrian#endif /* not lint */
1531921Sbrian
1631921Sbrian#include <sys/types.h>
1731921Sbrian#include <sys/queue.h>
1831921Sbrian#include <sys/time.h>
1931921Sbrian
2031921Sbrian#include <bitstring.h>
2131921Sbrian#include <ctype.h>
2231921Sbrian#include <stdio.h>
2331921Sbrian#include <stdlib.h>
2431921Sbrian#include <string.h>
2531921Sbrian#include <unistd.h>
2650479Speter
2731272Sbrian#include "../common/common.h"
2831272Sbrian#include "vi.h"
2936285Sbrian
3031272Sbriantypedef enum {
3131272Sbrian	SCROLL_W,			/* User wait. */
3249434Sbrian	SCROLL_W_EX,			/* User wait, or enter : to continue. */
3336285Sbrian	SCROLL_W_QUIT			/* User wait, or enter q to quit. */
3436285Sbrian					/*
3531272Sbrian					 * SCROLL_W_QUIT has another semantic
3631272Sbrian					 * -- only wait if the screen is full
3731343Sbrian					 */
3831272Sbrian} sw_t;
3931272Sbrian
4036285Sbrianstatic void	vs_divider __P((SCR *));
4136285Sbrianstatic void	vs_msgsave __P((SCR *, mtype_t, char *, size_t));
4231272Sbrianstatic void	vs_output __P((SCR *, mtype_t, const char *, int));
4364670Sbrianstatic void	vs_scroll __P((SCR *, int *, sw_t));
4431272Sbrianstatic void	vs_wait __P((SCR *, int *, sw_t));
4549434Sbrian
4631272Sbrian/*
4765178Sbrian * vs_busy --
4849434Sbrian *	Display, update or clear a busy message.
4964670Sbrian *
5064670Sbrian * This routine is the default editor interface for vi busy messages.  It
5164670Sbrian * implements a standard strategy of stealing lines from the bottom of the
5264670Sbrian * vi text screen.  Screens using an alternate method of displaying busy
5364670Sbrian * messages, e.g. X11 clock icons, should set their scr_busy function to the
5464670Sbrian * correct function before calling the main editor routine.
5564670Sbrian *
5649434Sbrian * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t));
5749434Sbrian */
5836285Sbrianvoid
5936285Sbrianvs_busy(SCR *sp, const char *msg, busy_t btype)
6036285Sbrian{
6149434Sbrian	GS *gp;
6236285Sbrian	VI_PRIVATE *vip;
6349434Sbrian	static const char flagc[] = "|/-\\";
6449434Sbrian	struct timespec ts, ts_diff;
6531272Sbrian	const struct timespec ts_min = { 0, 125000000 };
6631272Sbrian	size_t len, notused;
6731272Sbrian	const char *p;
6831272Sbrian
6949434Sbrian	/* Ex doesn't display busy messages. */
7049434Sbrian	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
7164670Sbrian		return;
7249434Sbrian
7364670Sbrian	gp = sp->gp;
7464670Sbrian	vip = VIP(sp);
7564670Sbrian
7664670Sbrian	/*
7749434Sbrian	 * Most of this routine is to deal with the screen sharing real estate
7849434Sbrian	 * between the normal edit messages and the busy messages.  Logically,
7949434Sbrian	 * all that's needed is something that puts up a message, periodically
8049434Sbrian	 * updates it, and then goes away.
8149434Sbrian	 */
8249434Sbrian	switch (btype) {
8349434Sbrian	case BUSY_ON:
8449434Sbrian		++vip->busy_ref;
8549434Sbrian		if (vip->totalcount != 0 || vip->busy_ref != 1)
8649447Sbrian			break;
8749447Sbrian
8849447Sbrian		/* Initialize state for updates. */
8949447Sbrian		vip->busy_ch = 0;
9049447Sbrian		timepoint_steady(&vip->busy_ts);
9164670Sbrian
9249447Sbrian		/* Save the current cursor. */
9349447Sbrian		(void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
9449447Sbrian
9549434Sbrian		/* Display the busy message. */
9649434Sbrian		p = msg_cat(sp, msg, &len);
9749434Sbrian		(void)gp->scr_move(sp, LASTLINE(sp), 0);
9849434Sbrian		(void)gp->scr_addstr(sp, p, len);
9936285Sbrian		(void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
10031272Sbrian		(void)gp->scr_clrtoeol(sp);
10149434Sbrian		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
10231272Sbrian		break;
10349434Sbrian	case BUSY_OFF:
10449434Sbrian		if (vip->busy_ref == 0)
10549434Sbrian			break;
10649434Sbrian		--vip->busy_ref;
10749434Sbrian
10849434Sbrian		/*
10949434Sbrian		 * If the line isn't in use for another purpose, clear it.
11049434Sbrian		 * Always return to the original position.
11149434Sbrian		 */
11249582Sbrian		if (vip->totalcount == 0 && vip->busy_ref == 0) {
11336285Sbrian			(void)gp->scr_move(sp, LASTLINE(sp), 0);
11465178Sbrian			(void)gp->scr_clrtoeol(sp);
11565178Sbrian		}
11636285Sbrian		(void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
11746686Sbrian		break;
11849434Sbrian	case BUSY_UPDATE:
11964670Sbrian		if (vip->totalcount != 0 || vip->busy_ref == 0)
12064670Sbrian			break;
12164670Sbrian
12264670Sbrian		/* Update no more than every 1/8 of a second. */
12349434Sbrian		timepoint_steady(&ts);
12446686Sbrian		ts_diff = ts;
12536819Sbrian		timespecsub(&ts_diff, &vip->busy_ts);
12631272Sbrian		if (timespeccmp(&ts_diff, &ts_min, <))
12749582Sbrian			return;
12849434Sbrian		vip->busy_ts = ts;
12931272Sbrian
13031272Sbrian		/* Display the update. */
13131272Sbrian		if (vip->busy_ch == sizeof(flagc) - 1)
13231272Sbrian			vip->busy_ch = 0;
13331272Sbrian		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
13431272Sbrian		(void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
13531272Sbrian		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
13631272Sbrian		break;
13731272Sbrian	}
13849434Sbrian	(void)gp->scr_refresh(sp, 0);
13964670Sbrian}
14064670Sbrian
14164670Sbrian/*
14265178Sbrian * vs_home --
14365178Sbrian *	Home the cursor to the bottom row, left-most column.
14465178Sbrian *
14565178Sbrian * PUBLIC: void vs_home __P((SCR *));
14631272Sbrian */
14731272Sbrianvoid
14836285Sbrianvs_home(SCR *sp)
14949582Sbrian{
15049434Sbrian	(void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
15149434Sbrian	(void)sp->gp->scr_refresh(sp, 0);
15231272Sbrian}
15349582Sbrian
15449434Sbrian/*
15531272Sbrian * vs_update --
15631272Sbrian *	Update a command.
15731272Sbrian *
15831272Sbrian * PUBLIC: void vs_update __P((SCR *, const char *, const CHAR_T *));
15931343Sbrian */
16031272Sbrianvoid
16131343Sbrianvs_update(SCR *sp, const char *m1, const CHAR_T *m2)
16246686Sbrian{
16349434Sbrian	GS *gp;
16464670Sbrian	size_t len, mlen, oldx, oldy;
16531272Sbrian	CONST char *np;
16636285Sbrian	size_t nlen;
16731272Sbrian
16849434Sbrian	gp = sp->gp;
16949434Sbrian
17064670Sbrian	/*
17164670Sbrian	 * This routine displays a message on the bottom line of the screen,
17264670Sbrian	 * without updating any of the command structures that would keep it
17364670Sbrian	 * there for any period of time, i.e. it is overwritten immediately.
17464670Sbrian	 *
17564670Sbrian	 * It's used by the ex read and ! commands when the user's command is
17664670Sbrian	 * expanded, and by the ex substitution confirmation prompt.
17764670Sbrian	 */
17864670Sbrian	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
17964670Sbrian		if (m2 != NULL)
18064670Sbrian			INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen);
18164670Sbrian		(void)ex_printf(sp,
18249434Sbrian		    "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np);
18336819Sbrian		(void)ex_fflush(sp);
18464670Sbrian	}
18549434Sbrian
18631272Sbrian	/*
18731272Sbrian	 * Save the cursor position, the substitute-with-confirmation code
18849434Sbrian	 * will have already set it correctly.
18949434Sbrian	 */
19049434Sbrian	(void)gp->scr_cursor(sp, &oldy, &oldx);
19136285Sbrian
19231272Sbrian	/* Clear the bottom line. */
19331272Sbrian	(void)gp->scr_move(sp, LASTLINE(sp), 0);
19431272Sbrian	(void)gp->scr_clrtoeol(sp);
19536285Sbrian
19631272Sbrian	/*
19749434Sbrian	 * XXX
19836285Sbrian	 * Don't let long file names screw up the screen.
19949434Sbrian	 */
20049434Sbrian	if (m1 != NULL) {
20164670Sbrian		mlen = len = strlen(m1);
20249434Sbrian		if (len > sp->cols - 2)
20349434Sbrian			mlen = len = sp->cols - 2;
20464670Sbrian		(void)gp->scr_addstr(sp, m1, mlen);
20549434Sbrian	} else
20649434Sbrian		len = 0;
20749434Sbrian	if (m2 != NULL) {
20849434Sbrian		mlen = STRLEN(m2);
20949434Sbrian		if (len + mlen > sp->cols - 2)
21049434Sbrian			mlen = (sp->cols - 2) - len;
21149434Sbrian		(void)gp->scr_waddstr(sp, m2, mlen);
21249434Sbrian	}
21349434Sbrian
21449434Sbrian	(void)gp->scr_move(sp, oldy, oldx);
21536285Sbrian	(void)gp->scr_refresh(sp, 0);
21636285Sbrian}
21731272Sbrian
21831272Sbrian/*
21936285Sbrian * vs_msg --
22031272Sbrian *	Display ex output or error messages for the screen.
22136285Sbrian *
22249434Sbrian * This routine is the default editor interface for all ex output, and all ex
22349434Sbrian * and vi error/informational messages.  It implements the standard strategy
22449434Sbrian * of stealing lines from the bottom of the vi text screen.  Screens using an
22549434Sbrian * alternate method of displaying messages, e.g. dialog boxes, should set their
22649434Sbrian * scr_msg function to the correct function before calling the editor.
22731272Sbrian *
22831272Sbrian * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t));
22931272Sbrian */
23031272Sbrianvoid
23131272Sbrianvs_msg(SCR *sp, mtype_t mtype, char *line, size_t len)
23231272Sbrian{
23349434Sbrian	GS *gp;
23449434Sbrian	VI_PRIVATE *vip;
23536285Sbrian	size_t maxcols, oldx, oldy, padding;
23631272Sbrian	const char *e, *s, *t;
23731272Sbrian
23831272Sbrian	gp = sp->gp;
23946686Sbrian	vip = VIP(sp);
24031272Sbrian
24131272Sbrian	/*
24265178Sbrian	 * Ring the bell if it's scheduled.
24331272Sbrian	 *
24431272Sbrian	 * XXX
24531272Sbrian	 * Shouldn't we save this, too?
24646686Sbrian	 */
24731272Sbrian	if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
24831272Sbrian		if (F_ISSET(sp, SC_SCR_VI)) {
24965178Sbrian			F_CLR(gp, G_BELLSCHED);
25031272Sbrian			(void)gp->scr_bell(sp);
25136934Sbrian		} else
25236934Sbrian			F_SET(gp, G_BELLSCHED);
25336934Sbrian
25436934Sbrian	/*
25536934Sbrian	 * If vi is using the error line for text input, there's no screen
25636934Sbrian	 * real-estate for the error message.  Nothing to do without some
25736934Sbrian	 * information as to how important the error message is.
25849434Sbrian	 */
25964670Sbrian	if (F_ISSET(sp, SC_TINPUT_INFO))
26036934Sbrian		return;
26136934Sbrian
26236934Sbrian	/*
26336934Sbrian	 * Ex or ex controlled screen output.
26449434Sbrian	 *
26536934Sbrian	 * If output happens during startup, e.g., a .exrc file, we may be
26649434Sbrian	 * in ex mode but haven't initialized the screen.  Initialize here,
26749434Sbrian	 * and in this case, stay in ex mode.
26846686Sbrian	 *
26949434Sbrian	 * If the SC_SCR_EXWROTE bit is set, then we're switching back and
27036934Sbrian	 * forth between ex and vi, but the screen is trashed and we have
27149434Sbrian	 * to respect that.  Switch to ex mode long enough to put out the
27249434Sbrian	 * message.
27336934Sbrian	 *
27436934Sbrian	 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
27536934Sbrian	 * the screen, so previous opinions are ignored.
27664670Sbrian	 */
27764670Sbrian	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
27864670Sbrian		if (!F_ISSET(sp, SC_SCR_EX))
27964670Sbrian			if (F_ISSET(sp, SC_SCR_EXWROTE)) {
28036934Sbrian				if (sp->gp->scr_screen(sp, SC_EX))
28136934Sbrian					return;
28236934Sbrian			} else
28336934Sbrian				if (ex_init(sp))
28436934Sbrian					return;
28536934Sbrian
28636934Sbrian		if (mtype == M_ERR)
28736934Sbrian			(void)gp->scr_attr(sp, SA_INVERSE, 1);
28836934Sbrian		(void)printf("%.*s", (int)len, line);
28946686Sbrian		if (mtype == M_ERR)
29049434Sbrian			(void)gp->scr_attr(sp, SA_INVERSE, 0);
29136934Sbrian		(void)fflush(stdout);
29249434Sbrian
29336934Sbrian		F_CLR(sp, SC_EX_WAIT_NO);
29436934Sbrian
29549434Sbrian		if (!F_ISSET(sp, SC_SCR_EX))
29649434Sbrian			(void)sp->gp->scr_screen(sp, SC_VI);
29749434Sbrian		return;
29849434Sbrian	}
29949434Sbrian
30049434Sbrian	/* If the vi screen isn't ready, save the message. */
30149434Sbrian	if (!F_ISSET(sp, SC_SCR_VI)) {
302		(void)vs_msgsave(sp, mtype, line, len);
303		return;
304	}
305
306	/* Save the cursor position. */
307	(void)gp->scr_cursor(sp, &oldy, &oldx);
308
309	/* If it's an ex output message, just write it out. */
310	if (mtype == M_NONE) {
311		vs_output(sp, mtype, line, len);
312		goto ret;
313	}
314
315	/*
316	 * If it's a vi message, strip the trailing <newline> so we can
317	 * try and paste messages together.
318	 */
319	if (line[len - 1] == '\n')
320		--len;
321
322	/*
323	 * If a message won't fit on a single line, try to split on a <blank>.
324	 * If a subsequent message fits on the same line, write a separator
325	 * and output it.  Otherwise, put out a newline.
326	 *
327	 * Need up to two padding characters normally; a semi-colon and a
328	 * separating space.  If only a single line on the screen, add some
329	 * more for the trailing continuation message.
330	 *
331	 * XXX
332	 * Assume that periods and semi-colons take up a single column on the
333	 * screen.
334	 *
335	 * XXX
336	 * There are almost certainly pathological cases that will break this
337	 * code.
338	 */
339	if (IS_ONELINE(sp))
340		(void)msg_cmsg(sp, CMSG_CONT_S, &padding);
341	else
342		padding = 0;
343	padding += 2;
344
345	maxcols = sp->cols - 1;
346	if (vip->lcontinue != 0)
347		if (len + vip->lcontinue + padding > maxcols)
348			vs_output(sp, vip->mtype, ".\n", 2);
349		else  {
350			vs_output(sp, vip->mtype, ";", 1);
351			vs_output(sp, M_NONE, " ", 1);
352		}
353	vip->mtype = mtype;
354	for (s = line;; s = t) {
355		for (; len > 0 && isblank(*s); --len, ++s);
356		if (len == 0)
357			break;
358		if (len + vip->lcontinue > maxcols) {
359			for (e = s + (maxcols - vip->lcontinue);
360			    e > s && !isblank(*e); --e);
361			if (e == s)
362				 e = t = s + (maxcols - vip->lcontinue);
363			else
364				for (t = e; isblank(e[-1]); --e);
365		} else
366			e = t = s + len;
367
368		/*
369		 * If the message ends in a period, discard it, we want to
370		 * gang messages where possible.
371		 */
372		len -= t - s;
373		if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
374			--e;
375		vs_output(sp, mtype, s, e - s);
376
377		if (len != 0)
378			vs_output(sp, M_NONE, "\n", 1);
379
380		if (INTERRUPTED(sp))
381			break;
382	}
383
384ret:	(void)gp->scr_move(sp, oldy, oldx);
385	(void)gp->scr_refresh(sp, 0);
386}
387
388/*
389 * vs_output --
390 *	Output the text to the screen.
391 */
392static void
393vs_output(SCR *sp, mtype_t mtype, const char *line, int llen)
394{
395	GS *gp;
396	VI_PRIVATE *vip;
397	size_t notused;
398	int len, rlen, tlen;
399	const char *p, *t;
400	char *cbp, *ecbp, cbuf[128];
401
402	gp = sp->gp;
403	vip = VIP(sp);
404	for (p = line, rlen = llen; llen > 0;) {
405		/* Get the next physical line. */
406		if ((p = memchr(line, '\n', llen)) == NULL)
407			len = llen;
408		else
409			len = p - line;
410
411		/*
412		 * The max is sp->cols characters, and we may have already
413		 * written part of the line.
414		 */
415		if (len + vip->lcontinue > sp->cols)
416			len = sp->cols - vip->lcontinue;
417
418		/*
419		 * If the first line output, do nothing.  If the second line
420		 * output, draw the divider line.  If drew a full screen, we
421		 * remove the divider line.  If it's a continuation line, move
422		 * to the continuation point, else, move the screen up.
423		 */
424		if (vip->lcontinue == 0) {
425			if (!IS_ONELINE(sp)) {
426				if (vip->totalcount == 1) {
427					(void)gp->scr_move(sp,
428					    LASTLINE(sp) - 1, 0);
429					(void)gp->scr_clrtoeol(sp);
430					(void)vs_divider(sp);
431					F_SET(vip, VIP_DIVIDER);
432					++vip->totalcount;
433					++vip->linecount;
434				}
435				if (vip->totalcount == sp->t_maxrows &&
436				    F_ISSET(vip, VIP_DIVIDER)) {
437					--vip->totalcount;
438					--vip->linecount;
439					F_CLR(vip, VIP_DIVIDER);
440				}
441			}
442			if (vip->totalcount != 0)
443				vs_scroll(sp, NULL, SCROLL_W_QUIT);
444
445			(void)gp->scr_move(sp, LASTLINE(sp), 0);
446			++vip->totalcount;
447			++vip->linecount;
448
449			if (INTERRUPTED(sp))
450				break;
451		} else
452			(void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
453
454		/* Error messages are in inverse video. */
455		if (mtype == M_ERR)
456			(void)gp->scr_attr(sp, SA_INVERSE, 1);
457
458		/* Display the line, doing character translation. */
459#define	FLUSH {								\
460	*cbp = '\0';							\
461	(void)gp->scr_addstr(sp, cbuf, cbp - cbuf);			\
462	cbp = cbuf;							\
463}
464		ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
465		for (t = line, tlen = len; tlen--; ++t) {
466			/*
467			 * Replace tabs with spaces, there are places in
468			 * ex that do column calculations without looking
469			 * at <tabs> -- and all routines that care about
470			 * <tabs> do their own expansions.  This catches
471			 * <tabs> in things like tag search strings.
472			 */
473			if (cbp + 1 >= ecbp)
474				FLUSH;
475			*cbp++ = *t == '\t' ? ' ' : *t;
476		}
477		if (cbp > cbuf)
478			FLUSH;
479		if (mtype == M_ERR)
480			(void)gp->scr_attr(sp, SA_INVERSE, 0);
481
482		/* Clear the rest of the line. */
483		(void)gp->scr_clrtoeol(sp);
484
485		/* If we loop, it's a new line. */
486		vip->lcontinue = 0;
487
488		/* Reset for the next line. */
489		line += len;
490		llen -= len;
491		if (p != NULL) {
492			++line;
493			--llen;
494		}
495	}
496
497	/* Set up next continuation line. */
498	if (p == NULL)
499		gp->scr_cursor(sp, &notused, &vip->lcontinue);
500}
501
502/*
503 * vs_ex_resolve --
504 *	Deal with ex message output.
505 *
506 * This routine is called when exiting a colon command to resolve any ex
507 * output that may have occurred.
508 *
509 * PUBLIC: int vs_ex_resolve __P((SCR *, int *));
510 */
511int
512vs_ex_resolve(SCR *sp, int *continuep)
513{
514	EVENT ev;
515	GS *gp;
516	VI_PRIVATE *vip;
517	sw_t wtype;
518
519	gp = sp->gp;
520	vip = VIP(sp);
521	*continuep = 0;
522
523	/* If we ran any ex command, we can't trust the cursor position. */
524	F_SET(vip, VIP_CUR_INVALID);
525
526	/* Terminate any partially written message. */
527	if (vip->lcontinue != 0) {
528		vs_output(sp, vip->mtype, ".", 1);
529		vip->lcontinue = 0;
530
531		vip->mtype = M_NONE;
532	}
533
534	/*
535	 * If we switched out of the vi screen into ex, switch back while we
536	 * figure out what to do with the screen and potentially get another
537	 * command to execute.
538	 *
539	 * If we didn't switch into ex, we're not required to wait, and less
540	 * than 2 lines of output, we can continue without waiting for the
541	 * wait.
542	 *
543	 * Note, all other code paths require waiting, so we leave the report
544	 * of modified lines until later, so that we won't wait for no other
545	 * reason than a threshold number of lines were modified.  This means
546	 * we display cumulative line modification reports for groups of ex
547	 * commands.  That seems right to me (well, at least not wrong).
548	 */
549	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
550		if (sp->gp->scr_screen(sp, SC_VI))
551			return (1);
552	} else
553		if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
554			F_CLR(sp, SC_EX_WAIT_NO);
555			return (0);
556		}
557
558	/* Clear the required wait flag, it's no longer needed. */
559	F_CLR(sp, SC_EX_WAIT_YES);
560
561	/*
562	 * Wait, unless explicitly told not to wait or the user interrupted
563	 * the command.  If the user is leaving the screen, for any reason,
564	 * they can't continue with further ex commands.
565	 */
566	if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
567		wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
568		    SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
569		if (F_ISSET(sp, SC_SCR_EXWROTE))
570			vs_wait(sp, continuep, wtype);
571		else
572			vs_scroll(sp, continuep, wtype);
573		if (*continuep)
574			return (0);
575	}
576
577	/* If ex wrote on the screen, refresh the screen image. */
578	if (F_ISSET(sp, SC_SCR_EXWROTE))
579		F_SET(vip, VIP_N_EX_PAINT);
580
581	/*
582	 * If we're not the bottom of the split screen stack, the screen
583	 * image itself is wrong, so redraw everything.
584	 */
585	if (TAILQ_NEXT(sp, q) != NULL)
586		F_SET(sp, SC_SCR_REDRAW);
587
588	/* If ex changed the underlying file, the map itself is wrong. */
589	if (F_ISSET(vip, VIP_N_EX_REDRAW))
590		F_SET(sp, SC_SCR_REFORMAT);
591
592	/* Ex may have switched out of the alternate screen, return. */
593	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
594
595	/*
596	 * Whew.  We're finally back home, after what feels like years.
597	 * Kiss the ground.
598	 */
599	F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
600
601	/*
602	 * We may need to repaint some of the screen, e.g.:
603	 *
604	 *	:set
605	 *	:!ls
606	 *
607	 * gives us a combination of some lines that are "wrong", and a need
608	 * for a full refresh.
609	 */
610	if (vip->totalcount > 1) {
611		/* Set up the redraw of the overwritten lines. */
612		ev.e_event = E_REPAINT;
613		ev.e_flno = vip->totalcount >=
614		    sp->rows ? 1 : sp->rows - vip->totalcount;
615		ev.e_tlno = sp->rows;
616
617		/* Reset the count of overwriting lines. */
618		vip->linecount = vip->lcontinue = vip->totalcount = 0;
619
620		/* Redraw. */
621		(void)vs_repaint(sp, &ev);
622	} else
623		/* Reset the count of overwriting lines. */
624		vip->linecount = vip->lcontinue = vip->totalcount = 0;
625
626	return (0);
627}
628
629/*
630 * vs_resolve --
631 *	Deal with message output.
632 *
633 * PUBLIC: int vs_resolve __P((SCR *, SCR *, int));
634 */
635int
636vs_resolve(SCR *sp, SCR *csp, int forcewait)
637{
638	EVENT ev;
639	GS *gp;
640	MSGS *mp;
641	VI_PRIVATE *vip;
642	size_t oldy, oldx;
643	int redraw;
644
645	/*
646	 * Vs_resolve is called from the main vi loop and the refresh function
647	 * to periodically ensure that the user has seen any messages that have
648	 * been displayed and that any status lines are correct.  The sp screen
649	 * is the screen we're checking, usually the current screen.  When it's
650	 * not, csp is the current screen, used for final cursor positioning.
651	 */
652	gp = sp->gp;
653	vip = VIP(sp);
654	if (csp == NULL)
655		csp = sp;
656
657	/* Save the cursor position. */
658	(void)gp->scr_cursor(csp, &oldy, &oldx);
659
660	/* Ring the bell if it's scheduled. */
661	if (F_ISSET(gp, G_BELLSCHED)) {
662		F_CLR(gp, G_BELLSCHED);
663		(void)gp->scr_bell(sp);
664	}
665
666	/* Display new file status line. */
667	if (F_ISSET(sp, SC_STATUS)) {
668		F_CLR(sp, SC_STATUS);
669		msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
670	}
671
672	/* Report on line modifications. */
673	mod_rpt(sp);
674
675	/*
676	 * Flush any saved messages.  If the screen isn't ready, refresh
677	 * it.  (A side-effect of screen refresh is that we can display
678	 * messages.)  Once this is done, don't trust the cursor.  That
679	 * extra refresh screwed the pooch.
680	 */
681	if (!SLIST_EMPTY(gp->msgq)) {
682		if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
683			return (1);
684		while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
685			gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
686			SLIST_REMOVE_HEAD(gp->msgq, q);
687			free(mp->buf);
688			free(mp);
689		}
690		F_SET(vip, VIP_CUR_INVALID);
691	}
692
693	switch (vip->totalcount) {
694	case 0:
695		redraw = 0;
696		break;
697	case 1:
698		/*
699		 * If we're switching screens, we have to wait for messages,
700		 * regardless.  If we don't wait, skip updating the modeline.
701		 */
702		if (forcewait)
703			vs_scroll(sp, NULL, SCROLL_W);
704		else
705			F_SET(vip, VIP_S_MODELINE);
706
707		redraw = 0;
708		break;
709	default:
710		/*
711		 * If >1 message line in use, prompt the user to continue and
712		 * repaint overwritten lines.
713		 */
714		vs_scroll(sp, NULL, SCROLL_W);
715
716		ev.e_event = E_REPAINT;
717		ev.e_flno = vip->totalcount >=
718		    sp->rows ? 1 : sp->rows - vip->totalcount;
719		ev.e_tlno = sp->rows;
720
721		redraw = 1;
722		break;
723	}
724
725	/* Reset the count of overwriting lines. */
726	vip->linecount = vip->lcontinue = vip->totalcount = 0;
727
728	/* Redraw. */
729	if (redraw)
730		(void)vs_repaint(sp, &ev);
731
732	/* Restore the cursor position. */
733	(void)gp->scr_move(csp, oldy, oldx);
734
735	return (0);
736}
737
738/*
739 * vs_scroll --
740 *	Scroll the screen for output.
741 */
742static void
743vs_scroll(SCR *sp, int *continuep, sw_t wtype)
744{
745	GS *gp;
746	VI_PRIVATE *vip;
747
748	gp = sp->gp;
749	vip = VIP(sp);
750	if (!IS_ONELINE(sp)) {
751		/*
752		 * Scroll the screen.  Instead of scrolling the entire screen,
753		 * delete the line above the first line output so preserve the
754		 * maximum amount of the screen.
755		 */
756		(void)gp->scr_move(sp, vip->totalcount <
757		    sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
758		(void)gp->scr_deleteln(sp);
759
760		/* If there are screens below us, push them back into place. */
761		if (TAILQ_NEXT(sp, q) != NULL) {
762			(void)gp->scr_move(sp, LASTLINE(sp), 0);
763			(void)gp->scr_insertln(sp);
764		}
765	}
766	if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
767		return;
768	vs_wait(sp, continuep, wtype);
769}
770
771/*
772 * vs_wait --
773 *	Prompt the user to continue.
774 */
775static void
776vs_wait(SCR *sp, int *continuep, sw_t wtype)
777{
778	EVENT ev;
779	VI_PRIVATE *vip;
780	const char *p;
781	GS *gp;
782	size_t len;
783
784	gp = sp->gp;
785	vip = VIP(sp);
786
787	(void)gp->scr_move(sp, LASTLINE(sp), 0);
788	if (IS_ONELINE(sp))
789		p = msg_cmsg(sp, CMSG_CONT_S, &len);
790	else
791		switch (wtype) {
792		case SCROLL_W_QUIT:
793			p = msg_cmsg(sp, CMSG_CONT_Q, &len);
794			break;
795		case SCROLL_W_EX:
796			p = msg_cmsg(sp, CMSG_CONT_EX, &len);
797			break;
798		case SCROLL_W:
799			p = msg_cmsg(sp, CMSG_CONT, &len);
800			break;
801		default:
802			abort();
803			/* NOTREACHED */
804		}
805	(void)gp->scr_addstr(sp, p, len);
806
807	++vip->totalcount;
808	vip->linecount = 0;
809
810	(void)gp->scr_clrtoeol(sp);
811	(void)gp->scr_refresh(sp, 0);
812
813	/* Get a single character from the terminal. */
814	if (continuep != NULL)
815		*continuep = 0;
816	for (;;) {
817		if (v_event_get(sp, &ev, 0, 0))
818			return;
819		if (ev.e_event == E_CHARACTER)
820			break;
821		if (ev.e_event == E_INTERRUPT) {
822			ev.e_c = CH_QUIT;
823			F_SET(gp, G_INTERRUPTED);
824			break;
825		}
826		(void)gp->scr_bell(sp);
827	}
828	switch (wtype) {
829	case SCROLL_W_QUIT:
830		if (ev.e_c == CH_QUIT)
831			F_SET(gp, G_INTERRUPTED);
832		break;
833	case SCROLL_W_EX:
834		if (ev.e_c == ':' && continuep != NULL)
835			*continuep = 1;
836		break;
837	case SCROLL_W:
838		break;
839	}
840}
841
842/*
843 * vs_divider --
844 *	Draw a dividing line between the screen and the output.
845 */
846static void
847vs_divider(SCR *sp)
848{
849	GS *gp;
850	size_t len;
851
852#define	DIVIDESTR	"+=+=+=+=+=+=+=+"
853	len =
854	    sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
855	gp = sp->gp;
856	(void)gp->scr_attr(sp, SA_INVERSE, 1);
857	(void)gp->scr_addstr(sp, DIVIDESTR, len);
858	(void)gp->scr_attr(sp, SA_INVERSE, 0);
859}
860
861/*
862 * vs_msgsave --
863 *	Save a message for later display.
864 */
865static void
866vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len)
867{
868	GS *gp;
869	MSGS *mp_c, *mp_n;
870
871	/*
872	 * We have to handle messages before we have any place to put them.
873	 * If there's no screen support yet, allocate a msg structure, copy
874	 * in the message, and queue it on the global structure.  If we can't
875	 * allocate memory here, we're genuinely screwed, dump the message
876	 * to stderr in the (probably) vain hope that someone will see it.
877	 */
878	CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
879	MALLOC_GOTO(sp, mp_n->buf, char *, len);
880
881	memmove(mp_n->buf, p, len);
882	mp_n->len = len;
883	mp_n->mtype = mt;
884
885	gp = sp->gp;
886	if (SLIST_EMPTY(gp->msgq)) {
887		SLIST_INSERT_HEAD(gp->msgq, mp_n, q);
888	} else {
889		SLIST_FOREACH(mp_c, gp->msgq, q)
890			if (SLIST_NEXT(mp_c, q) == NULL)
891				break;
892		SLIST_INSERT_AFTER(mp_c, mp_n, q);
893	}
894	return;
895
896alloc_err:
897	if (mp_n != NULL)
898		free(mp_n);
899	(void)fprintf(stderr, "%.*s\n", (int)len, p);
900}
901