ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/normal.c

This is normal.c in view mode; [Download] [Up]

/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * Contains the main routine for processing characters in command mode.
 * Communicates closely with the code in ops.c to handle the operators.
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"

/*
 * The Visual area is remembered for reselection.
 */
static int		resel_VIsual_mode = NUL;		/* 'v', 'V', or Ctrl-V */
static linenr_t	resel_VIsual_line_count;		/* number of lines */
static colnr_t	resel_VIsual_col;				/* nr of cols or end col */
static int		search_dont_set_mark = FALSE;	/* for "*" and "#" */

/*
 * Operator characters; The order must correspond to the defines in vim.h!
 */
static char_u *op_chars = (char_u *)"dyc<>!~=Q:UuJq";

static void		op_colon __ARGS((OPARG *oap, int VIsual_was_active));
#ifdef USE_MOUSE
static void		find_start_of_word __ARGS((FPOS *));
static void		find_end_of_word __ARGS((FPOS *));
static int		get_mouse_class __ARGS((int));
#endif
static void		prep_redo_cmd __ARGS((CMDARG *cap));
static void		prep_redo __ARGS((int regname, long, int, int, int, int));
static int		checkclearop __ARGS((OPARG *oap));
static int		checkclearopq __ARGS((OPARG *oap));
static void		clearop __ARGS((OPARG *oap));
static void		clearopbeep __ARGS((OPARG *oap));
static void		del_from_showcmd __ARGS((int));

static void		do_gd __ARGS((OPARG *oap, int nchar));
static int		screengo __ARGS((OPARG *oap, int dir, long dist));
static void		do_zet __ARGS((CMDARG *cap));
static void		do_ident __ARGS((CMDARG *cap, int whole_word));
static void		do_scroll __ARGS((CMDARG *cap));
static void		do_right __ARGS((CMDARG *cap));
static int		do_left __ARGS((CMDARG *cap));
static void		do_gotofile __ARGS((CMDARG *cap));
static void		do_csearch __ARGS((CMDARG *cap, int dir, int type));
static void		do_brackets __ARGS((CMDARG *cap, int dir));
static void		do_percent __ARGS((CMDARG *cap));
static int		do_replace __ARGS((CMDARG *cap));
static void		switch_visual __ARGS((void));
static void		do_swapchar __ARGS((CMDARG *cap));
static void		do_cursormark __ARGS((CMDARG *cap, int flag, FPOS *pos));
static void		do_visop __ARGS((CMDARG *cap));
static void		do_optrans __ARGS((CMDARG *cap));
static void		do_gomark __ARGS((CMDARG *cap, int flag));
static void		do_pcmark __ARGS((CMDARG *cap));
static void		do_regname __ARGS((CMDARG *cap, linenr_t opnum));
static void		do_visual __ARGS((CMDARG *cap));
static int		do_g_cmd __ARGS((CMDARG *cap));
static int		do_opencmd __ARGS((CMDARG *cap));
static void		do_operator __ARGS((CMDARG *cap));
static void		do_lineop __ARGS((CMDARG *cap));
static void		do_wordcmd __ARGS((CMDARG *cap, int type));
static void		do_goto __ARGS((OPARG *oap, long lnum));
static void		do_esc __ARGS((CMDARG *oap, linenr_t opnum));

/*
 * normal
 *
 * Execute a command in normal mode.
 *
 * This is basically a big switch with the cases arranged in rough categories
 * in the following order:
 *
 *	  0. Macros (q, @)
 *	  1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
 *	  2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
 *	  3. Cursor motions (G, H, M, L, l, K_RIGHT,  , h, K_LEFT, ^H, k, K_UP,
 *	     ^P, +, CR, LF, j, K_DOWN, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
 *	  4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
 *	  5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
 *	  6. Inserts (A, a, I, i, o, O, R)
 *	  7. Operators (~, d, c, y, >, <, !, =, Q)
 *	  8. Abbreviations (x, X, D, C, s, S, Y, &)
 *	  9. Marks (m, ', `, ^O, ^I)
 *	 10. Register name setting ('"')
 *	 11. Visual (v, V, ^V)
 *   12. Suspend (^Z)
 *   13. Window commands (^W)
 *   14. extended commands (starting with 'g')
 *   15. mouse click
 *   16. scrollbar movement
 *   17. The end (ESC)
 */

	void
normal_cmd(oap)
	OPARG		*oap;
{
	static linenr_t	opnum = 0;				/* count before an operator */
	CMDARG			ca;						/* command arguments */
	int				c;
	long 			n = 0;					/* init for GCC */
	int				flag = FALSE;
	int 			type = 0;				/* type of operation */
	int 			dir = FORWARD;			/* search direction */
	int				finish_op;
	char_u			*searchbuff = NULL;		/* buffer for search string */
	int				command_busy = FALSE;
	int				ctrl_w = FALSE;			/* got CTRL-W command */
	int				old_col = curwin->w_curswant;
	int				dont_adjust_op_end = FALSE;
	FPOS			old_pos;				/* cursor position before command */

	vim_memset(&ca, 0, sizeof(ca));
	ca.oap = oap;

	/*
	 * 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 = (oap->op_type != OP_NOP);

	if (!finish_op && !oap->regname)
		opnum = 0;

	State = NORMAL_BUSY;
	c = vgetc();
#ifdef HAVE_LANGMAP
	LANGMAP_ADJUST(c, TRUE);
#endif
	if (c == NUL)
		c = K_ZERO;
	(void)add_to_showcmd(c, FALSE);

getcount:
	/* Pick up any leading digits and compute ca.count0 */
	while (	   (c >= '1' && c <= '9')
			|| (ca.count0 != 0 && (c == K_DEL || c == '0')))
	{
		if (c == K_DEL)
		{
			ca.count0 /= 10;
			del_from_showcmd(4);		/* delete the digit and ~@% */
		}
		else
			ca.count0 = ca.count0 * 10 + (c - '0');
		if (ca.count0 < 0)			/* got too large! */
			ca.count0 = 999999999;
		if (ctrl_w)
		{
			++no_mapping;
			++allow_keys;				/* no mapping for nchar, but keys */
		}
		c = vgetc();
#ifdef HAVE_LANGMAP
		LANGMAP_ADJUST(c, TRUE);
#endif
		if (ctrl_w)
		{
			--no_mapping;
			--allow_keys;
		}
		(void)add_to_showcmd(c, FALSE);
	}

/*
 * If we got CTRL-W there may be a/another count
 */
	if (c == Ctrl('W') && !ctrl_w && oap->op_type == OP_NOP)
	{
		ctrl_w = TRUE;
		opnum = ca.count0;					/* remember first count */
		ca.count0 = 0;
		++no_mapping;
		++allow_keys;						/* no mapping for nchar, but keys */
		c = vgetc();						/* get next character */
#ifdef HAVE_LANGMAP
		LANGMAP_ADJUST(c, TRUE);
#endif
		--no_mapping;
		--allow_keys;
		(void)add_to_showcmd(c, FALSE);
		goto getcount;						/* jump back */
	}

	ca.cmdchar = c;

	/*
	 * If we're in the middle of an operator (including after entering a yank
	 * buffer with '"') AND we had a count before the
	 * operator, then that count overrides the current value of ca.count0.
	 * What * this means effectively, is that commands like "3dw" get turned
	 * into "d3w" which makes things fall into place pretty neatly.
	 * If you give a count before AND after the operator, they are multiplied.
	 */
	if (opnum != 0)
	{
			if (ca.count0)
				ca.count0 *= opnum;
			else
				ca.count0 = opnum;
	}

	/*
	 * Always remember the count.  It will be set to zero (on the next call,
	 * above) when there is no pending operator.
	 */
	opnum = ca.count0;

	ca.count1 = (ca.count0 == 0 ? 1 : ca.count0);

	/*
	 * Get an additional character if we need one.
	 * For CTRL-W we already got it when looking for a count.
	 */
	if (ctrl_w)
	{
		ca.nchar = ca.cmdchar;
		ca.cmdchar = Ctrl('W');
	}
	else if (	(oap->op_type == OP_NOP
				&& vim_strchr((char_u *)"@zm\"", ca.cmdchar) != NULL)
			|| (oap->op_type == OP_NOP
				&& !VIsual_active
				&& vim_strchr((char_u *)"rZ", ca.cmdchar) != NULL)
			|| vim_strchr((char_u *)"tTfF[]g'`", ca.cmdchar) != NULL
			|| (ca.cmdchar == 'q'
				&& !Recording
				&& !Exec_reg))
	{
		++no_mapping;
		++allow_keys;			/* no mapping for nchar, but allow key codes */
#ifdef USE_GUI
		if (ca.cmdchar == 'r' && gui.in_use)
		{
			State = REPLACE;	/* pretend Replace mode, for cursor shape */
			gui_update_cursor(TRUE);
		}
#endif
		ca.nchar = vgetc();
#ifdef USE_GUI
		State = NORMAL_BUSY;
#endif
#ifdef HAVE_LANGMAP
		/* adjust chars > 127, except after tTfFr command */
		LANGMAP_ADJUST(ca.nchar,
						   vim_strchr((char_u *)"tTfFr", ca.cmdchar) == NULL);
#endif
#ifdef RIGHTLEFT
		/* adjust Hebrew mapped char */
		if (p_hkmap && vim_strchr((char_u *)"tTfFr", ca.cmdchar) && KeyTyped)
			ca.nchar = hkmap(ca.nchar);
#endif
		--no_mapping;
		--allow_keys;
		(void)add_to_showcmd(ca.nchar, FALSE);
	}

	/*
	 * flush the showcmd characters onto the screen so we can see them while
	 * the command is being executed
	 */
	if (p_sc)
		flushbuf();

	State = NORMAL;
	if (ca.nchar == ESC)
	{
		clearop(oap);
		goto normal_end;
	}
	msg_didout = FALSE;		/* don't scroll screen up for normal command */
	msg_col = 0;
	old_pos = curwin->w_cursor;			/* remember where cursor was */

#ifdef RIGHTLEFT
	if (curwin->w_p_rl && KeyTyped)		/* invert horizontal operations */
		switch (ca.cmdchar)
		{
			case 'l':       ca.cmdchar = 'h'; break;
			case K_RIGHT:	ca.cmdchar = K_LEFT; break;
			case 'h':		ca.cmdchar = 'l'; break;
			case K_LEFT:	ca.cmdchar = K_RIGHT; break;
			case '>':		ca.cmdchar = '<'; break;
			case '<':		ca.cmdchar = '>'; break;
		}
#endif

/*
 * Generally speaking, every command below should either clear any pending
 * operator (with *clearop*()), or set the motion type variable
 * oap->motion_type.
 *
 * When a cursor motion command is made, it is marked as being a character or
 * line oriented motion.  Then, if an operator is in effect, the operation
 * becomes character or line oriented accordingly.
 */
/*
 * Variables available here:
 * ca.cmdchar	command character
 * ca.nchar		extra command character
 * ca.count0	count before command (0 if no count given)
 * ca.count1	count before command (1 if no count given)
 * oap			Operator Arguments (same as ca.oap)
 * flag			is FALSE, use as you like.
 * dir			is FORWARD, use as you like.
 */
	switch (ca.cmdchar)
	{
/*
 * 0: Macros
 */
	case 'q': 		/* (stop) recording into a named register */
		if (checkclearop(oap))
			break;
						/* command is ignored while executing a register */
		if (!Exec_reg && do_record(ca.nchar) == FAIL)
			clearopbeep(oap);
		break;

	case '@':			/* execute a named register */
		if (checkclearop(oap))
			break;
		while (ca.count1--)
		{
			if (do_execreg(ca.nchar, FALSE, FALSE) == FAIL)
			{
				clearopbeep(oap);
				break;
			}
		}
		break;

/*
 * 1: Screen positioning commands
 */
	case Ctrl('D'):
		flag = TRUE;

	case Ctrl('U'):
		if (	   (ca.cmdchar == Ctrl('U')
					&& curwin->w_cursor.lnum == 1)
				|| (ca.cmdchar == Ctrl('D')
					&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count))
			clearopbeep(oap);
		else
		{
			if (checkclearop(oap))
				break;
			halfpage(flag, ca.count0);
		}
		break;

	case Ctrl('B'):
	case K_S_UP:
	case K_PAGEUP:
	case K_KPAGEUP:
		dir = BACKWARD;

	case Ctrl('F'):
	case K_S_DOWN:
	case K_PAGEDOWN:
	case K_KPAGEDOWN:
		if (checkclearop(oap))
			break;
		(void)onepage(dir, ca.count1);
		break;

	case Ctrl('E'):
		if (checkclearop(oap))
			break;
		scrollup(ca.count1);
		if (p_so)
			cursor_correct();
		coladvance(curwin->w_curswant);
		update_screen(VALID);
		break;

	case Ctrl('Y'):
		if (checkclearop(oap))
			break;
		scrolldown(ca.count1);
		if (p_so)
			cursor_correct();
		coladvance(curwin->w_curswant);
		update_screen(VALID);
		break;

	case 'z':
		if (!checkclearop(oap))
			do_zet(&ca);
		break;

/*
 * 2: Control commands
 */
	case ':':
	    if (VIsual_active)
		{
			do_operator(&ca);
			break;
		}
		if (checkclearop(oap))
			break;
		/*
		 * translate "count:" into ":.,.+(count - 1)"
		 */
		if (ca.count0)
		{
			stuffReadbuff((char_u *)".");
			if (ca.count0 > 1)
			{
				stuffReadbuff((char_u *)",.+");
				stuffnumReadbuff((long)ca.count0 - 1L);
			}
		}
		do_cmdline(NULL, getexline, NULL, 0);
		break;

	case 'Q':
		/*
		 * Ignore 'Q' in Visual mode, just give a beep.
		 */
		if (VIsual_active)
			vim_beep();
		else
			do_exmode();
		break;

	case K_HELP:
	case K_F1:
		if (!checkclearopq(oap))
			do_help((char_u *)"");
		break;

	case Ctrl('L'):
		if (!checkclearop(oap))
		{
#ifdef BEBOX
			/*
			 * Right now, the BeBox doesn't seem to have an easy way to detect
			 * window resizing, so we cheat and make the user detect it
			 * manually with CTRL-L instead
			 */
			mch_get_winsize();
#endif
			update_screen(CLEAR);
		}
		break;

	case Ctrl('G'):
		/* print full name if count given or :cd used */
		if (!checkclearop(oap))
			fileinfo((int)ca.count0, FALSE, FALSE);
		break;

	case K_CCIRCM:			/* CTRL-^, short for ":e #" */
		if (!checkclearopq(oap))
			(void)buflist_getfile((int)ca.count0, (linenr_t)0,
												GETF_SETMARK|GETF_ALT, FALSE);
		break;

		/*
		 * "ZZ": write if changed, and exit window
		 * "ZQ": quit window (Elvis compatible)
		 */
	case 'Z':
		if (!checkclearopq(oap))
		{
			if (ca.nchar == 'Z')
				stuffReadbuff((char_u *)":x\n");
			else if (ca.nchar == 'Q')
				stuffReadbuff((char_u *)":q!\n");
			else
				clearopbeep(oap);
		}
		break;

	case 163:					/* the pound sign, '#' for English keyboards */
		ca.cmdchar = '#';
		/*FALLTHROUGH*/

	case Ctrl(']'):			/* :ta to current identifier */
	case 'K':					/* run program for current identifier */
	case '*': 				/* / to current identifier or string */
	case '#': 				/* ? to current identifier or string */
		do_ident(&ca, TRUE);
		break;

	case Ctrl('T'):			/* backwards in tag stack */
		if (!checkclearopq(oap))
			do_tag((char_u *)"", 2, (int)ca.count1, FALSE);
		break;

/*
 * Cursor motions
 */
	case 'G':
		do_goto(oap, ca.count0 == 0 ? (long)curbuf->b_ml.ml_line_count
									: ca.count0);
		break;

	case 'H':
	case 'M':
	case 'L':
		oap->motion_type = MLINE;
		setpcmark();
		do_scroll(&ca);
		cursor_correct();		/* correct for 'so' */
		beginline(MAYBE);
		break;

	case 'l':
	case K_RIGHT:
	case ' ':
		do_right(&ca);
		break;

	case 'h':
	case K_LEFT:
	case K_BS:
	case Ctrl('H'):
		dont_adjust_op_end = do_left(&ca);
		break;

	case '-':
		flag = TRUE;
		/* FALLTHROUGH */

	case 'k':
	case K_UP:
	case Ctrl('P'):
		oap->motion_type = MLINE;
		if (cursor_up(ca.count1, oap->op_type != OP_NOP) == FAIL)
			clearopbeep(oap);
		else if (flag)
			beginline(TRUE);
		break;

	case '+':
	case CR:
		flag = TRUE;
		/* FALLTHROUGH */

	case 'j':
	case K_DOWN:
	case Ctrl('N'):
	case NL:
		oap->motion_type = MLINE;
		if (cursor_down(ca.count1, oap->op_type != OP_NOP) == FAIL)
			clearopbeep(oap);
		else 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 '_':
		do_lineop(&ca);
		break;

	case K_HOME:
	case K_KHOME:
		if ((mod_mask & MOD_MASK_CTRL))		/* CTRL-HOME = goto line 1 */
		{
			do_goto(oap, 1L);
			break;
		}
		ca.count0 = 1;
		/* FALLTHROUGH */

	case '|':
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		beginline(FALSE);
		if (ca.count0 > 0)
		{
			coladvance((colnr_t)(ca.count0 - 1));
			curwin->w_curswant = (colnr_t)(ca.count0 - 1);
		}
		else
			curwin->w_curswant = 0;
		/* keep curswant at the column where we wanted to go, not where
				we ended; differs is line is too short */
		curwin->w_set_curswant = FALSE;
		break;

	/*
	 * Word Motions
	 */
	case 'B':
		type = 1;
		/* FALLTHROUGH */

	case 'b':
	case K_S_LEFT:
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		curwin->w_set_curswant = TRUE;
		if (bck_word(ca.count1, type, FALSE) == FAIL)
			clearopbeep(oap);
		break;

	case 'E':
		type = TRUE;
		/* FALLTHROUGH */

	case 'e':
		oap->inclusive = TRUE;
		do_wordcmd(&ca, type);
		break;

	case 'W':
		type = TRUE;
		/* FALLTHROUGH */

	case 'w':
	case K_S_RIGHT:
		oap->inclusive = FALSE;
		do_wordcmd(&ca, type);
		break;

	case K_END:
	case K_KEND:
		if ((mod_mask & MOD_MASK_CTRL))		/* CTRL-END = goto last line */
		{
			do_goto(oap, curbuf->b_ml.ml_line_count);
			break;
		}
		/* FALLTHROUGH */

	case '$':
		oap->motion_type = MCHAR;
		oap->inclusive = TRUE;
		curwin->w_curswant = MAXCOL;				/* so we stay at the end */
		if (cursor_down((long)(ca.count1 - 1), oap->op_type != OP_NOP) == FAIL)
		{
			clearopbeep(oap);
			break;
		}
		break;

	case '^':
		flag = TRUE;
		/* FALLTHROUGH */

	case '0':
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		beginline(flag);
		break;

/*
 * 4: Searches
 */
	case '?':
	case '/':
		if ((searchbuff = getcmdline(ca.cmdchar, ca.count1, 0)) == NULL)
		{
			clearop(oap);
			break;
		}
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		curwin->w_set_curswant = TRUE;

		n = do_search(oap, ca.cmdchar, searchbuff, ca.count1,
				(search_dont_set_mark ? 0 : SEARCH_MARK) |
									   SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
		if (n == 0)
			clearop(oap);
		else if (n == 2)
			oap->motion_type = MLINE;
		search_dont_set_mark = FALSE;

		/* "/$" will put the cursor after the end of the line, may need to
		 * correct that here */
		adjust_cursor();
		break;

	case 'N':
		flag = SEARCH_REV;

	case 'n':
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		curwin->w_set_curswant = TRUE;
		if (!do_search(oap, 0, NULL, ca.count1,
				  SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG | flag))
			clearop(oap);

		/* "/$" will put the cursor after the end of the line, may need to
		 * correct that here */
		adjust_cursor();
		break;

		/*
		 * Character searches
		 */
	case 'T':
		dir = BACKWARD;
		/* FALLTHROUGH */

	case 't':
		do_csearch(&ca, dir, TRUE);
		break;

	case 'F':
		dir = BACKWARD;
		/* FALLTHROUGH */

	case 'f':
		do_csearch(&ca, dir, FALSE);
		break;

	case ',':
		flag = 1;
		/* FALLTHROUGH */

	case ';':
		/* ca.nchar == NUL, thus repeat previous search */
		do_csearch(&ca, flag, FALSE);
		break;

		/*
		 * section or C function searches
		 */
	case '[':
		dir = BACKWARD;
		/* FALLTHROUGH */

	case ']':
		do_brackets(&ca, dir);
		break;

	case '%':
		do_percent(&ca);
		break;

	case '(':
		dir = BACKWARD;
		/* FALLTHROUGH */

	case ')':
		oap->motion_type = MCHAR;
		if (ca.cmdchar == ')')
			oap->inclusive = FALSE;
		else
			oap->inclusive = TRUE;
		curwin->w_set_curswant = TRUE;

		if (findsent(dir, ca.count1) == FAIL)
			clearopbeep(oap);
		break;

	case '{':
		dir = BACKWARD;
		/* FALLTHROUGH */

	case '}':
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		curwin->w_set_curswant = TRUE;
		if (!findpar(oap, dir, ca.count1, NUL, FALSE))
			clearopbeep(oap);
		break;

/*
 * 5: Edits
 */
	case '.':				/* redo command */
		if (!checkclearopq(oap))
		{
			/*
			 * if restart_edit is TRUE, the last but one command is repeated
			 * instead of the last command (inserting text). This is used for
			 * CTRL-O <.> in insert mode
			 */
			if (start_redo(ca.count0, restart_edit && !arrow_used) == FAIL)
				clearopbeep(oap);
		}
		break;

	case 'u':				/* undo */
	    if (VIsual_active ||
					 oap->op_type == vim_strchr(op_chars, 'u') - op_chars + 1)
		{
			do_operator(&ca);
			break;
		}
		/* FALLTHROUGH */

	case K_UNDO:
		if (!checkclearopq(oap))
		{
			u_undo((int)ca.count1);
			curwin->w_set_curswant = TRUE;
		}
		break;

	case Ctrl('R'):		/* undo undo */
		if (!checkclearopq(oap))
		{
			u_redo((int)ca.count1);
			curwin->w_set_curswant = TRUE;
		}
		break;

	case 'U':				/* Undo line */
		/* In visual mode and typing "gUU" triggers an operator */
	    if (VIsual_active ||
					 oap->op_type == vim_strchr(op_chars, 'U') - op_chars + 1)
		{
			do_operator(&ca);
			break;
		}
		if (!checkclearopq(oap))
		{
			u_undoline();
			curwin->w_set_curswant = TRUE;
		}
		break;

	case 'r':
	    if (VIsual_active)
		{
			ca.cmdchar = 'c';
			do_operator(&ca);
			break;
		}
		if (!checkclearop(oap))
			command_busy = do_replace(&ca);
		break;

	case 'J':
	    if (VIsual_active)		/* join the visual lines */
		{
			do_operator(&ca);
			break;
		}
		if (!checkclearop(oap))
		{
			if (ca.count0 <= 1)
				ca.count0 = 2; 			/* default for join is two lines! */
			if (curwin->w_cursor.lnum + ca.count0 - 1 >
												   curbuf->b_ml.ml_line_count)
			{
				clearopbeep(oap);			/* beyond last line */
				break;
			}

			prep_redo_cmd(&ca);
			do_do_join(ca.count0, TRUE, TRUE);
		}
		break;

	case 'P':
		dir = BACKWARD;
		/* FALLTHROUGH */

	case 'p':
		/*
		 * 'P' after an operator or with Visual: Set current block.
		 * 'p' after an operator or with Visual: Set current paragraph.
		 */
	    if (oap->op_type != OP_NOP || VIsual_active)
		{
			if (ca.cmdchar == 'P')
			{
				if (current_block(oap, '{', ca.count1) == FAIL)
					clearopbeep(oap);
			}
			else
			{
				if (current_par(oap, ca.cmdchar, ca.count1) == FAIL)
					clearopbeep(oap);
			}
			curwin->w_set_curswant = TRUE;
		}
		else
		{
			prep_redo_cmd(&ca);
			do_put(oap->regname, dir, ca.count1, FALSE);
		}
		break;

	case Ctrl('A'):			/* add to number */
	case Ctrl('X'):			/* subtract from number */
		if (!checkclearopq(oap))
		{
			if (do_addsub((int)ca.cmdchar, ca.count1) == OK)
				prep_redo_cmd(&ca);
		}
		break;

/*
 * 6: Inserts
 */
	case 'A':
	  	type = 1;
		/* FALLTHROUGH */

	case 'a':
		if (oap->op_type != OP_NOP || VIsual_active)
		{
			if (current_word(oap, ca.count1, type) == FAIL)
				clearopbeep(oap);
			curwin->w_set_curswant = TRUE;
		}
		else
		{
			if (ca.cmdchar == 'A')
			{
				curwin->w_set_curswant = TRUE;
				while (oneright() == OK)
					;
			}

			if (u_save_cursor() == OK)
			{
				/* Works just like an 'i'nsert on the next character. */
				if (!lineempty(curwin->w_cursor.lnum))
					inc_cursor();
				command_busy = edit(ca.cmdchar, FALSE, ca.count1);
			}
		}
		break;

	case 'I':
		if (checkclearopq(oap))
			break;
		beginline(TRUE);
		/* FALLTHROUGH */

	case 'i':
	case K_INS:
		if (!checkclearopq(oap))
		{
			if (u_save_cursor() == OK)
				command_busy = edit(ca.cmdchar, FALSE, ca.count1);
		}
		break;

	case 'o':
	  	if (VIsual_active)	/* switch start and end of visual */
		{
			switch_visual();
			break;
		}
		/* FALLTHROUGH */

	case 'O':
		command_busy = do_opencmd(&ca);
		break;

	case 'R':
	    if (VIsual_active)
		{
			ca.cmdchar = 'c';
			VIsual_mode = 'V';
			do_operator(&ca);
			break;
		}
		if (!checkclearopq(oap))
		{
			if (u_save_cursor() == OK)
				command_busy = edit('R', FALSE, ca.count1);
		}
		break;

/*
 * 7: Operators
 */
	case '~': 		/* swap case */
		/*
		 * if tilde is not an operator and Visual is off: swap case
		 * of a single character
		 */
		if (	   !p_to
				&& !VIsual_active
				&& oap->op_type != vim_strchr(op_chars, '~') - op_chars + 1)
		{
			do_swapchar(&ca);
			break;
		}
		/*FALLTHROUGH*/

	case 'd':
	case 'c':
	case 'y':
	case '>':
	case '<':
	case '!':
	case '=':
		do_operator(&ca);
		break;

/*
 * 8: Abbreviations
 */

	case 'S':
	case 's':
		/*
		 * 's' or 'S' with an operator: Operate on sentence or section.
		 */
	    if (oap->op_type != OP_NOP || VIsual_active)
		{
			if (ca.cmdchar == 's')							/* sentence */
				flag = current_sent(oap, ca.count1);
			else									/* block with () */
				flag = current_block(oap, '(', ca.count1);
			if (flag == FAIL)
				clearopbeep(oap);
			curwin->w_set_curswant = TRUE;
			break;
		}
		/* FALLTHROUGH */

	case K_DEL:
	case 'Y':
	case 'D':
	case 'C':
	case 'x':
	case 'X':
		if (ca.cmdchar == K_DEL)
			ca.cmdchar = 'x';			/* DEL key behaves like 'x' */

		/* with Visual these commands are operators */
	  	if (VIsual_active)
		{
			do_visop(&ca);
			break;
		}
		/* FALLTHROUGH */

	case '&':
		do_optrans(&ca);
		break;

/*
 * 9: Marks
 */

	case 'm':
		if (!checkclearop(oap))
		{
			if (setmark(ca.nchar) == FAIL)
				clearopbeep(oap);
		}
		break;

	case '\'':
		flag = TRUE;
		/* FALLTHROUGH */

	case '`':
		do_gomark(&ca, flag);
		break;

	case Ctrl('O'):			/* goto older pcmark */
		ca.count1 = -ca.count1;
		/* FALLTHROUGH */

	case Ctrl('I'):			/* goto newer pcmark */
		do_pcmark(&ca);
		break;

/*
 * 10. Register name setting
 */
	case '"':
		do_regname(&ca, opnum);
		break;

/*
 * 11. Visual
 */
	case 'v':
	case 'V':
	case Ctrl('V'):
		if (!checkclearop(oap))
			do_visual(&ca);
		break;

/*
 * 12. Suspend
 */

	case Ctrl('Z'):
		clearop(oap);
		if (VIsual_active)
			end_visual_mode();				/* stop Visual */
		stuffReadbuff((char_u *)":st\r");	/* with autowrite */
		break;

/*
 * 13. Window commands
 */

	case Ctrl('W'):
		if (!checkclearop(oap))
			do_window(ca.nchar, ca.count0);		/* everything is in window.c */
		break;

/*
 *   14. extended commands (starting with 'g')
 */
	case 'g':
		command_busy = do_g_cmd(&ca);
		break;

/*
 * 15. mouse click
 */
#ifdef USE_MOUSE
	case K_MIDDLEMOUSE:
	case K_MIDDLEDRAG:
	case K_MIDDLERELEASE:
	case K_LEFTMOUSE:
	case K_LEFTDRAG:
	case K_LEFTRELEASE:
	case K_RIGHTMOUSE:
	case K_RIGHTDRAG:
	case K_RIGHTRELEASE:
		(void)do_mouse(oap, ca.cmdchar, BACKWARD, ca.count1, FALSE);
		break;

	case K_IGNORE:
		break;
#endif

#ifdef USE_GUI
/*
 * 16. scrollbar movement
 */
	case K_SCROLLBAR:
		if (oap->op_type != OP_NOP)
			clearopbeep(oap);

		/* Even if an operator was pending, we still want to scroll */
		gui_do_scroll();
		break;

	case K_HORIZ_SCROLLBAR:
		if (oap->op_type != OP_NOP)
			clearopbeep(oap);

		/* Even if an operator was pending, we still want to scroll */
		gui_do_horiz_scroll();
		break;
#endif

/*
 * 17. The end
 */
	case ESC:
		do_esc(&ca, opnum);
		break;

	default:					/* not a known command */
		clearopbeep(oap);
		break;

	}	/* end of switch on command character */

/*
 * if we didn't start or finish an operator, reset oap->regname, unless we
 * need it later.
 */
	if (!finish_op && !oap->op_type &&
					   vim_strchr((char_u *)"\"DCYSsXx.", ca.cmdchar) == NULL)
		oap->regname = 0;

/*
 * If an operation is pending, handle it...
 */
	do_pending_operator(&ca, finish_op, searchbuff,
						   &command_busy, old_col, FALSE, dont_adjust_op_end);

	/*
	 * Wait when a message is displayed that will be overwritten by the mode
	 * message.
	 * In Visual mode and with "^O" in Insert mode, a short message will be
	 * overwritten by the mode message.  Wait a bit, until a key is hit.
	 * In Visual mode, it's more important to keep the Visual area updated
	 * than keeping a message (e.g. from a /pat search).
	 * Only do this if the command was typed, not from a mapping.
	 * Also wait a bit after an error message, e.g. for "^O:".
	 * Don't redraw the screen, it would remove the message.
	 */
	if (	   ((p_smd
					&& ((VIsual_active
							&& old_pos.lnum == curwin->w_cursor.lnum
							&& old_pos.col == curwin->w_cursor.col)
						|| restart_edit)
					&& (clear_cmdline
						|| redraw_cmdline)
					&& msg_didany
					&& KeyTyped)
				|| (restart_edit
					&& !VIsual_active
					&& (msg_scroll
						|| emsg_on_display
#ifdef SLEEP_IN_EMSG
						|| need_sleep
#endif
											)))
			&& oap->regname == 0
			&& !command_busy
			&& stuff_empty()
			&& oap->op_type == OP_NOP)
	{
		setcursor();
		flushbuf();
		if (msg_scroll || emsg_on_display
#ifdef SLEEP_IN_EMSG
											|| need_sleep
#endif
															)
		ui_delay(1000L, TRUE);			/* wait at least one second */
		ui_delay(10000L, FALSE);		/* wait up to ten seconds */

		msg_scroll = FALSE;
		emsg_on_display = FALSE;
#ifdef SLEEP_IN_EMSG
		need_sleep = FALSE;
#endif
	}

normal_end:
#ifdef USE_GUI
	if (ca.cmdchar == 'r' && gui.in_use)
		gui_update_cursor(TRUE);
#endif
	if (oap->op_type == OP_NOP && oap->regname == 0)
		clear_showcmd();

	if (	   restart_edit
			&& oap->op_type == OP_NOP
			&& !VIsual_active
			&& !command_busy
			&& stuff_empty()
			&& oap->regname == 0)
		(void)edit(restart_edit, FALSE, 1L);

	if (!search_dont_set_mark)
		checkpcmark();			/* check if we moved since setting pcmark */
	vim_free(searchbuff);

/*
 * Update the other windows for the current buffer if modified has been set in
 * set_Changed() (This should be done more efficiently)
 */
	if (modified)
	{
		update_other_win();
		modified = FALSE;
	}
}

/*
 * Handle an operator after visual mode or when the movement is finished
 */
	void
do_pending_operator(cap, finish_op, searchbuff,
						  command_busy, old_col, gui_yank, dont_adjust_op_end)
	CMDARG			*cap;
	int				finish_op;
	char_u			*searchbuff;
	int				*command_busy;
	int				old_col;
	int				gui_yank;		/* yanking visual area for GUI */
	int				dont_adjust_op_end;
{
	OPARG			*oap = cap->oap;
	FPOS			old_cursor;
	int				VIsual_was_active = VIsual_active;

	/* The visual area is remembered for redo */
	static int		redo_VIsual_mode = NUL;	/* 'v', 'V', or Ctrl-V */
	static linenr_t	redo_VIsual_line_count;		/* number of lines */
	static colnr_t	redo_VIsual_col;		/* number of cols or end column */
	static long		redo_VIsual_count;		/* count for Visual operator */

#ifdef USE_CLIPBOARD
	/*
	 * Yank the visual area into the GUI selection register before we operate
	 * on it and lose it forever.  This could call do_pending_operator()
	 * recursively, but that's OK because gui_yank will be TRUE for the
	 * nested call.  Note also that we call clip_copy_selection() and not
	 * clip_auto_select().  This is because even when 'autoselect' is not set,
	 * if we operate on the text, eg by deleting it, then this is considered to
	 * be an explicit request for it to be put in the global cut buffer, so we
	 * always want to do it here. -- webb
	 */
	if (clipboard.available && oap->op_type != OP_NOP &&
							  !gui_yank && VIsual_active && !redo_VIsual_busy)
		clip_copy_selection();
#endif
	old_cursor = curwin->w_cursor;

	/*
	 * If an operation is pending, handle it...
	 */
	if ((VIsual_active || finish_op) && oap->op_type != OP_NOP)
	{
		oap->is_VIsual = VIsual_active;
		if (oap->op_type != OP_YANK && !VIsual_active)	/* can't redo yank */
		{
			prep_redo(oap->regname, cap->count0, oap->op_prechar,
						op_chars[oap->op_type - 1], cap->cmdchar, cap->nchar);
			if (cap->cmdchar == '/' || cap->cmdchar == '?')	/* was a search */
			{
				/*
				 * If 'cpoptions' does not contain 'r', insert the search
				 * pattern to really repeat the same command.
				 */
				if (vim_strchr(p_cpo, CPO_REDO) == NULL)
					AppendToRedobuff(searchbuff);
				AppendToRedobuff(NL_STR);
			}
		}

		if (redo_VIsual_busy)
		{
			oap->start = curwin->w_cursor;
			curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
			if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
				curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
			VIsual_mode = redo_VIsual_mode;
			if (VIsual_mode == 'v')
			{
				if (redo_VIsual_line_count <= 1)
					curwin->w_cursor.col += redo_VIsual_col - 1;
				else
					curwin->w_cursor.col = redo_VIsual_col;
			}
			if (redo_VIsual_col == MAXCOL)
			{
				curwin->w_curswant = MAXCOL;
				coladvance(MAXCOL);
			}
			cap->count0 = redo_VIsual_count;
			if (redo_VIsual_count != 0)
				cap->count1 = redo_VIsual_count;
			else
				cap->count1 = 1;
		}
		else if (VIsual_active)
		{
			oap->start = VIsual;
			VIsual_end = curwin->w_cursor;
			if (VIsual_mode == 'V')
				oap->start.col = 0;
		}

		/*
		 * Set oap->start to the first position of the operated text, oap->end
		 * to the end of the operated text.  w_cursor is equal to oap->start.
		 */
		if (lt(oap->start, curwin->w_cursor))
		{
			oap->end = curwin->w_cursor;
			curwin->w_cursor = oap->start;
		}
		else
		{
			oap->end = oap->start;
			oap->start = curwin->w_cursor;
		}
		oap->line_count = oap->end.lnum - oap->start.lnum + 1;

		if (VIsual_active || redo_VIsual_busy)
		{
			if (VIsual_mode == Ctrl('V'))		/* block mode */
			{
				colnr_t		start, end;

				oap->block_mode = TRUE;

				getvcol(curwin, &(oap->start),
									  &oap->start_vcol, NULL, &oap->end_vcol);
				if (!redo_VIsual_busy)
				{
					getvcol(curwin, &(oap->end), &start, NULL, &end);
					if (start < oap->start_vcol)
						oap->start_vcol = start;
					if (end > oap->end_vcol)
						oap->end_vcol = end;
				}

				/* if '$' was used, get oap->end_vcol from longest line */
				if (curwin->w_curswant == MAXCOL)
				{
					curwin->w_cursor.col = MAXCOL;
					oap->end_vcol = 0;
					for (curwin->w_cursor.lnum = oap->start.lnum;
							curwin->w_cursor.lnum <= oap->end.lnum;
													  ++curwin->w_cursor.lnum)
					{
						getvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
						if (end > oap->end_vcol)
							oap->end_vcol = end;
					}
				}
				else if (redo_VIsual_busy)
					oap->end_vcol = oap->start_vcol + redo_VIsual_col - 1;
				/*
				 * Correct oap->end.col and oap->start.col to be the
				 * upper-left and lower-right corner of the block area.
				 */
				curwin->w_cursor.lnum = oap->end.lnum;
				coladvance(oap->end_vcol);
				oap->end = curwin->w_cursor;
				curwin->w_cursor = oap->start;
				coladvance(oap->start_vcol);
				oap->start = curwin->w_cursor;
			}

			if (!redo_VIsual_busy)
			{
				/*
				 * Prepare to reselect and redo Visual: this is based on the
				 * size of the Visual text
				 */
				resel_VIsual_mode = VIsual_mode;
				if (curwin->w_curswant == MAXCOL)
					resel_VIsual_col = MAXCOL;
				else if (VIsual_mode == Ctrl('V'))
					resel_VIsual_col = oap->end_vcol - oap->start_vcol + 1;
				else if (oap->line_count > 1)
					resel_VIsual_col = oap->end.col;
				else
					resel_VIsual_col = oap->end.col - oap->start.col + 1;
				resel_VIsual_line_count = oap->line_count;
			}
												/* can't redo yank and : */
			if (oap->op_type != OP_YANK && oap->op_type != OP_COLON)
			{
				prep_redo(oap->regname, 0L, NUL, 'v', oap->op_prechar,
												  op_chars[oap->op_type - 1]);
				redo_VIsual_mode = resel_VIsual_mode;
				redo_VIsual_col = resel_VIsual_col;
				redo_VIsual_line_count = resel_VIsual_line_count;
				redo_VIsual_count = cap->count0;
			}

			/*
			 * Mincl defaults to TRUE.
			 * If op_end is on a NUL (empty line) oap->inclusive becomes FALSE
			 * This makes "d}P" and "v}dP" work the same.
			 */
			oap->inclusive = TRUE;
			if (VIsual_mode == 'V')
				oap->motion_type = MLINE;
			else
			{
				oap->motion_type = MCHAR;
				if (*ml_get_pos(&(oap->end)) == NUL)
					oap->inclusive = FALSE;
			}

			redo_VIsual_busy = FALSE;
			/*
			 * Switch Visual off now, so screen updating does
			 * not show inverted text when the screen is redrawn.
			 * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is
			 * no screen redraw, so it is done here to remove the inverted
			 * part.
			 */
			if (!gui_yank)
			{
				VIsual_active = FALSE;
#ifdef USE_MOUSE
				setmouse();
#endif
				if (p_smd)
					clear_cmdline = TRUE;	/* unshow visual mode later */
				if (oap->op_type == OP_YANK || oap->op_type == OP_COLON ||
													 oap->op_type == OP_FILTER)
					update_curbuf(NOT_VALID);
			}
		}

		curwin->w_set_curswant = TRUE;

		/* oap->empty is set when start and end are the same */
		oap->empty = (oap->motion_type == MCHAR && !oap->inclusive &&
												 equal(oap->start, oap->end));

	/*
	 * If the end of an operator is in column one while oap->motion_type is
	 * MCHAR and oap->inclusive is FALSE, we put op_end after the last character
	 * in the previous line. If op_start is on or before the first non-blank
	 * in the line, the operator becomes linewise (strange, but that's the way
	 * vi does it).
	 */
		if (	   oap->motion_type == MCHAR
				&& oap->inclusive == FALSE
				&& !dont_adjust_op_end
				&& oap->end.col == 0
				&& oap->line_count > 1)
		{
			oap->end_adjusted = TRUE;		/* remember that we did this */
			--oap->line_count;
			--oap->end.lnum;
			if (inindent(0))
				oap->motion_type = MLINE;
			else
			{
				oap->end.col = STRLEN(ml_get(oap->end.lnum));
				if (oap->end.col)
				{
					--oap->end.col;
					oap->inclusive = TRUE;
				}
			}
		}
		else
			oap->end_adjusted = FALSE;

		switch (oap->op_type)
		{
		case OP_LSHIFT:
		case OP_RSHIFT:
			op_shift(oap, TRUE, VIsual_was_active ? (int)cap->count1 : 1);
			break;

		case DO_JOIN:
			if (oap->line_count < 2)
				oap->line_count = 2;
			if (curwin->w_cursor.lnum + oap->line_count - 1 >
												   curbuf->b_ml.ml_line_count)
				beep_flush();
			else
				do_do_join(oap->line_count, TRUE, TRUE);
			break;

		case OP_DELETE:
			if (!oap->empty)
				op_delete(oap);
			break;

		case OP_YANK:
			if (!oap->empty)
				(void)op_yank(oap, FALSE, !gui_yank);
			break;

		case OP_CHANGE:
			*command_busy = op_change(oap);	/* will call edit() */
			break;

		case OP_FILTER:
			if (vim_strchr(p_cpo, CPO_FILTER) != NULL)
				AppendToRedobuff((char_u *)"!\r");	/* use any last used !cmd */
			else
				bangredo = TRUE;	/* do_bang() will put cmd in redo buffer */

		case OP_INDENT:
		case OP_COLON:

#if defined(LISPINDENT) || defined(CINDENT)
			/*
			 * If 'equalprg' is empty, do the indenting internally.
			 */
			if (oap->op_type == OP_INDENT && *p_ep == NUL)
			{
# ifdef LISPINDENT
				if (curbuf->b_p_lisp)
				{
					op_reindent(oap, get_lisp_indent);
					break;
				}
# endif
# ifdef CINDENT
				op_reindent(oap, get_c_indent);
				break;
# endif
			}
#endif /* defined(LISPINDENT) || defined(CINDENT) */

			op_colon(oap, VIsual_was_active);
			break;

		case OP_TILDE:
		case OP_UPPER:
		case OP_LOWER:
			if (!oap->empty)
				op_tilde(oap);
			break;

		case OP_FORMAT:
		case OP_GFORMAT:
			if (*p_fp != NUL)
				op_colon(oap, VIsual_was_active);	/* use external command */
			else
				op_format(oap);						/* use internal function */
			break;

		default:
			clearopbeep(oap);
		}
		oap->op_prechar = NUL;
		if (!gui_yank)
		{
			/*
			 * if 'sol' not set, go back to old column for some commands
			 */
			if (!p_sol && oap->motion_type == MLINE &&
					(oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT ||
												   oap->op_type == OP_DELETE))
				coladvance(curwin->w_curswant = old_col);
			oap->op_type = OP_NOP;
		}
		else
			curwin->w_cursor = old_cursor;
		oap->block_mode = FALSE;
		oap->regname = 0;
	}
}

/*
 * Handle indent and format operators and visual mode ":".
 */
	static void
op_colon(oap, VIsual_was_active)
	OPARG		*oap;
	int			VIsual_was_active;
{
	stuffcharReadbuff(':');
	if (VIsual_was_active)
		stuffReadbuff((char_u *)"'<,'>");
	else
	{
		/*
		 * Make the range look nice, so it can be repeated.
		 */
		if (oap->start.lnum == curwin->w_cursor.lnum)
			stuffcharReadbuff('.');
		else
			stuffnumReadbuff((long)oap->start.lnum);
		if (oap->end.lnum != oap->start.lnum)
		{
			stuffcharReadbuff(',');
			if (oap->end.lnum == curwin->w_cursor.lnum)
				stuffcharReadbuff('.');
			else if (oap->end.lnum == curbuf->b_ml.ml_line_count)
				stuffcharReadbuff('$');
			else if (oap->start.lnum == curwin->w_cursor.lnum)
			{
				stuffReadbuff((char_u *)".+");
				stuffnumReadbuff((long)oap->line_count - 1);
			}
			else
				stuffnumReadbuff((long)oap->end.lnum);
		}
	}
	if (oap->op_type != OP_COLON)
		stuffReadbuff((char_u *)"!");
	if (oap->op_type == OP_INDENT)
	{
#ifndef CINDENT
		if (*p_ep == NUL)
			stuffReadbuff((char_u *)"indent");
		else
#endif
			stuffReadbuff(p_ep);
		stuffReadbuff((char_u *)"\n");
	}
	else if (oap->op_type == OP_FORMAT || oap->op_type == OP_GFORMAT)
	{
		if (*p_fp == NUL)
			stuffReadbuff((char_u *)"fmt");
		else
			stuffReadbuff(p_fp);
		stuffReadbuff((char_u *)"\n");
	}

	/*
	 * do_cmdline() does the rest
	 */
}

#ifdef USE_MOUSE
/*
 * Do the appropriate action for the current mouse click in the current mode.
 *
 * Normal Mode:
 * event	     modi-  position	  visual	   change	action
 *               fier   cursor	  	               window
 * left press	  -		yes			end				yes
 * left press	  C		yes			end				yes		"^]" (2)
 * left press	  S		yes			end				yes		"*" (2)
 * left drag	  -		yes		start if moved  	no
 * left relse	  -		yes		start if moved		no
 * middle press	  -  	yes	     if not active		no		put register
 * middle press	  - 	yes	     if active			no		yank and put
 * right press	  -		yes		start or extend		yes
 * right press	  S		yes		no change      		yes		"#" (2)
 * right drag	  -		yes		extend				no
 * right relse	  -		yes		extend				no
 *
 * Insert or Replace Mode:
 * event	     modi-  position	  visual	   change	action
 *               fier   cursor	  	               window
 * left press	  -		yes		(cannot be active)	yes
 * left press	  C		yes		(cannot be active)	yes		"CTRL-O^]" (2)
 * left press	  S		yes		(cannot be active)	yes		"CTRL-O*" (2)
 * left drag	  -		yes		start or extend (1)	no		CTRL-O (1)
 * left relse	  -		yes		start or extend (1)	no		CTRL-O (1)
 * middle press	  - 	no	  	(cannot be active)	no		put register
 * right press	  -		yes		start or extend		yes		CTRL-O
 * right press	  S		yes		(cannot be active)	yes		"CTRL-O#" (2)
 *
 * (1) only if mouse pointer moved since press
 * (2) only if click is in same buffer
 *
 * Return TRUE if start_arrow() should be called for edit mode.
 */
	int
do_mouse(oap, c, dir, count, fix_indent)
	OPARG	*oap;			/* operator argument, can be NULL */
	int		c;				/* K_LEFTMOUSE, etc */
	int		dir;			/* Direction to 'put' if necessary */
	long	count;
	int		fix_indent;		/* Do we fix indent for 'put' if necessary? */
{
	static int	ignore_drag_release = FALSE;
	static FPOS	orig_cursor;
	static int	do_always = FALSE;		/* ignore 'mouse' setting next time */
	static int	got_click = FALSE;		/* got a click some time back */

	int		which_button;		/* MOUSE_LEFT, _MIDDLE or _RIGHT */
	int		is_click;			/* If FALSE it's a drag or release event */
	int		is_drag;			/* If TRUE it's a drag event */
	int		jump_flags = 0;		/* flags for jump_to_mouse() */
	FPOS	start_visual;
	FPOS	end_visual;
	BUF		*save_buffer;
	int		diff;
	int		moved;				/* Has cursor moved? */
	int		c1, c2;
	int		VIsual_was_active = VIsual_active;
	int		regname;

	/*
	 * When GUI is active, always recognize mouse events, otherwise:
	 * - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
	 * - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
	 * - For command line and insert mode 'mouse' is checked before calling
	 *   do_mouse().
	 */
	if (do_always)
		do_always = FALSE;
	else
#ifdef USE_GUI
		if (!gui.in_use)
#endif
		{
			if (VIsual_active)
			{
				if (!mouse_has(MOUSE_VISUAL))
					return FALSE;
			}
			else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
				return FALSE;
		}

	which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);

	/*
	 * Ignore drag and release events if we didn't get a click.
	 */
	if (is_click)
		got_click = TRUE;
	else
	{
		if (!got_click)					/* didn't get click, ignore */
			return FALSE;
		if (!is_drag)					/* release, reset got_click */
			got_click = FALSE;
	}

	/*
	 * ALT is currently ignored
	 */
	if ((mod_mask & MOD_MASK_ALT))
		return FALSE;

	/*
	 * CTRL right mouse button does CTRL-T
	 */
	if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
	{
		if (State & INSERT)
			stuffcharReadbuff(Ctrl('O'));
		stuffcharReadbuff(Ctrl('T'));
		got_click = FALSE;				/* ignore drag&release now */
		return FALSE;
	}

	/*
	 * CTRL only works with left mouse button
	 */
	if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
		return FALSE;

	/*
	 * When a modifier is down, ignore drag and release events, as well as
	 * multiple clicks and the middle mouse button.
	 */
	if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) &&
							(!is_click || (mod_mask & MOD_MASK_MULTI_CLICK) ||
												which_button == MOUSE_MIDDLE))
		return FALSE;

	/*
	 * If the button press was used as the movement command for an operator
	 * (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
	 * drag/release events.
	 */
	if (!is_click && (ignore_drag_release || which_button == MOUSE_MIDDLE))
		return FALSE;

	if (oap != NULL)
		regname = oap->regname;
	else
		regname = 0;

	/*
	 * Middle mouse button does a 'put' of the selected text
	 */
	if (which_button == MOUSE_MIDDLE)
	{
		if (State == NORMAL)
		{
			/*
			 * If an operator was pending, we don't know what the user wanted
			 * to do. Go back to normal mode: Clear the operator and beep().
			 */
			if (oap != NULL && oap->op_type != OP_NOP)
			{
				clearopbeep(oap);
				return FALSE;
			}

			/*
			 * If visual was active, yank the highlighted text and put it
			 * before the mouse pointer position.
			 */
			if (VIsual_active)
			{
				stuffcharReadbuff('y');
				stuffcharReadbuff(K_MIDDLEMOUSE);
				do_always = TRUE;		/* ignore 'mouse' setting next time */
				return FALSE;
			}
			/*
			 * The rest is below jump_to_mouse()
			 */
		}

		/*
		 * Middle click in insert mode doesn't move the mouse, just insert the
		 * contents of a register.  '.' register is special, can't insert that
		 * with do_put().
		 */
		else if (State & INSERT)
		{
			if (regname == '.')
				insert_reg(regname);
			else
			{
#ifdef USE_CLIPBOARD
				if (clipboard.available && regname == 0)
					regname = '*';
#endif
				do_put(regname, BACKWARD, 1L, fix_indent);

				/* Put cursor after the end of the just pasted text. */
				curwin->w_cursor = curbuf->b_op_end;
				if (gchar_cursor() != NUL)
					++curwin->w_cursor.col;

				/* Repeat it with CTRL-R x, not exactly the same, but mostly
				 * works fine. */
				AppendCharToRedobuff(Ctrl('R'));
				if (regname == 0)
					AppendCharToRedobuff('"');
				else
					AppendCharToRedobuff(regname);
			}
			return FALSE;
		}
		else
			return FALSE;
	}

	if (!is_click)
		jump_flags |= MOUSE_FOCUS;

	start_visual.lnum = 0;

	if ((State & (NORMAL | INSERT)) &&
							   !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
	{
		if (which_button == MOUSE_LEFT)
		{
			if (is_click)
			{
				if (VIsual_active)
				{
					end_visual_mode();
					update_curbuf(NOT_VALID);
				}
			}
			else
				jump_flags |= MOUSE_MAY_VIS;
		}
		else if (which_button == MOUSE_RIGHT)
		{
			if (is_click && VIsual_active)
			{
				/*
				 * Remember the start and end of visual before moving the
				 * cursor.
				 */
				if (lt(curwin->w_cursor, VIsual))
				{
					start_visual = curwin->w_cursor;
					end_visual = VIsual;
				}
				else
				{
					start_visual = VIsual;
					end_visual = curwin->w_cursor;
				}
			}
			jump_flags |= MOUSE_MAY_VIS;
		}
	}

	if (!is_drag)
	{
		/*
		 * If an operator is pending, ignore all drags and releases until the
		 * next mouse click.
		 */
		ignore_drag_release = (oap != NULL && oap->op_type != OP_NOP);
	}

	/*
	 * Jump!
	 */
	if (!is_click)
		jump_flags |= MOUSE_DID_MOVE;
	save_buffer = curbuf;
	moved = (jump_to_mouse(jump_flags,
					  oap == NULL ? NULL : &(oap->inclusive)) & CURSOR_MOVED);

	/* When jumping to another buffer, stop visual mode */
	if (curbuf != save_buffer && VIsual_active)
	{
		end_visual_mode();
		update_curbuf(NOT_VALID);		/* delete the inversion */
	}
	else if (start_visual.lnum)		/* right click in visual mode */
	{
		/*
		 * If the click is before the start of visual, change the start.  If
		 * the click is after the end of visual, change the end.  If the click
		 * is inside the visual, change the closest side.
		 */
		if (lt(curwin->w_cursor, start_visual))
			VIsual = end_visual;
		else if (lt(end_visual, curwin->w_cursor))
			VIsual = start_visual;
		else
		{
			/* In the same line, compare column number */
			if (end_visual.lnum == start_visual.lnum)
			{
				if (curwin->w_cursor.col - start_visual.col >
								end_visual.col - curwin->w_cursor.col)
					VIsual = start_visual;
				else
					VIsual = end_visual;
			}

			/* In different lines, compare line number */
			else
			{
				diff = (curwin->w_cursor.lnum - start_visual.lnum) -
							(end_visual.lnum - curwin->w_cursor.lnum);

				if (diff > 0)			/* closest to end */
					VIsual = start_visual;
				else if (diff < 0)		/* closest to start */
					VIsual = end_visual;
				else					/* in the middle line */
				{
					if (curwin->w_cursor.col <
									(start_visual.col + end_visual.col) / 2)
						VIsual = end_visual;
					else
						VIsual = start_visual;
				}
			}
		}
	}
	/*
	 * If Visual mode started in insert mode, execute "CTRL-O"
	 */
	else if ((State & INSERT) && VIsual_active)
		stuffcharReadbuff(Ctrl('O'));

	/*
	 * When the cursor has moved in insert mode, and something was inserted,
	 * and there are several windows, need to redraw.
	 */
	if (moved && (State & INSERT) && modified && firstwin->w_next != NULL)
	{
		update_curbuf(NOT_VALID);
		modified = FALSE;
	}

	/*
	 * Middle mouse click: Put text before cursor.
	 */
	if (which_button == MOUSE_MIDDLE)
	{
#ifdef USE_CLIPBOARD
		if (clipboard.available && regname == 0)
			regname = '*';
#endif
		if (yank_register_mline(regname))
		{
			if (mouse_past_bottom)
				dir = FORWARD;
		}
		else if (mouse_past_eol)
			dir = FORWARD;

		if (fix_indent)
		{
			c1 = (dir == BACKWARD) ? '[' : ']';
			c2 = 'p';
		}
		else
		{
			c1 = (dir == FORWARD) ? 'p' : 'P';
			c2 = NUL;
		}
		prep_redo(regname, count, NUL, c1, c2, NUL);

		/*
		 * Remember where the paste started, so in edit() Insstart can be set
		 * to this position
		 */
		if (restart_edit)
			where_paste_started = curwin->w_cursor;
		do_put(regname, dir, count, fix_indent);

		/* Put cursor at the end of the just pasted text. */
		curwin->w_cursor = curbuf->b_op_end;
		if (restart_edit && gchar_cursor() != NUL)
			++curwin->w_cursor.col;			/* put cursor after the text */
	}

	/*
	 * Ctrl-Mouse click jumps to the tag under the mouse pointer
	 */
	else if ((mod_mask & MOD_MASK_CTRL))
	{
		if (State & INSERT)
			stuffcharReadbuff(Ctrl('O'));
		stuffcharReadbuff(Ctrl(']'));
		ignore_drag_release = TRUE;		/* ignore drag and release now */
	}

	/*
	 * Shift-Mouse click searches for the next occurrence of the word under
	 * the mouse pointer
	 */
	else if ((mod_mask & MOD_MASK_SHIFT))
	{
		if (State & INSERT)
			stuffcharReadbuff(Ctrl('O'));
		if (which_button == MOUSE_LEFT)
			stuffcharReadbuff('*');
		else	/* MOUSE_RIGHT */
			stuffcharReadbuff('#');
	}

	/* Handle double clicks */
	else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)))
	{
		if (is_click || !VIsual_active)
		{
			if (VIsual_active)
				orig_cursor = VIsual;
			else
			{
				check_visual_highlight();
				VIsual = curwin->w_cursor;
				orig_cursor = VIsual;
				VIsual_active = TRUE;
#ifdef USE_MOUSE
				setmouse();
#endif
				if (p_smd)
					redraw_cmdline = TRUE;	/* show visual mode later */
			}
			if (mod_mask & MOD_MASK_2CLICK)
				VIsual_mode = 'v';
			else if (mod_mask & MOD_MASK_3CLICK)
				VIsual_mode = 'V';
			else if (mod_mask & MOD_MASK_4CLICK)
				VIsual_mode = Ctrl('V');
		}
		if (mod_mask & MOD_MASK_2CLICK)
		{
			if (lt(curwin->w_cursor, orig_cursor))
			{
				find_start_of_word(&curwin->w_cursor);
				find_end_of_word(&VIsual);
			}
			else
			{
				find_start_of_word(&VIsual);
				find_end_of_word(&curwin->w_cursor);
			}
			curwin->w_set_curswant = TRUE;
		}
		if (is_click)
			update_curbuf(NOT_VALID);		/* update the inversion */
	}
	else if (VIsual_active && VIsual_was_active != VIsual_active)
		VIsual_mode = 'v';

	return moved;
}

	static void
find_start_of_word(pos)
	FPOS	*pos;
{
	char_u	*ptr;
	int		cclass;

	ptr = ml_get(pos->lnum);
	cclass = get_mouse_class(ptr[pos->col]);

	/* Can't test pos->col >= 0 because pos->col is unsigned */
	while (pos->col > 0 && get_mouse_class(ptr[pos->col]) == cclass)
		pos->col--;
	if (pos->col != 0 || get_mouse_class(ptr[0]) != cclass)
		pos->col++;
}

	static void
find_end_of_word(pos)
	FPOS	*pos;
{
	char_u	*ptr;
	int		cclass;

	ptr = ml_get(pos->lnum);
	cclass = get_mouse_class(ptr[pos->col]);
	while (ptr[pos->col] && get_mouse_class(ptr[pos->col]) == cclass)
		pos->col++;
	pos->col--;
}

	static int
get_mouse_class(c)
	int		c;
{
	if (c == ' ' || c == '\t')
		return ' ';

	if (isidchar(c))
		return 'a';

	/*
	 * There are a few special cases where we want certain combinations of
	 * characters to be considered as a single word.  These are things like
	 * "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc.  Otherwise, each
	 * character is in it's own class.
	 */
	if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
		return '=';
	return c;
}
#endif /* USE_MOUSE */

/*
 * Check if  highlighting for visual mode is possible, give a warning message
 * if not.
 */
	void
check_visual_highlight()
{
	static int		did_check = FALSE;

	if (!did_check && highlight_attr[HLF_V] == 0)
		MSG("Warning: terminal cannot highlight");
	did_check = TRUE;
}

/*
 * End visual mode.  If we are using the GUI, and autoselect is set, then
 * remember what was selected in case we need to paste it somewhere while we
 * still own the selection.  This function should ALWAYS be called to end
 * visual mode.
 */
	void
end_visual_mode()
{
#ifdef USE_CLIPBOARD
	if (clipboard.available)
		clip_auto_select();
#endif
	VIsual_active = FALSE;
#ifdef USE_MOUSE
	setmouse();
#endif
	VIsual_end = curwin->w_cursor;		/* remember for '> mark */
	if (p_smd)
		clear_cmdline = TRUE;			/* unshow visual mode later */
}

/*
 * Find the identifier under or to the right of the cursor.  If none is
 * found and find_type has FIND_STRING, then find any non-white string.  The
 * length of the string is returned, or zero if no string is found.  If a
 * string is found, a pointer to the string is put in *string, but note that
 * the caller must use the length returned as this string may not be NUL
 * terminated.
 */
	int
find_ident_under_cursor(string, find_type)
	char_u	**string;
	int		find_type;
{
	char_u	*ptr;
	int		col = 0;		/* init to shut up GCC */
	int		i;

	/*
	 * if i == 0: try to find an identifier
	 * if i == 1: try to find any string
	 */
	ptr = ml_get_curline();
	for (i = (find_type & FIND_IDENT) ? 0 : 1;	i < 2; ++i)
	{
		/*
		 * skip to start of identifier/string
		 */
		col = curwin->w_cursor.col;
		while (ptr[col] != NUL &&
					(i == 0 ? !iswordchar(ptr[col]) : vim_iswhite(ptr[col])))
			++col;

		/*
		 * Back up to start of identifier/string. This doesn't match the
		 * real vi but I like it a little better and it shouldn't bother
		 * anyone.
		 * When FIND_IDENT isn't defined, we backup until a blank.
		 */
		while (col > 0 && (i == 0 ? iswordchar(ptr[col - 1]) :
					(!vim_iswhite(ptr[col - 1]) &&
				   (!(find_type & FIND_IDENT) || !iswordchar(ptr[col - 1])))))
			--col;

		/*
		 * if we don't want just any old string, or we've found an identifier,
		 * stop searching.
		 */
		if (!(find_type & FIND_STRING) || iswordchar(ptr[col]))
			break;
	}
	/*
	 * didn't find an identifier or string
	 */
	if (ptr[col] == NUL || (!iswordchar(ptr[col]) && i == 0))
	{
		if (find_type & FIND_STRING)
			EMSG("No string under cursor");
		else
			EMSG("No identifier under cursor");
		return 0;
	}
	ptr += col;
	*string = ptr;
	col = 0;
	while (i == 0 ? iswordchar(*ptr) : (*ptr != NUL && !vim_iswhite(*ptr)))
	{
		++ptr;
		++col;
	}
	return col;
}

/*
 * Prepare for redo of a normal command.
 */
	static void
prep_redo_cmd(cap)
	CMDARG	*cap;
{
	prep_redo(cap->oap->regname, cap->count0,
										  NUL, cap->cmdchar, NUL, cap->nchar);
}

/*
 * Prepare for redo of any command.
 */
	static void
prep_redo(regname, num, prechar, cmd, c, nchar)
	int		regname;
	long 	num;
	int		prechar;
	int		cmd;
	int		c;
	int		nchar;
{
	ResetRedobuff();
	if (regname != 0)	/* yank from specified buffer */
	{
		AppendCharToRedobuff('\"');
		AppendCharToRedobuff(regname);
	}
	if (num)
		AppendNumberToRedobuff(num);
	if (prechar != NUL)
		AppendCharToRedobuff(prechar);
	AppendCharToRedobuff(cmd);
	if (c != NUL)
		AppendCharToRedobuff(c);
	if (nchar != NUL)
		AppendCharToRedobuff(nchar);
}

/*
 * check for operator active and clear it
 *
 * return TRUE if operator was active
 */
	static int
checkclearop(oap)
	OPARG		*oap;
{
	if (oap->op_type == OP_NOP)
		return FALSE;
	clearopbeep(oap);
	return TRUE;
}

/*
 * check for operator or Visual active and clear it
 *
 * return TRUE if operator was active
 */
	static int
checkclearopq(oap)
	OPARG		*oap;
{
	if (oap->op_type == OP_NOP && !VIsual_active)
		return FALSE;
	clearopbeep(oap);
	return TRUE;
}

	static void
clearop(oap)
	OPARG		*oap;
{
	oap->op_type = OP_NOP;
	oap->regname = 0;
}

	static void
clearopbeep(oap)
	OPARG		*oap;
{
	clearop(oap);
	beep_flush();
}

/*
 * Routines for displaying a partly typed command
 */

static char_u	showcmd_buf[SHOWCMD_COLS + 1];
static char_u	old_showcmd_buf[SHOWCMD_COLS + 1];	/* For push_showcmd() */
static int		is_showcmd_clear = TRUE;

static void display_showcmd __ARGS((void));

	void
clear_showcmd()
{
	if (!p_sc)
		return;

	showcmd_buf[0] = NUL;

	/*
	 * Don't actually display something if there is nothing to clear.
	 */
	if (is_showcmd_clear)
		return;

	display_showcmd();
}

/*
 * Add 'c' to string of shown command chars.
 * Return TRUE if setcursor() has been called.
 */
	int
add_to_showcmd(c, display_always)
	int 	c;
	int		display_always;
{
	char_u	*p;
	int		old_len;
	int		extra_len;
	int		overflow;

	if (!p_sc)
		return FALSE;

	p = transchar(c);
	old_len = STRLEN(showcmd_buf);
	extra_len = STRLEN(p);
	overflow = old_len + extra_len - SHOWCMD_COLS;
	if (overflow > 0)
		STRCPY(showcmd_buf, showcmd_buf + overflow);
	STRCAT(showcmd_buf, p);

	if (!display_always && char_avail())
		return FALSE;

	display_showcmd();

	return TRUE;
}

/*
 * Delete 'len' characters from the end of the shown command.
 */
	static void
del_from_showcmd(len)
	int 	len;
{
	int		old_len;

	if (!p_sc)
		return;

	old_len = STRLEN(showcmd_buf);
	if (len > old_len)
		len = old_len;
	showcmd_buf[old_len - len] = NUL;

	if (!char_avail())
		display_showcmd();
}

	void
push_showcmd()
{
	if (p_sc)
		STRCPY(old_showcmd_buf, showcmd_buf);
}

	void
pop_showcmd()
{
	if (!p_sc)
		return;

	STRCPY(showcmd_buf, old_showcmd_buf);

	display_showcmd();
}

	static void
display_showcmd()
{
	int		len;

	cursor_off();

	len = STRLEN(showcmd_buf);
	if (len == 0)
		is_showcmd_clear = TRUE;
	else
	{
		screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0);
		is_showcmd_clear = FALSE;
	}

	/*
	 * clear the rest of an old message by outputing up to SHOWCMD_COLS spaces
	 */
	screen_puts((char_u *)"          " + len, (int)Rows - 1, sc_col + len, 0);

	setcursor();			/* put cursor back where it belongs */
}

/*
 * Implementation of "gd" and "gD" command.
 */
	static void
do_gd(oap, nchar)
	OPARG	*oap;
	int		nchar;
{
	int			len;
	char_u		*pat;
	FPOS		old_pos;
	int			t;
	int			save_p_ws;
	int			save_p_scs;
	char_u		*ptr;

	if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0 ||
											   (pat = alloc(len + 5)) == NULL)
	{
		clearopbeep(oap);
		return;
	}
	sprintf((char *)pat, iswordchar(*ptr) ? "\\<%.*s\\>" :
			"%.*s", len, ptr);
	old_pos = curwin->w_cursor;
	save_p_ws = p_ws;
	save_p_scs = p_scs;
	p_ws = FALSE;		/* don't wrap around end of file now */
	p_scs = FALSE;		/* don't switch ignorecase off now */
	fo_do_comments = TRUE;

	/*
	 * Search back for the end of the previous function.
	 * If this fails, and with "gD", go to line 1.
	 * Search forward for the identifier, ignore comment lines.
	 */
	if (nchar == 'D' || !findpar(oap, BACKWARD, 1L, '}', FALSE))
	{
		setpcmark();					/* Set in findpar() otherwise */
		curwin->w_cursor.lnum = 1;
	}

	while ((t = searchit(&curwin->w_cursor, FORWARD, pat, 1L, 0, RE_LAST))
				== OK &&
			get_leader_len(ml_get_curline(), NULL) &&
			old_pos.lnum > curwin->w_cursor.lnum)
		++curwin->w_cursor.lnum;
	if (t == FAIL || old_pos.lnum <= curwin->w_cursor.lnum)
	{
		clearopbeep(oap);
		curwin->w_cursor = old_pos;
	}
	else
		curwin->w_set_curswant = TRUE;

	vim_free(pat);
	p_ws = save_p_ws;
	p_scs = save_p_scs;
	fo_do_comments = FALSE;
}

/*
 * screengo() --
 *
 * move 'dist' lines in direction 'dir', counting lines by *screen*
 * lines rather than lines in the file
 * 'dist' must be positive.
 *
 * return OK if able to move cursor, FAIL otherwise.
 */
	static int
screengo(oap, dir, dist)
	OPARG	*oap;
	int		dir;
	long	dist;
{
	int			linelen = linetabsize(ml_get_curline());
	int			retval = OK;
	int			atend = FALSE;
	int			n;

	oap->motion_type = MCHAR;
	oap->inclusive = FALSE;

	/*
	 * Instead of sticking at the last character of the line in the file we
	 * try to stick in the last column of the screen
	 */
	if (curwin->w_curswant == MAXCOL)
	{
		atend = TRUE;
		validate_virtcol();
		curwin->w_curswant = ((curwin->w_virtcol +
					   (curwin->w_p_nu ? 8 : 0)) / Columns + 1) * Columns - 1;
		if (curwin->w_p_nu && curwin->w_curswant > 8)
			curwin->w_curswant -= 8;
	}
	else
		while (curwin->w_curswant >= (colnr_t)(linelen + Columns))
			curwin->w_curswant -= Columns;

	while (dist--)
	{
		if (dir == BACKWARD)
		{
												/* move back within line */
			if ((long)curwin->w_curswant >= Columns)
				curwin->w_curswant -= Columns;
			else								/* to previous line */
			{
				if (curwin->w_cursor.lnum == 1)
				{
					retval = FAIL;
					break;
				}
				--curwin->w_cursor.lnum;
				linelen = linetabsize(ml_get_curline());
				n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns)
																	* Columns;
				if (curwin->w_p_nu &&
								 (long)curwin->w_curswant >= Columns - 8 && n)
					n -= Columns;
				curwin->w_curswant += n;
			}
		}
		else /* dir == FORWARD */
		{
			n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns) * Columns;
			if (curwin->w_p_nu && n > 8)
				n -= 8;
												/* move forward within line */
			if (curwin->w_curswant < (colnr_t)n)
				curwin->w_curswant += Columns;
			else								/* to next line */
			{
				if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
				{
					retval = FAIL;
					break;
				}
				curwin->w_cursor.lnum++;
				linelen = linetabsize(ml_get_curline());
				curwin->w_curswant %= Columns;
			}
		}
	}
	coladvance(curwin->w_curswant);
	if (atend)
		curwin->w_curswant = MAXCOL;		/* stick in the last column */

	return retval;
}

	static void
do_zet(cap)
	CMDARG	*cap;
{
	long		n;
	colnr_t		col;
	int			nchar = cap->nchar;


	if (nchar < 0x100 && isdigit(nchar))
	{
		n = nchar - '0';
		for (;;)
		{
			++no_mapping;
			++allow_keys;	/* no mapping for nchar, but allow key codes */
			nchar = vgetc();
#ifdef HAVE_LANGMAP
			LANGMAP_ADJUST(nchar, TRUE);
#endif
			--no_mapping;
			--allow_keys;
			(void)add_to_showcmd(nchar, FALSE);
			if (nchar == K_DEL)
				n /= 10;
			else if (nchar < 0x100 && isdigit(nchar))
				n = n * 10 + (nchar - '0');
			else if (nchar == CR)
			{
				win_setheight((int)n);
				break;
			}
			else if (nchar == 'l' || nchar == 'h' ||
										  nchar == K_LEFT || nchar == K_RIGHT)
			{
				n = n ? n : 1;
				goto dozet;
			}
			else
			{
				clearopbeep(cap->oap);
				break;
			}
		}
		cap->oap->op_type = OP_NOP;
		return;
	}
dozet:
	/*
	 * If line number given, set cursor, except for "zh", "zl", "ze" and
	 * "zs"
	 */
	if (	   vim_strchr((char_u *)"hles", nchar) == NULL
			&& nchar != K_LEFT
			&& nchar != K_RIGHT
			&& cap->count0
			&& cap->count0 != curwin->w_cursor.lnum)
	{
		setpcmark();
		if (cap->count0 > curbuf->b_ml.ml_line_count)
			curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
		else
			curwin->w_cursor.lnum = cap->count0;
	}

	switch (nchar)
	{
	case NL:				/* put curwin->w_cursor at top of screen */
	case CR:
		beginline(TRUE);
		/* FALLTHROUGH */
	case 't':
		scroll_cursor_top(0, TRUE);
		break;

	case '.': 			/* put curwin->w_cursor in middle of screen */
		beginline(TRUE);
		/* FALLTHROUGH */
	case 'z':
		scroll_cursor_halfway(TRUE);
		break;

	case '-': 			/* put curwin->w_cursor at bottom of screen */
		beginline(TRUE);
		/* FALLTHROUGH */
	case 'b':
		scroll_cursor_bot(0, TRUE);
		break;

		/* "zh" - scroll screen to the right */
	case 'h':
	case K_LEFT:
		if (!curwin->w_p_wrap)
		{
			if ((colnr_t)cap->count1 > curwin->w_leftcol)
				curwin->w_leftcol = 0;
			else
				curwin->w_leftcol -= (colnr_t)cap->count1;
			leftcol_changed();
		}
		break;

		/* "zl" - scroll screen to the left */
	case 'l':
	case K_RIGHT:
		if (!curwin->w_p_wrap)
		{
			/* scroll the window left */
			curwin->w_leftcol += (colnr_t)cap->count1;
			leftcol_changed();
		}
		break;

		/* "zs" - scroll screen, cursor at the start */
	case 's':
		if (!curwin->w_p_wrap)
		{
			getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
			curwin->w_leftcol = col;
			changed_cline_bef_curs();
			redraw_later(NOT_VALID);
		}
		break;

		/* "ze" - scroll screen, cursor at the end */
	case 'e':
		if (!curwin->w_p_wrap)
		{
			getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
			if ((long)col < Columns)
				curwin->w_leftcol = 0;
			else
				curwin->w_leftcol = col - Columns + 1;
			changed_cline_bef_curs();
			redraw_later(NOT_VALID);
		}
		break;

	case Ctrl('S'):	/* ignore CTRL-S and CTRL-Q to avoid problems */
	case Ctrl('Q'):	/* with terminals that use xon/xoff */
		break;

	default:
		clearopbeep(cap->oap);
	}
	update_screen(VALID);
}

/*
 * Handle the commands that use the word under the cursor.
 *
 * Returns TRUE for "*" and "#" commands, indicating that the next search
 * should not set the pcmark.
 */
	static void
do_ident(cap, whole_word)
	CMDARG	*cap;
	int		whole_word;		/* include \< and \> for ident search */
{
	char_u		*ptr = NULL;
	int			n = 0;		/* init for GCC */

	/*
	 * The "CTRL-]" and "K" commands accept an argument in Visual mode.
	 */
	if (cap->cmdchar == Ctrl(']') || cap->cmdchar == 'K')
	{
		if (VIsual_active)		/* :ta to visual highlighted text */
		{
			if (VIsual.lnum != curwin->w_cursor.lnum)
			{
				clearopbeep(cap->oap);
				return;
			}
			if (lt(curwin->w_cursor, VIsual))
			{
				ptr = ml_get_pos(&curwin->w_cursor);
				n = VIsual.col - curwin->w_cursor.col + 1;
			}
			else
			{
				ptr = ml_get_pos(&VIsual);
				n = curwin->w_cursor.col - VIsual.col + 1;
			}
			end_visual_mode();
			++RedrawingDisabled;
			update_curbuf(NOT_VALID);		/* update the inversion later */
			--RedrawingDisabled;
		}
		if (checkclearopq(cap->oap))
			return;
	}

	if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
					(cap->cmdchar == '*' || cap->cmdchar == '#')
								 ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
	{
		clearop(cap->oap);
		return;
	}

	if (cap->count0 && !(cap->cmdchar == 'K' && STRCMP(p_kp, "man") == 0))
		stuffnumReadbuff(cap->count0);
	switch (cap->cmdchar)
	{
		case '*':
			stuffReadbuff((char_u *)"/");
			/* FALLTHROUGH */

		case '#':
			if (cap->cmdchar == '#')
				stuffReadbuff((char_u *)"?");

			/*
			 * Put cursor at start of word, makes search skip the word
			 * under the cursor.
			 * Call setpcmark() first, so "*``" puts the cursor back where
			 * it was, and set search_dont_set_mark to avoid doing it
			 * again when searching.
			 */
			setpcmark();
			search_dont_set_mark = TRUE;
			curwin->w_cursor.col = ptr - ml_get_curline();

			if (whole_word && iswordchar(*ptr))
				stuffReadbuff((char_u *)"\\<");
			no_smartcase = TRUE;		/* don't use 'smartcase' now */
			break;

		case 'K':
			if (*p_kp == NUL)
				stuffReadbuff((char_u *)":he ");
			else
			{
				stuffReadbuff((char_u *)":! ");
				stuffReadbuff(p_kp);
				stuffReadbuff((char_u *)" ");
				if (STRCMP(p_kp, "man") == 0 && cap->count0)
				{
					stuffnumReadbuff(cap->count0);
					stuffReadbuff((char_u *)" ");
				}
			}
			break;

		default:
			if (curbuf->b_help)
				stuffReadbuff((char_u *)":he ");
			else
				stuffReadbuff((char_u *)":ta ");
	}

	/*
	 * Now grab the chars in the identifier
	 */
	while (n--)
	{
		/* put a backslash before \ and some others */
		if (*ptr == '\\' || (!(cap->cmdchar == '*' || cap->cmdchar == '#') &&
									  vim_strchr(escape_chars, *ptr) != NULL))
			stuffcharReadbuff('\\');
		/* don't interpret the characters as edit commands */
		if (*ptr < ' ' || *ptr > '~')
			stuffcharReadbuff(Ctrl('V'));
		stuffcharReadbuff(*ptr++);
	}

	if (	   whole_word
			&& (cap->cmdchar == '*' || cap->cmdchar == '#')
			&& iswordchar(ptr[-1]))
		stuffReadbuff((char_u *)"\\>");
	stuffReadbuff((char_u *)"\n");
}

/*
 * Handle scrolling command 'H', 'L' and 'M'.
 */
	static void
do_scroll(cap)
	CMDARG	*cap;
{
	int		used = 0;
	long	n;

	if (cap->cmdchar == 'L')
	{
		validate_botline();			/* make sure curwin->w_botline is valid */
		curwin->w_cursor.lnum = curwin->w_botline - 1;
		if (cap->count1 - 1 >= curwin->w_cursor.lnum)
			curwin->w_cursor.lnum = 1;
		else
			curwin->w_cursor.lnum -= cap->count1 - 1;
	}
	else
	{
		if (cap->cmdchar == 'M')
		{
			validate_botline();		/* make sure w_empty_rows is valid */
			for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
				if ((used += plines(curwin->w_topline + n)) >=
							(curwin->w_height - curwin->w_empty_rows + 1) / 2)
					break;
			if (n && used > curwin->w_height)
				--n;
		}
		else
			n = cap->count1 - 1;
		curwin->w_cursor.lnum = curwin->w_topline + n;
		if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
			curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
	}
}

/*
 * Cursor right commands.
 */
	static void
do_right(cap)
	CMDARG		*cap;
{
	long		n;

	cap->oap->motion_type = MCHAR;
	cap->oap->inclusive = FALSE;
	for (n = cap->count1; n > 0; --n)
	{
		if (oneright() == FAIL)
		{
			/*
			 *    <Space> wraps to next line if 'whichwrap' bit 1 set.
			 *        'l' wraps to next line if 'whichwrap' bit 2 set.
			 * CURS_RIGHT wraps to next line if 'whichwrap' bit 3 set
			 */
			if (	   ((cap->cmdchar == ' '
							&& vim_strchr(p_ww, 's') != NULL)
						|| (cap->cmdchar == 'l'
							&& vim_strchr(p_ww, 'l') != NULL)
						|| (cap->cmdchar == K_RIGHT
							&& vim_strchr(p_ww, '>') != NULL))
					&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
			{
				/* When deleting we also count the NL as a character.
				 * Set cap->oap->inclusive when last char in the line is
				 * included, move to next line after that */
				if (	   (cap->oap->op_type == OP_DELETE
							|| cap->oap->op_type == OP_CHANGE)
						&& !cap->oap->inclusive
						&& !lineempty(curwin->w_cursor.lnum))
					cap->oap->inclusive = TRUE;
				else
				{
					++curwin->w_cursor.lnum;
					curwin->w_cursor.col = 0;
					curwin->w_set_curswant = TRUE;
					cap->oap->inclusive = FALSE;
				}
				continue;
			}
			if (cap->oap->op_type == OP_NOP)
				beep_flush();
			else
			{
				if (lineempty(curwin->w_cursor.lnum))
					clearopbeep(cap->oap);
				else
				{
					cap->oap->inclusive = TRUE;
					if (n > 1)
						beep_flush();
				}
			}
			break;
		}
	}
}

/*
 * Cursor left commands.
 *
 * Returns TRUE when operator end should not be adjusted.
 */
	static int
do_left(cap)
	CMDARG		*cap;
{
	long		n;
	int			retval = FALSE;

	cap->oap->motion_type = MCHAR;
	cap->oap->inclusive = FALSE;
	n = cap->count1;
	while (n--)
	{
		if (oneleft() == FAIL)
		{
			/* <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
			 *           'h' wraps to previous line if 'whichwrap' has 'h'.
			 *     CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
			 */
			if (	   (((cap->cmdchar == K_BS
								|| cap->cmdchar == Ctrl('H'))
							&& vim_strchr(p_ww, 'b') != NULL)
						|| (cap->cmdchar == 'h'
							&& vim_strchr(p_ww, 'h') != NULL)
						|| (cap->cmdchar == K_LEFT
							&& vim_strchr(p_ww, '<') != NULL))
					&& curwin->w_cursor.lnum > 1)
			{
				--(curwin->w_cursor.lnum);
				coladvance(MAXCOL);
				curwin->w_set_curswant = TRUE;

				/* When the NL before the first char has to be deleted we
				 * put the cursor on the NUL after the previous line.
				 * This is a very special case, be careful!
				 * don't adjust op_end now, otherwise it won't work */
				if (	   (cap->oap->op_type == OP_DELETE
							|| cap->oap->op_type == OP_CHANGE)
						&& !lineempty(curwin->w_cursor.lnum))
				{
					++curwin->w_cursor.col;
					retval = TRUE;
				}
				continue;
			}
			else if (  cap->oap->op_type != OP_DELETE
					&& cap->oap->op_type != OP_CHANGE)
				beep_flush();
			else if (cap->count1 == 1)
				clearopbeep(cap->oap);
			break;
		}
	}

	return retval;
}

/*
 * Grab the filename under the cursor and edit it.
 */
	static void
do_gotofile(cap)
	CMDARG		*cap;
{
	char_u		*ptr;

	ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP, cap->count1);
	if (ptr != NULL)
	{
		/* do autowrite if necessary */
		if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
			autowrite(curbuf, FALSE);
		setpcmark();
		(void)do_ecmd(0, ptr, NULL, NULL, (linenr_t)0, p_hid ? ECMD_HIDE : 0);
		vim_free(ptr);
	}
	else
		clearop(cap->oap);
}

	static void
do_csearch(cap, dir, type)
	CMDARG		*cap;
	int			dir;
	int			type;
{
	cap->oap->motion_type = MCHAR;
	if (dir == BACKWARD)
		cap->oap->inclusive = FALSE;
	else
		cap->oap->inclusive = TRUE;
	curwin->w_set_curswant = TRUE;
	if (cap->nchar >= 0x100 || !searchc(cap->nchar, dir, type, cap->count1))
		clearopbeep(cap->oap);
}

	static void
do_brackets(cap, dir)
	CMDARG		*cap;
	int			dir;				/* BACKWARD or FORWARD */
{
	int			len;
	FPOS		new_pos;
	FPOS		*pos = NULL;		/* init for GCC */
	FPOS		old_pos;			/* cursor position before command */
	char_u		*ptr;
	int			flag;
	long		n;

	cap->oap->motion_type = MCHAR;
	cap->oap->inclusive = FALSE;
	old_pos = curwin->w_cursor;

	/*
	 * "[f" or "]f" : Edit file under the cursor (same as "gf")
	 */
	if (cap->nchar == 'f')
		do_gotofile(cap);

	/*
	 * Find the occurence(s) of the identifier or define under cursor
	 * in current and included files or jump to the first occurence.
	 *
	 * 					search 		 list		    jump
	 * 				  fwd   bwd    fwd   bwd     fwd    bwd
	 * identifier     "]i"  "[i"   "]I"  "[I"   "]^I"  "[^I"
	 * define		  "]d"  "[d"   "]D"  "[D"   "]^D"  "[^D"
	 */
	else if (vim_strchr((char_u *)"iI\011dD\004", cap->nchar) != NULL)
	{
		if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
			clearop(cap->oap);
		else
		{
			find_pattern_in_path(ptr, len, TRUE,
				cap->count0 == 0 ? !isupper(cap->nchar) : FALSE,
				((cap->nchar & 0xf) == ('d' & 0xf)) ?  FIND_DEFINE : FIND_ANY,
				cap->count1,
				isupper(cap->nchar) ? ACTION_SHOW_ALL :
							islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO,
				cap->cmdchar == ']' ? curwin->w_cursor.lnum : (linenr_t)1,
				(linenr_t)MAXLNUM);
			curwin->w_set_curswant = TRUE;
		}
	}

	/*
	 * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
	 * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
	 * "[/", "[*", "]/", "]*": go to Nth comment start/end.
	 */
	else if (  (cap->cmdchar == '['
				&& vim_strchr((char_u *)"{(*/#", cap->nchar) != NULL)
			|| (cap->cmdchar == ']'
				&& vim_strchr((char_u *)"})*/#", cap->nchar) != NULL))
	{
		if (cap->nchar == '*')
			cap->nchar = '/';
		new_pos.lnum = 0;
		for (n = cap->count1; n > 0; --n)
		{
			if ((pos = findmatchlimit(cap->oap, cap->nchar,
				(cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
			{
				if (new_pos.lnum == 0)	/* nothing found */
					clearopbeep(cap->oap);
				else
					pos = &new_pos;		/* use last one found */
				break;
			}
			curwin->w_cursor = *pos;
			new_pos = *pos;
		}
		curwin->w_cursor = old_pos;
		if (pos != NULL)
		{
			setpcmark();
			curwin->w_cursor = *pos;
			curwin->w_set_curswant = TRUE;
		}
	}

	/*
	 * "[[", "[]", "]]" and "][": move to start or end of function
	 */
	else if (cap->nchar == '[' || cap->nchar == ']')
	{
		if (cap->nchar == cap->cmdchar)				/* "]]" or "[[" */
			flag = '{';
		else
			flag = '}';				/* "][" or "[]" */

		curwin->w_set_curswant = TRUE;
		/*
		 * Imitate strange Vi behaviour: When using "]]" with an operator
		 * we also stop at '}'.
		 */
		if (!findpar(cap->oap, dir, cap->count1, flag,
			  (cap->oap->op_type != OP_NOP && dir == FORWARD && flag == '{')))
			clearopbeep(cap->oap);
		else if (cap->oap->op_type == OP_NOP)
			beginline(TRUE);
	}

	/*
	 * "[p", "[P", "]P" and "]p": put with indent adjustment
	 */
	else if (cap->nchar == 'p' || cap->nchar == 'P')
	{
		if (!checkclearopq(cap->oap))
		{
			prep_redo_cmd(cap);
			do_put(cap->oap->regname,
			  (cap->cmdchar == ']' && cap->nchar == 'p') ? FORWARD : BACKWARD,
														   cap->count1, TRUE);
		}
	}

#ifdef USE_MOUSE
	/*
	 * [ or ] followed by a middle mouse click: put selected text with
	 * indent adjustment.  Any other button just does as usual.
	 */
	else if (cap->nchar >= K_LEFTMOUSE && cap->nchar <= K_RIGHTRELEASE)
	{
		(void)do_mouse(cap->oap, cap->nchar,
			   (cap->cmdchar == ']') ? FORWARD : BACKWARD, cap->count1, TRUE);
	}
#endif /* USE_MOUSE */

	/* Not a valid cap->nchar. */
	else
		clearopbeep(cap->oap);
}

/*
 * Handle Normal mode "%" command.
 */
	static void
do_percent(cap)
	CMDARG		*cap;
{
	FPOS		*pos;

	cap->oap->inclusive = TRUE;
	if (cap->count0)		/* {cnt}% : goto {cnt} percentage in file */
	{
		if (cap->count0 > 100)
			clearopbeep(cap->oap);
		else
		{
			cap->oap->motion_type = MLINE;
			setpcmark();
							/* round up, so CTRL-G will give same value */
			curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
													  cap->count0 + 99) / 100;
			beginline(MAYBE);
		}
	}
	else					/* "%" : go to matching paren */
	{
		cap->oap->motion_type = MCHAR;
		if ((pos = findmatch(cap->oap, NUL)) == NULL)
			clearopbeep(cap->oap);
		else
		{
			setpcmark();
			curwin->w_cursor = *pos;
			curwin->w_set_curswant = TRUE;
		}
	}
}

/*
 * Handle the "r" command.
 */
	static int
do_replace(cap)
	CMDARG		*cap;
{
	char_u		*ptr;
	int			had_ctrl_v;
	int			command_busy = FALSE;
	long		n;

	ptr = ml_get_cursor();
		/* special key or not enough characters to replace */
	if (cap->nchar >= 0x100 || STRLEN(ptr) < (unsigned)cap->count1)
	{
		clearopbeep(cap->oap);
		return FALSE;
	}

	/*
	 * Replacing with a TAB is done by edit(), because it is complicated when
	 * 'expandtab' or 'smarttab' is set.
	 * Other characters are done below to avoid problems with things like
	 * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
	 */
	if (cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
	{
		stuffnumReadbuff(cap->count1);
		stuffcharReadbuff('R');
		stuffcharReadbuff('\t');
		stuffcharReadbuff(ESC);
		return FALSE;
	}

	if (cap->nchar == Ctrl('V'))				/* get another character */
	{
		had_ctrl_v = Ctrl('V');
		cap->nchar = get_literal();
	}
	else
		had_ctrl_v = NUL;
	if (u_save_cursor() == FAIL)		/* save line for undo */
		return FALSE;

	if (had_ctrl_v != Ctrl('V') && (cap->nchar == '\r' || cap->nchar == '\n'))
	{
		/*
		 * Replace character(s) by a single newline.
		 * Strange vi behaviour: Only one newline is inserted.
		 * Delete the characters here.
		 * Insert the newline with an insert command, takes care of
		 * autoindent.  The insert command depends on being on the last
		 * character of a line or not.
		 */
		(void)del_chars(cap->count1, FALSE);	/* delete the characters */
		stuffcharReadbuff('\r');
		stuffcharReadbuff(ESC);
		/*
		 * Give 'r' to edit(), to get the redo command right.
		 */
		command_busy = edit('r', FALSE, cap->count1);
	}
	else
	{
		prep_redo(cap->oap->regname, cap->count1,
											NUL, 'r', had_ctrl_v, cap->nchar);
		for (n = cap->count1; n > 0; --n)		/* replace the characters */
		{
			/*
			 * Replace a 'normal' character.
			 * Get ptr again, because u_save and/or showmatch() will have
			 * released the line.  At the same time we let know that the line
			 * will be changed.
			 */
			ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
			ptr[curwin->w_cursor.col] = cap->nchar;
			if (	   p_sm
					&& (cap->nchar == ')'
						|| cap->nchar == '}'
						|| cap->nchar == ']'))
				showmatch();
			++curwin->w_cursor.col;
		}
		--curwin->w_cursor.col;		/* cursor on the last replaced char */
		curwin->w_set_curswant = TRUE;
		set_last_insert(cap->nchar);
		changed_cline_bef_curs();	/* update cursor screen pos. later */
		/* w_botline might change a bit when replacing special characters */
		approximate_botline();
		update_screenline();
	}
	CHANGED;

	return command_busy;
}

/*
 * Exchange start and end of Visual area.
 */
	static void
switch_visual()
{
	linenr_t	lnum;
	colnr_t		col;

	lnum = VIsual.lnum;
	VIsual.lnum = curwin->w_cursor.lnum;
	curwin->w_cursor.lnum = lnum;
	col = VIsual.col;
	VIsual.col = curwin->w_cursor.col;
	curwin->w_cursor.col = col;
	curwin->w_set_curswant = TRUE;
}

/*
 * Swap case for "~" command, when it does not work like an operator.
 */
	static void
do_swapchar(cap)
	CMDARG		*cap;
{
	long		n;

	if (checkclearopq(cap->oap))
		return;

	if (lineempty(curwin->w_cursor.lnum))
	{
		clearopbeep(cap->oap);
		return;
	}

	prep_redo_cmd(cap);

	if (u_save_cursor() == FAIL)
		return;

	for (n = cap->count1; n > 0; --n)
	{
		if (gchar_cursor() == NUL)
			break;
		swapchar(cap->oap->op_type, &curwin->w_cursor);
		inc_cursor();
	}

	curwin->w_set_curswant = TRUE;
	CHANGED;

	/* assume that the length of the line doesn't change, so w_botline
	 * remains valid */
	update_screenline();
}

/*
 * Move cursor to mark.
 */
	static void
do_cursormark(cap, flag, pos)
	CMDARG		*cap;
	int			flag;
	FPOS		*pos;
{
	if (check_mark(pos) == FAIL)
		clearop(cap->oap);
	else
	{
		if (cap->cmdchar == '\'' || cap->cmdchar == '`')
			setpcmark();
		curwin->w_cursor = *pos;
		if (flag)
			beginline(TRUE);
		else
			adjust_cursor();
	}
	cap->oap->motion_type = flag ? MLINE : MCHAR;
	cap->oap->inclusive = FALSE;				/* ignored if not MCHAR */
	curwin->w_set_curswant = TRUE;
}

/*
 * Handle commands that are operators in Visual mode.
 */
	static void
do_visop(cap)
	CMDARG		*cap;
{
	static char_u trans[] = "YyDdCcxdXd";

	/* uppercase means linewise */
	if (isupper(cap->cmdchar) && VIsual_mode != Ctrl('V'))
		VIsual_mode = 'V';
	cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
	do_operator(cap);
}

/*
 * Translate a command into another command.
 */
	static void
do_optrans(cap)
	CMDARG		*cap;
{
	static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
							  (char_u *)"d$", (char_u *)"c$",
							  (char_u *)"cl", (char_u *)"cc",
							  (char_u *)"yy", (char_u *)":s\r"};
	static char_u *str = (char_u *)"xXDCsSY&";

	if (!checkclearopq(cap->oap))
	{
		if (cap->count0)
			stuffnumReadbuff(cap->count0);
		stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]);
	}
}

/*
 * Handle "'" and "`" commands.
 */
	static void
do_gomark(cap, flag)
	CMDARG		*cap;
	int			flag;
{
	FPOS		*pos;

	pos = getmark(cap->nchar, (cap->oap->op_type == OP_NOP));
	if (pos == (FPOS *)-1)			/* jumped to other file */
	{
		if (flag)
			beginline(TRUE);
	}
	else
		do_cursormark(cap, flag, pos);
}

/*
 * Handle CTRL-O and CTRL-I commands.
 */
	static void
do_pcmark(cap)
	CMDARG		*cap;
{
	FPOS		*pos;

	if (!checkclearopq(cap->oap))
	{
		pos = movemark((int)cap->count1);
		if (pos == (FPOS *)-1)			/* jump to other file */
			curwin->w_set_curswant = TRUE;
		else if (pos != NULL)				/* can jump */
			do_cursormark(cap, FALSE, pos);
		else
			clearopbeep(cap->oap);
	}
}

/*
 * Handle '"' command.
 */
	static void
do_regname(cap, opnum)
	CMDARG		*cap;
	linenr_t	opnum;
{
	if (checkclearop(cap->oap))
		return;
	if (cap->nchar == '=')
		cap->nchar = get_expr_register();
	if (cap->nchar != NUL && is_yank_register(cap->nchar, FALSE))
	{
		cap->oap->regname = cap->nchar;
		opnum = cap->count0;		/* remember count before '"' */
	}
	else
		clearopbeep(cap->oap);
}

/*
 * Handle "v", "V" and "CTRL-V" commands.
 */
	static void
do_visual(cap)
	CMDARG	*cap;
{
	if (VIsual_active)		/* change Visual mode */
	{
		if (VIsual_mode == cap->cmdchar)	/* stop visual mode */
			end_visual_mode();
		else								/* toggle char/block mode */
		{									/*     or char/line mode */
			VIsual_mode = cap->cmdchar;
			showmode();
		}
		update_curbuf(NOT_VALID);			/* update the inversion */
	}
	else					/* start Visual mode */
	{
		VIsual_save = VIsual;				/* keep for "gv" */
		VIsual_mode_save = VIsual_mode;
		check_visual_highlight();
		if (cap->count0)					/* use previously selected part */
		{
			if (resel_VIsual_mode == NUL)	/* there is none */
			{
				beep_flush();
				return;
			}
			VIsual = curwin->w_cursor;
			VIsual_active = TRUE;
#ifdef USE_MOUSE
			setmouse();
#endif
			if (p_smd)
				redraw_cmdline = TRUE;		/* show visual mode later */
			/*
			 * For V and ^V, we multiply the number of lines even if there
			 * was only one -- webb
			 */
			if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
			{
				curwin->w_cursor.lnum +=
									resel_VIsual_line_count * cap->count0 - 1;
				if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
					curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
			}
			VIsual_mode = resel_VIsual_mode;
			if (VIsual_mode == 'v')
			{
				if (resel_VIsual_line_count <= 1)
					curwin->w_cursor.col += resel_VIsual_col * cap->count0 - 1;
				else
					curwin->w_cursor.col = resel_VIsual_col;
			}
			if (resel_VIsual_col == MAXCOL)
			{
				curwin->w_curswant = MAXCOL;
				coladvance(MAXCOL);
			}
			else if (VIsual_mode == Ctrl('V'))
			{
				validate_virtcol();
				curwin->w_curswant = curwin->w_virtcol +
										   resel_VIsual_col * cap->count0 - 1;
				coladvance((colnr_t)curwin->w_curswant);
			}
			else
				curwin->w_set_curswant = TRUE;
			update_curbuf(NOT_VALID);	/* show the inversion */
		}
		else
		{
			VIsual = curwin->w_cursor;
			VIsual_mode = cap->cmdchar;
			VIsual_active = TRUE;
#ifdef USE_MOUSE
			setmouse();
#endif
			if (p_smd)
				redraw_cmdline = TRUE;	/* show visual mode later */
			update_screenline();		/* start the inversion */
		}
	}
}

/*
 * Handle the "g" command.
 */
	static int
do_g_cmd(cap)
	CMDARG		*cap;
{
	OPARG		*oap = cap->oap;
	FPOS		*pos;
	FPOS		tpos;
	int			i;
	int			flag = FALSE;
	int			command_busy = FALSE;

	switch (cap->nchar)
	{
	/*
	 * "gv": reselect the previous visual area
	 */
	case 'v':
		if (checkclearop(oap))
			break;
		if (VIsual_active)
			pos = &VIsual_save;
		else
			pos = &VIsual;
		if (pos->lnum == 0 || pos->lnum > curbuf->b_ml.ml_line_count ||
														 VIsual_end.lnum == 0)
			beep_flush();
		else
		{
			/* exchange previous and current visual area */
			if (VIsual_active)
			{
				tpos = VIsual;
				VIsual = VIsual_save;
				VIsual_save = tpos;
				i = VIsual_mode;
				VIsual_mode = VIsual_mode_save;
				VIsual_mode_save = i;
				tpos = curwin->w_cursor;
			}
			curwin->w_cursor = VIsual_end;
			if (VIsual_active)
				VIsual_end = tpos;
			check_cursor_lnum();
			VIsual_active = TRUE;
#ifdef USE_MOUSE
			setmouse();
#endif
			update_curbuf(NOT_VALID);
			showmode();
		}
		break;

	/*
	 * "gj" and "gk" two new funny movement keys -- up and down
	 * movement based on *screen* line rather than *file* line.
	 */
	case 'j':
	case K_DOWN:
		/* with 'nowrap' it works just like the normal "j" command */
		if (!curwin->w_p_wrap)
		{
			oap->motion_type = MLINE;
			i = cursor_down(cap->count1, oap->op_type != OP_NOP);
		}
		else
			i = screengo(oap, FORWARD, cap->count1);
		if (i == FAIL)
			clearopbeep(oap);
		break;

	case 'k':
	case K_UP:
		/* with 'nowrap' it works just like the normal "k" command */
		if (!curwin->w_p_wrap)
		{
			oap->motion_type = MLINE;
			i = cursor_up(cap->count1, oap->op_type != OP_NOP);
		}
		else
			i = screengo(oap, BACKWARD, cap->count1);
		if (i == FAIL)
			clearopbeep(oap);
		break;

	/*
	 * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
	 */
	case '^':
		flag = TRUE;
		/* FALLTHROUGH */

	case '0':
	case K_HOME:
	case K_KHOME:
		oap->motion_type = MCHAR;
		oap->inclusive = FALSE;
		if (curwin->w_p_wrap)
		{
			validate_virtcol();
			i = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
														   Columns) * Columns;
			if (curwin->w_p_nu && i > 8)
				i -= 8;
		}
		else
			i = curwin->w_leftcol;
		coladvance((colnr_t)i);
		if (flag)
			while (vim_iswhite(gchar_cursor()) && oneright() == OK)
				;
		curwin->w_set_curswant = TRUE;
		break;

	case '$':
	case K_END:
	case K_KEND:
		oap->motion_type = MCHAR;
		oap->inclusive = TRUE;
		if (curwin->w_p_wrap)
		{
			curwin->w_curswant = MAXCOL;	/* so we stay at the end */
			if (cap->count1 == 1)
			{
				validate_virtcol();
				i = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
												   Columns + 1) * Columns - 1;
				if (curwin->w_p_nu && i > 8)
					i -= 8;
				coladvance((colnr_t)i);
			}
			else if (screengo(oap, FORWARD, cap->count1 - 1) == FAIL)
				clearopbeep(oap);
		}
		else
		{
			i = curwin->w_leftcol + Columns - 1;
			if (curwin->w_p_nu)
				i -= 8;
			coladvance((colnr_t)i);
			curwin->w_set_curswant = TRUE;
		}
		break;

	/*
	 * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
	 */
	case '*':
	case '#':
		do_ident(cap, FALSE);
		break;

	/*
	 * ge and gE: go back to end of word
	 */
	case 'e':
	case 'E':
		oap->motion_type = MCHAR;
		curwin->w_set_curswant = TRUE;
		oap->inclusive = TRUE;
		if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL)
			clearopbeep(oap);
		break;

	/*
	 * "g CTRL-G": display info about cursor position
	 */
	case Ctrl('G'):
		cursor_pos_info();
		break;

	/*
	 * "gI": Start insert in column 1.
	 */
	case 'I':
		beginline(FALSE);
		if (!checkclearopq(oap))
		{
			if (u_save_cursor() == OK)
				command_busy = edit('g', FALSE, cap->count1);
		}
		break;

	/*
	 * "gf": goto file, edit file under cursor
	 * "]f" and "[f": can also be used.
	 */
	case 'f':
		do_gotofile(cap);
		break;

	/*
	 * "gs": Goto sleep, but keep on checking for CTRL-C
	 */
	case 's':
		while (cap->count1-- && !got_int)
		{
			ui_delay(1000L, TRUE);
			ui_breakcheck();
		}
		break;

	/*
	 * "ga": Display the ascii value of the character under the
	 * cursor.  It is displayed in decimal, hex, and octal. -- webb
	 */
	case 'a':
		do_ascii();
		break;

	/*
	 * "gg": Goto the first line in file.  With a count it goes to
	 * that line number like for "G". -- webb
	 */
	case 'g':
		do_goto(oap, cap->count0);		/* do_goto() will change 0 into 1 */
		break;

	/*
	 *   Two-character operators:
	 *   "gq"		Format text
	 *   "g~"		Toggle the case of the text.
	 *   "gu"		Change text to lower case.
	 *   "gU"		Change text to upper case.
	 */
	case 'q':
	case '~':
	case 'u':
	case 'U':
		oap->op_prechar = 'g';
		cap->cmdchar = cap->nchar;
		do_operator(cap);
		break;

/*
 * "gd": Find first occurence of pattern under the cursor in the
 *       current function
 * "gD": idem, but in the current file.
 */
	case 'd':
	case 'D':
		do_gd(oap, cap->nchar);
		break;

#ifdef USE_MOUSE
	/*
	 * g<*Mouse> : <C-*mouse>
	 */
	case K_MIDDLEMOUSE:
	case K_MIDDLEDRAG:
	case K_MIDDLERELEASE:
	case K_LEFTMOUSE:
	case K_LEFTDRAG:
	case K_LEFTRELEASE:
	case K_RIGHTMOUSE:
	case K_RIGHTDRAG:
	case K_RIGHTRELEASE:
		mod_mask = MOD_MASK_CTRL;
		(void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, FALSE);
		break;

	case K_IGNORE:
		break;
#endif

	default:
		clearopbeep(oap);
		break;
	}

	return command_busy;
}

/*
 * Handle "o" and "O" commands.
 */
	static int
do_opencmd(cap)
	CMDARG		*cap;
{
	int		command_busy = FALSE;

	if (!checkclearopq(cap->oap))
	{
		if (has_format_option(FO_OPEN_COMS))
			fo_do_comments = TRUE;
		if (u_save((linenr_t)(curwin->w_cursor.lnum -
											   (cap->cmdchar == 'O' ? 1 : 0)),
				   (linenr_t)(curwin->w_cursor.lnum +
											   (cap->cmdchar == 'o' ? 1 : 0))
					   ) == OK
				&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
														  TRUE, FALSE, 0))
			command_busy = edit(cap->cmdchar, TRUE, cap->count1);
		fo_do_comments = FALSE;
	}
	return command_busy;
}

/*
 * Handle an operator command.
 */
	static void
do_operator(cap)
	CMDARG		*cap;
{
	int		i;

	i = vim_strchr(op_chars, cap->cmdchar) - op_chars + 1;
	if (i == cap->oap->op_type)		/* double operator works on lines */
		do_lineop(cap);
	else if (!checkclearop(cap->oap))
	{
		cap->oap->start = curwin->w_cursor;
		cap->oap->op_type = i;
	}
}

/*
 * Handle linewise operator "dd", "yy", etc.
 */
	static void
do_lineop(cap)
	CMDARG		*cap;
{
	cap->oap->motion_type = MLINE;
	if (cursor_down(cap->count1 - 1L, cap->oap->op_type != OP_NOP) == FAIL)
		clearopbeep(cap->oap);
	else if (  cap->oap->op_type == OP_DELETE
			|| cap->oap->op_type == OP_LSHIFT
			|| cap->oap->op_type == OP_RSHIFT)
		beginline(MAYBE);
	else if (cap->oap->op_type != OP_YANK)		/* 'Y' does not move cursor */
		beginline(TRUE);
}

/*
 * Handle word motion commands "e", "E", "w" and "W".
 */
	static void
do_wordcmd(cap, type)
	CMDARG		*cap;
	int			type;
{
	int		n;
	int		word_end;
	int		flag = FALSE;

	/*
	 * Inclusive has been set for the "E" and "e" command.
	 */
	word_end = cap->oap->inclusive;

	/*
	 * For the "w" and "W" commands we need to check for special cases.
	 */
	if (!word_end)
	{
		/*
		 * "cw" and "cW" are a special case.
		 */
		if (cap->oap->op_type == OP_CHANGE)
		{
			n = gchar_cursor();
			if (n != NUL)					/* not an empty line */
			{
				if (vim_iswhite(n))
				{
					/*
					 * Reproduce a funny Vi behaviour: "cw" on a blank only
					 * changes one character, not all blanks until the start
					 * of the next word.  Only do this when the 'w' flag is
					 * included in 'cpoptions'.
					 */
					if (cap->count1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
					{
						cap->oap->inclusive = TRUE;
						cap->oap->motion_type = MCHAR;
						return;
					}
				}
				else
				{
					/*
					 * This is a little strange. To match what the real Vi
					 * does, we effectively map 'cw' to 'ce', and 'cW' to
					 * 'cE', provided that we are not on a space or a TAB.
					 * This seems impolite at first, but it's really more what
					 * we mean when we say 'cw'.
					 * Another strangeness: When standing on the end of a word
					 * "ce" will change until the end of the next wordt, but
					 * "cw" will change only one character! This is done by
					 * setting flag.
					 */
					cap->oap->inclusive = TRUE;
					word_end = TRUE;
					flag = TRUE;
				}
			}
		}
	}

	cap->oap->motion_type = MCHAR;
	curwin->w_set_curswant = TRUE;
	if (word_end)
		n = end_word(cap->count1, type, flag, FALSE);
	else
		n = fwd_word(cap->count1, type, cap->oap->op_type != OP_NOP);
	if (n == FAIL)
		clearopbeep(cap->oap);
}

	static void
do_goto(oap, lnum)
	OPARG	*oap;
	long	lnum;
{
	oap->motion_type = MLINE;
	setpcmark();
	if (lnum < 1L)
		lnum = 1L;
	else if (lnum > curbuf->b_ml.ml_line_count)
		lnum = curbuf->b_ml.ml_line_count;
	curwin->w_cursor.lnum = lnum;
	beginline(MAYBE);
}

/*
 * ESC in Normal mode: beep, but don't flush buffers.
 * Don't even beep if we are canceling a command.
 */
	static void
do_esc(cap, opnum)
	CMDARG		*cap;
	linenr_t	opnum;
{
	if (VIsual_active)
	{
		end_visual_mode();				/* stop Visual */
		update_curbuf(NOT_VALID);
	}
	else if (cap->oap->op_type == OP_NOP && opnum == 0 &&
								   cap->count0 == 0 && cap->oap->regname == 0)
		vim_beep();
	clearop(cap->oap);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.