#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 13)."
# Contents:  stevie/normal.c stevie/screen.c
# Wrapped by thor@surt on Fri Oct 16 09:43:47 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'stevie/normal.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stevie/normal.c'\"
else
echo shar: Extracting \"'stevie/normal.c'\" \(17903 characters\)
sed "s/^X//" >'stevie/normal.c' <<'END_OF_FILE'
X/* $Header: /nw/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $
X *
X * Contains the main routine for processing characters in command mode.
X * Communicates closely with the code in ops.c to handle the operators.
X */
X
X#include "stevie.h"
X#include "ops.h"
X
X/*
X * Generally speaking, every command in normal() should either clear any
X * pending operator (with CLEAROP), or set the motion type variable.
X */
X
X#define	CLEAROP	(operator=NOP)	/* clear any pending operator */
X
Xint	operator = NOP;		/* current pending operator */
Xint	mtype;			/* type of the current cursor motion */
Xbool_t	mincl;			/* true if char motion is inclusive */
XLPTR	startop;		/* cursor pos. at start of operator */
X
X/*
X * Operators can have counts either before the operator, or between the
X * operator and the following cursor motion as in:
X *
X *	d3w or 3dw
X *
X * If a count is given before the operator, it is saved in opnum. If
X * normal() is called with a pending operator, the count in opnum (if
X * present) overrides any count that came later.
X */
Xstatic	int	opnum = 0;
X
X#define	DEFAULT1(x)	(((x) == 0) ? 1 : (x))
X
X/*
X * normal(c)
X *
X * Execute a command in command mode.
X *
X * This is basically a big switch with the cases arranged in rough categories
X * in the following order:
X *
X *	1. File positioning commands
X *	2. Control commands (e.g. ^G, Z, screen redraw, etc)
X *	3. Character motions
X *	4. Search commands (of various kinds)
X *	5. Edit commands (e.g. J, x, X)
X *	6. Insert commands (e.g. i, o, O, A)
X *	7. Operators
X *	8. Abbreviations (e.g. D, C)
X *	9. Marks
X */
Xvoid
Xnormal(c)
Xregister int	c;
X{
X	register int	n;
X	register char	*s;	/* temporary variable for misc. strings */
X	bool_t	flag = FALSE;
X	int	type = 0;	/* used in some operations to modify type */
X	int	dir = FORWARD;	/* search direction */
X	int	nchar = NUL;
X	bool_t	finish_op;
X
X	/*
X	 * If there is an operator pending, then the command we take
X	 * this time will terminate it. Finish_op tells us to finish
X	 * the operation before returning this time (unless the operation
X	 * was cancelled.
X	 */
X	finish_op = (operator != NOP);
X
X	/*
X	 * If we're in the middle of an operator AND we had a count before
X	 * the operator, then that count overrides the current value of
X	 * Prenum. What this means effectively, is that commands like
X	 * "3dw" get turned into "d3w" which makes things fall into place
X	 * pretty neatly.
X	 */
X	if (finish_op) {
X		if (opnum != 0)
X			Prenum = opnum;
X	} else
X		opnum = 0;
X
X	u_lcheck();	/* clear the "line undo" buffer if we've moved */
X
X	switch (c & 0xff) {
X
X	/*
X	 * Screen positioning commands
X	 */
X	case CTRL('D'):
X		CLEAROP;
X		if (Prenum)
X			P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
X		scrollup(P(P_SS));
X		onedown(P(P_SS));
X		updatescreen();
X		break;
X
X	case CTRL('U'):
X		CLEAROP;
X		if (Prenum)
X			P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
X		scrolldown(P(P_SS));
X		oneup(P(P_SS));
X		updatescreen();
X		break;
X
X	/*
X	 * This is kind of a hack. If we're moving by one page, the calls
X	 * to stuffin() do exactly the right thing in terms of leaving
X	 * some context, and so on. If a count was given, we don't have
X	 * to worry about these issues.
X	 */
X	case CTRL('F'):
X		CLEAROP;
X		n = DEFAULT1(Prenum);
X		if (n > 1) {
X			if ( ! onedown(Rows * n) )
X				beep();
X			cursupdate();
X		} else {
X			/* screenclear(); */
X			stuffin("Lz\nM");
X		}
X		break;
X
X	case CTRL('B'):
X		CLEAROP;
X		n = DEFAULT1(Prenum);
X		if (n > 1) {
X			if ( ! oneup(Rows * n) )
X				beep();
X			cursupdate();
X		} else {
X			/* screenclear(); */
X			stuffin("Hz-M");
X		}
X		break;
X
X	case CTRL('E'):
X		CLEAROP;
X		scrollup(DEFAULT1(Prenum));
X		updatescreen();
X		break;
X
X	case CTRL('Y'):
X		CLEAROP;
X		scrolldown(DEFAULT1(Prenum));
X		updatescreen();
X		break;
X
X	case 'z':
X		CLEAROP;
X		switch (vgetc()) {
X		case NL:		/* put Curschar at top of screen */
X		case CR:
X			*Topchar = *Curschar;
X			Topchar->index = 0;
X			updatescreen();
X			break;
X
X		case '.':		/* put Curschar in middle of screen */
X			n = Rows/2;
X			goto dozcmd;
X
X		case '-':		/* put Curschar at bottom of screen */
X			n = Rows-1;
X			/* fall through */
X
X		dozcmd:
X			{
X				register LPTR	*lp = Curschar;
X				register int	l = 0;
X
X				while ((l < n) && (lp != NULL)) {
X					l += plines(lp);
X					*Topchar = *lp;
X					lp = prevline(lp);
X				}
X			}
X			Topchar->index = 0;
X			updatescreen();
X			break;
X
X		default:
X			beep();
X		}
X		break;
X
X	/*
X	 * Control commands
X	 */
X	case ':':
X		CLEAROP;
X		if ((s = getcmdln(c)) != NULL)
X			docmdln(s);
X		break;
X
X	case K_HELP:
X		CLEAROP;
X		if (vi_help()) {
X			screenclear();
X			updatescreen();
X		}
X		break;
X
X	case CTRL('L'):
X		CLEAROP;
X		screenclear();
X		updatescreen();
X		break;
X
X
X	case CTRL('O'):			/* ignored */
X		/*
X		 * A command that's ignored can be useful. We use it at
X		 * times when we want to postpone redraws. By stuffing
X		 * in a control-o, redraws get suspended until the editor
X		 * gets back around to processing input.
X		 */
X		break;
X
X	case CTRL('G'):
X		CLEAROP;
X		fileinfo();
X		break;
X
X	case K_CCIRCM:			/* shorthand command */
X		CLEAROP;
X#ifdef TAGSTACK
X		/* If tag stacking compiled in & enabled, this is an untag.
X		 * Otherwise, or if tag stack empty, edit alternate file.
X		 * "untage" is so interpreted by dountag().
X		 */
X		if (P(P_TG))
X			stuffin(":untage\n");
X		else
X#endif
X			stuffin(":e #\n");
X		break;
X
X	case 'Z':			/* write, if changed, and exit */
X		if (vgetc() != 'Z') {
X			beep();
X			break;
X		}
X		doxit();
X		break;
X
X	/*
X	 * Macro evaluates true if char 'c' is a valid identifier character
X	 */
X#	define	IDCHAR(c)	(isalpha(c) || isdigit(c) || (c) == '_')
X
X	case CTRL(']'):			/* :ta to current identifier */
X		CLEAROP;
X		{
X			char	ch;
X			LPTR	save;
X
X			save = *Curschar;
X			/*
X			 * First back up to start of identifier. This
X			 * doesn't match the real vi but I like it a
X			 * little better and it shouldn't bother anyone.
X			 */
X			ch = gchar(Curschar);
X			while (IDCHAR(ch)) {
X				if (!oneleft())
X					break;
X				ch = gchar(Curschar);
X			}
X			if (!IDCHAR(ch))
X				oneright();
X
X			stuffin(":ta ");
X			/*
X			 * Now grab the chars in the identifier
X			 */
X			ch = gchar(Curschar);
X			while (IDCHAR(ch)) {
X				stuffin(mkstr(ch));
X				if (!oneright())
X					break;
X				ch = gchar(Curschar);
X			}
X			stuffin("\n");
X
X			*Curschar = save;	/* restore, in case of error */
X		}
X		break;
X
X	/*
X	 * Character motion commands
X	 */
X	case 'G':
X		mtype = MLINE;
X		*Curschar = *gotoline(Prenum);
X		beginline(TRUE);
X		break;
X
X	case 'H':
X		mtype = MLINE;
X		*Curschar = *Topchar;
X		for (n = Prenum; n && onedown(1) ;n--)
X			;
X		beginline(TRUE);
X		break;
X
X	case 'M':
X		mtype = MLINE;
X		*Curschar = *Topchar;
X		for (n = 0; n < Rows/2 && onedown(1) ;n++)
X			;
X		beginline(TRUE);
X		break;
X
X	case 'L':
X		mtype = MLINE;
X		*Curschar = *prevline(Botchar);
X		for (n = Prenum; n && oneup(1) ;n--)
X			;
X		beginline(TRUE);
X		break;
X
X	case 'l':
X	case K_RARROW:
X	case ' ':
X		mtype = MCHAR;
X		mincl = FALSE;
X		n = DEFAULT1(Prenum);
X		while (n--) {
X			if ( ! oneright() )  {
X				beep();
X                                break;
X                        }
X		}
X		set_want_col = TRUE;
X		break;
X
X	case 'h':
X	case K_LARROW:
X	case CTRL('H'):
X		mtype = MCHAR;
X		mincl = FALSE;
X		n = DEFAULT1(Prenum);
X		while (n--) {
X			if ( ! oneleft() )  {
X				beep();
X                                break;
X                        }
X		}
X		set_want_col = TRUE;
X		break;
X
X	case '-':
X		flag = TRUE;
X		/* fall through */
X
X	case 'k':
X	case K_UARROW:
X	case CTRL('P'):
X		mtype = MLINE;
X		if ( ! oneup(DEFAULT1(Prenum)) )
X			beep();
X		if (flag)
X			beginline(TRUE);
X		break;
X
X	case '+':
X	case CR:
X	case NL:
X		flag = TRUE;
X		/* fall through */
X
X	case 'j':
X	case K_DARROW:
X	case CTRL('N'):
X		mtype = MLINE;
X		if ( ! onedown(DEFAULT1(Prenum)) )
X			beep();
X		if (flag)
X			beginline(TRUE);
X		break;
X
X	/*
X	 * This is a strange motion command that helps make operators
X	 * more logical. It is actually implemented, but not documented
X	 * in the real 'vi'. This motion command actually refers to "the
X	 * current line". Commands like "dd" and "yy" are really an alternate
X	 * form of "d_" and "y_". It does accept a count, so "d3_" works to
X	 * delete 3 lines.
X	 */
X	case '_':
X	lineop:
X		mtype = MLINE;
X		onedown(DEFAULT1(Prenum)-1);
X		break;
X
X	case '|':
X		mtype = MCHAR;
X		mincl = TRUE;
X		beginline(FALSE);
X		if (Prenum > 0)
X			*Curschar = *coladvance(Curschar, Prenum-1);
X		Curswant = Prenum - 1;
X		break;
X		
X	/*
X	 * Word Motions
X	 */
X
X	case 'B':
X		type = 1;
X		/* fall through */
X
X	case 'b':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			LPTR	*pos;
X
X			if ((pos = bck_word(Curschar, type)) == NULL) {
X				beep();
X				CLEAROP;
X				break;
X			} else
X				*Curschar = *pos;
X		}
X		break;
X
X	case 'W':
X		type = 1;
X		/* fall through */
X
X	case 'w':
X		/*
X		 * This is a little strange. To match what the real vi
X		 * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
X		 * This seems impolite at first, but it's really more
X		 * what we mean when we say 'cw'.
X		 */
X		if (operator == CHANGE)
X			goto doecmd;
X
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			LPTR	*pos;
X
X			if ((pos = fwd_word(Curschar, type)) == NULL) {
X				beep();
X				CLEAROP;
X				break;
X			} else
X				*Curschar = *pos;
X		}
X		break;
X
X	case 'E':
X		type = 1;
X		/* fall through */
X
X	case 'e':
X	doecmd:
X		mtype = MCHAR;
X		mincl = TRUE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			LPTR	*pos;
X
X			/*
X			 * The first motion gets special treatment if we're
X			 * do a 'CHANGE'.
X			 */
X			if (n == DEFAULT1(Prenum))
X				pos = end_word(Curschar,type,operator==CHANGE);
X			else
X				pos = end_word(Curschar, type, FALSE);
X
X			if (pos == NULL) {
X				beep();
X				CLEAROP;
X				break;
X			} else
X				*Curschar = *pos;
X		}
X		break;
X
X	case '$':
X		mtype = MCHAR;
X		mincl = TRUE;
X		while ( oneright() )
X			;
X		Curswant = 999;		/* so we stay at the end */
X		break;
X
X	case '^':
X		mtype = MCHAR;
X		mincl = FALSE;
X		beginline(TRUE);
X		break;
X
X	case '0':
X		mtype = MCHAR;
X		mincl = TRUE;
X		beginline(FALSE);
X		break;
X
X	/*
X	 * Searches of various kinds
X	 */
X	case '?':
X	case '/':
X		s = getcmdln(c);	/* get the search string */
X
X		/*
X		 * If they backspaced out of the search command,
X		 * just bag everything.
X		 */
X		if (s == NULL) {
X			CLEAROP;
X			break;
X		}
X
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X
X		/*
X		 * If no string given, pass NULL to repeat the prior search.
X		 * If the search fails, abort any pending operator.
X		 */
X		if (!dosearch(
X				(c == '/') ? FORWARD : BACKWARD,
X				(*s == NUL) ? NULL : s
X			     ))
X			CLEAROP;
X		break;
X
X	case 'n':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		if (!repsearch(0))
X			CLEAROP;
X		break;
X
X	case 'N':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		if (!repsearch(1))
X			CLEAROP;
X		break;
X
X	/*
X	 * Character searches
X	 */
X	case 'T':
X		dir = BACKWARD;
X		/* fall through */
X
X	case 't':
X		type = 1;
X		goto docsearch;
X
X	case 'F':
X		dir = BACKWARD;
X		/* fall through */
X
X	case 'f':
X	docsearch:
X		mtype = MCHAR;
X		mincl = TRUE;
X		set_want_col = TRUE;
X		if ((nchar = vgetc()) == ESC)	/* search char */
X			break;
X
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			if (!searchc(nchar, dir, type)) {
X				CLEAROP;
X				beep();
X				break;
X			}
X		}
X		break;
X
X	case ',':
X		flag = 1;
X		/* fall through */
X
X	case ';':
X		mtype = MCHAR;
X		mincl = TRUE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			if (!crepsearch(flag)) {
X				CLEAROP;
X				beep();
X				break;
X			}
X		}
X		break;
X
X	case '(':			/* sentence searches */
X		dir = BACKWARD;
X		/* fall through */
X
X	case ')':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		if (findsent(dir) == NULL) {
X			beep();
X			CLEAROP;
X		}
X		break;
X
X	case '{':			/* paragraph searches */
X		dir = BACKWARD;
X		/* fall through */
X
X	case '}':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		if (!findpara(dir)) {
X			beep();
X			CLEAROP;
X		}
X		break;
X
X	case '[':			/* function searches */
X		dir = BACKWARD;
X		/* fall through */
X
X	case ']':
X		mtype = MLINE;
X		set_want_col = TRUE;
X		if (vgetc() != c) {
X			beep();
X			CLEAROP;
X			break;
X		}
X
X		if (!findfunc(dir)) {
X			beep();
X			CLEAROP;
X		}
X		break;
X
X	case '%':
X		mtype = MCHAR;
X		mincl = TRUE;
X		{
X			LPTR	*pos;
X
X			if ((pos = showmatch()) == NULL) {
X				beep();
X				CLEAROP;
X			} else {
X				setpcmark();
X				*Curschar = *pos;
X				set_want_col = TRUE;
X			}
X		}
X		break;
X		
X	/*
X	 * Edits
X	 */
X	case '.':		/* repeat last change (usually) */
X		/*
X		 * If a delete is in effect, we let '.' help out the same
X		 * way that '_' helps for some line operations. It's like
X		 * an 'l', but subtracts one from the count and is inclusive.
X		 */
X		if (operator == DELETE || operator == CHANGE) {
X			if (Prenum != 0) {
X				n = DEFAULT1(Prenum) - 1;
X				while (n--)
X					if (! oneright())
X						break;
X			}
X			mtype = MCHAR;
X			mincl = TRUE;
X		} else {			/* a normal 'redo' */
X			CLEAROP;
X			stuffin(Redobuff);
X		}
X		break;
X
X	case 'u':
X	case K_UNDO:
X		CLEAROP;
X		u_undo();
X		break;
X
X	case 'U':
X		CLEAROP;
X		u_lundo();
X		break;
X
X	case 'x':
X		CLEAROP;
X		if (lineempty()) {	/* can't do it on a blank line */
X			beep();
X			break;
X		}
X		if (Prenum)
X			stuffnum(Prenum);
X		stuffin("d.");
X		break;
X
X	case 'X':
X		CLEAROP;
X		if (!oneleft())
X			beep();
X		else {
X			strcpy(Redobuff, "X");
X			u_saveline();
X			delchar(TRUE);
X			updateline();
X		}
X		break;
X
X	case 'r':
X		CLEAROP;
X		if (lineempty()) {	/* Nothing to replace */
X			beep();
X			break;
X		}
X		if ((nchar = vgetc()) == ESC)
X			break;
X
X		if (nchar==CR || nchar==NL) {
X			stuffin("R\n\033");
X			break;
X		}
X
X		if (nchar & 0x80) {
X			beep();
X			break;
X		}
X		u_saveline();
X
X		/* Change current character. */
X		pchar(Curschar, nchar);
X
X		/* Save stuff necessary to redo it */
X		sprintf(Redobuff, "r%c", nchar);
X
X		CHANGED;
X		updateline();
X		break;
X
X	case '~':		/* swap case */
X		if (!P(P_TO)) {
X			CLEAROP;
X			if (lineempty()) {
X				beep();
X				break;
X			}
X			c = gchar(Curschar);
X
X			if (isalpha(c)) {
X				if (islower(c))
X					c = toupper(c);
X				else
X					c = tolower(c);
X			}
X			u_saveline();
X
X			pchar(Curschar, c);	/* Change current character. */
X			oneright();
X
X			strcpy(Redobuff, "~");
X
X			CHANGED;
X			updateline();
X		}
X#ifdef	TILDEOP
X		else {
X			if (operator == TILDE)		/* handle '~~' */
X				goto lineop;
X			if (Prenum != 0)
X				opnum = Prenum;
X			startop = *Curschar;
X			operator = TILDE;
X		}
X#endif
X
X		break;
X
X	case 'J':
X		CLEAROP;
X
X		u_save(Curschar->linep->prev, Curschar->linep->next->next);
X
X		if (!dojoin(TRUE))
X			beep();
X
X		strcpy(Redobuff, "J");
X		updatescreen();
X		break;
X
X	/*
X	 * Inserts
X	 */
X	case 'A':
X		set_want_col = TRUE;
X		while (oneright())
X			;
X		/* fall through */
X
X	case 'a':
X		CLEAROP;
X		/* Works just like an 'i'nsert on the next character. */
X		if (!lineempty())
X			inc(Curschar);
X		u_saveline();
X		startinsert(mkstr(c), FALSE);
X		break;
X
X	case 'I':
X		beginline(TRUE);
X		/* fall through */
X
X	case 'i':
X	case K_INSERT:
X		CLEAROP;
X		u_saveline();
X		startinsert(mkstr(c), FALSE);
X		break;
X
X	case 'o':
X		CLEAROP;
X		u_save(Curschar->linep, Curschar->linep->next);
X		opencmd(FORWARD, TRUE);
X		startinsert("o", TRUE);
X		break;
X
X	case 'O':
X		CLEAROP;
X		u_save(Curschar->linep->prev, Curschar->linep);
X		opencmd(BACKWARD, TRUE);
X		startinsert("O", TRUE);
X		break;
X
X	case 'R':
X		CLEAROP;
X		u_saveline();
X		startinsert("R", FALSE);
X		break;
X
X	/*
X	 * Operators
X	 */
X	case 'd':
X		if (operator == DELETE)		/* handle 'dd' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = DELETE;
X		break;
X
X	case 'c':
X		if (operator == CHANGE)		/* handle 'cc' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = CHANGE;
X		break;
X
X	case 'y':
X		if (operator == YANK)		/* handle 'yy' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = YANK;
X		break;
X
X	case '>':
X		if (operator == RSHIFT)		/* handle >> */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = RSHIFT;
X		break;
X
X	case '<':
X		if (operator == LSHIFT)		/* handle << */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;	/* save current position */
X		operator = LSHIFT;
X		break;
X
X	case '!':
X		if (operator == FILTER)		/* handle '!!' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = FILTER;
X		break;
X
X	case 'p':
X		doput(FORWARD);
X		break;
X
X	case 'P':
X		doput(BACKWARD);
X		break;
X
X	/*
X	 * Abbreviations
X	 */
X	case 'D':
X		stuffin("d$");
X		break;
X
X	case 'Y':
X		if (Prenum)
X			stuffnum(Prenum);
X		stuffin("yy");
X		break;
X
X	case 'C':
X		stuffin("c$");
X		break;
X
X	case 's':				/* substitute characters */
X		if (lineempty()) {	/* can't do it on a blank line */
X			CLEAROP;
X			beep();
X			break;
X		}
X		if (Prenum)
X			stuffnum(Prenum);
X		stuffin("c.");
X		break;
X
X	/*
X	 * Marks
X	 */
X	case 'm':
X		CLEAROP;
X		if (!setmark(vgetc()))
X			beep();
X		break;
X
X	case '\'':
X		flag = TRUE;
X		/* fall through */
X
X	case '`':
X		{
X			LPTR	mtmp, *mark;
X
X			mark = getmark(vgetc());
X			if (mark == NULL) {
X				beep();
X				CLEAROP;
X			} else {
X				mtmp = *mark;
X				setpcmark();
X				*Curschar = mtmp;
X				if (flag)
X					beginline(TRUE);
X			}
X			mtype = flag ? MLINE : MCHAR;
X			mincl = FALSE;		/* ignored if not MCHAR */
X			set_want_col = TRUE;
X		}
X		break;
X
X	default:
X		CLEAROP;
X		beep();
X		break;
X	}
X
X	/*
X	 * If an operation is pending, handle it...
X	 */
X	if (finish_op) {		/* we just finished an operator */
X		if (operator == NOP)	/* ... but it was cancelled */
X			return;
X
X		switch (operator) {
X
X		case LSHIFT:
X		case RSHIFT:
X			doshift(operator, c, nchar, Prenum);
X			break;
X
X		case DELETE:
X			dodelete(c, nchar, Prenum);
X			break;
X
X		case YANK:
X			(void) doyank();	/* no redo on yank... */
X			break;
X
X		case CHANGE:
X			dochange(c, nchar, Prenum);
X			break;
X
X		case FILTER:
X			dofilter(c, nchar, Prenum);
X			break;
X
X#ifdef	TILDEOP
X		case TILDE:
X			dotilde(c, nchar, Prenum);
X			break;
X#endif
X
X		default:
X			beep();
X		}
X		operator = NOP;
X	}
X}
END_OF_FILE
if test 17903 -ne `wc -c <'stevie/normal.c'`; then
    echo shar: \"'stevie/normal.c'\" unpacked with wrong size!
fi
# end of 'stevie/normal.c'
fi
if test -f 'stevie/screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stevie/screen.c'\"
else
echo shar: Extracting \"'stevie/screen.c'\" \(15945 characters\)
sed "s/^X//" >'stevie/screen.c' <<'END_OF_FILE'
X/* $Header: /nw/tony/src/stevie/src/RCS/screen.c,v 1.8 89/08/02 09:26:33 tony Exp $
X *
X * Routines to manipulate the screen representations.
X */
X
X#include "stevie.h"
X
X/*
X * This gets set if we ignored an update request while input was pending.
X * We check this when the input is drained to see if the screen should be
X * updated.
X */
Xbool_t	need_redraw = FALSE;
X
X/*
X * The following variable is set (in filetonext) to the number of physical
X * lines taken by the line the cursor is on. We use this to avoid extra
X * calls to plines(). The optimized routines lfiletonext() and lnexttoscreen()
X * make sure that the size of the cursor line hasn't changed. If so, lines
X * below the cursor will move up or down and we need to call the routines
X * filetonext() and nexttoscreen() to examine the entire screen.
X */
Xstatic	int	Cline_size;	/* size (in rows) of the cursor line */
Xstatic	int	Cline_row;	/* starting row of the cursor line */
X
Xstatic	char	*mkline();	/* calculate line string for "number" mode */
X
X/*
X * filetonext()
X *
X * Based on the current value of Topchar, transfer a screenfull of
X * stuff from Filemem to Nextscreen, and update Botchar.
X */
X
Xstatic void
Xfiletonext()
X{
X	register int	row, col;
X	register char	*screenp = Nextscreen;
X	LPTR	memp;
X	LPTR	save;			/* save pos. in case line won't fit */
X	register char	*endscreen;
X	register char	*nextrow;
X	char	extra[16];
X	int	nextra = 0;
X	register int	c;
X	int	n;
X	bool_t	done;		/* if TRUE, we hit the end of the file */
X	bool_t	didline;	/* if TRUE, we finished the last line */
X	int	srow;		/* starting row of the current line */
X	int	lno;		/* number of the line we're doing */
X	int	coff;		/* column offset */
X
X	coff = P(P_NU) ? 8 : 0;
X
X	save = memp = *Topchar;
X
X	if (P(P_NU))
X		lno = cntllines(Filemem, Topchar);
X
X	/*
X	 * The number of rows shown is Rows-1.
X	 * The last line is the status/command line.
X	 */
X	endscreen = &screenp[(Rows-1)*Columns];
X
X	done = didline = FALSE;
X	srow = row = col = 0;
X	/*
X	 * We go one past the end of the screen so we can find out if the
X	 * last line fit on the screen or not.
X	 */
X	while ( screenp <= endscreen && !done) {
X
X
X		if (P(P_NU) && col == 0 && memp.index == 0) {
X			strcpy(extra, mkline(lno++));
X			nextra = 8;
X		}
X
X		/* Get the next character to put on the screen. */
X
X		/* The 'extra' array contains the extra stuff that is */
X		/* inserted to represent special characters (tabs, and */
X		/* other non-printable stuff.  The order in the 'extra' */
X		/* array is reversed. */
X
X		if ( nextra > 0 )
X			c = extra[--nextra];
X		else {
X			c = (unsigned)(0xff & gchar(&memp));
X			if (inc(&memp) == -1)
X				done = 1;
X			/* when getting a character from the file, we */
X			/* may have to turn it into something else on */
X			/* the way to putting it into 'Nextscreen'. */
X			if ( c == TAB && !P(P_LS) ) {
X				strcpy(extra,"        ");
X				/* tab amount depends on current column */
X				nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
X				c = ' ';
X			}
X			else if ( c == NUL && P(P_LS) ) {
X				extra[0] = NUL;
X				nextra = 1;
X				c = '$';
X			} else if ( (n = chars[c].ch_size) > 1 ) {
X				char *p;
X				nextra = 0;
X				p = chars[c].ch_str;
X				/* copy 'ch-str'ing into 'extra' in reverse */
X				while ( n > 1 )
X					extra[nextra++] = p[--n];
X				c = p[0];
X			}
X		}
X
X		if (screenp == endscreen) {
X			/*
X			 * We're one past the end of the screen. If the
X			 * current character is null, then we really did
X			 * finish, so set didline = TRUE. In either case,
X			 * break out because we're done.
X			 */
X			dec(&memp);
X			if (memp.index != 0 && c == NUL) {
X				didline = TRUE;
X				inc(&memp);
X			}
X			break;
X		}
X
X		if ( c == NUL ) {
X			srow = ++row;
X			/*
X			 * Save this position in case the next line won't
X			 * fit on the screen completely.
X			 */
X			save = memp;
X			/* get pointer to start of next row */
X			nextrow = &Nextscreen[row*Columns];
X			/* blank out the rest of this row */
X			while ( screenp != nextrow )
X				*screenp++ = ' ';
X			col = 0;
X			continue;
X		}
X		if ( col >= Columns ) {
X			row++;
X			col = 0;
X		}
X		/* store the character in Nextscreen */
X		*screenp++ = c;
X		col++;
X	}
X	/*
X	 * If we didn't hit the end of the file, and we didn't finish
X	 * the last line we were working on, then the line didn't fit.
X	 */
X	if (!done && !didline) {
X		/*
X		 * Clear the rest of the screen and mark the unused lines.
X		 */
X		screenp = &Nextscreen[srow * Columns];
X		while (screenp < endscreen)
X			*screenp++ = ' ';
X		for (; srow < (Rows-1) ;srow++)
X			Nextscreen[srow * Columns] = '@';
X		*Botchar = save;
X		return;
X	}
X	/* make sure the rest of the screen is blank */
X	while ( screenp < endscreen )
X		*screenp++ = ' ';
X	/* put '~'s on rows that aren't part of the file. */
X	if ( col != 0 )
X		row++;
X	while ( row < Rows ) {
X		Nextscreen[row*Columns] = '~';
X		row++;
X	}
X	if (done)	/* we hit the end of the file */
X		*Botchar = *Fileend;
X	else
X		*Botchar = memp;	/* FIX - prev? */
X}
X
X/*
X * nexttoscreen
X *
X * Transfer the contents of Nextscreen to the screen, using Realscreen
X * to avoid unnecessary output.
X */
Xstatic void
Xnexttoscreen()
X{
X	register char	*np = Nextscreen;
X	register char	*rp = Realscreen;
X	register char	*endscreen;
X	register int	row = 0, col = 0;
X	int	gorow = -1, gocol = -1;
X
X	if (anyinput()) {
X		need_redraw = TRUE;
X		return;
X	}
X
X	endscreen = &np[(Rows-1)*Columns];
X
X	CUROFF;		/* disable cursor */
X
X	for ( ; np < endscreen ; np++,rp++ ) {
X		/* If desired screen (contents of Nextscreen) does not */
X		/* match what's really there, put it there. */
X		if ( *np != *rp ) {
X			/* if we are positioned at the right place, */
X			/* we don't have to use windgoto(). */
X			if (gocol != col || gorow != row) {
X				/*
X				 * If we're just off by one, don't send
X				 * an entire esc. seq. (this happens a lot!)
X				 */
X				if (gorow == row && gocol+1 == col) {
X					outchar(*(np-1));
X					gocol++;
X				} else
X					windgoto(gorow=row,gocol=col);
X			}
X			outchar(*rp = *np);
X			gocol++;
X		}
X		if ( ++col >= Columns ) {
X			col = 0;
X			row++;
X		}
X	}
X	CURON;		/* enable cursor again */
X}
X
X/*
X * lfiletonext() - like filetonext() but only for cursor line
X *
X * Returns true if the size of the cursor line (in rows) hasn't changed.
X * This determines whether or not we need to call filetonext() to examine
X * the entire screen for changes.
X */
Xstatic bool_t
Xlfiletonext()
X{
X	register int	row, col;
X	register char	*screenp;
X	LPTR	memp;
X	register char	*nextrow;
X	char	extra[16];
X	int	nextra = 0;
X	register int	c;
X	int	n;
X	bool_t	eof;
X	int	lno;		/* number of the line we're doing */
X	int	coff;		/* column offset */
X
X	coff = P(P_NU) ? 8 : 0;
X
X	/*
X	 * This should be done more efficiently.
X	 */
X	if (P(P_NU))
X		lno = cntllines(Filemem, Curschar);
X
X	screenp = Nextscreen + (Cline_row * Columns);
X
X	memp = *Curschar;
X	memp.index = 0;
X
X	eof = FALSE;
X	col = 0;
X	row = Cline_row;
X
X	while (!eof) {
X
X		if (P(P_NU) && col == 0 && memp.index == 0) {
X			strcpy(extra, mkline(lno));
X			nextra = 8;
X		}
X
X		/* Get the next character to put on the screen. */
X
X		/* The 'extra' array contains the extra stuff that is */
X		/* inserted to represent special characters (tabs, and */
X		/* other non-printable stuff.  The order in the 'extra' */
X		/* array is reversed. */
X
X		if ( nextra > 0 )
X			c = extra[--nextra];
X		else {
X			c = (unsigned)(0xff & gchar(&memp));
X			if (inc(&memp) == -1)
X				eof = TRUE;
X			/* when getting a character from the file, we */
X			/* may have to turn it into something else on */
X			/* the way to putting it into 'Nextscreen'. */
X			if ( c == TAB && !P(P_LS) ) {
X				strcpy(extra,"        ");
X				/* tab amount depends on current column */
X				nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
X				c = ' ';
X			} else if ( c == NUL && P(P_LS) ) {
X				extra[0] = NUL;
X				nextra = 1;
X				c = '$';
X			} else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
X				char *p;
X				nextra = 0;
X				p = chars[c].ch_str;
X				/* copy 'ch-str'ing into 'extra' in reverse */
X				while ( n > 1 )
X					extra[nextra++] = p[--n];
X				c = p[0];
X			}
X		}
X
X		if ( c == NUL ) {
X			row++;
X			/* get pointer to start of next row */
X			nextrow = &Nextscreen[row*Columns];
X			/* blank out the rest of this row */
X			while ( screenp != nextrow )
X				*screenp++ = ' ';
X			col = 0;
X			break;
X		}
X
X		if ( col >= Columns ) {
X			row++;
X			col = 0;
X		}
X		/* store the character in Nextscreen */
X		*screenp++ = c;
X		col++;
X	}
X	return ((row - Cline_row) == Cline_size);
X}
X
X/*
X * lnexttoscreen
X *
X * Like nexttoscreen() but only for the cursor line.
X */
Xstatic void
Xlnexttoscreen()
X{
X	register char	*np = Nextscreen + (Cline_row * Columns);
X	register char	*rp = Realscreen + (Cline_row * Columns);
X	register char	*endline;
X	register int	row, col;
X	int	gorow = -1, gocol = -1;
X
X	if (anyinput()) {
X		need_redraw = TRUE;
X		return;
X	}
X
X	endline = np + (Cline_size * Columns);
X
X	row = Cline_row;
X	col = 0;
X
X	CUROFF;		/* disable cursor */
X
X	for ( ; np < endline ; np++,rp++ ) {
X		/* If desired screen (contents of Nextscreen) does not */
X		/* match what's really there, put it there. */
X		if ( *np != *rp ) {
X			/* if we are positioned at the right place, */
X			/* we don't have to use windgoto(). */
X			if (gocol != col || gorow != row) {
X				/*
X				 * If we're just off by one, don't send
X				 * an entire esc. seq. (this happens a lot!)
X				 */
X				if (gorow == row && gocol+1 == col) {
X					outchar(*(np-1));
X					gocol++;
X				} else
X					windgoto(gorow=row,gocol=col);
X			}
X			outchar(*rp = *np);
X			gocol++;
X		}
X		if ( ++col >= Columns ) {
X			col = 0;
X			row++;
X		}
X	}
X	CURON;		/* enable cursor again */
X}
X
Xstatic char *
Xmkline(n)
Xregister int	n;
X{
X	static	char	lbuf[9];
X	register int	i = 2;
X
X	strcpy(lbuf, "        ");
X
X	lbuf[i++] = (n % 10) + '0';
X	n /= 10;
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	return lbuf;
X}
X
X/*
X * updateline() - update the line the cursor is on
X *
X * Updateline() is called after changes that only affect the line that
X * the cursor is on. This improves performance tremendously for normal
X * insert mode operation. The only thing we have to watch for is when
X * the cursor line grows or shrinks around a row boundary. This means
X * we have to repaint other parts of the screen appropriately. If
X * lfiletonext() returns FALSE, the size of the cursor line (in rows)
X * has changed and we have to call updatescreen() to do a complete job.
X */
Xvoid
Xupdateline()
X{
X	if (!lfiletonext())
X		updatescreen();	/* bag it, do the whole screen */
X	else
X		lnexttoscreen();
X}
X
Xvoid
Xupdatescreen()
X{
X	extern	bool_t	interactive;
X
X	if (interactive) {
X		filetonext();
X		nexttoscreen();
X	}
X}
X
X/*
X * prt_line() - print the given line
X */
Xvoid
Xprt_line(s)
Xchar	*s;
X{
X	register int	si = 0;
X	register int	c;
X	register int	col = 0;
X
X	char	extra[16];
X	int	nextra = 0;
X	int	n;
X
X	for (;;) {
X
X		if ( nextra > 0 )
X			c = extra[--nextra];
X		else {
X			c = s[si++];
X			if ( c == TAB && !P(P_LS) ) {
X				strcpy(extra, "        ");
X				/* tab amount depends on current column */
X				nextra = (P(P_TS) - 1) - col%P(P_TS);
X				c = ' ';
X			} else if ( c == NUL && P(P_LS) ) {
X				extra[0] = NUL;
X				nextra = 1;
X				c = '$';
X			} else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
X				char	*p;
X
X				nextra = 0;
X				p = chars[c].ch_str;
X				/* copy 'ch-str'ing into 'extra' in reverse */
X				while ( n > 1 )
X					extra[nextra++] = p[--n];
X				c = p[0];
X			}
X		}
X
X		if ( c == NUL )
X			break;
X
X		outchar(c);
X		col++;
X	}
X}
X
Xvoid
Xscreenclear()
X{
X	register char	*rp, *np;
X	register char	*end;
X
X	CLS;		/* clear the display */
X
X	rp  = Realscreen;
X	end = Realscreen + Rows * Columns;
X	np  = Nextscreen;
X
X	/* blank out the stored screens */
X	while (rp != end)
X		*rp++ = *np++ = ' ';
X}
X
Xvoid
Xcursupdate()
X{
X	register LPTR	*p;
X	register int	icnt, c, nlines;
X	register int	i;
X	int	didinc;
X
X	if (bufempty()) {		/* special case - file is empty */
X		*Topchar  = *Filemem;
X		*Curschar = *Filemem;
X	} else if ( LINEOF(Curschar) < LINEOF(Topchar) ) {
X		nlines = cntllines(Curschar,Topchar);
X		/* if the cursor is above the top of */
X		/* the screen, put it at the top of the screen.. */
X		*Topchar = *Curschar;
X		Topchar->index = 0;
X		/* ... and, if we weren't very close to begin with, */
X		/* we scroll so that the line is close to the middle. */
X		if ( nlines > Rows/3 ) {
X			for (i=0, p = Topchar; i < Rows/3 ;i++, *Topchar = *p)
X				if ((p = prevline(p)) == NULL)
X					break;
X		} else
X			s_ins(0, nlines-1);
X		updatescreen();
X	}
X	else if (LINEOF(Curschar) >= LINEOF(Botchar)) {
X		nlines = cntllines(Botchar,Curschar);
X		/* If the cursor is off the bottom of the screen, */
X		/* put it at the top of the screen.. */
X		/* ... and back up */
X		if ( nlines > Rows/3 ) {
X			p = Curschar;
X			for (i=0; i < (2*Rows)/3 ;i++)
X				if ((p = prevline(p)) == NULL)
X					break;
X			*Topchar = *p;
X		} else {
X			scrollup(nlines);
X		}
X		updatescreen();
X	}
X
X	Cursrow = Curscol = Cursvcol = 0;
X	for ( p=Topchar; p->linep != Curschar->linep ;p = nextline(p) )
X		Cursrow += plines(p);
X
X	Cline_row = Cursrow;
X	Cline_size = plines(p);
X
X	if (P(P_NU))
X		Curscol = 8;
X
X	for (i=0; i <= Curschar->index ;i++) {
X		c = Curschar->linep->s[i];
X		/* A tab gets expanded, depending on the current column */
X		if ( c == TAB && !P(P_LS) )
X			icnt = P(P_TS) - (Cursvcol % P(P_TS));
X		else
X			icnt = chars[(unsigned)(c & 0xff)].ch_size;
X		Curscol += icnt;
X		Cursvcol += icnt;
X		if ( Curscol >= Columns ) {
X			Curscol -= Columns;
X			Cursrow++;
X			didinc = TRUE;
X		}
X		else
X			didinc = FALSE;
X	}
X	if (didinc)
X		Cursrow--;
X
X	if (c == TAB && State == NORMAL && !P(P_LS)) {
X		Curscol--;
X		Cursvcol--;
X	} else {
X		Curscol -= icnt;
X		Cursvcol -= icnt;
X	}
X	if (Curscol < 0)
X		Curscol += Columns;
X
X	if (set_want_col) {
X		Curswant = Cursvcol;
X		set_want_col = FALSE;
X	}
X}
X
X/*
X * The rest of the routines in this file perform screen manipulations.
X * The given operation is performed physically on the screen. The
X * corresponding change is also made to the internal screen image.
X * In this way, the editor anticipates the effect of editing changes
X * on the appearance of the screen. That way, when we call screenupdate
X * a complete redraw isn't usually necessary. Another advantage is that
X * we can keep adding code to anticipate screen changes, and in the
X * meantime, everything still works.
X */
X
X/*
X * s_ins(row, nlines) - insert 'nlines' lines at 'row'
X */
Xvoid
Xs_ins(row, nlines)
Xint	row;
Xint	nlines;
X{
X	register char	*s, *d;		/* src & dest for block copy */
X	register char	*e;		/* end point for copy */
X	register int	i;
X
X	if ( ! CANIL )		/* can't do it */
X		return;
X
X	/*
X	 * It "looks" better if we do all the inserts at once
X	 */
X	SAVCUR;			/* save position */
X	windgoto(row, 0);
X
X	CRTIL( row, nlines );
X
X	windgoto(Rows-1, 0);	/* delete any garbage that may have */
X	CLEOL;			/* been shifted to the bottom line */
X	RESCUR;			/* restore the cursor position */
X
X	/*
X	 * Now do a block move to update the internal screen image
X	 */
X	d = Realscreen + (Columns * (Rows - 1)) - 1;
X	s = d - (Columns * nlines);
X	e = Realscreen + (Columns * row);
X
X	while (s >= e)
X		*d-- = *s--;
X
X	/*
X	 * Clear the inserted lines
X	 */
X	s = Realscreen + (row * Columns);
X	e = s + (nlines * Columns);
X	while (s < e)
X		*s++ = ' ';
X}
X
X/*
X * s_del(row, nlines) - delete 'nlines' lines at 'row'
X */
Xvoid
Xs_del(row, nlines)
Xint	row;
Xint	nlines;
X{
X	register char	*s, *d, *e;
X	register int	i;
X
X#ifndef BIOS
X	if ( ! CANDL ) return;		/* can't do it */
X#endif
X
X	/* delete the lines */
X	SAVCUR;				/* save position */
X
X	windgoto (Rows-1, 0);		/* go to status line */
X	CLEOL;				/* Clear it */
X	windgoto (row, 0);		/* Go to 1st line-to-del */
X	CRTDL( row, nlines );		/* Delete the lines */
X	RESCUR;				/* Restore the cursor */
X
X	/*
X	 * do a block move to update the internal image
X	 */
X	d = Realscreen + (row * Columns);
X	s = d + (nlines * Columns);
X	e = Realscreen + ((Rows - 1) * Columns);
X
X	while (s < e)
X		*d++ = *s++;
X
X	while (d < e)		/* clear the lines at the bottom */
X		*d++ = ' ';
X}
END_OF_FILE
if test 15945 -ne `wc -c <'stevie/screen.c'`; then
    echo shar: \"'stevie/screen.c'\" unpacked with wrong size!
fi
# end of 'stevie/screen.c'
fi
echo shar: End of archive 9 \(of 13\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 13 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
