⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 normal.c

📁 STEVIE文本文件编缉器的C 语言源程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Header: /nw2/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $
 *
 * Contains the main routine for processing characters in command mode.
 * Communicates closely with the code in ops.c to handle the operators.
 */

#include "stevie.h"
#include "ops.h"

/*
 * Generally speaking, every command in normal() should either clear any
 * pending operator (with CLEAROP), or set the motion type variable.
 */

#define	CLEAROP	(operator=NOP)	/* clear any pending operator */

int	operator = NOP;		/* current pending operator */
int	mtype;			/* type of the current cursor motion */
bool_t	mincl;			/* true if char motion is inclusive */
LPTR	startop;		/* cursor pos. at start of operator */

/*
 * Operators can have counts either before the operator, or between the
 * operator and the following cursor motion as in:
 *
 *	d3w or 3dw
 *
 * If a count is given before the operator, it is saved in opnum. If
 * normal() is called with a pending operator, the count in opnum (if
 * present) overrides any count that came later.
 */
static	int	opnum = 0;

#define	DEFAULT1(x)	(((x) == 0) ? 1 : (x))

/*
 * normal(c)
 *
 * Execute a command in command mode.
 *
 * This is basically a big switch with the cases arranged in rough categories
 * in the following order:
 *
 *	1. File positioning commands
 *	2. Control commands (e.g. ^G, Z, screen redraw, etc)
 *	3. Character motions
 *	4. Search commands (of various kinds)
 *	5. Edit commands (e.g. J, x, X)
 *	6. Insert commands (e.g. i, o, O, A)
 *	7. Operators
 *	8. Abbreviations (e.g. D, C)
 *	9. Marks
 */
void
normal(c)
register int	c;
{
	register int	n;
	register char	*s;	/* temporary variable for misc. strings */
	bool_t	flag = FALSE;
	int	type = 0;	/* used in some operations to modify type */
	int	dir = FORWARD;	/* search direction */
	int	nchar = NUL;
	bool_t	finish_op;

	/*
	 * If there is an operator pending, then the command we take
	 * this time will terminate it. Finish_op tells us to finish
	 * the operation before returning this time (unless the operation
	 * was cancelled.
	 */
	finish_op = (operator != NOP);

	/*
	 * If we're in the middle of an operator AND we had a count before
	 * the operator, then that count overrides the current value of
	 * Prenum. What this means effectively, is that commands like
	 * "3dw" get turned into "d3w" which makes things fall into place
	 * pretty neatly.
	 */
	if (finish_op) {
		if (opnum != 0)
			Prenum = opnum;
	} else
		opnum = 0;

	u_lcheck();	/* clear the "line undo" buffer if we've moved */

	switch (c & 0xff) {

	/*
	 * Screen positioning commands
	 */
	case CTRL('D'):
		CLEAROP;
		if (Prenum)
			P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
		scrollup(P(P_SS));
		onedown(P(P_SS));
		updatescreen();
		break;

	case CTRL('U'):
		CLEAROP;
		if (Prenum)
			P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
		scrolldown(P(P_SS));
		oneup(P(P_SS));
		updatescreen();
		break;

	/*
	 * This is kind of a hack. If we're moving by one page, the calls
	 * to stuffin() do exactly the right thing in terms of leaving
	 * some context, and so on. If a count was given, we don't have
	 * to worry about these issues.
	 */
	case CTRL('F'):
		CLEAROP;
		n = DEFAULT1(Prenum);
		if (n > 1) {
			if ( ! onedown(Rows * n) )
				beep();
			cursupdate();
		} else {
			screenclear();
			stuffin("Lz\nM");
		}
		break;

	case CTRL('B'):
		CLEAROP;
		n = DEFAULT1(Prenum);
		if (n > 1) {
			if ( ! oneup(Rows * n) )
				beep();
			cursupdate();
		} else {
			screenclear();
			stuffin("Hz-M");
		}
		break;

	case CTRL('E'):
		CLEAROP;
		scrollup(DEFAULT1(Prenum));
		updatescreen();
		break;

	case CTRL('Y'):
		CLEAROP;
		scrolldown(DEFAULT1(Prenum));
		updatescreen();
		break;

	case 'z':
		CLEAROP;
		switch (vgetc()) {
		case NL:		/* put Curschar at top of screen */
		case CR:
			*Topchar = *Curschar;
			Topchar->index = 0;
			updatescreen();
			break;

		case '.':		/* put Curschar in middle of screen */
			n = Rows/2;
			goto dozcmd;

		case '-':		/* put Curschar at bottom of screen */
			n = Rows-1;
			/* fall through */

		dozcmd:
			{
				register LPTR	*lp = Curschar;
				register int	l = 0;

				while ((l < n) && (lp != NULL)) {
					l += plines(lp);
					*Topchar = *lp;
					lp = prevline(lp);
				}
			}
			Topchar->index = 0;
			updatescreen();
			break;

		default:
			beep();
		}
		break;

	/*
	 * Control commands
	 */
	case ':':
		CLEAROP;
		if ((s = getcmdln(c)) != NULL)
			docmdln(s);
		break;

	case K_HELP:
		CLEAROP;
		if (help()) {
			screenclear();
			updatescreen();
		}
		break;

	case CTRL('L'):
		CLEAROP;
		screenclear();
		updatescreen();
		break;


	case CTRL('O'):			/* ignored */
		/*
		 * A command that's ignored can be useful. We use it at
		 * times when we want to postpone redraws. By stuffing
		 * in a control-o, redraws get suspended until the editor
		 * gets back around to processing input.
		 */
		break;

	case CTRL('G'):
		CLEAROP;
		fileinfo();
		break;

	case K_CGRAVE:			/* shorthand command */
		CLEAROP;
		stuffin(":e #\n");
		break;

	case 'Z':			/* write, if changed, and exit */
		if (vgetc() != 'Z') {
			beep();
			break;
		}
		doxit();
		break;

	/*
	 * Macro evaluates true if char 'c' is a valid identifier character
	 */
#	define	IDCHAR(c)	(isalpha(c) || isdigit(c) || (c) == '_')

	case CTRL(']'):			/* :ta to current identifier */
		CLEAROP;
		{
			char	ch;
			LPTR	save;

			save = *Curschar;
			/*
			 * First back up to start of identifier. This
			 * doesn't match the real vi but I like it a
			 * little better and it shouldn't bother anyone.
			 */
			ch = gchar(Curschar);
			while (IDCHAR(ch)) {
				if (!oneleft())
					break;
				ch = gchar(Curschar);
			}
			if (!IDCHAR(ch))
				oneright();

			stuffin(":ta ");
			/*
			 * Now grab the chars in the identifier
			 */
			ch = gchar(Curschar);
			while (IDCHAR(ch)) {
				stuffin(mkstr(ch));
				if (!oneright())
					break;
				ch = gchar(Curschar);
			}
			stuffin("\n");

			*Curschar = save;	/* restore, in case of error */
		}
		break;

	/*
	 * Character motion commands
	 */
	case 'G':
		mtype = MLINE;
		*Curschar = *gotoline(Prenum);
		beginline(TRUE);
		break;

	case 'H':
		mtype = MLINE;
		*Curschar = *Topchar;
		for (n = Prenum; n && onedown(1) ;n--)
			;
		beginline(TRUE);
		break;

	case 'M':
		mtype = MLINE;
		*Curschar = *Topchar;
		for (n = 0; n < Rows/2 && onedown(1) ;n++)
			;
		beginline(TRUE);
		break;

	case 'L':
		mtype = MLINE;
		*Curschar = *prevline(Botchar);
		for (n = Prenum; n && oneup(1) ;n--)
			;
		beginline(TRUE);
		break;

	case 'l':
	case K_RARROW:
	case ' ':
		mtype = MCHAR;
		mincl = FALSE;
		n = DEFAULT1(Prenum);
		while (n--) {
			if ( ! oneright() )
				beep();
		}
		set_want_col = TRUE;
		break;

	case 'h':
	case K_LARROW:
	case CTRL('H'):
		mtype = MCHAR;
		mincl = FALSE;
		n = DEFAULT1(Prenum);
		while (n--) {
			if ( ! oneleft() )
				beep();
		}
		set_want_col = TRUE;
		break;

	case '-':
		flag = TRUE;
		/* fall through */

	case 'k':
	case K_UARROW:
	case CTRL('P'):
		mtype = MLINE;
		if ( ! oneup(DEFAULT1(Prenum)) )
			beep();
		if (flag)
			beginline(TRUE);
		break;

	case '+':
	case CR:
	case NL:
		flag = TRUE;
		/* fall through */

	case 'j':
	case K_DARROW:
	case CTRL('N'):
		mtype = MLINE;
		if ( ! onedown(DEFAULT1(Prenum)) )
			beep();
		if (flag)
			beginline(TRUE);
		break;

	/*
	 * This is a strange motion command that helps make operators
	 * more logical. It is actually implemented, but not documented
	 * in the real 'vi'. This motion command actually refers to "the
	 * current line". Commands like "dd" and "yy" are really an alternate
	 * form of "d_" and "y_". It does accept a count, so "d3_" works to
	 * delete 3 lines.
	 */
	case '_':
	lineop:
		mtype = MLINE;
		onedown(DEFAULT1(Prenum)-1);
		break;

	case '|':
		mtype = MCHAR;
		mincl = TRUE;
		beginline(FALSE);
		if (Prenum > 0)
			*Curschar = *coladvance(Curschar, Prenum-1);
		Curswant = Prenum - 1;
		break;
		
	/*
	 * Word Motions
	 */

	case 'B':
		type = 1;
		/* fall through */

	case 'b':
		mtype = MCHAR;
		mincl = FALSE;
		set_want_col = TRUE;
		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
			LPTR	*pos;

			if ((pos = bck_word(Curschar, type)) == NULL) {
				beep();
				CLEAROP;
				break;
			} else
				*Curschar = *pos;
		}
		break;

	case 'W':
		type = 1;
		/* fall through */

	case 'w':
		/*
		 * This is a little strange. To match what the real vi
		 * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
		 * This seems impolite at first, but it's really more
		 * what we mean when we say 'cw'.
		 */
		if (operator == CHANGE)
			goto doecmd;

		mtype = MCHAR;
		mincl = FALSE;
		set_want_col = TRUE;
		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
			LPTR	*pos;

			if ((pos = fwd_word(Curschar, type)) == NULL) {
				beep();
				CLEAROP;
				break;
			} else
				*Curschar = *pos;
		}
		break;

	case 'E':
		type = 1;
		/* fall through */

	case 'e':
	doecmd:
		mtype = MCHAR;
		mincl = TRUE;
		set_want_col = TRUE;
		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
			LPTR	*pos;

			/*
			 * The first motion gets special treatment if we're
			 * do a 'CHANGE'.
			 */
			if (n == DEFAULT1(Prenum))
				pos = end_word(Curschar,type,operator==CHANGE);
			else
				pos = end_word(Curschar, type, FALSE);

			if (pos == NULL) {
				beep();
				CLEAROP;
				break;
			} else
				*Curschar = *pos;
		}
		break;

	case '$':
		mtype = MCHAR;
		mincl = TRUE;
		while ( oneright() )
			;
		Curswant = 999;		/* so we stay at the end */
		break;

	case '^':

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -