[BACK]Return to normal.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

File: [local] / src / usr.bin / vim / Attic / normal.c (download)

Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:25 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42
Changes since 1.1: +0 -0 lines

Initial import of vim 4.2.

This is meant to replace nvi in the tree.  Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.

On the other hand, vim lacks a non-visual ex mode, in addition to open mode.

This includes the GUI (X11) code, but doesn't try to compile it.


/*	$OpenBSD: normal.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $	*/
/* 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"

#undef EXTERN
#undef INIT
#define EXTERN
#define INIT(x) x
#include "ops.h"

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

/*
 * If a count is given before the operator, it is saved in opnum.
 */
static linenr_t	opnum = 0;
static linenr_t	Prenum; 		/* The (optional) number before a command. */
static int		prechar = NUL;	/* prepended command char */
/*
 * 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;			/* number of cols or end column */

#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 __ARGS((long, int, int, int, int));
static int		checkclearop __ARGS((void));
static int		checkclearopq __ARGS((void));
static void		clearop __ARGS((void));
static void		clearopbeep __ARGS((void));
static void		del_from_showcmd __ARGS((int));
static void		do_gd __ARGS((int nchar));

/*
 * 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. Buffer 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()
{
	register int	c;
	long 			n = 0;					/* init for GCC */
	int				flag = FALSE;
	int				flag2 = FALSE;
	int 			type = 0;				/* type of operation */
	int 			dir = FORWARD;			/* search direction */
	int				nchar = NUL;			/* next command char */
	int				finish_op;
	linenr_t		Prenum1;
	char_u			*searchbuff = NULL;		/* buffer for search string */
	FPOS			*pos = NULL;			/* init for gcc */
	char_u			*ptr = NULL;
	int				command_busy = FALSE;
	int				ctrl_w = FALSE;			/* got CTRL-W command */
	int				old_col = 0;
	int				dont_adjust_op_end = FALSE;

	Prenum = 0;
	/*
	 * 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 = (op_type != NOP);

	if (!finish_op && !yankbuffer)
		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 'Prenum' */
	while ((c >= '1' && c <= '9') || (Prenum != 0 && (c == K_DEL || c == '0')))
	{
		if (c == K_DEL)
		{
			Prenum /= 10;
			del_from_showcmd(4);		/* delete the digit and ~@% */
		}
		else
			Prenum = Prenum * 10 + (c - '0');
		if (Prenum < 0)			/* got too large! */
			Prenum = 999999999;
		c = vgetc();
#ifdef HAVE_LANGMAP
		LANGMAP_ADJUST(c, TRUE);
#endif
		(void)add_to_showcmd(c, FALSE);
	}

/*
 * If we got CTRL-W there may be a/another count
 */
	if (c == Ctrl('W') && !ctrl_w && op_type == NOP)
	{
		ctrl_w = TRUE;
		opnum = Prenum;						/* remember first count */
		Prenum = 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 */
	}

	/*
	 * 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 Prenum. 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 (Prenum)
				Prenum *= opnum;
			else
				Prenum = opnum;
			opnum = 0;
	}

	Prenum1 = (Prenum == 0 ? 1 : Prenum);		/* Prenum often defaults to 1 */

	/*
	 * Get an additional character if we need one.
	 * For CTRL-W we already got it when looking for a count.
	 */
	if (ctrl_w)
	{
		nchar = c;
		c = Ctrl('W');
	}
	else if ((op_type == NOP && vim_strchr((char_u *)"@zm\"", c) != NULL) ||
			(op_type == NOP && !VIsual_active &&
				 vim_strchr((char_u *)"rZ", c) != NULL) ||
			vim_strchr((char_u *)"tTfF[]g'`", c) != NULL ||
			(c == 'q' && !Recording && !Exec_reg))
	{
		++no_mapping;
		++allow_keys;			/* no mapping for nchar, but allow key codes */
		nchar = vgetc();
#ifdef HAVE_LANGMAP
		/* adjust chars > 127: tTfFr should leave lang of nchar unchanged! */
		LANGMAP_ADJUST(nchar, vim_strchr((char_u *)"tTfFr", c) == NULL);
#endif
#ifdef RIGHTLEFT
		if (p_hkmap && strchr("tTfFr", c) && KeyTyped) 	/* Hebrew mapped char */
			nchar = hkmap(nchar);
#endif
		--no_mapping;
		--allow_keys;
		(void)add_to_showcmd(nchar, FALSE);
	}
	if (p_sc)
		flushbuf();				/* flush the showcmd characters onto the
								 * screen so we can see them while the command
								 * is being executed
								 */

	State = NORMAL;
	if (nchar == ESC)
	{
		clearop();
		goto normal_end;
	}
	msg_didout = FALSE;		/* don't scroll screen up for normal command */
	msg_col = 0;

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

/*
 * 0: Macros
 */
	  case 'q': 		/* (stop) recording into a named register */
		if (checkclearop())
			break;
						/* command is ignored while executing a register */
		if (!Exec_reg && do_record(nchar) == FAIL)
			clearopbeep();
		break;

	 case '@':			/* execute a named buffer */
		if (checkclearop())
			break;
		while (Prenum1--)
		{
			if (do_execbuf(nchar, FALSE, FALSE) == FAIL)
			{
				clearopbeep();
				break;
			}
		}
		break;

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

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

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

	  case Ctrl('F'):
	  case K_S_DOWN:
	  case K_PAGEDOWN:
		if (checkclearop())
			break;
		(void)onepage(dir, Prenum1);
		break;

	  case Ctrl('E'):
		if (checkclearop())
			break;
		scrollup(Prenum1);
		if (p_so)
			cursor_correct();
		/* We may have moved to another line -- webb */
		coladvance(curwin->w_curswant);
		cursupdate();
		updateScreen(VALID);
		break;

	  case Ctrl('Y'):
		if (checkclearop())
			break;
		scrolldown(Prenum1);
		if (p_so)
			cursor_correct();
		/* We may have moved to another line -- webb */
		coladvance(curwin->w_curswant);
		updateScreen(VALID);
		break;

	  case 'z':
		if (checkclearop())
			break;
		if (nchar < 0x100 && isdigit(nchar))
		{
			Prenum = nchar - '0';
			for (;;)
			{
				++no_mapping;
				++allow_keys;	/* no mapping for nchar, but allow key codes */
				nchar = vgetc();
#ifdef HAVE_LANGMAP
				LANGMAP_ADJUST(c, TRUE);
#endif
				--no_mapping;
				--allow_keys;
				(void)add_to_showcmd(nchar, FALSE);
				if (c == K_DEL)
					Prenum /= 10;
				else if (nchar < 0x100 && isdigit(nchar))
					Prenum = Prenum * 10 + (nchar - '0');
				else if (nchar == CR)
				{
					win_setheight((int)Prenum);
					break;
				}
				else if (nchar == 'l' || nchar == 'h' ||
										  nchar == K_LEFT || nchar == K_RIGHT)
				{
					Prenum1 = Prenum ? Prenum : 1;
					goto dozet;
				}
				else
				{
					clearopbeep();
					break;
				}
			}
			op_type = NOP;
			break;
		}
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 &&
									Prenum && Prenum != curwin->w_cursor.lnum)
		{
			setpcmark();
			if (Prenum > curbuf->b_ml.ml_line_count)
				curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
			else
				curwin->w_cursor.lnum = Prenum;
		}
		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)
			{
				colnr_t		s, e;

				if ((colnr_t)Prenum1 > curwin->w_leftcol)
					curwin->w_leftcol = 0;
				else
					curwin->w_leftcol -= (colnr_t)Prenum1;
				n = curwin->w_leftcol + Columns -
					(curwin->w_p_nu ? 8 : 0) - 1;
				if (curwin->w_virtcol > (colnr_t)n)
					coladvance((colnr_t)n);

				getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
				if (e > (colnr_t)n)
					coladvance(s - 1);
				redraw_later(NOT_VALID);
			}
			break;

			/* "zl" - scroll screen to the left */
		  case 'l':
		  case K_RIGHT:
			if (!curwin->w_p_wrap)
			{
				colnr_t		s, e;

				/* scroll the window left */
				curwin->w_leftcol += (colnr_t)Prenum1;

				/* If the cursor has moved off the screen, put it at the
				 * first char on the screen */
				if (curwin->w_leftcol > curwin->w_virtcol)
					(void)coladvance(curwin->w_leftcol);

				/* If the start of the character under the cursor is not
				 * on the screen, advance the cursor one more char.  If
				 * this fails (last char of the line) adjust the
				 * scrolling. */
				getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
				if (s < curwin->w_leftcol)
					if (coladvance(e + 1) == FAIL)
						curwin->w_leftcol = s;

				redraw_later(NOT_VALID);
			}
			break;

			/* "zs" - scroll screen, cursor at the start */
		  case 's':
			if (!curwin->w_p_wrap)
			{
				colnr_t		s;

				getvcol(curwin, &curwin->w_cursor, &s, NULL, NULL);
				curwin->w_leftcol = s;
				redraw_later(NOT_VALID);
			}
			break;

			/* "ze" - scroll screen, cursor at the end */
		  case 'e':
			if (!curwin->w_p_wrap)
			{
				colnr_t		e;

				getvcol(curwin, &curwin->w_cursor, NULL, NULL, &e);
				if ((long)e < Columns)
					curwin->w_leftcol = 0;
				else
					curwin->w_leftcol = e - Columns + 1;
				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();
		}
		updateScreen(VALID);
		break;

/*
 *	  2: Control commands
 */
	  case ':':
	    if (VIsual_active)
			goto dooperator;
		if (checkclearop())
			break;
		/*
		 * translate "count:" into ":.,.+(count - 1)"
		 */
		if (Prenum)
		{
			stuffReadbuff((char_u *)".");
			if (Prenum > 1)
			{
				stuffReadbuff((char_u *)",.+");
				stuffnumReadbuff((long)Prenum - 1L);
			}
		}
		do_cmdline(NULL, FALSE, FALSE);
		break;

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

	  case Ctrl('L'):
		if (checkclearop())
			break;
		updateScreen(CLEAR);
		break;

	  case Ctrl('G'):
		if (checkclearop())
			break;
			/* print full name if count given or :cd used */
		fileinfo(did_cd | (int)Prenum, FALSE, FALSE);

		/*
		 * In Visual mode and "^O^G" in Insert mode, the message will be
		 * overwritten by the mode message.  Wait a bit, until a key is hit.
		 */
		if ((VIsual_active || (restart_edit && p_smd)) && KeyTyped)
		{
			setcursor();
			flushbuf();
			mch_delay(10000L, FALSE);
		}
		break;

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

	  case 'Z': 		/* write, if changed, and exit */
		if (checkclearopq())
			break;
		if (nchar != 'Z')
		{
			clearopbeep();
			break;
		}
		stuffReadbuff((char_u *)":x\n");
		break;

	  case Ctrl(']'):			/* :ta to current identifier */
	  case 'K':					/* run program for current identifier */
		if (VIsual_active)		/* :ta to visual highlighted text */
		{
			if (VIsual.lnum != curwin->w_cursor.lnum)
			{
				clearopbeep();
				break;
			}
			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())
			break;
		/*FALLTHROUGH*/

	  case 163:					/* the pound sign, '#' for English keyboards */
		if (c == 163)
	  		c = '#';
		/*FALLTHROUGH*/

	  case '*': 				/* / to current identifier or string */
	  case '#': 				/* ? to current identifier or string */
search_word:
		if (c == 'g')
			type = nchar;		/* "g*" or "g#" */
		else
			type = c;
		if (ptr == NULL && (n = find_ident_under_cursor(&ptr, (type == '*' ||
					type == '#') ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
		{
			clearop();
			break;
		}

		if (Prenum)
			stuffnumReadbuff(Prenum);
		switch (type)
		{
			case '*':
				stuffReadbuff((char_u *)"/");
				/* FALLTHROUGH */

			case '#':
				if (type == '#')
					stuffReadbuff((char_u *)"?");

				/*
				 * put cursor at start of word, makes search skip the word
				 * under the cursor
				 */
				curwin->w_cursor.col = ptr - ml_get_curline();

				if (c != 'g' && 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 *)" ");
				}
				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 == '\\' || (!(type == '*' || type == '#') &&
									  vim_strchr(escape_chars, *ptr) != NULL))
				stuffcharReadbuff('\\');
				/* don't interpret the characters as edit commands */
			if (*ptr < ' ' || *ptr > '~')
				stuffcharReadbuff(Ctrl('V'));
			stuffcharReadbuff(*ptr++);
		}

		if (c != 'g' && (type == '*' || type == '#') && iswordchar(ptr[-1]))
			stuffReadbuff((char_u *)"\\>");
		stuffReadbuff((char_u *)"\n");
		break;

	  case Ctrl('T'):		/* backwards in tag stack */
		if (checkclearopq())
			break;
		do_tag((char_u *)"", 2, (int)Prenum1);
		break;

/*
 * Cursor motions
 */
	  case 'G':
goto_line:
		op_motion_type = MLINE;
		setpcmark();
		if (Prenum == 0 || Prenum > curbuf->b_ml.ml_line_count)
			curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
		else
			curwin->w_cursor.lnum = Prenum;
		beginline(MAYBE);
		break;

	  case 'H':
	  case 'M':
		if (c == 'M')
		{
			int		used = 0;

			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 = Prenum;
		op_motion_type = MLINE;
		setpcmark();
		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_correct();		/* correct for 'so' */
		beginline(MAYBE);
		break;

	  case 'L':
		op_motion_type = MLINE;
		setpcmark();
		curwin->w_cursor.lnum = curwin->w_botline - 1;
		if (Prenum >= curwin->w_cursor.lnum)
			curwin->w_cursor.lnum = 1;
		else
			curwin->w_cursor.lnum -= Prenum;
		cursor_correct();		/* correct for 'so' */
		beginline(MAYBE);
		break;

	  case 'l':
	  case K_RIGHT:
	  case ' ':
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		n = Prenum1;
		while (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 (((c == ' '     && vim_strchr(p_ww, 's') != NULL) ||
					 (c == 'l'     && vim_strchr(p_ww, 'l') != NULL) ||
					 (c == 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 op_inclusive when last char in the line is
					 * included, move to next line after that */
					if ((op_type == DELETE || op_type == CHANGE) &&
						   !op_inclusive && !lineempty(curwin->w_cursor.lnum))
						op_inclusive = TRUE;
					else
					{
						++curwin->w_cursor.lnum;
						curwin->w_cursor.col = 0;
						curwin->w_set_curswant = TRUE;
						op_inclusive = FALSE;
					}
					continue;
				}
				if (op_type == NOP)
					beep_flush();
				else
				{
					if (lineempty(curwin->w_cursor.lnum))
						clearopbeep();
					else
					{
						op_inclusive = TRUE;
						if (n)
							beep_flush();
					}
				}
				break;
			}
		}
		break;

	  case 'h':
	  case K_LEFT:
	  case K_BS:
	  case Ctrl('H'):
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		n = Prenum1;
		while (n--)
		{
			if (oneleft() == FAIL)
			{
					/* backspace and del wrap to previous line if 'whichwrap'
					 * 	  bit 0 set.
					 * 'h' wraps to previous line if 'whichwrap' bit 2 set.
					 * CURS_LEFT wraps to previous line if 'whichwrap' bit 3
					 * set. */
				if (   (((c == K_BS || c == Ctrl('H'))
									 && vim_strchr(p_ww, 'b') != NULL) ||
						(c == 'h'    && vim_strchr(p_ww, 'h') != NULL) ||
						(c == 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 ((op_type == DELETE || op_type == CHANGE) &&
											!lineempty(curwin->w_cursor.lnum))
					{
						++curwin->w_cursor.col;
						dont_adjust_op_end = TRUE;
					}
					continue;
				}
				else if (op_type != DELETE && op_type != CHANGE)
					beep_flush();
				else if (Prenum1 == 1)
					clearopbeep();
				break;
			}
		}
		break;

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

	  case 'k':
	  case K_UP:
	  case Ctrl('P'):
normal_k:
		op_motion_type = MLINE;
		if (cursor_up(Prenum1) == FAIL)
			clearopbeep();
		else if (flag)
			beginline(TRUE);
		break;

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

	  case 'j':
	  case K_DOWN:
	  case Ctrl('N'):
	  case NL:
normal_j:
		op_motion_type = MLINE;
		if (cursor_down(Prenum1) == FAIL)
			clearopbeep();
		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 '_':
lineop:
		old_col = curwin->w_curswant;
		op_motion_type = MLINE;
		if (cursor_down((long)(Prenum1 - 1)) == FAIL)
			clearopbeep();
		if (op_type == DELETE || op_type == LSHIFT || op_type == RSHIFT)
			beginline(MAYBE);
		else if (op_type != YANK)			/* 'Y' does not move cursor */
			beginline(TRUE);
		break;

	  case K_HOME:
		if ((mod_mask & MOD_MASK_CTRL))
			goto goto_line_one;
		Prenum = 1;
		/* FALLTHROUGH */

	  case '|':
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		beginline(FALSE);
		if (Prenum > 0)
		{
			coladvance((colnr_t)(Prenum - 1));
			curwin->w_curswant = (colnr_t)(Prenum - 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:
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		curwin->w_set_curswant = TRUE;
		if (bck_word(Prenum1, type, FALSE) == FAIL)
			clearopbeep();
		break;

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

	  case 'e':
		op_inclusive = TRUE;
		goto dowrdcmd;

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

	  case 'w':
	  case K_S_RIGHT:
		op_inclusive = FALSE;
		flag = TRUE;
		/*
		 * 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 type to 2.
		 */
		if (op_type == CHANGE && (n = gchar_cursor()) != ' ' && n != TAB &&
																n != NUL)
		{
			op_inclusive = TRUE;
			flag = FALSE;
			flag2 = TRUE;
		}

dowrdcmd:
		op_motion_type = MCHAR;
		curwin->w_set_curswant = TRUE;
		if (flag)
			n = fwd_word(Prenum1, type, op_type != NOP);
		else
			n = end_word(Prenum1, type, flag2, FALSE);
		if (n == FAIL)
			clearopbeep();
		break;

	  case K_END:
		if ((mod_mask & MOD_MASK_CTRL))
			goto goto_line;
		/* FALLTHROUGH */

	  case '$':
		op_motion_type = MCHAR;
		op_inclusive = TRUE;
		curwin->w_curswant = MAXCOL;				/* so we stay at the end */
		if (cursor_down((long)(Prenum1 - 1)) == FAIL)
		{
			clearopbeep();
			break;
		}
		break;

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

	  case '0':
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		beginline(flag);
		break;

/*
 * 4: Searches
 */
	  case '?':
	  case '/':
		if ((searchbuff = getcmdline(c, Prenum1)) == NULL)
		{
			clearop();
			break;
		}
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		curwin->w_set_curswant = TRUE;

		n = do_search(c, searchbuff, Prenum1,
						 SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
		if (n == 0)
			clearop();
		else if (n == 2)
			op_motion_type = MLINE;
		break;

	  case 'N':
		flag = SEARCH_REV;

	  case 'n':
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		curwin->w_set_curswant = TRUE;
		if (!do_search(0, NULL, Prenum1,
				  SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG | flag))
			clearop();
		break;

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

	  case 't':
		type = 1;
		goto docsearch;

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

	  case 'f':
docsearch:
		op_motion_type = MCHAR;
		if (dir == BACKWARD)
			op_inclusive = FALSE;
		else
			op_inclusive = TRUE;
		curwin->w_set_curswant = TRUE;
		if (nchar >= 0x100 || !searchc(nchar, dir, type, Prenum1))
			clearopbeep();
		break;

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

	  case ';':
	    dir = flag;
	    goto docsearch;		/* nchar == NUL, thus repeat previous search */

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

	  case ']':
		op_motion_type = MCHAR;
		op_inclusive = FALSE;

		/*
		 * "[f" or "]f" : Edit file under the cursor (same as "gf")
		 */
		if (nchar == 'f')
			goto gotofile;

		/*
		 * 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"
		 */
		if (nchar == 'i' || nchar == 'I' || nchar == Ctrl('I') ||
			nchar == 'd' || nchar == 'D' || nchar == Ctrl('D'))
		{
			int			len;

			if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
			{
				clearop();
				break;
			}
			find_pattern_in_path(ptr, len, TRUE,
				Prenum == 0 ? !isupper(nchar) : FALSE,
				((nchar & 0xf) == ('d' & 0xf)) ?  FIND_DEFINE : FIND_ANY,
				Prenum1,
				isupper(nchar) ? ACTION_SHOW_ALL :
							islower(nchar) ? ACTION_SHOW : ACTION_GOTO,
				c == ']' ? curwin->w_cursor.lnum : (linenr_t)1,
				(linenr_t)MAXLNUM);
			curwin->w_set_curswant = TRUE;
			break;
		}

		/*
		 * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
		 * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
		 * "[/", "[*", "]/", "]*": go to Nth comment start/end.
		 */
		if ((c == '[' && vim_strchr((char_u *)"{(*/#", nchar) != NULL) ||
		    (c == ']' && vim_strchr((char_u *)"})*/#", nchar) != NULL))
		{
			FPOS old_pos;
			FPOS new_pos;

			if (nchar == '*')
				nchar = '/';
			old_pos = curwin->w_cursor;
			new_pos.lnum = 0;
			while (Prenum1--)
			{
				if ((pos = findmatchlimit(nchar,
						   (c == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
				{
					if (new_pos.lnum == 0)	/* nothing found */
						clearopbeep();
					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;
			}
			break;
		}

		/*
		 * "[[", "[]", "]]" and "][": move to start or end of function
		 */
		if (nchar == '[' || nchar == ']')
		{
			if (nchar == c)				/* "]]" 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(dir, Prenum1, flag,
						   (op_type != NOP && dir == FORWARD && flag == '{')))
				clearopbeep();
			else if (op_type == NOP)
				beginline(TRUE);
			break;
		}

		/*
		 * "[p", "[P", "]P" and "]p": put with indent adjustment
		 */
		if (nchar == 'p' || nchar == 'P')
		{
			if (checkclearopq())
				break;
			prep_redo(Prenum, NUL, c, nchar, NUL);
			do_put((c == ']' && nchar == 'p') ? FORWARD : BACKWARD,
															Prenum1, TRUE);
			break;
		}

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

		/*
		 * end of '[' and ']': not a valid nchar
		 */
		clearopbeep();
		break;

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

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

	  case ')':
		op_motion_type = MCHAR;
		if (c == ')')
			op_inclusive = FALSE;
		else
			op_inclusive = TRUE;
		curwin->w_set_curswant = TRUE;

		if (findsent(dir, Prenum1) == FAIL)
			clearopbeep();
		break;

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

	  case '}':
		op_motion_type = MCHAR;
		op_inclusive = FALSE;
		curwin->w_set_curswant = TRUE;
		if (!findpar(dir, Prenum1, NUL, FALSE))
			clearopbeep();
		break;

/*
 * 5: Edits
 */
	  case '.':				/* redo command */
		if (checkclearopq())
			break;
		/*
		 * 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(Prenum, restart_edit && !arrow_used) == FAIL)
			clearopbeep();
		break;

	  case 'u':				/* undo */
	    if (VIsual_active || op_type == vim_strchr(opchars, 'u') - opchars + 1)
			goto dooperator;
	  case K_UNDO:
		if (checkclearopq())
			break;
		u_undo((int)Prenum1);
		curwin->w_set_curswant = TRUE;
		break;

	  case Ctrl('R'):		/* undo undo */
		if (checkclearopq())
			break;
	  	u_redo((int)Prenum1);
		curwin->w_set_curswant = TRUE;
		break;

	  case 'U':				/* Undo line */
	    if (VIsual_active || op_type == vim_strchr(opchars, 'U') - opchars + 1)
			goto dooperator;
		if (checkclearopq())
			break;
		u_undoline();
		curwin->w_set_curswant = TRUE;
		break;

	  case 'r':
	    if (VIsual_active)
		{
			c = 'c';
			goto dooperator;
		}
		if (checkclearop())
			break;
		ptr = ml_get_cursor();
			/* special key or not enough characters to replace */
		if (nchar >= 0x100 || STRLEN(ptr) < (unsigned)Prenum1)
		{
			clearopbeep();
			break;
		}
		/*
		 * Replacing with a TAB is done by edit(), because it is complicated
		 * when 'expandtab' 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 (nchar == '\t' && curbuf->b_p_et)
		{
			prep_redo(Prenum1, NUL, 'r', '\t', NUL);
			stuffnumReadbuff(Prenum1);
			stuffcharReadbuff('R');
			stuffcharReadbuff('\t');
			stuffcharReadbuff(ESC);
			break;
		}

		if (nchar == Ctrl('V'))				/* get another character */
		{
			c = Ctrl('V');
			nchar = get_literal();
		}
		else
			c = NUL;
		prep_redo(Prenum1, NUL, 'r', c, nchar);
		if (u_save_cursor() == FAIL)		/* save line for undo */
			break;
		/*
		 * Replace characters by a newline.
		 * Strange vi behaviour: Only one newline is inserted.
		 * Delete the characters here.
		 * Insert the newline with an insert command, takes care of
		 * autoindent.
		 */
		if (c != Ctrl('V') && (nchar == '\r' || nchar == '\n'))
		{
			while (Prenum1--)					/* delete the characters */
				delchar(FALSE);
				/* replacing the last character of a line is different */
			if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL)
			{
				--curwin->w_cursor.col;
				stuffcharReadbuff('a');
			}
			else
				stuffcharReadbuff('i');
			stuffcharReadbuff('\r');
			stuffcharReadbuff(ESC);
		}
		else
		{
			while (Prenum1--)					/* 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] = nchar;
				if (p_sm && (nchar == ')' || nchar == '}' || nchar == ']'))
					showmatch();
				++curwin->w_cursor.col;
			}
			--curwin->w_cursor.col;		/* cursor on the last replaced char */
		}
		curwin->w_set_curswant = TRUE;
		CHANGED;
		updateline();
		set_last_insert(nchar);
		break;

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

		prep_redo(Prenum, NUL, 'J', NUL, NUL);
		do_do_join(Prenum, 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 (op_type != NOP || VIsual_active)
		{
			if (c == 'P')
			{
				if (current_block('{', Prenum1) == FAIL)
					clearopbeep();
			}
			else
			{
				if (current_par(c, Prenum1) == FAIL)
					clearopbeep();
			}
			curwin->w_set_curswant = TRUE;
		}
		else
		{
			prep_redo(Prenum, NUL, c, NUL, NUL);
			do_put(dir, Prenum1, FALSE);
		}
		break;

	  case Ctrl('A'):			/* add to number */
	  case Ctrl('X'):			/* subtract from number */
		if (checkclearopq())
			break;
		if (do_addsub((int)c, Prenum1) == OK)
			prep_redo(Prenum1, NUL, c, NUL, NUL);
		break;

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

	  case 'a':
		if (op_type != NOP || VIsual_active)
		{
			if (current_word(Prenum1, type) == FAIL)
				clearopbeep();
			curwin->w_set_curswant = TRUE;
		}
		else
		{
			if (c == 'A')
			{
				curwin->w_set_curswant = TRUE;
				while (oneright() == OK)
					;
			}

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

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

	  case 'i':
	  case K_INS:
insert_command:
		if (checkclearopq())
			break;
		if (u_save_cursor() == OK)
			command_busy = edit(c, FALSE, Prenum1);
		break;

	  case 'o':
	  	if (VIsual_active)	/* switch start and end of visual */
		{
			Prenum = VIsual.lnum;
			VIsual.lnum = curwin->w_cursor.lnum;
			curwin->w_cursor.lnum = Prenum;
			n = VIsual.col;
			VIsual.col = curwin->w_cursor.col;
			curwin->w_cursor.col = (int)n;
			curwin->w_set_curswant = TRUE;
			break;
		}
		if (checkclearop())
			break;
		if (has_format_option(FO_OPEN_COMS))
			fo_do_comments = TRUE;
		if (u_save(curwin->w_cursor.lnum,
								(linenr_t)(curwin->w_cursor.lnum + 1)) == OK &&
						Opencmd(FORWARD, TRUE, FALSE))
			command_busy = edit('o', TRUE, Prenum1);
		fo_do_comments = FALSE;
		break;

	  case 'O':
		if (checkclearopq())
			break;
		if (has_format_option(FO_OPEN_COMS))
			fo_do_comments = TRUE;
		if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
			   curwin->w_cursor.lnum) == OK && Opencmd(BACKWARD, TRUE, FALSE))
			command_busy = edit('O', TRUE, Prenum1);
		fo_do_comments = FALSE;
		break;

	  case 'R':
	    if (VIsual_active)
		{
			c = 'c';
			VIsual_mode = 'V';
			goto dooperator;
		}
		if (checkclearopq())
			break;
		if (u_save_cursor() == OK)
			command_busy = edit('R', FALSE, Prenum1);
		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 &&
					op_type != vim_strchr(opchars, '~') - opchars + 1)
		{
			if (checkclearopq())
				break;
			if (lineempty(curwin->w_cursor.lnum))
			{
				clearopbeep();
				break;
			}
			prep_redo(Prenum, NUL, '~', NUL, NUL);

			if (u_save_cursor() == FAIL)
				break;

			for (; Prenum1 > 0; --Prenum1)
			{
				if (gchar_cursor() == NUL)
					break;
				swapchar(&curwin->w_cursor);
				inc_cursor();
			}

			curwin->w_set_curswant = TRUE;
			CHANGED;
			updateline();
			break;
		}
		/*FALLTHROUGH*/

	  case 'd':
	  case 'c':
	  case 'y':
	  case '>':
	  case '<':
	  case '!':
	  case '=':
	  case 'Q':					/* should start Ex mode */
dooperator:
		n = vim_strchr(opchars, c) - opchars + 1;
		if (n == op_type)		/* double operator works on lines */
			goto lineop;
		if (checkclearop())
			break;
		if (Prenum != 0)
			opnum = Prenum;
		curbuf->b_op_start = curwin->w_cursor;
		op_type = (int)n;
		break;

/*
 * 8: Abbreviations
 */

	 /* when Visual the next commands are operators */
	  case K_DEL:
	  		c = 'x';			/* DEL key behaves like 'x' */
	  case 'S':
	  case 'Y':
	  case 'D':
	  case 'C':
	  case 'x':
	  case 'X':
	  case 's':
		/*
		 * 's' or 'S' with an operator: Operate on sentence or section.
		 */
	    if (op_type != NOP || VIsual_active)
		{
			if (c == 's')		/* sentence */
			{
				if (current_sent(Prenum1) == FAIL)
					clearopbeep();
				curwin->w_set_curswant = TRUE;
				break;
			}
			if (c == 'S')		/* block with () */
			{
				if (current_block('(', Prenum1) == FAIL)
					clearopbeep();
				curwin->w_set_curswant = TRUE;
				break;
			}
		}
	  	if (VIsual_active)
		{
			static char_u trans[] = "YyDdCcxdXd";

											/* uppercase means linewise */
			if (isupper(c) && VIsual_mode != Ctrl('V'))
				VIsual_mode = 'V';
			c = *(vim_strchr(trans, c) + 1);
			goto dooperator;
		}

	  case '&':
		if (checkclearopq())
			break;
		if (Prenum)
			stuffnumReadbuff(Prenum);

		{
				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&";

				stuffReadbuff(ar[(int)(vim_strchr(str, c) - str)]);
		}
		break;

/*
 * 9: Marks
 */

	  case 'm':
		if (checkclearop())
			break;
		if (setmark(nchar) == FAIL)
			clearopbeep();
		break;

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

	  case '`':
		pos = getmark(nchar, (op_type == NOP));
		if (pos == (FPOS *)-1)	/* jumped to other file */
		{
			if (flag)
				beginline(TRUE);
			break;
		}

cursormark:
		if (check_mark(pos) == FAIL)
			clearop();
		else
		{
			if (c == '\'' || c == '`')
				setpcmark();
			curwin->w_cursor = *pos;
			if (flag)
				beginline(TRUE);
		}
		op_motion_type = flag ? MLINE : MCHAR;
		op_inclusive = FALSE;		/* ignored if not MCHAR */
		curwin->w_set_curswant = TRUE;
		break;

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

	case Ctrl('I'):			/* goto newer pcmark */
		if (checkclearopq())
			break;
		pos = movemark((int)Prenum1);
		if (pos == (FPOS *)-1)	/* jump to other file */
		{
			curwin->w_set_curswant = TRUE;
			break;
		}
		if (pos != NULL)	/* can jump */
			goto cursormark;
		clearopbeep();
		break;

/*
 * 10. Buffer setting
 */
	  case '"':
		if (checkclearop())
			break;
		if (nchar != NUL && is_yank_buffer(nchar, FALSE))
		{
			yankbuffer = nchar;
			opnum = Prenum;		/* remember count before '"' */
		}
		else
			clearopbeep();
		break;

/*
 * 11. Visual
 */
 	  case 'v':
	  case 'V':
	  case Ctrl('V'):
		if (checkclearop())
			break;

			/* change Visual mode */
		if (VIsual_active)
		{
			if (VIsual_mode == c)			/* stop visual mode */
			{
				end_visual_mode();
			}
			else							/* toggle char/block mode */
			{								/*     or char/line mode */
				VIsual_mode = c;
				showmode();
			}
			update_curbuf(NOT_VALID);		/* update the inversion */
		}
			/* start Visual mode */
		else
		{
			VIsual_save = VIsual;			/* keep for "gv" */
			VIsual_mode_save = VIsual_mode;
			start_visual_highlight();
			if (Prenum)						/* use previously selected part */
			{
				if (resel_VIsual_mode == NUL)	/* there is none */
				{
					beep_flush();
					break;
				}
				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 * Prenum - 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 * Prenum - 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'))
				{
					curwin->w_curswant = curwin->w_virtcol +
											resel_VIsual_col * Prenum - 1;
					coladvance((colnr_t)curwin->w_curswant);
				}
				else
					curwin->w_set_curswant = TRUE;
				curs_columns(TRUE);			/* recompute w_virtcol */
				update_curbuf(NOT_VALID);	/* show the inversion */
			}
			else
			{
				VIsual = curwin->w_cursor;
				VIsual_mode = c;
				VIsual_active = TRUE;
#ifdef USE_MOUSE
				setmouse();
#endif
				if (p_smd)
					redraw_cmdline = TRUE;	/* show visual mode later */
				updateline();				/* start the inversion */
			}
		}
		break;

/*
 * 12. Suspend
 */

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

/*
 * 13. Window commands
 */

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

/*
 *   14. extended commands (starting with 'g')
 */
 	case 'g':
		switch (nchar)
		{
			/*
			 * "gv": reselect the previous visual area
			 */
			case 'v':
				if (checkclearop())
					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
				{
					FPOS	tt;
					int		t;

					/* exchange previous and current visual area */
					if (VIsual_active)
					{
						tt = VIsual;
						VIsual = VIsual_save;
						VIsual_save = tt;
						t = VIsual_mode;
						VIsual_mode = VIsual_mode_save;
						VIsual_mode_save = t;
						tt = curwin->w_cursor;
					}
					curwin->w_cursor = VIsual_end;
					if (VIsual_active)
						VIsual_end = tt;
					check_cursor();
					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:
				if (!curwin->w_p_wrap)
					goto normal_j;
				if (screengo(FORWARD, Prenum1) == FAIL)
					clearopbeep();
				break;

			case 'k':
			case K_UP:
				if (!curwin->w_p_wrap)
					goto normal_k;
				if (screengo(BACKWARD, Prenum1) == FAIL)
					clearopbeep();
				break;

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

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

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

			/*
			 * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
			 */
			case '*':
			case '#':
				goto search_word;

			/*
			 * ge and gE: go back to end of word
			 */
			case 'e':
			case 'E':
				op_motion_type = MCHAR;
				curwin->w_set_curswant = TRUE;
				op_inclusive = TRUE;
				if (bckend_word(Prenum1, nchar == 'E', FALSE) == FAIL)
					clearopbeep();
				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);
				goto insert_command;

			/*
			 * "gf": goto file, edit file under cursor
			 * "]f" and "[f": can also be used.
			 */
			case 'f':
gotofile:
				ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
				if (ptr != NULL)
				{
					/* do autowrite if necessary */
					if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
						autowrite(curbuf);
					setpcmark();
					(void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0,
																	   FALSE);
					vim_free(ptr);
				}
				else
					clearop();
				break;

			/*
			 * "gs": Goto sleep, but keep on checking for CTRL-C
			 */
			case 's':
				while (Prenum1-- && !got_int)
				{
					mch_delay(1000L, TRUE);
					mch_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':
goto_line_one:
				if (Prenum == 0)
					Prenum = 1;
				goto goto_line;

			/*
			 * Operater to format text:
			 *   gq		same as 'Q' operator.
			 * Operators to change the case of text:
			 *   g~		Toggle the case of the text.
			 *   gu		Change text to lower case.
			 *   gU		Change text to upper case.
			 *									--webb
			 */
			case 'q':
			case '~':
			case 'u':
			case 'U':
				prechar = c;
				c = nchar;
				goto dooperator;

		/*
		 * "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(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(nchar, BACKWARD, Prenum1, FALSE);
				break;

			case K_IGNORE:
				break;
#endif

			default:
				clearopbeep();
				break;
		}
		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(c, BACKWARD, Prenum1, FALSE);
		break;

	  case K_IGNORE:
		break;
#endif

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

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

	  case K_HORIZ_SCROLLBAR:
		if (op_type != NOP)
			clearopbeep();

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

/*
 * 17. The end
 */
	  case ESC:
		/* Don't drop through and beep if we are canceling a command: */
		if (!VIsual_active && (op_type != NOP ||
											   opnum || Prenum || yankbuffer))
		{
			clearop();					/* don't beep */
			break;
		}
	    if (VIsual_active)
		{
			end_visual_mode();			/* stop Visual */
			update_curbuf(NOT_VALID);
			clearop();					/* don't beep */
			break;
		}
		/* ESC in normal mode: beep, but don't flush buffers */
		clearop();
		vim_beep();
		break;

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

	}	/* end of switch on command character */

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

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

normal_end:
	if (op_type == NOP && yankbuffer == 0)
		clear_showcmd();

	if (restart_edit && op_type == NOP && !VIsual_active
						 && !command_busy && stuff_empty() && yankbuffer == 0)
		(void)edit(restart_edit, FALSE, 1L);

	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)
	{
		WIN		*wp;

        for (wp = firstwin; wp; wp = wp->w_next)
			if (wp != curwin && wp->w_buffer == curbuf)
			{
				cursor_off();
				wp->w_redr_type = NOT_VALID;
				/*
				 * don't do the actual redraw if wait_return() has just been
				 * called and the user typed a ":"
				 */
				if (!skip_redraw)
					win_update(wp);
			}
		modified = FALSE;
	}
}

/*
 * Handle an operator after visual mode or when the movement is finished
 */
	void
do_pending_operator(c, nchar, finish_op, searchbuff, command_busy,
										old_col, gui_yank, dont_adjust_op_end)
	register int	c;
	int				nchar;
	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;
{
	/* 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_Prenum;		/* Prenum for operator */

	linenr_t		Prenum1 = 1L;
	FPOS			old_cursor;
	int				VIsual_was_active = VIsual_active;
	int				redraw;

#ifdef USE_GUI
	/*
	 * 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 gui_copy_selection() and not
	 * gui_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 (gui.in_use && op_type != NOP && !gui_yank && VIsual_active
														 && !redo_VIsual_busy)
		gui_copy_selection();
#endif
	old_cursor = curwin->w_cursor;

	/*
	 * If an operation is pending, handle it...
	 */
	if ((VIsual_active || finish_op) && op_type != NOP)
	{
		op_is_VIsual = VIsual_active;
		if (op_type != YANK && !VIsual_active)		/* can't redo yank */
		{
			prep_redo(Prenum, prechar, opchars[op_type - 1], c, nchar);
			if (c == '/' || c == '?')				/* 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)
		{
			curbuf->b_op_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);
			}
			Prenum = redo_VIsual_Prenum;
		}
		else if (VIsual_active)
		{
			curbuf->b_op_start = VIsual;
			VIsual_end = curwin->w_cursor;
			if (VIsual_mode == 'V')
				curbuf->b_op_start.col = 0;
		}

		if (lt(curbuf->b_op_start, curwin->w_cursor))
		{
			curbuf->b_op_end = curwin->w_cursor;
			curwin->w_cursor = curbuf->b_op_start;
		}
		else
		{
			curbuf->b_op_end = curbuf->b_op_start;
			curbuf->b_op_start = curwin->w_cursor;
		}
		op_line_count = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;

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

				op_block_mode = TRUE;
				getvcol(curwin, &(curbuf->b_op_start),
										  &op_start_vcol, NULL, &op_end_vcol);
				if (!redo_VIsual_busy)
				{
					getvcol(curwin, &(curbuf->b_op_end), &start, NULL, &end);
					if (start < op_start_vcol)
						op_start_vcol = start;
					if (end > op_end_vcol)
						op_end_vcol = end;
				}

				/* if '$' was used, get op_end_vcol from longest line */
				if (curwin->w_curswant == MAXCOL)
				{
					curwin->w_cursor.col = MAXCOL;
					op_end_vcol = 0;
					for (curwin->w_cursor.lnum = curbuf->b_op_start.lnum;
							curwin->w_cursor.lnum <= curbuf->b_op_end.lnum;
							++curwin->w_cursor.lnum)
					{
						getvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
						if (end > op_end_vcol)
							op_end_vcol = end;
					}
					curwin->w_cursor = curbuf->b_op_start;
				}
				else if (redo_VIsual_busy)
					op_end_vcol = op_start_vcol + redo_VIsual_col - 1;
				coladvance(op_start_vcol);
			}

			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 = op_end_vcol - op_start_vcol + 1;
				else if (op_line_count > 1)
					resel_VIsual_col = curbuf->b_op_end.col;
				else
					resel_VIsual_col = curbuf->b_op_end.col -
												curbuf->b_op_start.col + 1;
				resel_VIsual_line_count = op_line_count;
			}
												/* can't redo yank and : */
			if (op_type != YANK && op_type != COLON)
			{
				prep_redo(0L, NUL, 'v', prechar, opchars[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_Prenum = Prenum;
			}

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

			redo_VIsual_busy = FALSE;
			/*
			 * Switch Visual off now, so screen updating does
			 * not show inverted text when the screen is redrawn.
			 * With YANK and sometimes with COLON and 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 (op_type == YANK || op_type == COLON || op_type == FILTER)
					update_curbuf(NOT_VALID);
			}

			/* set Prenum1 for LSHIFT and RSHIFT, e.g. "V3j2>" */
			if (Prenum == 0)
				Prenum1 = 1L;
			else
				Prenum1 = Prenum;
		}

		curwin->w_set_curswant = TRUE;

			/* op_empty is set when start and end are the same */
		op_empty = (op_motion_type == MCHAR && !op_inclusive &&
								 equal(curbuf->b_op_start, curbuf->b_op_end));

	/*
	 * If the end of an operator is in column one while op_motion_type is
	 * MCHAR and op_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 (op_motion_type == MCHAR && op_inclusive == FALSE &&
						   !dont_adjust_op_end && curbuf->b_op_end.col == 0 &&
															op_line_count > 1)
		{
			op_end_adjusted = TRUE;		/* remember that we did this */
			--op_line_count;
			--curbuf->b_op_end.lnum;
			if (inindent(0))
				op_motion_type = MLINE;
			else
			{
				curbuf->b_op_end.col = STRLEN(ml_get(curbuf->b_op_end.lnum));
				if (curbuf->b_op_end.col)
				{
					--curbuf->b_op_end.col;
					op_inclusive = TRUE;
				}
			}
		}
		else
			op_end_adjusted = FALSE;
		switch (op_type)
		{
		  case LSHIFT:
		  case RSHIFT:
			do_shift(op_type, TRUE, (int)Prenum1);
			break;

		  case JOIN:
			if (op_line_count < 2)
				op_line_count = 2;
			if (curwin->w_cursor.lnum + op_line_count - 1 >
												   curbuf->b_ml.ml_line_count)
				beep_flush();
			else
			{
				/*
				 * If the cursor position has been changed, recompute the
				 * current cursor position in the window. If it's not visible,
				 * don't keep the window updated when joining the lines.
				 */
				if (old_cursor.lnum != curwin->w_cursor.lnum ||
									   old_cursor.col != curwin->w_cursor.col)
					redraw = (curs_rows() == OK);
				else
					redraw = TRUE;
				do_do_join(op_line_count, TRUE, redraw);
			}
			break;

		  case DELETE:
			if (!op_empty)
				do_delete();
			break;

		  case YANK:
			if (!op_empty)
				(void)do_yank(FALSE, !gui_yank);
			break;

		  case CHANGE:
			*command_busy = do_change();	/* will set op_type to NOP */
			break;

		  case 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 INDENT:
		  case COLON:

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

dofilter:
			if (VIsual_was_active)
				sprintf((char *)IObuff, ":'<,'>");
			else
				sprintf((char *)IObuff, ":%ld,%ld",
						(long)curbuf->b_op_start.lnum,
						(long)curbuf->b_op_end.lnum);
			stuffReadbuff(IObuff);
			if (op_type != COLON)
				stuffReadbuff((char_u *)"!");
			if (op_type == INDENT)
			{
#ifndef CINDENT
				if (*p_ep == NUL)
					stuffReadbuff((char_u *)"indent");
				else
#endif
					stuffReadbuff(p_ep);
				stuffReadbuff((char_u *)"\n");
			}
			else if (op_type == FORMAT || op_type == GFORMAT)
			{
				if (*p_fp == NUL)
					stuffReadbuff((char_u *)"fmt");
				else
					stuffReadbuff(p_fp);
				stuffReadbuff((char_u *)"\n");
			}
				/*	do_cmdline() does the rest */
			break;

		  case TILDE:
		  case UPPER:
		  case LOWER:
			if (!op_empty)
				do_tilde();
			break;

		  case FORMAT:
		  case GFORMAT:
			if (*p_fp != NUL)
				goto dofilter;		/* use external command */
			do_format();			/* use internal function */
			break;

		  default:
			clearopbeep();
		}
		prechar = NUL;
		if (!gui_yank)
		{
			/*
			 * if 'sol' not set, go back to old column for some commands
			 */
			if (!p_sol && op_motion_type == MLINE && (op_type == LSHIFT ||
									op_type == RSHIFT || op_type == DELETE))
				coladvance(curwin->w_curswant = old_col);
			op_type = NOP;
		}
		else
			curwin->w_cursor = old_cursor;
		op_block_mode = FALSE;
		yankbuffer = 0;
	}
}

#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(c, dir, count, fix_indent)
	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;

	/*
	 * 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;

	/*
	 * 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 (op_type != NOP)
			{
				clearopbeep();
				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 (yankbuffer == '.')
				insertbuf(yankbuffer);
			else
			{
#ifdef USE_GUI
				if (gui.in_use && yankbuffer == 0)
					yankbuffer = '*';
#endif
				do_put(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 (yankbuffer == 0)
					AppendCharToRedobuff('"');
				else
					AppendCharToRedobuff(yankbuffer);
			}
			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 = (op_type != NOP);
	}

	/*
	 * Jump!
	 */
	if (!is_click)
		jump_flags |= MOUSE_DID_MOVE;
	save_buffer = curbuf;
	moved = (jump_to_mouse(jump_flags) & 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'));
	/*
	 * If cursor has moved, need to update Cline_row
	 */
	else if (moved)
		cursupdate();

	/*
	 * Middle mouse click: Put text before cursor.
	 */
	if (which_button == MOUSE_MIDDLE)
	{
#ifdef USE_GUI
		if (gui.in_use && yankbuffer == 0)
			yankbuffer = '*';
#endif
		if (yank_buffer_mline())
		{
			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(Prenum, 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(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
			{
				start_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)
		{
			curs_columns(TRUE);				/* recompute w_virtcol */
			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 */

/*
 * start highlighting for visual mode
 */
	void
start_visual_highlight()
{
	static int		didwarn = FALSE;		/* warned for broken inversion */

	if (!didwarn && set_highlight('v') == FAIL)/* cannot highlight */
	{
		EMSG("Warning: terminal cannot highlight");
		didwarn = 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_GUI
	if (gui.in_use)
		gui_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;
}

	static void
prep_redo(num, pre_char, cmd, c, nchar)
	long 	num;
	int		pre_char;
	int		cmd;
	int		c;
	int		nchar;
{
	ResetRedobuff();
	if (yankbuffer != 0)	/* yank from specified buffer */
	{
		AppendCharToRedobuff('\"');
		AppendCharToRedobuff(yankbuffer);
	}
	if (num)
		AppendNumberToRedobuff(num);
	if (pre_char != NUL)
		AppendCharToRedobuff(pre_char);
	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()
{
	if (op_type == NOP)
		return (FALSE);
	clearopbeep();
	return (TRUE);
}

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

	static void
clearop()
{
	op_type = NOP;
	yankbuffer = 0;
	prechar = NUL;
}

	static void
clearopbeep()
{
	clearop();
	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_msg(showcmd_buf, (int)Rows - 1, sc_col);
		is_showcmd_clear = FALSE;
	}

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

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

/*
 * Implementation of "gd" and "gD" command.
 */
	static void
do_gd(nchar)
	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();
		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(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();
		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;
}