[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.5, Mon Oct 14 03:55:20 1996 UTC (27 years, 7 months ago) by downsj
Branch: MAIN
CVS Tags: OPENBSD_2_2_BASE, OPENBSD_2_2, OPENBSD_2_1_BASE, OPENBSD_2_1
Changes since 1.4: +16 -4 lines

Backout a previous change that didn't work, and update to version 4.5.

/*	$OpenBSD: normal.c,v 1.5 1996/10/14 03:55:20 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;
	static int		search_dont_set_mark = FALSE;	/* for "*" and "#" */
	FPOS			old_pos;				/* cursor position before command */

	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;
		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 && 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;
	old_pos = curwin->w_cursor;			/* remember where cursor was */

#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:
	  case K_KPAGEUP:
		dir = BACKWARD;

	  case Ctrl('F'):
	  case K_S_DOWN:
	  case K_PAGEDOWN:
	  case K_KPAGEDOWN:
		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 'Q':
		do_exmode();
		updateScreen(CLEAR);
		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);
		break;

	  case K_CCIRCM:			/* CTRL-^, short for ":e #" */
		if (checkclearopq())
			break;
		(void)buflist_getfile((int)Prenum, (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())
			break;
		if (nchar == 'Z')
			stuffReadbuff((char_u *)":x\n");
		else if (nchar == 'Q')
			stuffReadbuff((char_u *)":q!\n");
		else
			clearopbeep();
		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.
				 * 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 (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, FALSE);
		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 = Prenum1 - 1;
		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 (Prenum1 - 1 >= curwin->w_cursor.lnum)
			curwin->w_cursor.lnum = 1;
		else
			curwin->w_cursor.lnum -= Prenum1 - 1;
		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:
	  case K_KHOME:
		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;

		/*
		 * "cw" and "cW" are a special case.
		 */
		if (op_type == CHANGE)
		{
			n = gchar_cursor();
			if (n != NUL)					/* not an empty line */
			{
				op_inclusive = TRUE;
				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 (Prenum1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
					{
						op_inclusive = TRUE;
						op_motion_type = MCHAR;
						break;
					}
				}
				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 flag2.
					 */
					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:
	  case K_KEND:
		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_dont_set_mark ? 0 : SEARCH_MARK) |
				SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
		if (n == 0)
			clearop();
		else if (n == 2)
			op_motion_type = MLINE;
		search_dont_set_mark = FALSE;
		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 new_pos;

			if (nchar == '*')
				nchar = '/';
			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 '=':
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:
			case K_KHOME:
				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:
			case K_KEND:
				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, FALSE);
					setpcmark();
					(void)do_ecmd(0, ptr, NULL, NULL, (linenr_t)0,
													   p_hid ? ECMD_HIDE : 0);
					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);

	/*
	 * 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
														))) &&
			yankbuffer == 0 && !command_busy && stuff_empty() && op_type == NOP)
	{
		++RedrawingDisabled;
		cursupdate();
		--RedrawingDisabled;
		setcursor();
		flushbuf();
		if (msg_scroll || emsg_on_display
#ifdef SLEEP_IN_EMSG
											|| need_sleep
#endif
															)
			mch_delay(1000L, TRUE);		/* wait at least one second */
		mch_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:
	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);

	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)
	{
		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;

				/* make the start the upper left corner of the block */
				if (curbuf->b_op_start.col > curbuf->b_op_end.col)
				{
					int t;

					t = curbuf->b_op_start.col;
					curbuf->b_op_start.col = curbuf->b_op_end.col;
					curbuf->b_op_end.col = t;
				}
				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;
}