4.4BSD/usr/src/contrib/nvi/nvi/svi/svi_ex.c

Compare this file to the similar file:
Show the results in this format:

/*-
 * Copyright (c) 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static char sccsid[] = "@(#)svi_ex.c	8.1 (Berkeley) 6/9/93";
#endif /* not lint */

#include <sys/types.h>

#include <ctype.h>
#include <curses.h>
#include <stdlib.h>
#include <string.h>

#include "vi.h"
#include "vcmd.h"
#include "excmd.h"
#include "svi_screen.h"

static int	svi_ex_scroll __P((SCR *, int, int, int *));
static int	svi_ex_done __P((SCR *, EXF *, MARK *));

/*
 * svi_ex_cmd --
 *	Execute an ex command.
 */
int
svi_ex_cmd(sp, ep, exp, rp)
	SCR *sp;
	EXF *ep;
	EXCMDARG *exp;
	MARK *rp;
{
	int rval;

	sp->exlcontinue = sp->exlinecount = sp->extotalcount = 0;

	(void)svi_busy_cursor(sp, NULL);
	rval = exp->cmd->fn(sp, ep, exp);

	msg_rpt(sp, sp->stdfp);
	(void)fflush(sp->stdfp);

	/* If only one line, don't wait. */
	if (sp->extotalcount >= 1)
		if (sp->extotalcount == 1)
			F_SET(sp, S_UPDATE_MODE);
		else
			(void)svi_ex_scroll(sp, 1, 0, NULL);

	return (svi_ex_done(sp, ep, rp) || rval);
}

/*
 * svi_ex_run --
 *	Execute strings of ex commands.
 */
int
svi_ex_run(sp, ep, rp)
	SCR *sp;
	EXF *ep;
	MARK *rp;
{
	TEXT *tp;
	int key;

	sp->exlcontinue = sp->exlinecount = sp->extotalcount = 0;
	for (;;) {
		/* Get an ex command. */
		if (svi_get(sp, ep, &sp->bhdr, ':', TXT_BS | TXT_PROMPT))
			break;

		tp = sp->bhdr.next;
		if (tp->len == 1)
			break;

		(void)svi_busy_cursor(sp, NULL);
		(void)ex_cstring(sp, ep, tp->lb, tp->len);
		(void)fflush(sp->stdfp);

		/*
		 * The file or screen may have changed, in which case,
		 * the main editor loop takes care of it.
		 */
		if (F_ISSET(sp, S_MAJOR_CHANGE))
			break;
		
		/* If only one line, don't wait. */
		if (sp->extotalcount <= 1) {
			if (sp->extotalcount == 1)
				F_SET(sp, S_UPDATE_MODE);
			break;
		}

		/* The user may continue in ex mode by entering a ':'. */
		(void)svi_ex_scroll(sp, 1, 1, &key);
		if (key != ':')
                        break;

		++sp->extotalcount;
		++sp->exlinecount;
	}
	(void)svi_ex_done(sp, ep, rp);
	return (0);
}

/*
 * svi_ex_done --
 *	Cleanup from dipping into ex.
 */
static int
svi_ex_done(sp, ep, rp)
	SCR *sp;
	EXF *ep;
	MARK *rp;
{
	recno_t lno;
	size_t len, line_off;

	/*
	 * The file or screen may have changed, in which case,
	 * the main editor loop takes care of it.
	 */
	if (F_ISSET(sp, S_MAJOR_CHANGE))
		return (0);

	/*
	 * Otherwise, the only cursor modifications will be real, however, the
	 * underlying line may have changed; don't trust anything.  This code
	 * has been a remarkably fertile place for bugs.
	 *
	 * Repaint the entire screen if at least half the screen is trashed.
	 * Else, repaint only over the overwritten lines.  The "-2" comes
	 * from one for the mode line and one for the fact that it's an offset.
	 *
	 * Don't trust ANYTHING.
	 */
	if (sp->extotalcount > 1)
		if (sp->extotalcount >= HALFSCREEN(sp))
			F_SET(sp, S_REDRAW);
		else
			for (line_off = sp->rows - 2;
			    sp->extotalcount--; --line_off)
				if (svi_line(sp, ep,
				    HMAP + line_off, NULL, 0, NULL, NULL))
					return (1);

	/*
	 * Do a reality check on a cursor value, and make sure it's okay.
	 * If necessary, change it, but keep it as close as possible to
	 * the claimed value.  The main reason is to make sure that we
	 * haven't lost because ex doesn't care about the column and it's
	 * disappeared.
	 */
	lno = sp->lno;
	if (file_gline(sp, ep, lno, &len) == NULL) {
		if (file_lline(sp, ep, &lno))
			return (1);
		if (lno == 0) {
			sp->lno = 1;
			sp->cno = 0;
		} else {
			GETLINE_ERR(sp, sp->lno);
			if (file_gline(sp, ep, lno, &len) == NULL) {
				sp->lno = 1;
				sp->cno = 0;
			} else
				sp->cno = sp->s_relative(sp, ep, sp->lno);
		}
	} else if (sp->cno >= len)
		sp->cno = len ? len - 1 : 0;

	rp->lno = sp->lno;
	rp->cno = sp->cno;
	return (0);
}

/*
 * svi_ex_write --
 *	Write out the ex messages.
 */
int
svi_ex_write(cookie, line, llen)
	void *cookie;
	const char *line;
	int llen;
{
	SCR *sp;
	size_t new_lcontinue;
	int len, rlen;
	const char *p;

	new_lcontinue = 0;		/* In case of a write of 0. */
	p = line;

	rlen = llen;
	for (sp = cookie; llen;) {
		/* Get the next line. */
		if ((p = memchr(line, '\n', llen)) == NULL)
			len = llen;
		else
			len = p - line;

		/*
		 * The max is sp->cols characters, and we may
		 * have already written part of the line.
		 */
		if (len + sp->exlcontinue > sp->cols)
			len = sp->cols - sp->exlcontinue;

		/*
		 * If the first line output, do nothing.
		 * If the second line output, move the screen up and draw the
		 * divider line.
		 * Else, if it's a continuation line, move to the continuation
		 * point, else, move the screen up.
		 */
		if (sp->exlcontinue == 0) {
			if (sp->extotalcount == 1) {
				MOVE(sp, INFOLINE(sp) - 1, 0);
				clrtoeol();
				if (svi_divider(sp))
					return (-1);
				++sp->extotalcount;
				++sp->exlinecount;
			}
			if (sp->extotalcount != 0 &&
			    svi_ex_scroll(sp, 0, 0, NULL))
				return (-1);
			MOVE(sp, INFOLINE(sp), 0);
			++sp->extotalcount;
			++sp->exlinecount;
		} else
			MOVE(sp, INFOLINE(sp), sp->exlcontinue);

		/* Display the line. */
		if (len)
			ADDNSTR(line, len);

		/* Clear to EOL. */
		if (len + sp->exlcontinue < sp->cols)
			clrtoeol();

		/* Set up exlcontinue. */
		new_lcontinue = len + sp->exlcontinue;
		sp->exlcontinue = 0;

		/* Reset for the next line. */
		line += len;
		llen -= len;
		if (p != NULL) {
			++line;
			--llen;
		}
	}
	/* Refresh the screen, even if it's a partial. */
	refresh();

	/* Set up next continuation line. */
	if (p == NULL)
		sp->exlcontinue = new_lcontinue;
	return (rlen);
}

/*
 * svi_ex_scroll --
 *	Scroll the screen for ex output.
 */
static int
svi_ex_scroll(sp, mustwait, colon_ok, chp)
	SCR *sp;
	int mustwait, colon_ok, *chp;
{
	int ch;

	/*
	 * Scroll the screen.  Instead of scrolling the entire screen, delete
	 * the line above the first line output so preserve the maximum amount
	 * of the screen.
	 */
	if (sp->extotalcount >= sp->rows) {
		MOVE(sp, 0, 0);
	} else
		MOVE(sp, INFOLINE(sp) - sp->extotalcount, 0);

	deleteln();
	if (sp->child != NULL) {
		MOVE(sp, INFOLINE(sp), 0);
		insertln();
	}

	/* If just displayed a full screen, wait. */
	if (mustwait || sp->exlinecount == sp->rows) {
		MOVE(sp, INFOLINE(sp), 0);
		ADDNSTR(CONTMSG, (int)sizeof(CONTMSG) - 1);
		clrtoeol();
		refresh();
		while (sp->special[ch = term_key(sp, 0)] != K_CR &&
		    !isspace(ch) && (!colon_ok || ch != ':'))
			svi_bell(sp);
		if (chp != NULL)
			*chp = ch;
		sp->exlinecount = 0;
	}
	return (0);
}