119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: vs_msg.c,v 10.88 2013/03/19 09:59:03 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <stdio.h>
2319304Speter#include <stdlib.h>
2419304Speter#include <string.h>
2519304Speter#include <unistd.h>
2619304Speter
2719304Speter#include "../common/common.h"
2819304Speter#include "vi.h"
2919304Speter
3019304Spetertypedef enum {
3119304Speter	SCROLL_W,			/* User wait. */
3219304Speter	SCROLL_W_EX,			/* User wait, or enter : to continue. */
3319304Speter	SCROLL_W_QUIT			/* User wait, or enter q to quit. */
3419304Speter					/*
3519304Speter					 * SCROLL_W_QUIT has another semantic
3619304Speter					 * -- only wait if the screen is full
3719304Speter					 */
3819304Speter} sw_t;
3919304Speter
4019304Speterstatic void	vs_divider __P((SCR *));
4119304Speterstatic void	vs_msgsave __P((SCR *, mtype_t, char *, size_t));
4219304Speterstatic void	vs_output __P((SCR *, mtype_t, const char *, int));
4319304Speterstatic void	vs_scroll __P((SCR *, int *, sw_t));
4419304Speterstatic void	vs_wait __P((SCR *, int *, sw_t));
4519304Speter
4619304Speter/*
4719304Speter * vs_busy --
4819304Speter *	Display, update or clear a busy message.
4919304Speter *
5019304Speter * This routine is the default editor interface for vi busy messages.  It
5119304Speter * implements a standard strategy of stealing lines from the bottom of the
5219304Speter * vi text screen.  Screens using an alternate method of displaying busy
5319304Speter * messages, e.g. X11 clock icons, should set their scr_busy function to the
5419304Speter * correct function before calling the main editor routine.
5519304Speter *
5619304Speter * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t));
5719304Speter */
5819304Spetervoid
59254225Spetervs_busy(SCR *sp, const char *msg, busy_t btype)
6019304Speter{
6119304Speter	GS *gp;
6219304Speter	VI_PRIVATE *vip;
6319304Speter	static const char flagc[] = "|/-\\";
64254225Speter	struct timespec ts, ts_diff;
65254225Speter	const struct timespec ts_min = { 0, 125000000 };
6619304Speter	size_t len, notused;
6719304Speter	const char *p;
6819304Speter
6919304Speter	/* Ex doesn't display busy messages. */
7019304Speter	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
7119304Speter		return;
7219304Speter
7319304Speter	gp = sp->gp;
7419304Speter	vip = VIP(sp);
7519304Speter
7619304Speter	/*
7719304Speter	 * Most of this routine is to deal with the screen sharing real estate
7819304Speter	 * between the normal edit messages and the busy messages.  Logically,
7919304Speter	 * all that's needed is something that puts up a message, periodically
8019304Speter	 * updates it, and then goes away.
8119304Speter	 */
8219304Speter	switch (btype) {
8319304Speter	case BUSY_ON:
8419304Speter		++vip->busy_ref;
8519304Speter		if (vip->totalcount != 0 || vip->busy_ref != 1)
8619304Speter			break;
8719304Speter
8819304Speter		/* Initialize state for updates. */
8919304Speter		vip->busy_ch = 0;
90254225Speter		timepoint_steady(&vip->busy_ts);
9119304Speter
9219304Speter		/* Save the current cursor. */
9319304Speter		(void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
9419304Speter
9519304Speter		/* Display the busy message. */
9619304Speter		p = msg_cat(sp, msg, &len);
9719304Speter		(void)gp->scr_move(sp, LASTLINE(sp), 0);
9819304Speter		(void)gp->scr_addstr(sp, p, len);
9919304Speter		(void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
10019304Speter		(void)gp->scr_clrtoeol(sp);
10119304Speter		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
10219304Speter		break;
10319304Speter	case BUSY_OFF:
10419304Speter		if (vip->busy_ref == 0)
10519304Speter			break;
10619304Speter		--vip->busy_ref;
10719304Speter
10819304Speter		/*
10919304Speter		 * If the line isn't in use for another purpose, clear it.
11019304Speter		 * Always return to the original position.
11119304Speter		 */
11219304Speter		if (vip->totalcount == 0 && vip->busy_ref == 0) {
11319304Speter			(void)gp->scr_move(sp, LASTLINE(sp), 0);
11419304Speter			(void)gp->scr_clrtoeol(sp);
11519304Speter		}
11619304Speter		(void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
11719304Speter		break;
11819304Speter	case BUSY_UPDATE:
11919304Speter		if (vip->totalcount != 0 || vip->busy_ref == 0)
12019304Speter			break;
12119304Speter
12219304Speter		/* Update no more than every 1/8 of a second. */
123254225Speter		timepoint_steady(&ts);
124254225Speter		ts_diff = ts;
125254225Speter		timespecsub(&ts_diff, &vip->busy_ts);
126254225Speter		if (timespeccmp(&ts_diff, &ts_min, <))
12719304Speter			return;
128254225Speter		vip->busy_ts = ts;
12919304Speter
13019304Speter		/* Display the update. */
13119304Speter		if (vip->busy_ch == sizeof(flagc) - 1)
13219304Speter			vip->busy_ch = 0;
13319304Speter		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
13419304Speter		(void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
13519304Speter		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
13619304Speter		break;
13719304Speter	}
13819304Speter	(void)gp->scr_refresh(sp, 0);
13919304Speter}
14019304Speter
14119304Speter/*
14219304Speter * vs_home --
14319304Speter *	Home the cursor to the bottom row, left-most column.
14419304Speter *
14519304Speter * PUBLIC: void vs_home __P((SCR *));
14619304Speter */
14719304Spetervoid
148254225Spetervs_home(SCR *sp)
14919304Speter{
15019304Speter	(void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
15119304Speter	(void)sp->gp->scr_refresh(sp, 0);
15219304Speter}
15319304Speter
15419304Speter/*
15519304Speter * vs_update --
15619304Speter *	Update a command.
15719304Speter *
158254225Speter * PUBLIC: void vs_update __P((SCR *, const char *, const CHAR_T *));
15919304Speter */
16019304Spetervoid
161254225Spetervs_update(SCR *sp, const char *m1, const CHAR_T *m2)
16219304Speter{
16319304Speter	GS *gp;
16419304Speter	size_t len, mlen, oldx, oldy;
165254225Speter	CONST char *np;
166254225Speter	size_t nlen;
16719304Speter
16819304Speter	gp = sp->gp;
16919304Speter
17019304Speter	/*
17119304Speter	 * This routine displays a message on the bottom line of the screen,
17219304Speter	 * without updating any of the command structures that would keep it
17319304Speter	 * there for any period of time, i.e. it is overwritten immediately.
17419304Speter	 *
17519304Speter	 * It's used by the ex read and ! commands when the user's command is
17619304Speter	 * expanded, and by the ex substitution confirmation prompt.
17719304Speter	 */
17819304Speter	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
179254225Speter		if (m2 != NULL)
180254225Speter			INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen);
18119304Speter		(void)ex_printf(sp,
182254225Speter		    "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np);
18319304Speter		(void)ex_fflush(sp);
18419304Speter	}
18519304Speter
18619304Speter	/*
18719304Speter	 * Save the cursor position, the substitute-with-confirmation code
18819304Speter	 * will have already set it correctly.
18919304Speter	 */
19019304Speter	(void)gp->scr_cursor(sp, &oldy, &oldx);
19119304Speter
19219304Speter	/* Clear the bottom line. */
19319304Speter	(void)gp->scr_move(sp, LASTLINE(sp), 0);
19419304Speter	(void)gp->scr_clrtoeol(sp);
19519304Speter
19619304Speter	/*
19719304Speter	 * XXX
19819304Speter	 * Don't let long file names screw up the screen.
19919304Speter	 */
20019304Speter	if (m1 != NULL) {
20119304Speter		mlen = len = strlen(m1);
20219304Speter		if (len > sp->cols - 2)
20319304Speter			mlen = len = sp->cols - 2;
20419304Speter		(void)gp->scr_addstr(sp, m1, mlen);
20519304Speter	} else
20619304Speter		len = 0;
20719304Speter	if (m2 != NULL) {
208254225Speter		mlen = STRLEN(m2);
20919304Speter		if (len + mlen > sp->cols - 2)
21019304Speter			mlen = (sp->cols - 2) - len;
211254225Speter		(void)gp->scr_waddstr(sp, m2, mlen);
21219304Speter	}
21319304Speter
21419304Speter	(void)gp->scr_move(sp, oldy, oldx);
21519304Speter	(void)gp->scr_refresh(sp, 0);
21619304Speter}
21719304Speter
21819304Speter/*
21919304Speter * vs_msg --
22019304Speter *	Display ex output or error messages for the screen.
22119304Speter *
22219304Speter * This routine is the default editor interface for all ex output, and all ex
22319304Speter * and vi error/informational messages.  It implements the standard strategy
22419304Speter * of stealing lines from the bottom of the vi text screen.  Screens using an
22519304Speter * alternate method of displaying messages, e.g. dialog boxes, should set their
22619304Speter * scr_msg function to the correct function before calling the editor.
22719304Speter *
22819304Speter * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t));
22919304Speter */
23019304Spetervoid
231254225Spetervs_msg(SCR *sp, mtype_t mtype, char *line, size_t len)
23219304Speter{
23319304Speter	GS *gp;
23419304Speter	VI_PRIVATE *vip;
23519304Speter	size_t maxcols, oldx, oldy, padding;
23619304Speter	const char *e, *s, *t;
23719304Speter
23819304Speter	gp = sp->gp;
23919304Speter	vip = VIP(sp);
24019304Speter
24119304Speter	/*
24219304Speter	 * Ring the bell if it's scheduled.
24319304Speter	 *
24419304Speter	 * XXX
24519304Speter	 * Shouldn't we save this, too?
24619304Speter	 */
24719304Speter	if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
24819304Speter		if (F_ISSET(sp, SC_SCR_VI)) {
24919304Speter			F_CLR(gp, G_BELLSCHED);
25019304Speter			(void)gp->scr_bell(sp);
25119304Speter		} else
25219304Speter			F_SET(gp, G_BELLSCHED);
25319304Speter
25419304Speter	/*
25519304Speter	 * If vi is using the error line for text input, there's no screen
25619304Speter	 * real-estate for the error message.  Nothing to do without some
25719304Speter	 * information as to how important the error message is.
25819304Speter	 */
25919304Speter	if (F_ISSET(sp, SC_TINPUT_INFO))
26019304Speter		return;
26119304Speter
26219304Speter	/*
26319304Speter	 * Ex or ex controlled screen output.
26419304Speter	 *
26519304Speter	 * If output happens during startup, e.g., a .exrc file, we may be
26619304Speter	 * in ex mode but haven't initialized the screen.  Initialize here,
26719304Speter	 * and in this case, stay in ex mode.
26819304Speter	 *
26919304Speter	 * If the SC_SCR_EXWROTE bit is set, then we're switching back and
27019304Speter	 * forth between ex and vi, but the screen is trashed and we have
27119304Speter	 * to respect that.  Switch to ex mode long enough to put out the
27219304Speter	 * message.
27319304Speter	 *
27419304Speter	 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
27519304Speter	 * the screen, so previous opinions are ignored.
27619304Speter	 */
27719304Speter	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
27819304Speter		if (!F_ISSET(sp, SC_SCR_EX))
27919304Speter			if (F_ISSET(sp, SC_SCR_EXWROTE)) {
28019304Speter				if (sp->gp->scr_screen(sp, SC_EX))
28119304Speter					return;
28219304Speter			} else
28319304Speter				if (ex_init(sp))
28419304Speter					return;
28519304Speter
28619304Speter		if (mtype == M_ERR)
28719304Speter			(void)gp->scr_attr(sp, SA_INVERSE, 1);
28819304Speter		(void)printf("%.*s", (int)len, line);
28919304Speter		if (mtype == M_ERR)
29019304Speter			(void)gp->scr_attr(sp, SA_INVERSE, 0);
29119304Speter		(void)fflush(stdout);
29219304Speter
29319304Speter		F_CLR(sp, SC_EX_WAIT_NO);
29419304Speter
29519304Speter		if (!F_ISSET(sp, SC_SCR_EX))
29619304Speter			(void)sp->gp->scr_screen(sp, SC_VI);
29719304Speter		return;
29819304Speter	}
29919304Speter
30019304Speter	/* If the vi screen isn't ready, save the message. */
30119304Speter	if (!F_ISSET(sp, SC_SCR_VI)) {
30219304Speter		(void)vs_msgsave(sp, mtype, line, len);
30319304Speter		return;
30419304Speter	}
30519304Speter
30619304Speter	/* Save the cursor position. */
30719304Speter	(void)gp->scr_cursor(sp, &oldy, &oldx);
30819304Speter
30919304Speter	/* If it's an ex output message, just write it out. */
31019304Speter	if (mtype == M_NONE) {
31119304Speter		vs_output(sp, mtype, line, len);
31219304Speter		goto ret;
31319304Speter	}
31419304Speter
31519304Speter	/*
31619304Speter	 * If it's a vi message, strip the trailing <newline> so we can
31719304Speter	 * try and paste messages together.
31819304Speter	 */
31919304Speter	if (line[len - 1] == '\n')
32019304Speter		--len;
32119304Speter
32219304Speter	/*
32319304Speter	 * If a message won't fit on a single line, try to split on a <blank>.
32419304Speter	 * If a subsequent message fits on the same line, write a separator
32519304Speter	 * and output it.  Otherwise, put out a newline.
32619304Speter	 *
32719304Speter	 * Need up to two padding characters normally; a semi-colon and a
32819304Speter	 * separating space.  If only a single line on the screen, add some
32919304Speter	 * more for the trailing continuation message.
33019304Speter	 *
33119304Speter	 * XXX
33219304Speter	 * Assume that periods and semi-colons take up a single column on the
33319304Speter	 * screen.
33419304Speter	 *
33519304Speter	 * XXX
33619304Speter	 * There are almost certainly pathological cases that will break this
33719304Speter	 * code.
33819304Speter	 */
33919304Speter	if (IS_ONELINE(sp))
34019304Speter		(void)msg_cmsg(sp, CMSG_CONT_S, &padding);
34119304Speter	else
34219304Speter		padding = 0;
34319304Speter	padding += 2;
34419304Speter
34519304Speter	maxcols = sp->cols - 1;
34619304Speter	if (vip->lcontinue != 0)
34719304Speter		if (len + vip->lcontinue + padding > maxcols)
34819304Speter			vs_output(sp, vip->mtype, ".\n", 2);
34919304Speter		else  {
35019304Speter			vs_output(sp, vip->mtype, ";", 1);
35119304Speter			vs_output(sp, M_NONE, " ", 1);
35219304Speter		}
35319304Speter	vip->mtype = mtype;
35419304Speter	for (s = line;; s = t) {
35519304Speter		for (; len > 0 && isblank(*s); --len, ++s);
35619304Speter		if (len == 0)
35719304Speter			break;
35819304Speter		if (len + vip->lcontinue > maxcols) {
35919304Speter			for (e = s + (maxcols - vip->lcontinue);
36019304Speter			    e > s && !isblank(*e); --e);
36119304Speter			if (e == s)
36219304Speter				 e = t = s + (maxcols - vip->lcontinue);
36319304Speter			else
36419304Speter				for (t = e; isblank(e[-1]); --e);
36519304Speter		} else
36619304Speter			e = t = s + len;
36719304Speter
36819304Speter		/*
36919304Speter		 * If the message ends in a period, discard it, we want to
37019304Speter		 * gang messages where possible.
37119304Speter		 */
37219304Speter		len -= t - s;
37319304Speter		if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
37419304Speter			--e;
37519304Speter		vs_output(sp, mtype, s, e - s);
37619304Speter
37719304Speter		if (len != 0)
37819304Speter			vs_output(sp, M_NONE, "\n", 1);
37919304Speter
38019304Speter		if (INTERRUPTED(sp))
38119304Speter			break;
38219304Speter	}
38319304Speter
38419304Speterret:	(void)gp->scr_move(sp, oldy, oldx);
38519304Speter	(void)gp->scr_refresh(sp, 0);
38619304Speter}
38719304Speter
38819304Speter/*
38919304Speter * vs_output --
39019304Speter *	Output the text to the screen.
39119304Speter */
39219304Speterstatic void
393254225Spetervs_output(SCR *sp, mtype_t mtype, const char *line, int llen)
39419304Speter{
39519304Speter	GS *gp;
39619304Speter	VI_PRIVATE *vip;
397254225Speter	size_t notused;
398254225Speter	int len, rlen, tlen;
39919304Speter	const char *p, *t;
40019304Speter	char *cbp, *ecbp, cbuf[128];
40119304Speter
40219304Speter	gp = sp->gp;
40319304Speter	vip = VIP(sp);
40419304Speter	for (p = line, rlen = llen; llen > 0;) {
40519304Speter		/* Get the next physical line. */
40619304Speter		if ((p = memchr(line, '\n', llen)) == NULL)
40719304Speter			len = llen;
40819304Speter		else
40919304Speter			len = p - line;
41019304Speter
41119304Speter		/*
41219304Speter		 * The max is sp->cols characters, and we may have already
41319304Speter		 * written part of the line.
41419304Speter		 */
41519304Speter		if (len + vip->lcontinue > sp->cols)
41619304Speter			len = sp->cols - vip->lcontinue;
41719304Speter
41819304Speter		/*
41919304Speter		 * If the first line output, do nothing.  If the second line
42019304Speter		 * output, draw the divider line.  If drew a full screen, we
42119304Speter		 * remove the divider line.  If it's a continuation line, move
42219304Speter		 * to the continuation point, else, move the screen up.
42319304Speter		 */
42419304Speter		if (vip->lcontinue == 0) {
42519304Speter			if (!IS_ONELINE(sp)) {
42619304Speter				if (vip->totalcount == 1) {
42719304Speter					(void)gp->scr_move(sp,
42819304Speter					    LASTLINE(sp) - 1, 0);
42919304Speter					(void)gp->scr_clrtoeol(sp);
43019304Speter					(void)vs_divider(sp);
43119304Speter					F_SET(vip, VIP_DIVIDER);
43219304Speter					++vip->totalcount;
43319304Speter					++vip->linecount;
43419304Speter				}
43519304Speter				if (vip->totalcount == sp->t_maxrows &&
43619304Speter				    F_ISSET(vip, VIP_DIVIDER)) {
43719304Speter					--vip->totalcount;
43819304Speter					--vip->linecount;
43919304Speter					F_CLR(vip, VIP_DIVIDER);
44019304Speter				}
44119304Speter			}
44219304Speter			if (vip->totalcount != 0)
44319304Speter				vs_scroll(sp, NULL, SCROLL_W_QUIT);
44419304Speter
44519304Speter			(void)gp->scr_move(sp, LASTLINE(sp), 0);
44619304Speter			++vip->totalcount;
44719304Speter			++vip->linecount;
44819304Speter
44919304Speter			if (INTERRUPTED(sp))
45019304Speter				break;
45119304Speter		} else
45219304Speter			(void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
45319304Speter
45419304Speter		/* Error messages are in inverse video. */
45519304Speter		if (mtype == M_ERR)
45619304Speter			(void)gp->scr_attr(sp, SA_INVERSE, 1);
45719304Speter
45819304Speter		/* Display the line, doing character translation. */
45919304Speter#define	FLUSH {								\
46019304Speter	*cbp = '\0';							\
46119304Speter	(void)gp->scr_addstr(sp, cbuf, cbp - cbuf);			\
46219304Speter	cbp = cbuf;							\
46319304Speter}
46419304Speter		ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
46519304Speter		for (t = line, tlen = len; tlen--; ++t) {
46619304Speter			/*
46719304Speter			 * Replace tabs with spaces, there are places in
46819304Speter			 * ex that do column calculations without looking
46919304Speter			 * at <tabs> -- and all routines that care about
47019304Speter			 * <tabs> do their own expansions.  This catches
47119304Speter			 * <tabs> in things like tag search strings.
47219304Speter			 */
473254225Speter			if (cbp + 1 >= ecbp)
47419304Speter				FLUSH;
475254225Speter			*cbp++ = *t == '\t' ? ' ' : *t;
47619304Speter		}
47719304Speter		if (cbp > cbuf)
47819304Speter			FLUSH;
47919304Speter		if (mtype == M_ERR)
48019304Speter			(void)gp->scr_attr(sp, SA_INVERSE, 0);
48119304Speter
48219304Speter		/* Clear the rest of the line. */
48319304Speter		(void)gp->scr_clrtoeol(sp);
48419304Speter
48519304Speter		/* If we loop, it's a new line. */
48619304Speter		vip->lcontinue = 0;
48719304Speter
48819304Speter		/* Reset for the next line. */
48919304Speter		line += len;
49019304Speter		llen -= len;
49119304Speter		if (p != NULL) {
49219304Speter			++line;
49319304Speter			--llen;
49419304Speter		}
49519304Speter	}
49619304Speter
49719304Speter	/* Set up next continuation line. */
49819304Speter	if (p == NULL)
49919304Speter		gp->scr_cursor(sp, &notused, &vip->lcontinue);
50019304Speter}
50119304Speter
50219304Speter/*
50319304Speter * vs_ex_resolve --
50419304Speter *	Deal with ex message output.
50519304Speter *
50619304Speter * This routine is called when exiting a colon command to resolve any ex
50719304Speter * output that may have occurred.
50819304Speter *
50919304Speter * PUBLIC: int vs_ex_resolve __P((SCR *, int *));
51019304Speter */
51119304Speterint
512254225Spetervs_ex_resolve(SCR *sp, int *continuep)
51319304Speter{
51419304Speter	EVENT ev;
51519304Speter	GS *gp;
51619304Speter	VI_PRIVATE *vip;
51719304Speter	sw_t wtype;
51819304Speter
51919304Speter	gp = sp->gp;
52019304Speter	vip = VIP(sp);
52119304Speter	*continuep = 0;
52219304Speter
52319304Speter	/* If we ran any ex command, we can't trust the cursor position. */
52419304Speter	F_SET(vip, VIP_CUR_INVALID);
52519304Speter
52619304Speter	/* Terminate any partially written message. */
52719304Speter	if (vip->lcontinue != 0) {
52819304Speter		vs_output(sp, vip->mtype, ".", 1);
52919304Speter		vip->lcontinue = 0;
53019304Speter
53119304Speter		vip->mtype = M_NONE;
53219304Speter	}
53319304Speter
53419304Speter	/*
53519304Speter	 * If we switched out of the vi screen into ex, switch back while we
53619304Speter	 * figure out what to do with the screen and potentially get another
53719304Speter	 * command to execute.
53819304Speter	 *
53919304Speter	 * If we didn't switch into ex, we're not required to wait, and less
54019304Speter	 * than 2 lines of output, we can continue without waiting for the
54119304Speter	 * wait.
54219304Speter	 *
54319304Speter	 * Note, all other code paths require waiting, so we leave the report
54419304Speter	 * of modified lines until later, so that we won't wait for no other
54519304Speter	 * reason than a threshold number of lines were modified.  This means
54619304Speter	 * we display cumulative line modification reports for groups of ex
54719304Speter	 * commands.  That seems right to me (well, at least not wrong).
54819304Speter	 */
54919304Speter	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
55019304Speter		if (sp->gp->scr_screen(sp, SC_VI))
55119304Speter			return (1);
55219304Speter	} else
55319304Speter		if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
55419304Speter			F_CLR(sp, SC_EX_WAIT_NO);
55519304Speter			return (0);
55619304Speter		}
55719304Speter
55819304Speter	/* Clear the required wait flag, it's no longer needed. */
55919304Speter	F_CLR(sp, SC_EX_WAIT_YES);
56019304Speter
56119304Speter	/*
56219304Speter	 * Wait, unless explicitly told not to wait or the user interrupted
56319304Speter	 * the command.  If the user is leaving the screen, for any reason,
56419304Speter	 * they can't continue with further ex commands.
56519304Speter	 */
56619304Speter	if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
56719304Speter		wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
56819304Speter		    SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
56919304Speter		if (F_ISSET(sp, SC_SCR_EXWROTE))
57019304Speter			vs_wait(sp, continuep, wtype);
57119304Speter		else
57219304Speter			vs_scroll(sp, continuep, wtype);
57319304Speter		if (*continuep)
57419304Speter			return (0);
57519304Speter	}
57619304Speter
57719304Speter	/* If ex wrote on the screen, refresh the screen image. */
57819304Speter	if (F_ISSET(sp, SC_SCR_EXWROTE))
57919304Speter		F_SET(vip, VIP_N_EX_PAINT);
58019304Speter
58119304Speter	/*
58219304Speter	 * If we're not the bottom of the split screen stack, the screen
58319304Speter	 * image itself is wrong, so redraw everything.
58419304Speter	 */
585254225Speter	if (TAILQ_NEXT(sp, q) != NULL)
58619304Speter		F_SET(sp, SC_SCR_REDRAW);
58719304Speter
58819304Speter	/* If ex changed the underlying file, the map itself is wrong. */
58919304Speter	if (F_ISSET(vip, VIP_N_EX_REDRAW))
59019304Speter		F_SET(sp, SC_SCR_REFORMAT);
59119304Speter
59219304Speter	/* Ex may have switched out of the alternate screen, return. */
59319304Speter	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
59419304Speter
59519304Speter	/*
59619304Speter	 * Whew.  We're finally back home, after what feels like years.
59719304Speter	 * Kiss the ground.
59819304Speter	 */
59919304Speter	F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
60019304Speter
60119304Speter	/*
60219304Speter	 * We may need to repaint some of the screen, e.g.:
60319304Speter	 *
60419304Speter	 *	:set
60519304Speter	 *	:!ls
60619304Speter	 *
60719304Speter	 * gives us a combination of some lines that are "wrong", and a need
60819304Speter	 * for a full refresh.
60919304Speter	 */
61019304Speter	if (vip->totalcount > 1) {
61119304Speter		/* Set up the redraw of the overwritten lines. */
61219304Speter		ev.e_event = E_REPAINT;
61319304Speter		ev.e_flno = vip->totalcount >=
61419304Speter		    sp->rows ? 1 : sp->rows - vip->totalcount;
61519304Speter		ev.e_tlno = sp->rows;
61619304Speter
61719304Speter		/* Reset the count of overwriting lines. */
61819304Speter		vip->linecount = vip->lcontinue = vip->totalcount = 0;
61919304Speter
62019304Speter		/* Redraw. */
62119304Speter		(void)vs_repaint(sp, &ev);
62219304Speter	} else
62319304Speter		/* Reset the count of overwriting lines. */
62419304Speter		vip->linecount = vip->lcontinue = vip->totalcount = 0;
62519304Speter
62619304Speter	return (0);
62719304Speter}
62819304Speter
62919304Speter/*
63019304Speter * vs_resolve --
63119304Speter *	Deal with message output.
63219304Speter *
63319304Speter * PUBLIC: int vs_resolve __P((SCR *, SCR *, int));
63419304Speter */
63519304Speterint
636254225Spetervs_resolve(SCR *sp, SCR *csp, int forcewait)
63719304Speter{
63819304Speter	EVENT ev;
63919304Speter	GS *gp;
64019304Speter	MSGS *mp;
64119304Speter	VI_PRIVATE *vip;
64219304Speter	size_t oldy, oldx;
64319304Speter	int redraw;
64419304Speter
64519304Speter	/*
64619304Speter	 * Vs_resolve is called from the main vi loop and the refresh function
64719304Speter	 * to periodically ensure that the user has seen any messages that have
64819304Speter	 * been displayed and that any status lines are correct.  The sp screen
64919304Speter	 * is the screen we're checking, usually the current screen.  When it's
65019304Speter	 * not, csp is the current screen, used for final cursor positioning.
65119304Speter	 */
65219304Speter	gp = sp->gp;
65319304Speter	vip = VIP(sp);
65419304Speter	if (csp == NULL)
65519304Speter		csp = sp;
65619304Speter
65719304Speter	/* Save the cursor position. */
65819304Speter	(void)gp->scr_cursor(csp, &oldy, &oldx);
65919304Speter
66019304Speter	/* Ring the bell if it's scheduled. */
66119304Speter	if (F_ISSET(gp, G_BELLSCHED)) {
66219304Speter		F_CLR(gp, G_BELLSCHED);
66319304Speter		(void)gp->scr_bell(sp);
66419304Speter	}
66519304Speter
66619304Speter	/* Display new file status line. */
66719304Speter	if (F_ISSET(sp, SC_STATUS)) {
66819304Speter		F_CLR(sp, SC_STATUS);
66919304Speter		msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
67019304Speter	}
67119304Speter
67219304Speter	/* Report on line modifications. */
67319304Speter	mod_rpt(sp);
67419304Speter
67519304Speter	/*
67619304Speter	 * Flush any saved messages.  If the screen isn't ready, refresh
67719304Speter	 * it.  (A side-effect of screen refresh is that we can display
67819304Speter	 * messages.)  Once this is done, don't trust the cursor.  That
67919304Speter	 * extra refresh screwed the pooch.
68019304Speter	 */
681254225Speter	if (!SLIST_EMPTY(gp->msgq)) {
68219304Speter		if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
68319304Speter			return (1);
684254225Speter		while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
68519304Speter			gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
686254225Speter			SLIST_REMOVE_HEAD(gp->msgq, q);
68719304Speter			free(mp->buf);
68819304Speter			free(mp);
68919304Speter		}
69019304Speter		F_SET(vip, VIP_CUR_INVALID);
69119304Speter	}
69219304Speter
69319304Speter	switch (vip->totalcount) {
69419304Speter	case 0:
69519304Speter		redraw = 0;
69619304Speter		break;
69719304Speter	case 1:
69819304Speter		/*
69919304Speter		 * If we're switching screens, we have to wait for messages,
70019304Speter		 * regardless.  If we don't wait, skip updating the modeline.
70119304Speter		 */
70219304Speter		if (forcewait)
70319304Speter			vs_scroll(sp, NULL, SCROLL_W);
70419304Speter		else
70519304Speter			F_SET(vip, VIP_S_MODELINE);
70619304Speter
70719304Speter		redraw = 0;
70819304Speter		break;
70919304Speter	default:
71019304Speter		/*
71119304Speter		 * If >1 message line in use, prompt the user to continue and
71219304Speter		 * repaint overwritten lines.
71319304Speter		 */
71419304Speter		vs_scroll(sp, NULL, SCROLL_W);
71519304Speter
71619304Speter		ev.e_event = E_REPAINT;
71719304Speter		ev.e_flno = vip->totalcount >=
71819304Speter		    sp->rows ? 1 : sp->rows - vip->totalcount;
71919304Speter		ev.e_tlno = sp->rows;
72019304Speter
72119304Speter		redraw = 1;
72219304Speter		break;
72319304Speter	}
72419304Speter
72519304Speter	/* Reset the count of overwriting lines. */
72619304Speter	vip->linecount = vip->lcontinue = vip->totalcount = 0;
72719304Speter
72819304Speter	/* Redraw. */
72919304Speter	if (redraw)
73019304Speter		(void)vs_repaint(sp, &ev);
73119304Speter
73219304Speter	/* Restore the cursor position. */
73319304Speter	(void)gp->scr_move(csp, oldy, oldx);
73419304Speter
73519304Speter	return (0);
73619304Speter}
73719304Speter
73819304Speter/*
73919304Speter * vs_scroll --
74019304Speter *	Scroll the screen for output.
74119304Speter */
74219304Speterstatic void
743254225Spetervs_scroll(SCR *sp, int *continuep, sw_t wtype)
74419304Speter{
74519304Speter	GS *gp;
74619304Speter	VI_PRIVATE *vip;
74719304Speter
74819304Speter	gp = sp->gp;
74919304Speter	vip = VIP(sp);
75019304Speter	if (!IS_ONELINE(sp)) {
75119304Speter		/*
75219304Speter		 * Scroll the screen.  Instead of scrolling the entire screen,
75319304Speter		 * delete the line above the first line output so preserve the
75419304Speter		 * maximum amount of the screen.
75519304Speter		 */
75619304Speter		(void)gp->scr_move(sp, vip->totalcount <
75719304Speter		    sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
75819304Speter		(void)gp->scr_deleteln(sp);
75919304Speter
76019304Speter		/* If there are screens below us, push them back into place. */
761254225Speter		if (TAILQ_NEXT(sp, q) != NULL) {
76219304Speter			(void)gp->scr_move(sp, LASTLINE(sp), 0);
76319304Speter			(void)gp->scr_insertln(sp);
76419304Speter		}
76519304Speter	}
76619304Speter	if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
76719304Speter		return;
76819304Speter	vs_wait(sp, continuep, wtype);
76919304Speter}
77019304Speter
77119304Speter/*
77219304Speter * vs_wait --
77319304Speter *	Prompt the user to continue.
77419304Speter */
77519304Speterstatic void
776254225Spetervs_wait(SCR *sp, int *continuep, sw_t wtype)
77719304Speter{
77819304Speter	EVENT ev;
77919304Speter	VI_PRIVATE *vip;
78019304Speter	const char *p;
78119304Speter	GS *gp;
78219304Speter	size_t len;
78319304Speter
78419304Speter	gp = sp->gp;
78519304Speter	vip = VIP(sp);
78619304Speter
78719304Speter	(void)gp->scr_move(sp, LASTLINE(sp), 0);
78819304Speter	if (IS_ONELINE(sp))
78919304Speter		p = msg_cmsg(sp, CMSG_CONT_S, &len);
79019304Speter	else
79119304Speter		switch (wtype) {
79219304Speter		case SCROLL_W_QUIT:
79319304Speter			p = msg_cmsg(sp, CMSG_CONT_Q, &len);
79419304Speter			break;
79519304Speter		case SCROLL_W_EX:
79619304Speter			p = msg_cmsg(sp, CMSG_CONT_EX, &len);
79719304Speter			break;
79819304Speter		case SCROLL_W:
79919304Speter			p = msg_cmsg(sp, CMSG_CONT, &len);
80019304Speter			break;
80119304Speter		default:
80219304Speter			abort();
80319304Speter			/* NOTREACHED */
80419304Speter		}
80519304Speter	(void)gp->scr_addstr(sp, p, len);
80619304Speter
80719304Speter	++vip->totalcount;
80819304Speter	vip->linecount = 0;
80919304Speter
81019304Speter	(void)gp->scr_clrtoeol(sp);
81119304Speter	(void)gp->scr_refresh(sp, 0);
81219304Speter
81319304Speter	/* Get a single character from the terminal. */
81419304Speter	if (continuep != NULL)
81519304Speter		*continuep = 0;
81619304Speter	for (;;) {
81719304Speter		if (v_event_get(sp, &ev, 0, 0))
81819304Speter			return;
81919304Speter		if (ev.e_event == E_CHARACTER)
82019304Speter			break;
82119304Speter		if (ev.e_event == E_INTERRUPT) {
82219304Speter			ev.e_c = CH_QUIT;
82319304Speter			F_SET(gp, G_INTERRUPTED);
82419304Speter			break;
82519304Speter		}
82619304Speter		(void)gp->scr_bell(sp);
82719304Speter	}
82819304Speter	switch (wtype) {
82919304Speter	case SCROLL_W_QUIT:
83019304Speter		if (ev.e_c == CH_QUIT)
83119304Speter			F_SET(gp, G_INTERRUPTED);
83219304Speter		break;
83319304Speter	case SCROLL_W_EX:
83419304Speter		if (ev.e_c == ':' && continuep != NULL)
83519304Speter			*continuep = 1;
83619304Speter		break;
83719304Speter	case SCROLL_W:
83819304Speter		break;
83919304Speter	}
84019304Speter}
84119304Speter
84219304Speter/*
84319304Speter * vs_divider --
84419304Speter *	Draw a dividing line between the screen and the output.
84519304Speter */
84619304Speterstatic void
847254225Spetervs_divider(SCR *sp)
84819304Speter{
84919304Speter	GS *gp;
85019304Speter	size_t len;
85119304Speter
85219304Speter#define	DIVIDESTR	"+=+=+=+=+=+=+=+"
85319304Speter	len =
85419304Speter	    sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
85519304Speter	gp = sp->gp;
85619304Speter	(void)gp->scr_attr(sp, SA_INVERSE, 1);
85719304Speter	(void)gp->scr_addstr(sp, DIVIDESTR, len);
85819304Speter	(void)gp->scr_attr(sp, SA_INVERSE, 0);
85919304Speter}
86019304Speter
86119304Speter/*
86219304Speter * vs_msgsave --
86319304Speter *	Save a message for later display.
86419304Speter */
86519304Speterstatic void
866254225Spetervs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len)
86719304Speter{
86819304Speter	GS *gp;
86919304Speter	MSGS *mp_c, *mp_n;
87019304Speter
87119304Speter	/*
87219304Speter	 * We have to handle messages before we have any place to put them.
87319304Speter	 * If there's no screen support yet, allocate a msg structure, copy
87419304Speter	 * in the message, and queue it on the global structure.  If we can't
87519304Speter	 * allocate memory here, we're genuinely screwed, dump the message
87619304Speter	 * to stderr in the (probably) vain hope that someone will see it.
87719304Speter	 */
87819304Speter	CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
87919304Speter	MALLOC_GOTO(sp, mp_n->buf, char *, len);
88019304Speter
88119304Speter	memmove(mp_n->buf, p, len);
88219304Speter	mp_n->len = len;
88319304Speter	mp_n->mtype = mt;
88419304Speter
88519304Speter	gp = sp->gp;
886254225Speter	if (SLIST_EMPTY(gp->msgq)) {
887254225Speter		SLIST_INSERT_HEAD(gp->msgq, mp_n, q);
88819304Speter	} else {
889254225Speter		SLIST_FOREACH(mp_c, gp->msgq, q)
890254225Speter			if (SLIST_NEXT(mp_c, q) == NULL)
891254225Speter				break;
892254225Speter		SLIST_INSERT_AFTER(mp_c, mp_n, q);
89319304Speter	}
89419304Speter	return;
89519304Speter
89619304Speteralloc_err:
89719304Speter	if (mp_n != NULL)
89819304Speter		free(mp_n);
89919304Speter	(void)fprintf(stderr, "%.*s\n", (int)len, p);
90019304Speter}
901