diff src/search.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children 7edf9b6e4c36
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/search.c
@@ -0,0 +1,4487 @@
+/* vi:set ts=8 sts=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.
+ * See README.txt for an overview of the Vim source code.
+ */
+/*
+ * search.c: code for normal mode searching commands
+ */
+
+#include "vim.h"
+
+static void save_re_pat __ARGS((int idx, char_u *pat, int magic));
+#ifdef FEAT_EVAL
+static int first_submatch __ARGS((regmmatch_T *rp));
+#endif
+static int check_prevcol __ARGS((char_u *linep, int col, int ch, int *prevcol));
+static int inmacro __ARGS((char_u *, char_u *));
+static int check_linecomment __ARGS((char_u *line));
+static int cls __ARGS((void));
+static int skip_chars __ARGS((int, int));
+#ifdef FEAT_TEXTOBJ
+static void back_in_line __ARGS((void));
+static void find_first_blank __ARGS((pos_T *));
+static void findsent_forward __ARGS((long count, int at_start_sent));
+#endif
+#ifdef FEAT_FIND_ID
+static void show_pat_in_path __ARGS((char_u *, int,
+					 int, int, FILE *, linenr_T *, long));
+#endif
+#ifdef FEAT_VIMINFO
+static void wvsp_one __ARGS((FILE *fp, int idx, char *s, int sc));
+#endif
+
+static char_u *top_bot_msg = (char_u *)N_("search hit TOP, continuing at BOTTOM");
+static char_u *bot_top_msg = (char_u *)N_("search hit BOTTOM, continuing at TOP");
+
+/*
+ * This file contains various searching-related routines. These fall into
+ * three groups:
+ * 1. string searches (for /, ?, n, and N)
+ * 2. character searches within a single line (for f, F, t, T, etc)
+ * 3. "other" kinds of searches like the '%' command, and 'word' searches.
+ */
+
+/*
+ * String searches
+ *
+ * The string search functions are divided into two levels:
+ * lowest:  searchit(); uses an pos_T for starting position and found match.
+ * Highest: do_search(); uses curwin->w_cursor; calls searchit().
+ *
+ * The last search pattern is remembered for repeating the same search.
+ * This pattern is shared between the :g, :s, ? and / commands.
+ * This is in search_regcomp().
+ *
+ * The actual string matching is done using a heavily modified version of
+ * Henry Spencer's regular expression library.  See regexp.c.
+ */
+
+/* The offset for a search command is store in a soff struct */
+/* Note: only spats[0].off is really used */
+struct soffset
+{
+    int		dir;		/* search direction */
+    int		line;		/* search has line offset */
+    int		end;		/* search set cursor at end */
+    long	off;		/* line or char offset */
+};
+
+/* A search pattern and its attributes are stored in a spat struct */
+struct spat
+{
+    char_u	    *pat;	/* the pattern (in allocated memory) or NULL */
+    int		    magic;	/* magicness of the pattern */
+    int		    no_scs;	/* no smarcase for this pattern */
+    struct soffset  off;
+};
+
+/*
+ * Two search patterns are remembered: One for the :substitute command and
+ * one for other searches.  last_idx points to the one that was used the last
+ * time.
+ */
+static struct spat spats[2] =
+{
+    {NULL, TRUE, FALSE, {'/', 0, 0, 0L}},	/* last used search pat */
+    {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}	/* last used substitute pat */
+};
+
+static int last_idx = 0;	/* index in spats[] for RE_LAST */
+
+#if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
+/* copy of spats[], for keeping the search patterns while executing autocmds */
+static struct spat  saved_spats[2];
+static int	    saved_last_idx = 0;
+# ifdef FEAT_SEARCH_EXTRA
+static int	    saved_no_hlsearch = 0;
+# endif
+#endif
+
+static char_u	    *mr_pattern = NULL;	/* pattern used by search_regcomp() */
+#ifdef FEAT_RIGHTLEFT
+static int	    mr_pattern_alloced = FALSE; /* mr_pattern was allocated */
+static char_u	    *reverse_text __ARGS((char_u *s));
+#endif
+
+#ifdef FEAT_FIND_ID
+/*
+ * Type used by find_pattern_in_path() to remember which included files have
+ * been searched already.
+ */
+typedef struct SearchedFile
+{
+    FILE	*fp;		/* File pointer */
+    char_u	*name;		/* Full name of file */
+    linenr_T	lnum;		/* Line we were up to in file */
+    int		matched;	/* Found a match in this file */
+} SearchedFile;
+#endif
+
+/*
+ * translate search pattern for vim_regcomp()
+ *
+ * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd)
+ * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command)
+ * pat_save == RE_BOTH: save pat in both patterns (:global command)
+ * pat_use  == RE_SEARCH: use previous search pattern if "pat" is NULL
+ * pat_use  == RE_SUBST: use previous sustitute pattern if "pat" is NULL
+ * pat_use  == RE_LAST: use last used pattern if "pat" is NULL
+ * options & SEARCH_HIS: put search string in history
+ * options & SEARCH_KEEP: keep previous search pattern
+ *
+ * returns FAIL if failed, OK otherwise.
+ */
+    int
+search_regcomp(pat, pat_save, pat_use, options, regmatch)
+    char_u	*pat;
+    int		pat_save;
+    int		pat_use;
+    int		options;
+    regmmatch_T	*regmatch;	/* return: pattern and ignore-case flag */
+{
+    int		magic;
+    int		i;
+
+    rc_did_emsg = FALSE;
+    magic = p_magic;
+
+    /*
+     * If no pattern given, use a previously defined pattern.
+     */
+    if (pat == NULL || *pat == NUL)
+    {
+	if (pat_use == RE_LAST)
+	    i = last_idx;
+	else
+	    i = pat_use;
+	if (spats[i].pat == NULL)	/* pattern was never defined */
+	{
+	    if (pat_use == RE_SUBST)
+		EMSG(_(e_nopresub));
+	    else
+		EMSG(_(e_noprevre));
+	    rc_did_emsg = TRUE;
+	    return FAIL;
+	}
+	pat = spats[i].pat;
+	magic = spats[i].magic;
+	no_smartcase = spats[i].no_scs;
+    }
+#ifdef FEAT_CMDHIST
+    else if (options & SEARCH_HIS)	/* put new pattern in history */
+	add_to_history(HIST_SEARCH, pat, TRUE, NUL);
+#endif
+
+#ifdef FEAT_RIGHTLEFT
+    if (mr_pattern_alloced)
+    {
+	vim_free(mr_pattern);
+	mr_pattern_alloced = FALSE;
+    }
+
+    if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
+    {
+	char_u *rev_pattern;
+
+	rev_pattern = reverse_text(pat);
+	if (rev_pattern == NULL)
+	    mr_pattern = pat;	    /* out of memory, keep normal pattern. */
+	else
+	{
+	    mr_pattern = rev_pattern;
+	    mr_pattern_alloced = TRUE;
+	}
+    }
+    else
+#endif
+	mr_pattern = pat;
+
+    /*
+     * Save the currently used pattern in the appropriate place,
+     * unless the pattern should not be remembered.
+     */
+    if (!(options & SEARCH_KEEP))
+    {
+	/* search or global command */
+	if (pat_save == RE_SEARCH || pat_save == RE_BOTH)
+	    save_re_pat(RE_SEARCH, pat, magic);
+	/* substitute or global command */
+	if (pat_save == RE_SUBST || pat_save == RE_BOTH)
+	    save_re_pat(RE_SUBST, pat, magic);
+    }
+
+    regmatch->rmm_ic = ignorecase(pat);
+    regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0);
+    if (regmatch->regprog == NULL)
+	return FAIL;
+    return OK;
+}
+
+/*
+ * Get search pattern used by search_regcomp().
+ */
+    char_u *
+get_search_pat()
+{
+    return mr_pattern;
+}
+
+#ifdef FEAT_RIGHTLEFT
+/*
+ * Reverse text into allocated memory.
+ * Returns the allocated string, NULL when out of memory.
+ */
+    static char_u *
+reverse_text(s)
+    char_u *s;
+{
+    unsigned	len;
+    unsigned	s_i, rev_i;
+    char_u	*rev;
+
+    /*
+     * Reverse the pattern.
+     */
+    len = (unsigned)STRLEN(s);
+    rev = alloc(len + 1);
+    if (rev != NULL)
+    {
+	rev_i = len;
+	for (s_i = 0; s_i < len; ++s_i)
+	{
+# ifdef FEAT_MBYTE
+	    if (has_mbyte)
+	    {
+		int	mb_len;
+
+		mb_len = (*mb_ptr2len_check)(s + s_i);
+		rev_i -= mb_len;
+		mch_memmove(rev + rev_i, s + s_i, mb_len);
+		s_i += mb_len - 1;
+	    }
+	    else
+# endif
+		rev[--rev_i] = s[s_i];
+
+	}
+	rev[len] = NUL;
+    }
+    return rev;
+}
+#endif
+
+    static void
+save_re_pat(idx, pat, magic)
+    int		idx;
+    char_u	*pat;
+    int		magic;
+{
+    if (spats[idx].pat != pat)
+    {
+	vim_free(spats[idx].pat);
+	spats[idx].pat = vim_strsave(pat);
+	spats[idx].magic = magic;
+	spats[idx].no_scs = no_smartcase;
+	last_idx = idx;
+#ifdef FEAT_SEARCH_EXTRA
+	/* If 'hlsearch' set and search pat changed: need redraw. */
+	if (p_hls)
+	    redraw_all_later(NOT_VALID);
+	no_hlsearch = FALSE;
+#endif
+    }
+}
+
+#if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Save the search patterns, so they can be restored later.
+ * Used before/after executing autocommands and user functions.
+ */
+static int save_level = 0;
+
+    void
+save_search_patterns()
+{
+    if (save_level++ == 0)
+    {
+	saved_spats[0] = spats[0];
+	if (spats[0].pat != NULL)
+	    saved_spats[0].pat = vim_strsave(spats[0].pat);
+	saved_spats[1] = spats[1];
+	if (spats[1].pat != NULL)
+	    saved_spats[1].pat = vim_strsave(spats[1].pat);
+	saved_last_idx = last_idx;
+# ifdef FEAT_SEARCH_EXTRA
+	saved_no_hlsearch = no_hlsearch;
+# endif
+    }
+}
+
+    void
+restore_search_patterns()
+{
+    if (--save_level == 0)
+    {
+	vim_free(spats[0].pat);
+	spats[0] = saved_spats[0];
+	vim_free(spats[1].pat);
+	spats[1] = saved_spats[1];
+	last_idx = saved_last_idx;
+# ifdef FEAT_SEARCH_EXTRA
+	no_hlsearch = saved_no_hlsearch;
+# endif
+    }
+}
+#endif
+
+/*
+ * Return TRUE when case should be ignored for search pattern "pat".
+ * Uses the 'ignorecase' and 'smartcase' options.
+ */
+    int
+ignorecase(pat)
+    char_u	*pat;
+{
+    char_u	*p;
+    int		ic;
+
+    ic = p_ic;
+    if (ic && !no_smartcase && p_scs
+#ifdef FEAT_INS_EXPAND
+				&& !(ctrl_x_mode && curbuf->b_p_inf)
+#endif
+								    )
+    {
+	/* don't ignore case if pattern has uppercase */
+	for (p = pat; *p; )
+	{
+#ifdef FEAT_MBYTE
+	    int		l;
+
+	    if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
+	    {
+		if (enc_utf8 && utf_isupper(utf_ptr2char(p)))
+		{
+		    ic = FALSE;
+		    break;
+		}
+		p += l;
+	    }
+	    else
+#endif
+		if (*p == '\\' && p[1] != NUL)	/* skip "\S" et al. */
+		    p += 2;
+		else if (isupper(*p++))
+		{
+		    ic = FALSE;
+		    break;
+		}
+	}
+    }
+    no_smartcase = FALSE;
+
+    return ic;
+}
+
+    char_u *
+last_search_pat()
+{
+    return spats[last_idx].pat;
+}
+
+/*
+ * Reset search direction to forward.  For "gd" and "gD" commands.
+ */
+    void
+reset_search_dir()
+{
+    spats[0].off.dir = '/';
+}
+
+#if defined(FEAT_EVAL) || defined(FEAT_VIMINFO)
+/*
+ * Set the last search pattern.  For ":let @/ =" and viminfo.
+ * Also set the saved search pattern, so that this works in an autocommand.
+ */
+    void
+set_last_search_pat(s, idx, magic, setlast)
+    char_u	*s;
+    int		idx;
+    int		magic;
+    int		setlast;
+{
+    vim_free(spats[idx].pat);
+    /* An empty string means that nothing should be matched. */
+    if (*s == NUL)
+	spats[idx].pat = NULL;
+    else
+	spats[idx].pat = vim_strsave(s);
+    spats[idx].magic = magic;
+    spats[idx].no_scs = FALSE;
+    spats[idx].off.dir = '/';
+    spats[idx].off.line = FALSE;
+    spats[idx].off.end = FALSE;
+    spats[idx].off.off = 0;
+    if (setlast)
+	last_idx = idx;
+    if (save_level)
+    {
+	vim_free(saved_spats[idx].pat);
+	saved_spats[idx] = spats[0];
+	if (spats[idx].pat == NULL)
+	    saved_spats[idx].pat = NULL;
+	else
+	    saved_spats[idx].pat = vim_strsave(spats[idx].pat);
+	saved_last_idx = last_idx;
+    }
+# ifdef FEAT_SEARCH_EXTRA
+    /* If 'hlsearch' set and search pat changed: need redraw. */
+    if (p_hls && idx == last_idx && !no_hlsearch)
+	redraw_all_later(NOT_VALID);
+# endif
+}
+#endif
+
+#ifdef FEAT_SEARCH_EXTRA
+/*
+ * Get a regexp program for the last used search pattern.
+ * This is used for highlighting all matches in a window.
+ * Values returned in regmatch->regprog and regmatch->rmm_ic.
+ */
+    void
+last_pat_prog(regmatch)
+    regmmatch_T	*regmatch;
+{
+    if (spats[last_idx].pat == NULL)
+    {
+	regmatch->regprog = NULL;
+	return;
+    }
+    ++emsg_off;		/* So it doesn't beep if bad expr */
+    (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
+    --emsg_off;
+}
+#endif
+
+/*
+ * lowest level search function.
+ * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'.
+ * Start at position 'pos' and return the found position in 'pos'.
+ *
+ * if (options & SEARCH_MSG) == 0 don't give any messages
+ * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
+ * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
+ * if (options & SEARCH_HIS) put search pattern in history
+ * if (options & SEARCH_END) return position at end of match
+ * if (options & SEARCH_START) accept match at pos itself
+ * if (options & SEARCH_KEEP) keep previous search pattern
+ * if (options & SEARCH_FOLD) match only once in a closed fold
+ * if (options & SEARCH_PEEK) check for typed char, cancel search
+ *
+ * Return FAIL (zero) for failure, non-zero for success.
+ * When FEAT_EVAL is defined, returns the index of the first matching
+ * subpattern plus one; one if there was none.
+ */
+    int
+searchit(win, buf, pos, dir, pat, count, options, pat_use)
+    win_T	*win;		/* window to search in; can be NULL for a
+				   buffer without a window! */
+    buf_T	*buf;
+    pos_T	*pos;
+    int		dir;
+    char_u	*pat;
+    long	count;
+    int		options;
+    int		pat_use;
+{
+    int		found;
+    linenr_T	lnum;		/* no init to shut up Apollo cc */
+    regmmatch_T	regmatch;
+    char_u	*ptr;
+    colnr_T	matchcol;
+    colnr_T	startcol;
+    lpos_T	endpos;
+    int		loop;
+    pos_T	start_pos;
+    int		at_first_line;
+    int		extra_col;
+    int		match_ok;
+    long	nmatched;
+    int		submatch = 0;
+    linenr_T	first_lnum;
+#ifdef FEAT_SEARCH_EXTRA
+    int		break_loop = FALSE;
+#else
+# define break_loop FALSE
+#endif
+
+    if (search_regcomp(pat, RE_SEARCH, pat_use,
+		   (options & (SEARCH_HIS + SEARCH_KEEP)), &regmatch) == FAIL)
+    {
+	if ((options & SEARCH_MSG) && !rc_did_emsg)
+	    EMSG2(_("E383: Invalid search string: %s"), mr_pattern);
+	return FAIL;
+    }
+
+    if (options & SEARCH_START)
+	extra_col = 0;
+#ifdef FEAT_MBYTE
+    /* Watch out for the "col" being MAXCOL - 2, used in a closed fold. */
+    else if (has_mbyte && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
+						     && pos->col < MAXCOL - 2)
+	extra_col = (*mb_ptr2len_check)(ml_get_buf(buf, pos->lnum, FALSE)
+								  + pos->col);
+#endif
+    else
+	extra_col = 1;
+
+/*
+ * find the string
+ */
+    called_emsg = FALSE;
+    do	/* loop for count */
+    {
+	start_pos = *pos;	/* remember start pos for detecting no match */
+	found = 0;		/* default: not found */
+	at_first_line = TRUE;	/* default: start in first line */
+	if (pos->lnum == 0)	/* correct lnum for when starting in line 0 */
+	{
+	    pos->lnum = 1;
+	    pos->col = 0;
+	    at_first_line = FALSE;  /* not in first line now */
+	}
+
+	/*
+	 * Start searching in current line, unless searching backwards and
+	 * we're in column 0.
+	 */
+	if (dir == BACKWARD && start_pos.col == 0)
+	{
+	    lnum = pos->lnum - 1;
+	    at_first_line = FALSE;
+	}
+	else
+	    lnum = pos->lnum;
+
+	for (loop = 0; loop <= 1; ++loop)   /* loop twice if 'wrapscan' set */
+	{
+	    for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
+					   lnum += dir, at_first_line = FALSE)
+	    {
+		/*
+		 * Look for a match somewhere in the line.
+		 */
+		first_lnum = lnum;
+		nmatched = vim_regexec_multi(&regmatch, win, buf,
+							    lnum, (colnr_T)0);
+		/* Abort searching on an error (e.g., out of stack). */
+		if (called_emsg)
+		    break;
+		if (nmatched > 0)
+		{
+		    /* match may actually be in another line when using \zs */
+		    lnum += regmatch.startpos[0].lnum;
+		    ptr = ml_get_buf(buf, lnum, FALSE);
+		    startcol = regmatch.startpos[0].col;
+		    endpos = regmatch.endpos[0];
+# ifdef FEAT_EVAL
+		    submatch = first_submatch(&regmatch);
+# endif
+
+		    /*
+		     * Forward search in the first line: match should be after
+		     * the start position. If not, continue at the end of the
+		     * match (this is vi compatible) or on the next char.
+		     */
+		    if (dir == FORWARD && at_first_line)
+		    {
+			match_ok = TRUE;
+			/*
+			 * When match lands on a NUL the cursor will be put
+			 * one back afterwards, compare with that position,
+			 * otherwise "/$" will get stuck on end of line.
+			 */
+			while ((options & SEARCH_END)
+				?  (nmatched == 1
+				    && (int)endpos.col - 1
+					     < (int)start_pos.col + extra_col)
+				: ((int)startcol - (ptr[startcol] == NUL)
+					    < (int)start_pos.col + extra_col))
+			{
+			    /*
+			     * If vi-compatible searching, continue at the end
+			     * of the match, otherwise continue one position
+			     * forward.
+			     */
+			    if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
+			    {
+				if (nmatched > 1)
+				{
+				    /* end is in next line, thus no match in
+				     * this line */
+				    match_ok = FALSE;
+				    break;
+				}
+				matchcol = endpos.col;
+				/* for empty match: advance one char */
+				if (matchcol == startcol
+						      && ptr[matchcol] != NUL)
+				{
+#ifdef FEAT_MBYTE
+				    if (has_mbyte)
+					matchcol +=
+					  (*mb_ptr2len_check)(ptr + matchcol);
+				    else
+#endif
+					++matchcol;
+				}
+			    }
+			    else
+			    {
+				matchcol = startcol;
+				if (ptr[matchcol] != NUL)
+				{
+#ifdef FEAT_MBYTE
+				    if (has_mbyte)
+					matchcol += (*mb_ptr2len_check)(ptr
+								  + matchcol);
+				    else
+#endif
+					++matchcol;
+				}
+			    }
+			    if (ptr[matchcol] == NUL
+				    || (nmatched = vim_regexec_multi(&regmatch,
+					      win, buf, lnum, matchcol)) == 0)
+			    {
+				match_ok = FALSE;
+				break;
+			    }
+			    startcol = regmatch.startpos[0].col;
+			    endpos = regmatch.endpos[0];
+# ifdef FEAT_EVAL
+			    submatch = first_submatch(&regmatch);
+# endif
+
+			    /* Need to get the line pointer again, a
+			     * multi-line search may have made it invalid. */
+			    ptr = ml_get_buf(buf, lnum, FALSE);
+			}
+			if (!match_ok)
+			    continue;
+		    }
+		    if (dir == BACKWARD)
+		    {
+			/*
+			 * Now, if there are multiple matches on this line,
+			 * we have to get the last one. Or the last one before
+			 * the cursor, if we're on that line.
+			 * When putting the new cursor at the end, compare
+			 * relative to the end of the match.
+			 */
+			match_ok = FALSE;
+			for (;;)
+			{
+			    if (!at_first_line
+				    || ((options & SEARCH_END)
+					?  (nmatched == 1
+					    && (int)regmatch.endpos[0].col - 1
+								   + extra_col
+							<= (int)start_pos.col)
+					: ((int)regmatch.startpos[0].col
+								   + extra_col
+						      <= (int)start_pos.col)))
+			    {
+				/* Remember this position, we use it if it's
+				 * the last match in the line. */
+				match_ok = TRUE;
+				startcol = regmatch.startpos[0].col;
+				endpos = regmatch.endpos[0];
+# ifdef FEAT_EVAL
+				submatch = first_submatch(&regmatch);
+# endif
+			    }
+			    else
+				break;
+
+			    /*
+			     * We found a valid match, now check if there is
+			     * another one after it.
+			     * If vi-compatible searching, continue at the end
+			     * of the match, otherwise continue one position
+			     * forward.
+			     */
+			    if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
+			    {
+				if (nmatched > 1)
+				    break;
+				matchcol = endpos.col;
+				/* for empty match: advance one char */
+				if (matchcol == startcol
+						      && ptr[matchcol] != NUL)
+				{
+#ifdef FEAT_MBYTE
+				    if (has_mbyte)
+					matchcol +=
+					  (*mb_ptr2len_check)(ptr + matchcol);
+				    else
+#endif
+					++matchcol;
+				}
+			    }
+			    else
+			    {
+				matchcol = startcol;
+				if (ptr[matchcol] != NUL)
+				{
+#ifdef FEAT_MBYTE
+				    if (has_mbyte)
+					matchcol +=
+					  (*mb_ptr2len_check)(ptr + matchcol);
+				    else
+#endif
+					++matchcol;
+				}
+			    }
+			    if (ptr[matchcol] == NUL
+				    || (nmatched = vim_regexec_multi(&regmatch,
+					      win, buf, lnum, matchcol)) == 0)
+				break;
+
+			    /* Need to get the line pointer again, a
+			     * multi-line search may have made it invalid. */
+			    ptr = ml_get_buf(buf, lnum, FALSE);
+			}
+
+			/*
+			 * If there is only a match after the cursor, skip
+			 * this match.
+			 */
+			if (!match_ok)
+			    continue;
+		    }
+
+		    if (options & SEARCH_END && !(options & SEARCH_NOOF))
+		    {
+			pos->lnum = endpos.lnum + first_lnum;
+			pos->col = endpos.col - 1;
+		    }
+		    else
+		    {
+			pos->lnum = lnum;
+			pos->col = startcol;
+		    }
+#ifdef FEAT_VIRTUALEDIT
+		    pos->coladd = 0;
+#endif
+		    found = 1;
+
+		    /* Set variables used for 'incsearch' highlighting. */
+		    search_match_lines = endpos.lnum - (lnum - first_lnum);
+		    search_match_endcol = endpos.col;
+		    break;
+		}
+		line_breakcheck();	/* stop if ctrl-C typed */
+		if (got_int)
+		    break;
+
+#ifdef FEAT_SEARCH_EXTRA
+		/* Cancel searching if a character was typed.  Used for
+		 * 'incsearch'.  Don't check too often, that would slowdown
+		 * searching too much. */
+		if ((options & SEARCH_PEEK)
+			&& ((lnum - pos->lnum) & 0x3f) == 0
+			&& char_avail())
+		{
+		    break_loop = TRUE;
+		    break;
+		}
+#endif
+
+		if (loop && lnum == start_pos.lnum)
+		    break;	    /* if second loop, stop where started */
+	    }
+	    at_first_line = FALSE;
+
+	    /*
+	     * Stop the search if wrapscan isn't set, after an interrupt,
+	     * after a match and after looping twice.
+	     */
+	    if (!p_ws || got_int || called_emsg || break_loop || found || loop)
+		break;
+
+	    /*
+	     * If 'wrapscan' is set we continue at the other end of the file.
+	     * If 'shortmess' does not contain 's', we give a message.
+	     * This message is also remembered in keep_msg for when the screen
+	     * is redrawn. The keep_msg is cleared whenever another message is
+	     * written.
+	     */
+	    if (dir == BACKWARD)    /* start second loop at the other end */
+	    {
+		lnum = buf->b_ml.ml_line_count;
+		if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
+		    give_warning((char_u *)_(top_bot_msg), TRUE);
+	    }
+	    else
+	    {
+		lnum = 1;
+		if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
+		    give_warning((char_u *)_(bot_top_msg), TRUE);
+	    }
+	}
+	if (got_int || called_emsg || break_loop)
+	    break;
+    }
+    while (--count > 0 && found);   /* stop after count matches or no match */
+
+    vim_free(regmatch.regprog);
+
+    if (!found)		    /* did not find it */
+    {
+	if (got_int)
+	    EMSG(_(e_interr));
+	else if ((options & SEARCH_MSG) == SEARCH_MSG)
+	{
+	    if (p_ws)
+		EMSG2(_(e_patnotf2), mr_pattern);
+	    else if (lnum == 0)
+		EMSG2(_("E384: search hit TOP without match for: %s"),
+								  mr_pattern);
+	    else
+		EMSG2(_("E385: search hit BOTTOM without match for: %s"),
+								  mr_pattern);
+	}
+	return FAIL;
+    }
+
+    return submatch + 1;
+}
+
+#ifdef FEAT_EVAL
+/*
+ * Return the number of the first subpat that matched.
+ */
+    static int
+first_submatch(rp)
+    regmmatch_T	*rp;
+{
+    int		submatch;
+
+    for (submatch = 1; ; ++submatch)
+    {
+	if (rp->startpos[submatch].lnum >= 0)
+	    break;
+	if (submatch == 9)
+	{
+	    submatch = 0;
+	    break;
+	}
+    }
+    return submatch;
+}
+#endif
+
+/*
+ * Highest level string search function.
+ * Search for the 'count'th occurence of pattern 'pat' in direction 'dirc'
+ *		  If 'dirc' is 0: use previous dir.
+ *    If 'pat' is NULL or empty : use previous string.
+ *    If 'options & SEARCH_REV' : go in reverse of previous dir.
+ *    If 'options & SEARCH_ECHO': echo the search command and handle options
+ *    If 'options & SEARCH_MSG' : may give error message
+ *    If 'options & SEARCH_OPT' : interpret optional flags
+ *    If 'options & SEARCH_HIS' : put search pattern in history
+ *    If 'options & SEARCH_NOOF': don't add offset to position
+ *    If 'options & SEARCH_MARK': set previous context mark
+ *    If 'options & SEARCH_KEEP': keep previous search pattern
+ *    If 'options & SEARCH_START': accept match at curpos itself
+ *    If 'options & SEARCH_PEEK': check for typed char, cancel search
+ *
+ * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
+ * makes the movement linewise without moving the match position.
+ *
+ * return 0 for failure, 1 for found, 2 for found and line offset added
+ */
+    int
+do_search(oap, dirc, pat, count, options)
+    oparg_T	    *oap;	/* can be NULL */
+    int		    dirc;	/* '/' or '?' */
+    char_u	   *pat;
+    long	    count;
+    int		    options;
+{
+    pos_T	    pos;	/* position of the last match */
+    char_u	    *searchstr;
+    struct soffset  old_off;
+    int		    retval;	/* Return value */
+    char_u	    *p;
+    long	    c;
+    char_u	    *dircp;
+    char_u	    *strcopy = NULL;
+    char_u	    *ps;
+
+    /*
+     * A line offset is not remembered, this is vi compatible.
+     */
+    if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
+    {
+	spats[0].off.line = FALSE;
+	spats[0].off.off = 0;
+    }
+
+    /*
+     * Save the values for when (options & SEARCH_KEEP) is used.
+     * (there is no "if ()" around this because gcc wants them initialized)
+     */
+    old_off = spats[0].off;
+
+    pos = curwin->w_cursor;	/* start searching at the cursor position */
+
+    /*
+     * Find out the direction of the search.
+     */
+    if (dirc == 0)
+	dirc = spats[0].off.dir;
+    else
+	spats[0].off.dir = dirc;
+    if (options & SEARCH_REV)
+    {
+#ifdef WIN32
+	/* There is a bug in the Visual C++ 2.2 compiler which means that
+	 * dirc always ends up being '/' */
+	dirc = (dirc == '/')  ?  '?'  :  '/';
+#else
+	if (dirc == '/')
+	    dirc = '?';
+	else
+	    dirc = '/';
+#endif
+    }
+
+#ifdef FEAT_FOLDING
+    /* If the cursor is in a closed fold, don't find another match in the same
+     * fold. */
+    if (dirc == '/')
+    {
+	if (hasFolding(pos.lnum, NULL, &pos.lnum))
+	    pos.col = MAXCOL - 2;	/* avoid overflow when adding 1 */
+    }
+    else
+    {
+	if (hasFolding(pos.lnum, &pos.lnum, NULL))
+	    pos.col = 0;
+    }
+#endif
+
+#ifdef FEAT_SEARCH_EXTRA
+    /*
+     * Turn 'hlsearch' highlighting back on.
+     */
+    if (no_hlsearch && !(options & SEARCH_KEEP))
+    {
+	redraw_all_later(NOT_VALID);
+	no_hlsearch = FALSE;
+    }
+#endif
+
+    /*
+     * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
+     */
+    for (;;)
+    {
+	searchstr = pat;
+	dircp = NULL;
+					    /* use previous pattern */
+	if (pat == NULL || *pat == NUL || *pat == dirc)
+	{
+	    if (spats[RE_SEARCH].pat == NULL)	    /* no previous pattern */
+	    {
+		EMSG(_(e_noprevre));
+		retval = 0;
+		goto end_do_search;
+	    }
+	    /* make search_regcomp() use spats[RE_SEARCH].pat */
+	    searchstr = (char_u *)"";
+	}
+
+	if (pat != NULL && *pat != NUL)	/* look for (new) offset */
+	{
+	    /*
+	     * Find end of regular expression.
+	     * If there is a matching '/' or '?', toss it.
+	     */
+	    ps = strcopy;
+	    p = skip_regexp(pat, dirc, (int)p_magic, &strcopy);
+	    if (strcopy != ps)
+	    {
+		/* made a copy of "pat" to change "\?" to "?" */
+		searchcmdlen += STRLEN(pat) - STRLEN(strcopy);
+		pat = strcopy;
+		searchstr = strcopy;
+	    }
+	    if (*p == dirc)
+	    {
+		dircp = p;	/* remember where we put the NUL */
+		*p++ = NUL;
+	    }
+	    spats[0].off.line = FALSE;
+	    spats[0].off.end = FALSE;
+	    spats[0].off.off = 0;
+	    /*
+	     * Check for a line offset or a character offset.
+	     * For get_address (echo off) we don't check for a character
+	     * offset, because it is meaningless and the 's' could be a
+	     * substitute command.
+	     */
+	    if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p))
+		spats[0].off.line = TRUE;
+	    else if ((options & SEARCH_OPT) &&
+					(*p == 'e' || *p == 's' || *p == 'b'))
+	    {
+		if (*p == 'e')		/* end */
+		    spats[0].off.end = SEARCH_END;
+		++p;
+	    }
+	    if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-')  /* got an offset */
+	    {
+					    /* 'nr' or '+nr' or '-nr' */
+		if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1)))
+		    spats[0].off.off = atol((char *)p);
+		else if (*p == '-')	    /* single '-' */
+		    spats[0].off.off = -1;
+		else			    /* single '+' */
+		    spats[0].off.off = 1;
+		++p;
+		while (VIM_ISDIGIT(*p))	    /* skip number */
+		    ++p;
+	    }
+
+	    /* compute length of search command for get_address() */
+	    searchcmdlen += (int)(p - pat);
+
+	    pat = p;			    /* put pat after search command */
+	}
+
+	if ((options & SEARCH_ECHO) && messaging()
+					    && !cmd_silent && msg_silent == 0)
+	{
+	    char_u	*msgbuf;
+	    char_u	*trunc;
+
+	    if (*searchstr == NUL)
+		p = spats[last_idx].pat;
+	    else
+		p = searchstr;
+	    msgbuf = alloc((unsigned)(STRLEN(p) + 40));
+	    if (msgbuf != NULL)
+	    {
+		msgbuf[0] = dirc;
+		STRCPY(msgbuf + 1, p);
+		if (spats[0].off.line || spats[0].off.end || spats[0].off.off)
+		{
+		    p = msgbuf + STRLEN(msgbuf);
+		    *p++ = dirc;
+		    if (spats[0].off.end)
+			*p++ = 'e';
+		    else if (!spats[0].off.line)
+			*p++ = 's';
+		    if (spats[0].off.off > 0 || spats[0].off.line)
+			*p++ = '+';
+		    if (spats[0].off.off != 0 || spats[0].off.line)
+			sprintf((char *)p, "%ld", spats[0].off.off);
+		    else
+			*p = NUL;
+		}
+
+		msg_start();
+		trunc = msg_strtrunc(msgbuf);
+
+#ifdef FEAT_RIGHTLEFT
+		/* The search pattern could be shown on the right in rightleft
+		 * mode, but the 'ruler' and 'showcmd' area use it too, thus
+		 * it would be blanked out again very soon.  Show it on the
+		 * left, but do reverse the text. */
+		if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
+		{
+		    char_u *r;
+
+		    r = reverse_text(trunc != NULL ? trunc : msgbuf);
+		    if (r != NULL)
+		    {
+			vim_free(trunc);
+			trunc = r;
+		    }
+		}
+#endif
+		if (trunc != NULL)
+		{
+		    msg_outtrans(trunc);
+		    vim_free(trunc);
+		}
+		else
+		    msg_outtrans(msgbuf);
+		msg_clr_eos();
+		msg_check();
+		vim_free(msgbuf);
+
+		gotocmdline(FALSE);
+		out_flush();
+		msg_nowait = TRUE;	    /* don't wait for this message */
+	    }
+	}
+
+	/*
+	 * If there is a character offset, subtract it from the current
+	 * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
+	 * This is not done for a line offset, because then we would not be vi
+	 * compatible.
+	 */
+	if (!spats[0].off.line && spats[0].off.off)
+	{
+	    if (spats[0].off.off > 0)
+	    {
+		for (c = spats[0].off.off; c; --c)
+		    if (decl(&pos) == -1)
+			break;
+		if (c)			/* at start of buffer */
+		{
+		    pos.lnum = 0;	/* allow lnum == 0 here */
+		    pos.col = MAXCOL;
+		}
+	    }
+	    else
+	    {
+		for (c = spats[0].off.off; c; ++c)
+		    if (incl(&pos) == -1)
+			break;
+		if (c)			/* at end of buffer */
+		{
+		    pos.lnum = curbuf->b_ml.ml_line_count + 1;
+		    pos.col = 0;
+		}
+	    }
+	}
+
+#ifdef FEAT_FKMAP	/* when in Farsi mode, reverse the character flow */
+	if (p_altkeymap && curwin->w_p_rl)
+	     lrFswap(searchstr,0);
+#endif
+
+	c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD,
+		searchstr, count, spats[0].off.end + (options &
+		       (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS
+			+ SEARCH_MSG + SEARCH_START
+			+ ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF))),
+		RE_LAST);
+
+	if (dircp != NULL)
+	    *dircp = dirc;	/* restore second '/' or '?' for normal_cmd() */
+	if (c == FAIL)
+	{
+	    retval = 0;
+	    goto end_do_search;
+	}
+	if (spats[0].off.end && oap != NULL)
+	    oap->inclusive = TRUE;  /* 'e' includes last character */
+
+	retval = 1;		    /* pattern found */
+
+	/*
+	 * Add character and/or line offset
+	 */
+	if (!(options & SEARCH_NOOF) || *pat == ';')
+	{
+	    if (spats[0].off.line)	/* Add the offset to the line number. */
+	    {
+		c = pos.lnum + spats[0].off.off;
+		if (c < 1)
+		    pos.lnum = 1;
+		else if (c > curbuf->b_ml.ml_line_count)
+		    pos.lnum = curbuf->b_ml.ml_line_count;
+		else
+		    pos.lnum = c;
+		pos.col = 0;
+
+		retval = 2;	    /* pattern found, line offset added */
+	    }
+	    else
+	    {
+		/* to the right, check for end of file */
+		if (spats[0].off.off > 0)
+		{
+		    for (c = spats[0].off.off; c; --c)
+			if (incl(&pos) == -1)
+			    break;
+		}
+		/* to the left, check for start of file */
+		else
+		{
+		    if ((c = pos.col + spats[0].off.off) >= 0)
+			pos.col = c;
+		    else
+			for (c = spats[0].off.off; c; ++c)
+			    if (decl(&pos) == -1)
+				break;
+		}
+	    }
+	}
+
+	/*
+	 * The search command can be followed by a ';' to do another search.
+	 * For example: "/pat/;/foo/+3;?bar"
+	 * This is like doing another search command, except:
+	 * - The remembered direction '/' or '?' is from the first search.
+	 * - When an error happens the cursor isn't moved at all.
+	 * Don't do this when called by get_address() (it handles ';' itself).
+	 */
+	if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';')
+	    break;
+
+	dirc = *++pat;
+	if (dirc != '?' && dirc != '/')
+	{
+	    retval = 0;
+	    EMSG(_("E386: Expected '?' or '/'  after ';'"));
+	    goto end_do_search;
+	}
+	++pat;
+    }
+
+    if (options & SEARCH_MARK)
+	setpcmark();
+    curwin->w_cursor = pos;
+    curwin->w_set_curswant = TRUE;
+
+end_do_search:
+    if (options & SEARCH_KEEP)
+	spats[0].off = old_off;
+    vim_free(strcopy);
+
+    return retval;
+}
+
+#if defined(FEAT_INS_EXPAND) || defined(PROTO)
+/*
+ * search_for_exact_line(buf, pos, dir, pat)
+ *
+ * Search for a line starting with the given pattern (ignoring leading
+ * white-space), starting from pos and going in direction dir.	pos will
+ * contain the position of the match found.    Blank lines match only if
+ * ADDING is set.  if p_ic is set then the pattern must be in lowercase.
+ * Return OK for success, or FAIL if no line found.
+ */
+    int
+search_for_exact_line(buf, pos, dir, pat)
+    buf_T	*buf;
+    pos_T	*pos;
+    int		dir;
+    char_u	*pat;
+{
+    linenr_T	start = 0;
+    char_u	*ptr;
+    char_u	*p;
+
+    if (buf->b_ml.ml_line_count == 0)
+	return FAIL;
+    for (;;)
+    {
+	pos->lnum += dir;
+	if (pos->lnum < 1)
+	{
+	    if (p_ws)
+	    {
+		pos->lnum = buf->b_ml.ml_line_count;
+		if (!shortmess(SHM_SEARCH))
+		    give_warning((char_u *)_(top_bot_msg), TRUE);
+	    }
+	    else
+	    {
+		pos->lnum = 1;
+		break;
+	    }
+	}
+	else if (pos->lnum > buf->b_ml.ml_line_count)
+	{
+	    if (p_ws)
+	    {
+		pos->lnum = 1;
+		if (!shortmess(SHM_SEARCH))
+		    give_warning((char_u *)_(bot_top_msg), TRUE);
+	    }
+	    else
+	    {
+		pos->lnum = 1;
+		break;
+	    }
+	}
+	if (pos->lnum == start)
+	    break;
+	if (start == 0)
+	    start = pos->lnum;
+	ptr = ml_get_buf(buf, pos->lnum, FALSE);
+	p = skipwhite(ptr);
+	pos->col = (colnr_T) (p - ptr);
+
+	/* when adding lines the matching line may be empty but it is not
+	 * ignored because we are interested in the next line -- Acevedo */
+	if ((continue_status & CONT_ADDING) && !(continue_status & CONT_SOL))
+	{
+	    if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0)
+		return OK;
+	}
+	else if (*p != NUL)	/* ignore empty lines */
+	{	/* expanding lines or words */
+	    if ((p_ic ? MB_STRNICMP(p, pat, completion_length)
+				   : STRNCMP(p, pat, completion_length)) == 0)
+		return OK;
+	}
+    }
+    return FAIL;
+}
+#endif /* FEAT_INS_EXPAND */
+
+/*
+ * Character Searches
+ */
+
+/*
+ * Search for a character in a line.  If "t_cmd" is FALSE, move to the
+ * position of the character, otherwise move to just before the char.
+ * Do this "cap->count1" times.
+ * Return FAIL or OK.
+ */
+    int
+searchc(cap, t_cmd)
+    cmdarg_T	*cap;
+    int		t_cmd;
+{
+    int			c = cap->nchar;	/* char to search for */
+    int			dir = cap->arg;	/* TRUE for searching forward */
+    long		count = cap->count1;	/* repeat count */
+    static int		lastc = NUL;	/* last character searched for */
+    static int		lastcdir;	/* last direction of character search */
+    static int		last_t_cmd;	/* last search t_cmd */
+    int			col;
+    char_u		*p;
+    int			len;
+#ifdef FEAT_MBYTE
+    static char_u	bytes[MB_MAXBYTES];
+    static int		bytelen = 1;	/* >1 for multi-byte char */
+#endif
+
+    if (c != NUL)	/* normal search: remember args for repeat */
+    {
+	if (!KeyStuffed)    /* don't remember when redoing */
+	{
+	    lastc = c;
+	    lastcdir = dir;
+	    last_t_cmd = t_cmd;
+#ifdef FEAT_MBYTE
+	    bytelen = (*mb_char2bytes)(c, bytes);
+	    if (cap->ncharC1 != 0)
+	    {
+		bytelen += (*mb_char2bytes)(cap->ncharC1, bytes + bytelen);
+		if (cap->ncharC2 != 0)
+		    bytelen += (*mb_char2bytes)(cap->ncharC2, bytes + bytelen);
+	    }
+#endif
+	}
+    }
+    else		/* repeat previous search */
+    {
+	if (lastc == NUL)
+	    return FAIL;
+	if (dir)	/* repeat in opposite direction */
+	    dir = -lastcdir;
+	else
+	    dir = lastcdir;
+	t_cmd = last_t_cmd;
+	c = lastc;
+	/* For multi-byte re-use last bytes[] and bytelen. */
+    }
+
+    p = ml_get_curline();
+    col = curwin->w_cursor.col;
+    len = (int)STRLEN(p);
+
+    while (count--)
+    {
+#ifdef FEAT_MBYTE
+	if (has_mbyte)
+	{
+	    for (;;)
+	    {
+		if (dir > 0)
+		{
+		    col += (*mb_ptr2len_check)(p + col);
+		    if (col >= len)
+			return FAIL;
+		}
+		else
+		{
+		    if (col == 0)
+			return FAIL;
+		    col -= (*mb_head_off)(p, p + col - 1) + 1;
+		}
+		if (bytelen == 1)
+		{
+		    if (p[col] == c)
+			break;
+		}
+		else
+		{
+		    if (vim_memcmp(p + col, bytes, bytelen) == 0)
+			break;
+		}
+	    }
+	}
+	else
+#endif
+	{
+	    for (;;)
+	    {
+		if ((col += dir) < 0 || col >= len)
+		    return FAIL;
+		if (p[col] == c)
+		    break;
+	    }
+	}
+    }
+
+    if (t_cmd)
+    {
+	/* backup to before the character (possibly double-byte) */
+	col -= dir;
+#ifdef FEAT_MBYTE
+	if (has_mbyte)
+	{
+	    if (dir < 0)
+		/* Landed on the search char which is bytelen long */
+		col += bytelen - 1;
+	    else
+		/* To previous char, which may be multi-byte. */
+		col -= (*mb_head_off)(p, p + col);
+	}
+#endif
+    }
+    curwin->w_cursor.col = col;
+
+    return OK;
+}
+
+/*
+ * "Other" Searches
+ */
+
+/*
+ * findmatch - find the matching paren or brace
+ *
+ * Improvement over vi: Braces inside quotes are ignored.
+ */
+    pos_T *
+findmatch(oap, initc)
+    oparg_T   *oap;
+    int	    initc;
+{
+    return findmatchlimit(oap, initc, 0, 0);
+}
+
+/*
+ * Return TRUE if the character before "linep[col]" equals "ch".
+ * Return FALSE if "col" is zero.
+ * Update "*prevcol" to the column of the previous character, unless "prevcol"
+ * is NULL.
+ * Handles multibyte string correctly.
+ */
+    static int
+check_prevcol(linep, col, ch, prevcol)
+    char_u	*linep;
+    int		col;
+    int		ch;
+    int		*prevcol;
+{
+    --col;
+#ifdef FEAT_MBYTE
+    if (col > 0 && has_mbyte)
+	col -= (*mb_head_off)(linep, linep + col);
+#endif
+    if (prevcol)
+	*prevcol = col;
+    return (col >= 0 && linep[col] == ch) ? TRUE : FALSE;
+}
+
+/*
+ * findmatchlimit -- find the matching paren or brace, if it exists within
+ * maxtravel lines of here.  A maxtravel of 0 means search until falling off
+ * the edge of the file.
+ *
+ * "initc" is the character to find a match for.  NUL means to find the
+ * character at or after the cursor.
+ *
+ * flags: FM_BACKWARD	search backwards (when initc is '/', '*' or '#')
+ *	  FM_FORWARD	search forwards (when initc is '/', '*' or '#')
+ *	  FM_BLOCKSTOP	stop at start/end of block ({ or } in column 0)
+ *	  FM_SKIPCOMM	skip comments (not implemented yet!)
+ */
+
+    pos_T *
+findmatchlimit(oap, initc, flags, maxtravel)
+    oparg_T	*oap;
+    int		initc;
+    int		flags;
+    int		maxtravel;
+{
+    static pos_T pos;			/* current search position */
+    int		findc = 0;		/* matching brace */
+    int		c;
+    int		count = 0;		/* cumulative number of braces */
+    int		backwards = FALSE;	/* init for gcc */
+    int		inquote = FALSE;	/* TRUE when inside quotes */
+    char_u	*linep;			/* pointer to current line */
+    char_u	*ptr;
+    int		do_quotes;		/* check for quotes in current line */
+    int		at_start;		/* do_quotes value at start position */
+    int		hash_dir = 0;		/* Direction searched for # things */
+    int		comment_dir = 0;	/* Direction searched for comments */
+    pos_T	match_pos;		/* Where last slash-star was found */
+    int		start_in_quotes;	/* start position is in quotes */
+    int		traveled = 0;		/* how far we've searched so far */
+    int		ignore_cend = FALSE;    /* ignore comment end */
+    int		cpo_match;		/* vi compatible matching */
+    int		cpo_bsl;		/* don't recognize backslashes */
+    int		match_escaped = 0;	/* search for escaped match */
+    int		dir;			/* Direction to search */
+    int		comment_col = MAXCOL;   /* start of / / comment */
+
+    pos = curwin->w_cursor;
+    linep = ml_get(pos.lnum);
+
+    cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
+    cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL);
+
+    /* Direction to search when initc is '/', '*' or '#' */
+    if (flags & FM_BACKWARD)
+	dir = BACKWARD;
+    else if (flags & FM_FORWARD)
+	dir = FORWARD;
+    else
+	dir = 0;
+
+    /*
+     * if initc given, look in the table for the matching character
+     * '/' and '*' are special cases: look for start or end of comment.
+     * When '/' is used, we ignore running backwards into an star-slash, for
+     * "[*" command, we just want to find any comment.
+     */
+    if (initc == '/' || initc == '*')
+    {
+	comment_dir = dir;
+	if (initc == '/')
+	    ignore_cend = TRUE;
+	backwards = (dir == FORWARD) ? FALSE : TRUE;
+	initc = NUL;
+    }
+    else if (initc != '#' && initc != NUL)
+    {
+	/* 'matchpairs' is "x:y,x:y" */
+	for (ptr = curbuf->b_p_mps; *ptr; ptr += 2)
+	{
+	    if (*ptr == initc)
+	    {
+		findc = initc;
+		initc = ptr[2];
+		backwards = TRUE;
+		break;
+	    }
+	    ptr += 2;
+	    if (*ptr == initc)
+	    {
+		findc = initc;
+		initc = ptr[-2];
+		backwards = FALSE;
+		break;
+	    }
+	    if (ptr[1] != ',')
+		break;
+	}
+	if (!findc)		/* invalid initc! */
+	    return NULL;
+    }
+    /*
+     * Either initc is '#', or no initc was given and we need to look under the
+     * cursor.
+     */
+    else
+    {
+	if (initc == '#')
+	{
+	    hash_dir = dir;
+	}
+	else
+	{
+	    /*
+	     * initc was not given, must look for something to match under
+	     * or near the cursor.
+	     * Only check for special things when 'cpo' doesn't have '%'.
+	     */
+	    if (!cpo_match)
+	    {
+		/* Are we before or at #if, #else etc.? */
+		ptr = skipwhite(linep);
+		if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep))
+		{
+		    ptr = skipwhite(ptr + 1);
+		    if (   STRNCMP(ptr, "if", 2) == 0
+			|| STRNCMP(ptr, "endif", 5) == 0
+			|| STRNCMP(ptr, "el", 2) == 0)
+			hash_dir = 1;
+		}
+
+		/* Are we on a comment? */
+		else if (linep[pos.col] == '/')
+		{
+		    if (linep[pos.col + 1] == '*')
+		    {
+			comment_dir = FORWARD;
+			backwards = FALSE;
+			pos.col++;
+		    }
+		    else if (pos.col > 0 && linep[pos.col - 1] == '*')
+		    {
+			comment_dir = BACKWARD;
+			backwards = TRUE;
+			pos.col--;
+		    }
+		}
+		else if (linep[pos.col] == '*')
+		{
+		    if (linep[pos.col + 1] == '/')
+		    {
+			comment_dir = BACKWARD;
+			backwards = TRUE;
+		    }
+		    else if (pos.col > 0 && linep[pos.col - 1] == '/')
+		    {
+			comment_dir = FORWARD;
+			backwards = FALSE;
+		    }
+		}
+	    }
+
+	    /*
+	     * If we are not on a comment or the # at the start of a line, then
+	     * look for brace anywhere on this line after the cursor.
+	     */
+	    if (!hash_dir && !comment_dir)
+	    {
+		/*
+		 * Find the brace under or after the cursor.
+		 * If beyond the end of the line, use the last character in
+		 * the line.
+		 */
+		if (linep[pos.col] == NUL && pos.col)
+		    --pos.col;
+		for (;;)
+		{
+		    initc = linep[pos.col];
+		    if (initc == NUL)
+			break;
+
+		    for (ptr = curbuf->b_p_mps; *ptr; ++ptr)
+		    {
+			if (*ptr == initc)
+			{
+			    findc = ptr[2];
+			    backwards = FALSE;
+			    break;
+			}
+			ptr += 2;
+			if (*ptr == initc)
+			{
+			    findc = ptr[-2];
+			    backwards = TRUE;
+			    break;
+			}
+			if (!*++ptr)
+			    break;
+		    }
+		    if (findc)
+			break;
+#ifdef FEAT_MBYTE
+		    if (has_mbyte)
+			pos.col += (*mb_ptr2len_check)(linep + pos.col);
+		    else
+#endif
+			++pos.col;
+		}
+		if (!findc)
+		{
+		    /* no brace in the line, maybe use "  #if" then */
+		    if (!cpo_match && *skipwhite(linep) == '#')
+			hash_dir = 1;
+		    else
+			return NULL;
+		}
+		else if (!cpo_bsl)
+		{
+		    int col, bslcnt = 0;
+
+		    /* Set "match_escaped" if there are an odd number of
+		     * backslashes. */
+		    for (col = pos.col; check_prevcol(linep, col, '\\', &col);)
+			bslcnt++;
+		    match_escaped = (bslcnt & 1);
+		}
+	    }
+	}
+	if (hash_dir)
+	{
+	    /*
+	     * Look for matching #if, #else, #elif, or #endif
+	     */
+	    if (oap != NULL)
+		oap->motion_type = MLINE;   /* Linewise for this case only */
+	    if (initc != '#')
+	    {
+		ptr = skipwhite(skipwhite(linep) + 1);
+		if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0)
+		    hash_dir = 1;
+		else if (STRNCMP(ptr, "endif", 5) == 0)
+		    hash_dir = -1;
+		else
+		    return NULL;
+	    }
+	    pos.col = 0;
+	    while (!got_int)
+	    {
+		if (hash_dir > 0)
+		{
+		    if (pos.lnum == curbuf->b_ml.ml_line_count)
+			break;
+		}
+		else if (pos.lnum == 1)
+		    break;
+		pos.lnum += hash_dir;
+		linep = ml_get(pos.lnum);
+		line_breakcheck();	/* check for CTRL-C typed */
+		ptr = skipwhite(linep);
+		if (*ptr != '#')
+		    continue;
+		pos.col = (colnr_T) (ptr - linep);
+		ptr = skipwhite(ptr + 1);
+		if (hash_dir > 0)
+		{
+		    if (STRNCMP(ptr, "if", 2) == 0)
+			count++;
+		    else if (STRNCMP(ptr, "el", 2) == 0)
+		    {
+			if (count == 0)
+			    return &pos;
+		    }
+		    else if (STRNCMP(ptr, "endif", 5) == 0)
+		    {
+			if (count == 0)
+			    return &pos;
+			count--;
+		    }
+		}
+		else
+		{
+		    if (STRNCMP(ptr, "if", 2) == 0)
+		    {
+			if (count == 0)
+			    return &pos;
+			count--;
+		    }
+		    else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0)
+		    {
+			if (count == 0)
+			    return &pos;
+		    }
+		    else if (STRNCMP(ptr, "endif", 5) == 0)
+			count++;
+		}
+	    }
+	    return NULL;
+	}
+    }
+
+#ifdef FEAT_RIGHTLEFT
+    /* This is just guessing: when 'rightleft' is set, search for a maching
+     * paren/brace in the other direction. */
+    if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL)
+	backwards = !backwards;
+#endif
+
+    do_quotes = -1;
+    start_in_quotes = MAYBE;
+    /* backward search: Check if this line contains a single-line comment */
+    if (backwards && comment_dir)
+	comment_col = check_linecomment(linep);
+    while (!got_int)
+    {
+	/*
+	 * Go to the next position, forward or backward. We could use
+	 * inc() and dec() here, but that is much slower
+	 */
+	if (backwards)
+	{
+	    if (pos.col == 0)		/* at start of line, go to prev. one */
+	    {
+		if (pos.lnum == 1)	/* start of file */
+		    break;
+		--pos.lnum;
+
+		if (maxtravel && traveled++ > maxtravel)
+		    break;
+
+		linep = ml_get(pos.lnum);
+		pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */
+		do_quotes = -1;
+		line_breakcheck();
+
+		/* Check if this line contains a single-line comment */
+		if (comment_dir)
+		    comment_col = check_linecomment(linep);
+	    }
+	    else
+	    {
+		--pos.col;
+#ifdef FEAT_MBYTE
+		if (has_mbyte)
+		    pos.col -= (*mb_head_off)(linep, linep + pos.col);
+#endif
+	    }
+	}
+	else				/* forward search */
+	{
+	    if (linep[pos.col] == NUL)	/* at end of line, go to next one */
+	    {
+		if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
+		    break;
+		++pos.lnum;
+
+		if (maxtravel && traveled++ > maxtravel)
+		    break;
+
+		linep = ml_get(pos.lnum);
+		pos.col = 0;
+		do_quotes = -1;
+		line_breakcheck();
+	    }
+	    else
+	    {
+#ifdef FEAT_MBYTE
+		if (has_mbyte)
+		    pos.col += (*mb_ptr2len_check)(linep + pos.col);
+		else
+#endif
+		    ++pos.col;
+	    }
+	}
+
+	/*
+	 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
+	 */
+	if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
+					 (linep[0] == '{' || linep[0] == '}'))
+	{
+	    if (linep[0] == findc && count == 0)	/* match! */
+		return &pos;
+	    break;					/* out of scope */
+	}
+
+	if (comment_dir)
+	{
+	    /* Note: comments do not nest, and we ignore quotes in them */
+	    /* TODO: ignore comment brackets inside strings */
+	    if (comment_dir == FORWARD)
+	    {
+		if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
+		{
+		    pos.col++;
+		    return &pos;
+		}
+	    }
+	    else    /* Searching backwards */
+	    {
+		/*
+		 * A comment may contain / * or / /, it may also start or end
+		 * with / * /.	Ignore a / * after / /.
+		 */
+		if (pos.col == 0)
+		    continue;
+		else if (  linep[pos.col - 1] == '/'
+			&& linep[pos.col] == '*'
+			&& (int)pos.col < comment_col)
+		{
+		    count++;
+		    match_pos = pos;
+		    match_pos.col--;
+		}
+		else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
+		{
+		    if (count > 0)
+			pos = match_pos;
+		    else if (pos.col > 1 && linep[pos.col - 2] == '/'
+					       && (int)pos.col <= comment_col)
+			pos.col -= 2;
+		    else if (ignore_cend)
+			continue;
+		    else
+			return NULL;
+		    return &pos;
+		}
+	    }
+	    continue;
+	}
+
+	/*
+	 * If smart matching ('cpoptions' does not contain '%'), braces inside
+	 * of quotes are ignored, but only if there is an even number of
+	 * quotes in the line.
+	 */
+	if (cpo_match)
+	    do_quotes = 0;
+	else if (do_quotes == -1)
+	{
+	    /*
+	     * Count the number of quotes in the line, skipping \" and '"'.
+	     * Watch out for "\\".
+	     */
+	    at_start = do_quotes;
+	    for (ptr = linep; *ptr; ++ptr)
+	    {
+		if (ptr == linep + pos.col + backwards)
+		    at_start = (do_quotes & 1);
+		if (*ptr == '"'
+			&& (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
+		    ++do_quotes;
+		if (*ptr == '\\' && ptr[1] != NUL)
+		    ++ptr;
+	    }
+	    do_quotes &= 1;	    /* result is 1 with even number of quotes */
+
+	    /*
+	     * If we find an uneven count, check current line and previous
+	     * one for a '\' at the end.
+	     */
+	    if (!do_quotes)
+	    {
+		inquote = FALSE;
+		if (ptr[-1] == '\\')
+		{
+		    do_quotes = 1;
+		    if (start_in_quotes == MAYBE)
+		    {
+			/* Do we need to use at_start here? */
+			inquote = TRUE;
+			start_in_quotes = TRUE;
+		    }
+		    else if (backwards)
+			inquote = TRUE;
+		}
+		if (pos.lnum > 1)
+		{
+		    ptr = ml_get(pos.lnum - 1);
+		    if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
+		    {
+			do_quotes = 1;
+			if (start_in_quotes == MAYBE)
+			{
+			    inquote = at_start;
+			    if (inquote)
+				start_in_quotes = TRUE;
+			}
+			else if (!backwards)
+			    inquote = TRUE;
+		    }
+		}
+	    }
+	}
+	if (start_in_quotes == MAYBE)
+	    start_in_quotes = FALSE;
+
+	/*
+	 * If 'smartmatch' is set:
+	 *   Things inside quotes are ignored by setting 'inquote'.  If we
+	 *   find a quote without a preceding '\' invert 'inquote'.  At the
+	 *   end of a line not ending in '\' we reset 'inquote'.
+	 *
+	 *   In lines with an uneven number of quotes (without preceding '\')
+	 *   we do not know which part to ignore. Therefore we only set
+	 *   inquote if the number of quotes in a line is even, unless this
+	 *   line or the previous one ends in a '\'.  Complicated, isn't it?
+	 */
+	switch (c = linep[pos.col])
+	{
+	case NUL:
+	    /* at end of line without trailing backslash, reset inquote */
+	    if (pos.col == 0 || linep[pos.col - 1] != '\\')
+	    {
+		inquote = FALSE;
+		start_in_quotes = FALSE;
+	    }
+	    break;
+
+	case '"':
+	    /* a quote that is preceded with an odd number of backslashes is
+	     * ignored */
+	    if (do_quotes)
+	    {
+		int col;
+
+		for (col = pos.col - 1; col >= 0; --col)
+		    if (linep[col] != '\\')
+			break;
+		if ((((int)pos.col - 1 - col) & 1) == 0)
+		{
+		    inquote = !inquote;
+		    start_in_quotes = FALSE;
+		}
+	    }
+	    break;
+
+	/*
+	 * If smart matching ('cpoptions' does not contain '%'):
+	 *   Skip things in single quotes: 'x' or '\x'.  Be careful for single
+	 *   single quotes, eg jon's.  Things like '\233' or '\x3f' are not
+	 *   skipped, there is never a brace in them.
+	 *   Ignore this when finding matches for `'.
+	 */
+	case '\'':
+	    if (!cpo_match && initc != '\'' && findc != '\'')
+	    {
+		if (backwards)
+		{
+		    if (pos.col > 1)
+		    {
+			if (linep[pos.col - 2] == '\'')
+			{
+			    pos.col -= 2;
+			    break;
+			}
+			else if (linep[pos.col - 2] == '\\' &&
+				    pos.col > 2 && linep[pos.col - 3] == '\'')
+			{
+			    pos.col -= 3;
+			    break;
+			}
+		    }
+		}
+		else if (linep[pos.col + 1])	/* forward search */
+		{
+		    if (linep[pos.col + 1] == '\\' &&
+			    linep[pos.col + 2] && linep[pos.col + 3] == '\'')
+		    {
+			pos.col += 3;
+			break;
+		    }
+		    else if (linep[pos.col + 2] == '\'')
+		    {
+			pos.col += 2;
+			break;
+		    }
+		}
+	    }
+	    /* FALLTHROUGH */
+
+	default:
+#ifdef FEAT_LISP
+	    /* For Lisp skip over backslashed (), {} and []. */
+	    if (curbuf->b_p_lisp
+		    && vim_strchr((char_u *)"(){}[]", c) != NULL
+		    && pos.col > 0
+		    && check_prevcol(linep, pos.col, '\\', NULL))
+		break;
+#endif
+
+	    /* Check for match outside of quotes, and inside of
+	     * quotes when the start is also inside of quotes. */
+	    if ((!inquote || start_in_quotes == TRUE)
+		    && (c == initc || c == findc))
+	    {
+		int	col, bslcnt = 0;
+
+		if (!cpo_bsl)
+		{
+		    for (col = pos.col; check_prevcol(linep, col, '\\', &col);)
+			bslcnt++;
+		}
+		/* Only accept a match when 'M' is in 'cpo' or when ecaping is
+		 * what we expect. */
+		if (cpo_bsl || (bslcnt & 1) == match_escaped)
+		{
+		    if (c == initc)
+			count++;
+		    else
+		    {
+			if (count == 0)
+			    return &pos;
+			count--;
+		    }
+		}
+	    }
+	}
+    }
+
+    if (comment_dir == BACKWARD && count > 0)
+    {
+	pos = match_pos;
+	return &pos;
+    }
+    return (pos_T *)NULL;	/* never found it */
+}
+
+/*
+ * Check if line[] contains a / / comment.
+ * Return MAXCOL if not, otherwise return the column.
+ * TODO: skip strings.
+ */
+    static int
+check_linecomment(line)
+    char_u	*line;
+{
+    char_u  *p;
+
+    p = line;
+    while ((p = vim_strchr(p, '/')) != NULL)
+    {
+	if (p[1] == '/')
+	    break;
+	++p;
+    }
+
+    if (p == NULL)
+	return MAXCOL;
+    return (int)(p - line);
+}
+
+/*
+ * Move cursor briefly to character matching the one under the cursor.
+ * Used for Insert mode and "r" command.
+ * Show the match only if it is visible on the screen.
+ * If there isn't a match, then beep.
+ */
+    void
+showmatch(c)
+    int		c;	    /* char to show match for */
+{
+    pos_T	*lpos, save_cursor;
+    pos_T	mpos;
+    colnr_T	vcol;
+    long	save_so;
+    long	save_siso;
+#ifdef CURSOR_SHAPE
+    int		save_state;
+#endif
+    colnr_T	save_dollar_vcol;
+    char_u	*p;
+
+    /*
+     * Only show match for chars in the 'matchpairs' option.
+     */
+    /* 'matchpairs' is "x:y,x:y" */
+    for (p = curbuf->b_p_mps; *p != NUL; p += 2)
+    {
+#ifdef FEAT_RIGHTLEFT
+	if (*p == c && (curwin->w_p_rl ^ p_ri))
+	    break;
+#endif
+	p += 2;
+	if (*p == c
+#ifdef FEAT_RIGHTLEFT
+		&& !(curwin->w_p_rl ^ p_ri)
+#endif
+	   )
+	    break;
+	if (p[1] != ',')
+	    return;
+    }
+
+    if ((lpos = findmatch(NULL, NUL)) == NULL)	    /* no match, so beep */
+	vim_beep();
+    else if (lpos->lnum >= curwin->w_topline)
+    {
+	if (!curwin->w_p_wrap)
+	    getvcol(curwin, lpos, NULL, &vcol, NULL);
+	if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol
+			       && vcol < curwin->w_leftcol + W_WIDTH(curwin)))
+	{
+	    mpos = *lpos;    /* save the pos, update_screen() may change it */
+	    save_cursor = curwin->w_cursor;
+	    save_so = p_so;
+	    save_siso = p_siso;
+	    /* Handle "$" in 'cpo': If the ')' is typed on top of the "$",
+	     * stop displaying the "$". */
+	    if (dollar_vcol > 0 && dollar_vcol == curwin->w_virtcol)
+		dollar_vcol = 0;
+	    ++curwin->w_virtcol;	/* do display ')' just before "$" */
+	    update_screen(VALID);	/* show the new char first */
+
+	    save_dollar_vcol = dollar_vcol;
+#ifdef CURSOR_SHAPE
+	    save_state = State;
+	    State = SHOWMATCH;
+	    ui_cursor_shape();		/* may show different cursor shape */
+#endif
+	    curwin->w_cursor = mpos;	/* move to matching char */
+	    p_so = 0;			/* don't use 'scrolloff' here */
+	    p_siso = 0;			/* don't use 'sidescrolloff' here */
+	    showruler(FALSE);
+	    setcursor();
+	    cursor_on();		/* make sure that the cursor is shown */
+	    out_flush();
+#ifdef FEAT_GUI
+	    if (gui.in_use)
+	    {
+		gui_update_cursor(TRUE, FALSE);
+		gui_mch_flush();
+	    }
+#endif
+	    /* Restore dollar_vcol(), because setcursor() may call curs_rows()
+	     * which resets it if the matching position is in a previous line
+	     * and has a higher column number. */
+	    dollar_vcol = save_dollar_vcol;
+
+	    /*
+	     * brief pause, unless 'm' is present in 'cpo' and a character is
+	     * available.
+	     */
+	    if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
+		ui_delay(p_mat * 100L, TRUE);
+	    else if (!char_avail())
+		ui_delay(p_mat * 100L, FALSE);
+	    curwin->w_cursor = save_cursor;	/* restore cursor position */
+	    p_so = save_so;
+	    p_siso = save_siso;
+#ifdef CURSOR_SHAPE
+	    State = save_state;
+	    ui_cursor_shape();		/* may show different cursor shape */
+#endif
+	}
+    }
+}
+
+/*
+ * findsent(dir, count) - Find the start of the next sentence in direction
+ * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
+ * space or a line break. Also stop at an empty line.
+ * Return OK if the next sentence was found.
+ */
+    int
+findsent(dir, count)
+    int		dir;
+    long	count;
+{
+    pos_T	pos, tpos;
+    int		c;
+    int		(*func) __ARGS((pos_T *));
+    int		startlnum;
+    int		noskip = FALSE;	    /* do not skip blanks */
+    int		cpo_J;
+
+    pos = curwin->w_cursor;
+    if (dir == FORWARD)
+	func = incl;
+    else
+	func = decl;
+
+    while (count--)
+    {
+	/*
+	 * if on an empty line, skip upto a non-empty line
+	 */
+	if (gchar_pos(&pos) == NUL)
+	{
+	    do
+		if ((*func)(&pos) == -1)
+		    break;
+	    while (gchar_pos(&pos) == NUL);
+	    if (dir == FORWARD)
+		goto found;
+	}
+	/*
+	 * if on the start of a paragraph or a section and searching forward,
+	 * go to the next line
+	 */
+	else if (dir == FORWARD && pos.col == 0 &&
+						startPS(pos.lnum, NUL, FALSE))
+	{
+	    if (pos.lnum == curbuf->b_ml.ml_line_count)
+		return FAIL;
+	    ++pos.lnum;
+	    goto found;
+	}
+	else if (dir == BACKWARD)
+	    decl(&pos);
+
+	/* go back to the previous non-blank char */
+	while ((c = gchar_pos(&pos)) == ' ' || c == '\t' ||
+	     (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
+	{
+	    if (decl(&pos) == -1)
+		break;
+	    /* when going forward: Stop in front of empty line */
+	    if (lineempty(pos.lnum) && dir == FORWARD)
+	    {
+		incl(&pos);
+		goto found;
+	    }
+	}
+
+	/* remember the line where the search started */
+	startlnum = pos.lnum;
+	cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
+
+	for (;;)		/* find end of sentence */
+	{
+	    c = gchar_pos(&pos);
+	    if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
+	    {
+		if (dir == BACKWARD && pos.lnum != startlnum)
+		    ++pos.lnum;
+		break;
+	    }
+	    if (c == '.' || c == '!' || c == '?')
+	    {
+		tpos = pos;
+		do
+		    if ((c = inc(&tpos)) == -1)
+			break;
+		while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
+			!= NULL);
+		if (c == -1  || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
+		    || (cpo_J && (c == ' ' && inc(&tpos) >= 0
+			      && gchar_pos(&tpos) == ' ')))
+		{
+		    pos = tpos;
+		    if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */
+			inc(&pos);
+		    break;
+		}
+	    }
+	    if ((*func)(&pos) == -1)
+	    {
+		if (count)
+		    return FAIL;
+		noskip = TRUE;
+		break;
+	    }
+	}
+found:
+	    /* skip white space */
+	while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
+	    if (incl(&pos) == -1)
+		break;
+    }
+
+    setpcmark();
+    curwin->w_cursor = pos;
+    return OK;
+}
+
+/*
+ * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
+ * Paragraphs are currently supposed to be separated by empty lines.
+ * Return TRUE if the next paragraph was found.
+ * If 'what' is '{' or '}' we go to the next section.
+ * If 'both' is TRUE also stop at '}'.
+ */
+    int
+findpar(oap, dir, count, what, both)
+    oparg_T	    *oap;
+    int		    dir;
+    long	    count;
+    int		    what;
+    int		    both;
+{
+    linenr_T	curr;
+    int		did_skip;   /* TRUE after separating lines have been skipped */
+    int		first;	    /* TRUE on first line */
+#ifdef FEAT_FOLDING
+    linenr_T	fold_first; /* first line of a closed fold */
+    linenr_T	fold_last;  /* last line of a closed fold */
+    int		fold_skipped; /* TRUE if a closed fold was skipped this
+				 iteration */
+#endif
+
+    curr = curwin->w_cursor.lnum;
+
+    while (count--)
+    {
+	did_skip = FALSE;
+	for (first = TRUE; ; first = FALSE)
+	{
+	    if (*ml_get(curr) != NUL)
+		did_skip = TRUE;
+
+#ifdef FEAT_FOLDING
+	    /* skip folded lines */
+	    fold_skipped = FALSE;
+	    if (first && hasFolding(curr, &fold_first, &fold_last))
+	    {
+		curr = ((dir > 0) ? fold_last : fold_first) + dir;
+		fold_skipped = TRUE;
+	    }
+#endif
+
+	    if (!first && did_skip && startPS(curr, what, both))
+		break;
+
+#ifdef FEAT_FOLDING
+	    if (fold_skipped)
+		curr -= dir;
+#endif
+	    if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
+	    {
+		if (count)
+		    return FALSE;
+		curr -= dir;
+		break;
+	    }
+	}
+    }
+    setpcmark();
+    if (both && *ml_get(curr) == '}')	/* include line with '}' */
+	++curr;
+    curwin->w_cursor.lnum = curr;
+    if (curr == curbuf->b_ml.ml_line_count && what != '}')
+    {
+	if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0)
+	{
+	    --curwin->w_cursor.col;
+	    oap->inclusive = TRUE;
+	}
+    }
+    else
+	curwin->w_cursor.col = 0;
+    return TRUE;
+}
+
+/*
+ * check if the string 's' is a nroff macro that is in option 'opt'
+ */
+    static int
+inmacro(opt, s)
+    char_u	*opt;
+    char_u	*s;
+{
+    char_u	*macro;
+
+    for (macro = opt; macro[0]; ++macro)
+    {
+	/* Accept two characters in the option being equal to two characters
+	 * in the line.  A space in the option matches with a space in the
+	 * line or the line having ended. */
+	if (       (macro[0] == s[0]
+		    || (macro[0] == ' '
+			&& (s[0] == NUL || s[0] == ' ')))
+		&& (macro[1] == s[1]
+		    || ((macro[1] == NUL || macro[1] == ' ')
+			&& (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
+	    break;
+	++macro;
+	if (macro[0] == NUL)
+	    break;
+    }
+    return (macro[0] != NUL);
+}
+
+/*
+ * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
+ * If 'para' is '{' or '}' only check for sections.
+ * If 'both' is TRUE also stop at '}'
+ */
+    int
+startPS(lnum, para, both)
+    linenr_T	lnum;
+    int		para;
+    int		both;
+{
+    char_u	*s;
+
+    s = ml_get(lnum);
+    if (*s == para || *s == '\f' || (both && *s == '}'))
+	return TRUE;
+    if (*s == '.' && (inmacro(p_sections, s + 1) ||
+					   (!para && inmacro(p_para, s + 1))))
+	return TRUE;
+    return FALSE;
+}
+
+/*
+ * The following routines do the word searches performed by the 'w', 'W',
+ * 'b', 'B', 'e', and 'E' commands.
+ */
+
+/*
+ * To perform these searches, characters are placed into one of three
+ * classes, and transitions between classes determine word boundaries.
+ *
+ * The classes are:
+ *
+ * 0 - white space
+ * 1 - punctuation
+ * 2 or higher - keyword characters (letters, digits and underscore)
+ */
+
+static int	cls_bigword;	/* TRUE for "W", "B" or "E" */
+
+/*
+ * cls() - returns the class of character at curwin->w_cursor
+ *
+ * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
+ * from class 2 and higher are reported as class 1 since only white space
+ * boundaries are of interest.
+ */
+    static int
+cls()
+{
+    int	    c;
+
+    c = gchar_cursor();
+#ifdef FEAT_FKMAP	/* when 'akm' (Farsi mode), take care of Farsi blank */
+    if (p_altkeymap && c == F_BLANK)
+	return 0;
+#endif
+    if (c == ' ' || c == '\t' || c == NUL)
+	return 0;
+#ifdef FEAT_MBYTE
+    if (enc_dbcs != 0 && c > 0xFF)
+    {
+	/* If cls_bigword, report multi-byte chars as class 1. */
+	if (enc_dbcs == DBCS_KOR && cls_bigword)
+	    return 1;
+
+	/* process code leading/trailing bytes */
+	return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
+    }
+    if (enc_utf8)
+    {
+	c = utf_class(c);
+	if (c != 0 && cls_bigword)
+	    return 1;
+	return c;
+    }
+#endif
+
+    /* If cls_bigword is TRUE, report all non-blanks as class 1. */
+    if (cls_bigword)
+	return 1;
+
+    if (vim_iswordc(c))
+	return 2;
+    return 1;
+}
+
+
+/*
+ * fwd_word(count, type, eol) - move forward one word
+ *
+ * Returns FAIL if the cursor was already at the end of the file.
+ * If eol is TRUE, last word stops at end of line (for operators).
+ */
+    int
+fwd_word(count, bigword, eol)
+    long	count;
+    int		bigword;    /* "W", "E" or "B" */
+    int		eol;
+{
+    int		sclass;	    /* starting class */
+    int		i;
+    int		last_line;
+
+#ifdef FEAT_VIRTUALEDIT
+    curwin->w_cursor.coladd = 0;
+#endif
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+#ifdef FEAT_FOLDING
+	/* When inside a range of folded lines, move to the last char of the
+	 * last line. */
+	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
+	    coladvance((colnr_T)MAXCOL);
+#endif
+	sclass = cls();
+
+	/*
+	 * We always move at least one character, unless on the last
+	 * character in the buffer.
+	 */
+	last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
+	i = inc_cursor();
+	if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */
+	    return FAIL;
+	if (i == 1 && eol && count == 0)      /* started at last char in line */
+	    return OK;
+
+	/*
+	 * Go one char past end of current word (if any)
+	 */
+	if (sclass != 0)
+	    while (cls() == sclass)
+	    {
+		i = inc_cursor();
+		if (i == -1 || (i >= 1 && eol && count == 0))
+		    return OK;
+	    }
+
+	/*
+	 * go to next non-white
+	 */
+	while (cls() == 0)
+	{
+	    /*
+	     * We'll stop if we land on a blank line
+	     */
+	    if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
+		break;
+
+	    i = inc_cursor();
+	    if (i == -1 || (i >= 1 && eol && count == 0))
+		return OK;
+	}
+    }
+    return OK;
+}
+
+/*
+ * bck_word() - move backward 'count' words
+ *
+ * If stop is TRUE and we are already on the start of a word, move one less.
+ *
+ * Returns FAIL if top of the file was reached.
+ */
+    int
+bck_word(count, bigword, stop)
+    long	count;
+    int		bigword;
+    int		stop;
+{
+    int		sclass;	    /* starting class */
+
+#ifdef FEAT_VIRTUALEDIT
+    curwin->w_cursor.coladd = 0;
+#endif
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+#ifdef FEAT_FOLDING
+	/* When inside a range of folded lines, move to the first char of the
+	 * first line. */
+	if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
+	    curwin->w_cursor.col = 0;
+#endif
+	sclass = cls();
+	if (dec_cursor() == -1)		/* started at start of file */
+	    return FAIL;
+
+	if (!stop || sclass == cls() || sclass == 0)
+	{
+	    /*
+	     * Skip white space before the word.
+	     * Stop on an empty line.
+	     */
+	    while (cls() == 0)
+	    {
+		if (curwin->w_cursor.col == 0
+				      && lineempty(curwin->w_cursor.lnum))
+		    goto finished;
+		if (dec_cursor() == -1) /* hit start of file, stop here */
+		    return OK;
+	    }
+
+	    /*
+	     * Move backward to start of this word.
+	     */
+	    if (skip_chars(cls(), BACKWARD))
+		return OK;
+	}
+
+	inc_cursor();			/* overshot - forward one */
+finished:
+	stop = FALSE;
+    }
+    return OK;
+}
+
+/*
+ * end_word() - move to the end of the word
+ *
+ * There is an apparent bug in the 'e' motion of the real vi. At least on the
+ * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
+ * motion crosses blank lines. When the real vi crosses a blank line in an
+ * 'e' motion, the cursor is placed on the FIRST character of the next
+ * non-blank line. The 'E' command, however, works correctly. Since this
+ * appears to be a bug, I have not duplicated it here.
+ *
+ * Returns FAIL if end of the file was reached.
+ *
+ * If stop is TRUE and we are already on the end of a word, move one less.
+ * If empty is TRUE stop on an empty line.
+ */
+    int
+end_word(count, bigword, stop, empty)
+    long	count;
+    int		bigword;
+    int		stop;
+    int		empty;
+{
+    int		sclass;	    /* starting class */
+
+#ifdef FEAT_VIRTUALEDIT
+    curwin->w_cursor.coladd = 0;
+#endif
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+#ifdef FEAT_FOLDING
+	/* When inside a range of folded lines, move to the last char of the
+	 * last line. */
+	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
+	    coladvance((colnr_T)MAXCOL);
+#endif
+	sclass = cls();
+	if (inc_cursor() == -1)
+	    return FAIL;
+
+	/*
+	 * If we're in the middle of a word, we just have to move to the end
+	 * of it.
+	 */
+	if (cls() == sclass && sclass != 0)
+	{
+	    /*
+	     * Move forward to end of the current word
+	     */
+	    if (skip_chars(sclass, FORWARD))
+		return FAIL;
+	}
+	else if (!stop || sclass == 0)
+	{
+	    /*
+	     * We were at the end of a word. Go to the end of the next word.
+	     * First skip white space, if 'empty' is TRUE, stop at empty line.
+	     */
+	    while (cls() == 0)
+	    {
+		if (empty && curwin->w_cursor.col == 0
+					  && lineempty(curwin->w_cursor.lnum))
+		    goto finished;
+		if (inc_cursor() == -1)	    /* hit end of file, stop here */
+		    return FAIL;
+	    }
+
+	    /*
+	     * Move forward to the end of this word.
+	     */
+	    if (skip_chars(cls(), FORWARD))
+		return FAIL;
+	}
+	dec_cursor();			/* overshot - one char backward */
+finished:
+	stop = FALSE;			/* we move only one word less */
+    }
+    return OK;
+}
+
+/*
+ * Move back to the end of the word.
+ *
+ * Returns FAIL if start of the file was reached.
+ */
+    int
+bckend_word(count, bigword, eol)
+    long	count;
+    int		bigword;    /* TRUE for "B" */
+    int		eol;	    /* TRUE: stop at end of line. */
+{
+    int		sclass;	    /* starting class */
+    int		i;
+
+#ifdef FEAT_VIRTUALEDIT
+    curwin->w_cursor.coladd = 0;
+#endif
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+	sclass = cls();
+	if ((i = dec_cursor()) == -1)
+	    return FAIL;
+	if (eol && i == 1)
+	    return OK;
+
+	/*
+	 * Move backward to before the start of this word.
+	 */
+	if (sclass != 0)
+	{
+	    while (cls() == sclass)
+		if ((i = dec_cursor()) == -1 || (eol && i == 1))
+		    return OK;
+	}
+
+	/*
+	 * Move backward to end of the previous word
+	 */
+	while (cls() == 0)
+	{
+	    if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
+		break;
+	    if ((i = dec_cursor()) == -1 || (eol && i == 1))
+		return OK;
+	}
+    }
+    return OK;
+}
+
+/*
+ * Skip a row of characters of the same class.
+ * Return TRUE when end-of-file reached, FALSE otherwise.
+ */
+    static int
+skip_chars(cclass, dir)
+    int		cclass;
+    int		dir;
+{
+    while (cls() == cclass)
+	if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
+	    return TRUE;
+    return FALSE;
+}
+
+#ifdef FEAT_TEXTOBJ
+/*
+ * Go back to the start of the word or the start of white space
+ */
+    static void
+back_in_line()
+{
+    int		sclass;		    /* starting class */
+
+    sclass = cls();
+    for (;;)
+    {
+	if (curwin->w_cursor.col == 0)	    /* stop at start of line */
+	    break;
+	dec_cursor();
+	if (cls() != sclass)		    /* stop at start of word */
+	{
+	    inc_cursor();
+	    break;
+	}
+    }
+}
+
+    static void
+find_first_blank(posp)
+    pos_T    *posp;
+{
+    int	    c;
+
+    while (decl(posp) != -1)
+    {
+	c = gchar_pos(posp);
+	if (!vim_iswhite(c))
+	{
+	    incl(posp);
+	    break;
+	}
+    }
+}
+
+/*
+ * Skip count/2 sentences and count/2 separating white spaces.
+ */
+    static void
+findsent_forward(count, at_start_sent)
+    long    count;
+    int	    at_start_sent;	/* cursor is at start of sentence */
+{
+    while (count--)
+    {
+	findsent(FORWARD, 1L);
+	if (at_start_sent)
+	    find_first_blank(&curwin->w_cursor);
+	if (count == 0 || at_start_sent)
+	    decl(&curwin->w_cursor);
+	at_start_sent = !at_start_sent;
+    }
+}
+
+/*
+ * Find word under cursor, cursor at end.
+ * Used while an operator is pending, and in Visual mode.
+ */
+    int
+current_word(oap, count, include, bigword)
+    oparg_T	*oap;
+    long	count;
+    int		include;	/* TRUE: include word and white space */
+    int		bigword;	/* FALSE == word, TRUE == WORD */
+{
+    pos_T	start_pos;
+    pos_T	pos;
+    int		inclusive = TRUE;
+    int		include_white = FALSE;
+
+    cls_bigword = bigword;
+
+#ifdef FEAT_VISUAL
+    /* Correct cursor when 'selection' is exclusive */
+    if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor))
+	dec_cursor();
+
+    /*
+     * When Visual mode is not active, or when the VIsual area is only one
+     * character, select the word and/or white space under the cursor.
+     */
+    if (!VIsual_active || equalpos(curwin->w_cursor, VIsual))
+#endif
+    {
+	/*
+	 * Go to start of current word or white space.
+	 */
+	back_in_line();
+	start_pos = curwin->w_cursor;
+
+	/*
+	 * If the start is on white space, and white space should be included
+	 * ("	word"), or start is not on white space, and white space should
+	 * not be included ("word"), find end of word.
+	 */
+	if ((cls() == 0) == include)
+	{
+	    if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
+		return FAIL;
+	}
+	else
+	{
+	    /*
+	     * If the start is not on white space, and white space should be
+	     * included ("word	 "), or start is on white space and white
+	     * space should not be included ("	 "), find start of word.
+	     * If we end up in the first column of the next line (single char
+	     * word) back up to end of the line.
+	     */
+	    fwd_word(1L, bigword, TRUE);
+	    if (curwin->w_cursor.col == 0)
+		decl(&curwin->w_cursor);
+	    else
+		oneleft();
+
+	    if (include)
+		include_white = TRUE;
+	}
+
+#ifdef FEAT_VISUAL
+	if (VIsual_active)
+	{
+	    /* should do something when inclusive == FALSE ! */
+	    VIsual = start_pos;
+	    redraw_curbuf_later(INVERTED);	/* update the inversion */
+	}
+	else
+#endif
+	{
+	    oap->start = start_pos;
+	    oap->motion_type = MCHAR;
+	}
+	--count;
+    }
+
+    /*
+     * When count is still > 0, extend with more objects.
+     */
+    while (count > 0)
+    {
+	inclusive = TRUE;
+#ifdef FEAT_VISUAL
+	if (VIsual_active && lt(curwin->w_cursor, VIsual))
+	{
+	    /*
+	     * In Visual mode, with cursor at start: move cursor back.
+	     */
+	    if (decl(&curwin->w_cursor) == -1)
+		return FAIL;
+	    if (include != (cls() != 0))
+	    {
+		if (bck_word(1L, bigword, TRUE) == FAIL)
+		    return FAIL;
+	    }
+	    else
+	    {
+		if (bckend_word(1L, bigword, TRUE) == FAIL)
+		    return FAIL;
+		(void)incl(&curwin->w_cursor);
+	    }
+	}
+	else
+#endif
+	{
+	    /*
+	     * Move cursor forward one word and/or white area.
+	     */
+	    if (incl(&curwin->w_cursor) == -1)
+		return FAIL;
+	    if (include != (cls() == 0))
+	    {
+		if (fwd_word(1L, bigword, TRUE) == FAIL)
+		    return FAIL;
+		/*
+		 * If end is just past a new-line, we don't want to include
+		 * the first character on the line
+		 */
+		if (oneleft() == FAIL)	/* put cursor on last char of white */
+		    inclusive = FALSE;
+	    }
+	    else
+	    {
+		if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
+		    return FAIL;
+	    }
+	}
+	--count;
+    }
+
+    if (include_white && cls() != 0)
+    {
+	/*
+	 * If we don't include white space at the end, move the start
+	 * to include some white space there. This makes "daw" work
+	 * better on the last word in a sentence (and "2daw" on last-but-one
+	 * word).  But don't delete white space at start of line (indent).
+	 */
+	pos = curwin->w_cursor;	/* save cursor position */
+	curwin->w_cursor = start_pos;
+	if (oneleft() == OK)
+	{
+	    back_in_line();
+	    if (cls() == 0 && curwin->w_cursor.col > 0)
+	    {
+#ifdef FEAT_VISUAL
+		if (VIsual_active)
+		    VIsual = curwin->w_cursor;
+		else
+#endif
+		    oap->start = curwin->w_cursor;
+	    }
+	}
+	curwin->w_cursor = pos;	/* put cursor back at end */
+    }
+
+#ifdef FEAT_VISUAL
+    if (VIsual_active)
+    {
+	if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor))
+	    inc_cursor();
+	if (VIsual_mode == 'V')
+	{
+	    VIsual_mode = 'v';
+	    redraw_cmdline = TRUE;		/* show mode later */
+	}
+    }
+    else
+#endif
+	oap->inclusive = inclusive;
+
+    return OK;
+}
+
+/*
+ * Find sentence(s) under the cursor, cursor at end.
+ * When Visual active, extend it by one or more sentences.
+ */
+    int
+current_sent(oap, count, include)
+    oparg_T	*oap;
+    long	count;
+    int		include;
+{
+    pos_T	start_pos;
+    pos_T	pos;
+    int		start_blank;
+    int		c;
+    int		at_start_sent;
+    long	ncount;
+
+    start_pos = curwin->w_cursor;
+    pos = start_pos;
+    findsent(FORWARD, 1L);	/* Find start of next sentence. */
+
+#ifdef FEAT_VISUAL
+    /*
+     * When visual area is bigger than one character: Extend it.
+     */
+    if (VIsual_active && !equalpos(start_pos, VIsual))
+    {
+extend:
+	if (lt(start_pos, VIsual))
+	{
+	    /*
+	     * Cursor at start of Visual area.
+	     * Find out where we are:
+	     * - in the white space before a sentence
+	     * - in a sentence or just after it
+	     * - at the start of a sentence
+	     */
+	    at_start_sent = TRUE;
+	    decl(&pos);
+	    while (lt(pos, curwin->w_cursor))
+	    {
+		c = gchar_pos(&pos);
+		if (!vim_iswhite(c))
+		{
+		    at_start_sent = FALSE;
+		    break;
+		}
+		incl(&pos);
+	    }
+	    if (!at_start_sent)
+	    {
+		findsent(BACKWARD, 1L);
+		if (equalpos(curwin->w_cursor, start_pos))
+		    at_start_sent = TRUE;  /* exactly at start of sentence */
+		else
+		    /* inside a sentence, go to its end (start of next) */
+		    findsent(FORWARD, 1L);
+	    }
+	    if (include)	/* "as" gets twice as much as "is" */
+		count *= 2;
+	    while (count--)
+	    {
+		if (at_start_sent)
+		    find_first_blank(&curwin->w_cursor);
+		c = gchar_cursor();
+		if (!at_start_sent || (!include && !vim_iswhite(c)))
+		    findsent(BACKWARD, 1L);
+		at_start_sent = !at_start_sent;
+	    }
+	}
+	else
+	{
+	    /*
+	     * Cursor at end of Visual area.
+	     * Find out where we are:
+	     * - just before a sentence
+	     * - just before or in the white space before a sentence
+	     * - in a sentence
+	     */
+	    incl(&pos);
+	    at_start_sent = TRUE;
+	    if (!equalpos(pos, curwin->w_cursor)) /* not just before a sentence */
+	    {
+		at_start_sent = FALSE;
+		while (lt(pos, curwin->w_cursor))
+		{
+		    c = gchar_pos(&pos);
+		    if (!vim_iswhite(c))
+		    {
+			at_start_sent = TRUE;
+			break;
+		    }
+		    incl(&pos);
+		}
+		if (at_start_sent)	/* in the sentence */
+		    findsent(BACKWARD, 1L);
+		else		/* in/before white before a sentence */
+		    curwin->w_cursor = start_pos;
+	    }
+
+	    if (include)	/* "as" gets twice as much as "is" */
+		count *= 2;
+	    findsent_forward(count, at_start_sent);
+	    if (*p_sel == 'e')
+		++curwin->w_cursor.col;
+	}
+	return OK;
+    }
+#endif
+
+    /*
+     * If cursor started on blank, check if it is just before the start of the
+     * next sentence.
+     */
+    while (c = gchar_pos(&pos), vim_iswhite(c))	/* vim_iswhite() is a macro */
+	incl(&pos);
+    if (equalpos(pos, curwin->w_cursor))
+    {
+	start_blank = TRUE;
+	find_first_blank(&start_pos);	/* go back to first blank */
+    }
+    else
+    {
+	start_blank = FALSE;
+	findsent(BACKWARD, 1L);
+	start_pos = curwin->w_cursor;
+    }
+    if (include)
+	ncount = count * 2;
+    else
+    {
+	ncount = count;
+	if (start_blank)
+	    --ncount;
+    }
+    if (ncount)
+	findsent_forward(ncount, TRUE);
+    else
+	decl(&curwin->w_cursor);
+
+    if (include)
+    {
+	/*
+	 * If the blank in front of the sentence is included, exclude the
+	 * blanks at the end of the sentence, go back to the first blank.
+	 * If there are no trailing blanks, try to include leading blanks.
+	 */
+	if (start_blank)
+	{
+	    find_first_blank(&curwin->w_cursor);
+	    c = gchar_pos(&curwin->w_cursor);	/* vim_iswhite() is a macro */
+	    if (vim_iswhite(c))
+		decl(&curwin->w_cursor);
+	}
+	else if (c = gchar_cursor(), !vim_iswhite(c))
+	    find_first_blank(&start_pos);
+    }
+
+#ifdef FEAT_VISUAL
+    if (VIsual_active)
+    {
+	/* avoid getting stuck with "is" on a single space before a sent. */
+	if (equalpos(start_pos, curwin->w_cursor))
+	    goto extend;
+	if (*p_sel == 'e')
+	    ++curwin->w_cursor.col;
+	VIsual = start_pos;
+	VIsual_mode = 'v';
+	redraw_curbuf_later(INVERTED);	/* update the inversion */
+    }
+    else
+#endif
+    {
+	/* include a newline after the sentence, if there is one */
+	if (incl(&curwin->w_cursor) == -1)
+	    oap->inclusive = TRUE;
+	else
+	    oap->inclusive = FALSE;
+	oap->start = start_pos;
+	oap->motion_type = MCHAR;
+    }
+    return OK;
+}
+
+    int
+current_block(oap, count, include, what, other)
+    oparg_T	*oap;
+    long	count;
+    int		include;	/* TRUE == include white space */
+    int		what;		/* '(', '{', etc. */
+    int		other;		/* ')', '}', etc. */
+{
+    pos_T	old_pos;
+    pos_T	*pos = NULL;
+    pos_T	start_pos;
+    pos_T	*end_pos;
+    pos_T	old_start, old_end;
+    char_u	*save_cpo;
+    int		sol = FALSE;	/* { at start of line */
+
+    old_pos = curwin->w_cursor;
+    old_end = curwin->w_cursor;		    /* remember where we started */
+    old_start = old_end;
+
+    /*
+     * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
+     */
+#ifdef FEAT_VISUAL
+    if (!VIsual_active || equalpos(VIsual, curwin->w_cursor))
+#endif
+    {
+	setpcmark();
+	if (what == '{')		    /* ignore indent */
+	    while (inindent(1))
+		if (inc_cursor() != 0)
+		    break;
+	if (gchar_cursor() == what)	    /* cursor on '(' or '{' */
+	    ++curwin->w_cursor.col;
+    }
+#ifdef FEAT_VISUAL
+    else if (lt(VIsual, curwin->w_cursor))
+    {
+	old_start = VIsual;
+	curwin->w_cursor = VIsual;	    /* cursor at low end of Visual */
+    }
+    else
+	old_end = VIsual;
+#endif
+
+    /*
+     * Search backwards for unclosed '(', '{', etc..
+     * Put this position in start_pos.
+     * Ignory quotes here.
+     */
+    save_cpo = p_cpo;
+    p_cpo = (char_u *)"%";
+    while (count-- > 0)
+    {
+	if ((pos = findmatch(NULL, what)) == NULL)
+	    break;
+	curwin->w_cursor = *pos;
+	start_pos = *pos;   /* the findmatch for end_pos will overwrite *pos */
+    }
+    p_cpo = save_cpo;
+
+    /*
+     * Search for matching ')', '}', etc.
+     * Put this position in curwin->w_cursor.
+     */
+    if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
+    {
+	curwin->w_cursor = old_pos;
+	return FAIL;
+    }
+    curwin->w_cursor = *end_pos;
+
+    /*
+     * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
+     * If the ending '}' is only preceded by indent, skip that indent.
+     * But only if the resulting area is not smaller than what we started with.
+     */
+    while (!include)
+    {
+	incl(&start_pos);
+	sol = (curwin->w_cursor.col == 0);
+	decl(&curwin->w_cursor);
+	if (what == '{')
+	    while (inindent(1))
+	    {
+		sol = TRUE;
+		if (decl(&curwin->w_cursor) != 0)
+		    break;
+	    }
+#ifdef FEAT_VISUAL
+	/*
+	 * In Visual mode, when the resulting area is not bigger than what we
+	 * started with, extend it to the next block, and then exclude again.
+	 */
+	if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
+		&& VIsual_active)
+	{
+	    curwin->w_cursor = old_start;
+	    decl(&curwin->w_cursor);
+	    if ((pos = findmatch(NULL, what)) == NULL)
+	    {
+		curwin->w_cursor = old_pos;
+		return FAIL;
+	    }
+	    start_pos = *pos;
+	    curwin->w_cursor = *pos;
+	    if ((end_pos = findmatch(NULL, other)) == NULL)
+	    {
+		curwin->w_cursor = old_pos;
+		return FAIL;
+	    }
+	    curwin->w_cursor = *end_pos;
+	}
+	else
+#endif
+	    break;
+    }
+
+#ifdef FEAT_VISUAL
+    if (VIsual_active)
+    {
+	if (*p_sel == 'e')
+	    ++curwin->w_cursor.col;
+	if (sol)
+	    inc(&curwin->w_cursor);	/* include the line break */
+	VIsual = start_pos;
+	VIsual_mode = 'v';
+	redraw_curbuf_later(INVERTED);	/* update the inversion */
+	showmode();
+    }
+    else
+#endif
+    {
+	oap->start = start_pos;
+	oap->motion_type = MCHAR;
+	if (sol)
+	{
+	    incl(&curwin->w_cursor);
+	    oap->inclusive = FALSE;
+	}
+	else
+	    oap->inclusive = TRUE;
+    }
+
+    return OK;
+}
+
+    int
+current_par(oap, count, include, type)
+    oparg_T	*oap;
+    long	count;
+    int		include;	/* TRUE == include white space */
+    int		type;		/* 'p' for paragraph, 'S' for section */
+{
+    linenr_T	start_lnum;
+    linenr_T	end_lnum;
+    int		white_in_front;
+    int		dir;
+    int		start_is_white;
+    int		prev_start_is_white;
+    int		retval = OK;
+    int		do_white = FALSE;
+    int		t;
+    int		i;
+
+    if (type == 'S')	    /* not implemented yet */
+	return FAIL;
+
+    start_lnum = curwin->w_cursor.lnum;
+
+#ifdef FEAT_VISUAL
+    /*
+     * When visual area is more than one line: extend it.
+     */
+    if (VIsual_active && start_lnum != VIsual.lnum)
+    {
+extend:
+	if (start_lnum < VIsual.lnum)
+	    dir = BACKWARD;
+	else
+	    dir = FORWARD;
+	for (i = count; --i >= 0; )
+	{
+	    if (start_lnum ==
+			   (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
+	    {
+		retval = FAIL;
+		break;
+	    }
+
+	    prev_start_is_white = -1;
+	    for (t = 0; t < 2; ++t)
+	    {
+		start_lnum += dir;
+		start_is_white = linewhite(start_lnum);
+		if (prev_start_is_white == start_is_white)
+		{
+		    start_lnum -= dir;
+		    break;
+		}
+		for (;;)
+		{
+		    if (start_lnum == (dir == BACKWARD
+					    ? 1 : curbuf->b_ml.ml_line_count))
+			break;
+		    if (start_is_white != linewhite(start_lnum + dir)
+			    || (!start_is_white
+				    && startPS(start_lnum + (dir > 0
+							     ? 1 : 0), 0, 0)))
+			break;
+		    start_lnum += dir;
+		}
+		if (!include)
+		    break;
+		if (start_lnum == (dir == BACKWARD
+					    ? 1 : curbuf->b_ml.ml_line_count))
+		    break;
+		prev_start_is_white = start_is_white;
+	    }
+	}
+	curwin->w_cursor.lnum = start_lnum;
+	curwin->w_cursor.col = 0;
+	return retval;
+    }
+#endif
+
+    /*
+     * First move back to the start_lnum of the paragraph or white lines
+     */
+    white_in_front = linewhite(start_lnum);
+    while (start_lnum > 1)
+    {
+	if (white_in_front)	    /* stop at first white line */
+	{
+	    if (!linewhite(start_lnum - 1))
+		break;
+	}
+	else		/* stop at first non-white line of start of paragraph */
+	{
+	    if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
+		break;
+	}
+	--start_lnum;
+    }
+
+    /*
+     * Move past the end of any white lines.
+     */
+    end_lnum = start_lnum;
+    while (linewhite(end_lnum) && end_lnum < curbuf->b_ml.ml_line_count)
+	    ++end_lnum;
+
+    --end_lnum;
+    i = count;
+    if (!include && white_in_front)
+	--i;
+    while (i--)
+    {
+	if (end_lnum == curbuf->b_ml.ml_line_count)
+	    return FAIL;
+
+	if (!include)
+	    do_white = linewhite(end_lnum + 1);
+
+	if (include || !do_white)
+	{
+	    ++end_lnum;
+	    /*
+	     * skip to end of paragraph
+	     */
+	    while (end_lnum < curbuf->b_ml.ml_line_count
+		    && !linewhite(end_lnum + 1)
+		    && !startPS(end_lnum + 1, 0, 0))
+		++end_lnum;
+	}
+
+	if (i == 0 && white_in_front && include)
+	    break;
+
+	/*
+	 * skip to end of white lines after paragraph
+	 */
+	if (include || do_white)
+	    while (end_lnum < curbuf->b_ml.ml_line_count
+						   && linewhite(end_lnum + 1))
+		++end_lnum;
+    }
+
+    /*
+     * If there are no empty lines at the end, try to find some empty lines at
+     * the start (unless that has been done already).
+     */
+    if (!white_in_front && !linewhite(end_lnum) && include)
+	while (start_lnum > 1 && linewhite(start_lnum - 1))
+	    --start_lnum;
+
+#ifdef FEAT_VISUAL
+    if (VIsual_active)
+    {
+	/* Problem: when doing "Vipipip" nothing happens in a single white
+	 * line, we get stuck there.  Trap this here. */
+	if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
+	    goto extend;
+	VIsual.lnum = start_lnum;
+	VIsual_mode = 'V';
+	redraw_curbuf_later(INVERTED);	/* update the inversion */
+	showmode();
+    }
+    else
+#endif
+    {
+	oap->start.lnum = start_lnum;
+	oap->start.col = 0;
+	oap->motion_type = MLINE;
+    }
+    curwin->w_cursor.lnum = end_lnum;
+    curwin->w_cursor.col = 0;
+
+    return OK;
+}
+#endif
+
+#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \
+	|| defined(PROTO)
+/*
+ * return TRUE if line 'lnum' is empty or has white chars only.
+ */
+    int
+linewhite(lnum)
+    linenr_T	lnum;
+{
+    char_u  *p;
+
+    p = skipwhite(ml_get(lnum));
+    return (*p == NUL);
+}
+#endif
+
+#if defined(FEAT_FIND_ID) || defined(PROTO)
+/*
+ * Find identifiers or defines in included files.
+ * if p_ic && (continue_status & CONT_SOL) then ptr must be in lowercase.
+ */
+/*ARGSUSED*/
+    void
+find_pattern_in_path(ptr, dir, len, whole, skip_comments,
+				    type, count, action, start_lnum, end_lnum)
+    char_u	*ptr;		/* pointer to search pattern */
+    int		dir;		/* direction of expansion */
+    int		len;		/* length of search pattern */
+    int		whole;		/* match whole words only */
+    int		skip_comments;	/* don't match inside comments */
+    int		type;		/* Type of search; are we looking for a type?
+				   a macro? */
+    long	count;
+    int		action;		/* What to do when we find it */
+    linenr_T	start_lnum;	/* first line to start searching */
+    linenr_T	end_lnum;	/* last line for searching */
+{
+    SearchedFile *files;		/* Stack of included files */
+    SearchedFile *bigger;		/* When we need more space */
+    int		max_path_depth = 50;
+    long	match_count = 1;
+
+    char_u	*pat;
+    char_u	*new_fname;
+    char_u	*curr_fname = curbuf->b_fname;
+    char_u	*prev_fname = NULL;
+    linenr_T	lnum;
+    int		depth;
+    int		depth_displayed;	/* For type==CHECK_PATH */
+    int		old_files;
+    int		already_searched;
+    char_u	*file_line;
+    char_u	*line;
+    char_u	*p;
+    char_u	save_char;
+    int		define_matched;
+    regmatch_T	regmatch;
+    regmatch_T	incl_regmatch;
+    regmatch_T	def_regmatch;
+    int		matched = FALSE;
+    int		did_show = FALSE;
+    int		found = FALSE;
+    int		i;
+    char_u	*already = NULL;
+    char_u	*startp = NULL;
+#ifdef RISCOS
+    int		previous_munging = __riscosify_control;
+#endif
+#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
+    win_T	*curwin_save = NULL;
+#endif
+
+    regmatch.regprog = NULL;
+    incl_regmatch.regprog = NULL;
+    def_regmatch.regprog = NULL;
+
+    file_line = alloc(LSIZE);
+    if (file_line == NULL)
+	return;
+
+#ifdef RISCOS
+    /* UnixLib knows best how to munge c file names - turn munging back on. */
+    __riscosify_control = __RISCOSIFY_LONG_TRUNCATE;
+#endif
+
+    if (type != CHECK_PATH && type != FIND_DEFINE
+#ifdef FEAT_INS_EXPAND
+	/* when CONT_SOL is set compare "ptr" with the beginning of the line
+	 * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */
+	    && !(continue_status & CONT_SOL)
+#endif
+       )
+    {
+	pat = alloc(len + 5);
+	if (pat == NULL)
+	    goto fpip_end;
+	sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
+	/* ignore case according to p_ic, p_scs and pat */
+	regmatch.rm_ic = ignorecase(pat);
+	regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
+	vim_free(pat);
+	if (regmatch.regprog == NULL)
+	    goto fpip_end;
+    }
+    if (*curbuf->b_p_inc != NUL || *p_inc != NUL)
+    {
+	incl_regmatch.regprog = vim_regcomp(*curbuf->b_p_inc == NUL
+			   ? p_inc : curbuf->b_p_inc, p_magic ? RE_MAGIC : 0);
+	if (incl_regmatch.regprog == NULL)
+	    goto fpip_end;
+	incl_regmatch.rm_ic = FALSE;	/* don't ignore case in incl. pat. */
+    }
+    if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL))
+    {
+	def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
+			   ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0);
+	if (def_regmatch.regprog == NULL)
+	    goto fpip_end;
+	def_regmatch.rm_ic = FALSE;	/* don't ignore case in define pat. */
+    }
+    files = (SearchedFile *)lalloc_clear((long_u)
+			       (max_path_depth * sizeof(SearchedFile)), TRUE);
+    if (files == NULL)
+	goto fpip_end;
+    old_files = max_path_depth;
+    depth = depth_displayed = -1;
+
+    lnum = start_lnum;
+    if (end_lnum > curbuf->b_ml.ml_line_count)
+	end_lnum = curbuf->b_ml.ml_line_count;
+    if (lnum > end_lnum)		/* do at least one line */
+	lnum = end_lnum;
+    line = ml_get(lnum);
+
+    for (;;)
+    {
+	if (incl_regmatch.regprog != NULL
+		&& vim_regexec(&incl_regmatch, line, (colnr_T)0))
+	{
+	    new_fname = file_name_in_line(incl_regmatch.endp[0],
+		    0, FNAME_EXP|FNAME_INCL|FNAME_REL, 1L,
+		    curr_fname == curbuf->b_fname
+					     ? curbuf->b_ffname : curr_fname);
+	    already_searched = FALSE;
+	    if (new_fname != NULL)
+	    {
+		/* Check whether we have already searched in this file */
+		for (i = 0;; i++)
+		{
+		    if (i == depth + 1)
+			i = old_files;
+		    if (i == max_path_depth)
+			break;
+		    if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME)
+		    {
+			if (type != CHECK_PATH &&
+				action == ACTION_SHOW_ALL && files[i].matched)
+			{
+			    msg_putchar('\n');	    /* cursor below last one */
+			    if (!got_int)	    /* don't display if 'q'
+						       typed at "--more--"
+						       mesage */
+			    {
+				msg_home_replace_hl(new_fname);
+				MSG_PUTS(_(" (includes previously listed match)"));
+				prev_fname = NULL;
+			    }
+			}
+			vim_free(new_fname);
+			new_fname = NULL;
+			already_searched = TRUE;
+			break;
+		    }
+		}
+	    }
+
+	    if (type == CHECK_PATH && (action == ACTION_SHOW_ALL
+				 || (new_fname == NULL && !already_searched)))
+	    {
+		if (did_show)
+		    msg_putchar('\n');	    /* cursor below last one */
+		else
+		{
+		    gotocmdline(TRUE);	    /* cursor at status line */
+		    MSG_PUTS_TITLE(_("--- Included files "));
+		    if (action != ACTION_SHOW_ALL)
+			MSG_PUTS_TITLE(_("not found "));
+		    MSG_PUTS_TITLE(_("in path ---\n"));
+		}
+		did_show = TRUE;
+		while (depth_displayed < depth && !got_int)
+		{
+		    ++depth_displayed;
+		    for (i = 0; i < depth_displayed; i++)
+			MSG_PUTS("  ");
+		    msg_home_replace(files[depth_displayed].name);
+		    MSG_PUTS(" -->\n");
+		}
+		if (!got_int)		    /* don't display if 'q' typed
+					       for "--more--" message */
+		{
+		    for (i = 0; i <= depth_displayed; i++)
+			MSG_PUTS("  ");
+		    if (new_fname != NULL)
+		    {
+			/* using "new_fname" is more reliable, e.g., when
+			 * 'includeexpr' is set. */
+			msg_outtrans_attr(new_fname, hl_attr(HLF_D));
+		    }
+		    else
+		    {
+			/*
+			 * Isolate the file name.
+			 * Include the surrounding "" or <> if present.
+			 */
+			for (p = incl_regmatch.endp[0]; !vim_isfilec(*p); p++)
+			    ;
+			for (i = 0; vim_isfilec(p[i]); i++)
+			    ;
+			if (i == 0)
+			{
+			    /* Nothing found, use the rest of the line. */
+			    p = incl_regmatch.endp[0];
+			    i = STRLEN(p);
+			}
+			else
+			{
+			    if (p[-1] == '"' || p[-1] == '<')
+			    {
+				--p;
+				++i;
+			    }
+			    if (p[i] == '"' || p[i] == '>')
+				++i;
+			}
+			save_char = p[i];
+			p[i] = NUL;
+			msg_outtrans_attr(p, hl_attr(HLF_D));
+			p[i] = save_char;
+		    }
+
+		    if (new_fname == NULL && action == ACTION_SHOW_ALL)
+		    {
+			if (already_searched)
+			    MSG_PUTS(_("  (Already listed)"));
+			else
+			    MSG_PUTS(_("  NOT FOUND"));
+		    }
+		}
+		out_flush();	    /* output each line directly */
+	    }
+
+	    if (new_fname != NULL)
+	    {
+		/* Push the new file onto the file stack */
+		if (depth + 1 == old_files)
+		{
+		    bigger = (SearchedFile *)lalloc((long_u)(
+			    max_path_depth * 2 * sizeof(SearchedFile)), TRUE);
+		    if (bigger != NULL)
+		    {
+			for (i = 0; i <= depth; i++)
+			    bigger[i] = files[i];
+			for (i = depth + 1; i < old_files + max_path_depth; i++)
+			{
+			    bigger[i].fp = NULL;
+			    bigger[i].name = NULL;
+			    bigger[i].lnum = 0;
+			    bigger[i].matched = FALSE;
+			}
+			for (i = old_files; i < max_path_depth; i++)
+			    bigger[i + max_path_depth] = files[i];
+			old_files += max_path_depth;
+			max_path_depth *= 2;
+			vim_free(files);
+			files = bigger;
+		    }
+		}
+		if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r"))
+								    == NULL)
+		    vim_free(new_fname);
+		else
+		{
+		    if (++depth == old_files)
+		    {
+			/*
+			 * lalloc() for 'bigger' must have failed above.  We
+			 * will forget one of our already visited files now.
+			 */
+			vim_free(files[old_files].name);
+			++old_files;
+		    }
+		    files[depth].name = curr_fname = new_fname;
+		    files[depth].lnum = 0;
+		    files[depth].matched = FALSE;
+#ifdef FEAT_INS_EXPAND
+		    if (action == ACTION_EXPAND)
+		    {
+			sprintf((char*)IObuff, _("Scanning included file: %s"),
+			    (char *)new_fname);
+			msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R));
+		    }
+#endif
+		}
+	    }
+	}
+	else
+	{
+	    /*
+	     * Check if the line is a define (type == FIND_DEFINE)
+	     */
+	    p = line;
+search_line:
+	    define_matched = FALSE;
+	    if (def_regmatch.regprog != NULL
+			      && vim_regexec(&def_regmatch, line, (colnr_T)0))
+	    {
+		/*
+		 * Pattern must be first identifier after 'define', so skip
+		 * to that position before checking for match of pattern.  Also
+		 * don't let it match beyond the end of this identifier.
+		 */
+		p = def_regmatch.endp[0];
+		while (*p && !vim_iswordc(*p))
+		    p++;
+		define_matched = TRUE;
+	    }
+
+	    /*
+	     * Look for a match.  Don't do this if we are looking for a
+	     * define and this line didn't match define_prog above.
+	     */
+	    if (def_regmatch.regprog == NULL || define_matched)
+	    {
+		if (define_matched
+#ifdef FEAT_INS_EXPAND
+			|| (continue_status & CONT_SOL)
+#endif
+		    )
+		{
+		    /* compare the first "len" chars from "ptr" */
+		    startp = skipwhite(p);
+		    if (p_ic)
+			matched = !MB_STRNICMP(startp, ptr, len);
+		    else
+			matched = !STRNCMP(startp, ptr, len);
+		    if (matched && define_matched && whole
+						  && vim_iswordc(startp[len]))
+			matched = FALSE;
+		}
+		else if (regmatch.regprog != NULL
+			 && vim_regexec(&regmatch, line, (colnr_T)(p - line)))
+		{
+		    matched = TRUE;
+		    startp = regmatch.startp[0];
+		    /*
+		     * Check if the line is not a comment line (unless we are
+		     * looking for a define).  A line starting with "# define"
+		     * is not considered to be a comment line.
+		     */
+		    if (!define_matched && skip_comments)
+		    {
+#ifdef FEAT_COMMENTS
+			if ((*line != '#' ||
+				STRNCMP(skipwhite(line + 1), "define", 6) != 0)
+				&& get_leader_len(line, NULL, FALSE))
+			    matched = FALSE;
+
+			/*
+			 * Also check for a "/ *" or "/ /" before the match.
+			 * Skips lines like "int backwards;  / * normal index
+			 * * /" when looking for "normal".
+			 * Note: Doesn't skip "/ *" in comments.
+			 */
+			p = skipwhite(line);
+			if (matched
+				|| (p[0] == '/' && p[1] == '*') || p[0] == '*')
+#endif
+			    for (p = line; *p && p < startp; ++p)
+			    {
+				if (matched
+					&& p[0] == '/'
+					&& (p[1] == '*' || p[1] == '/'))
+				{
+				    matched = FALSE;
+				    /* After "//" all text is comment */
+				    if (p[1] == '/')
+					break;
+				    ++p;
+				}
+				else if (!matched && p[0] == '*' && p[1] == '/')
+				{
+				    /* Can find match after "* /". */
+				    matched = TRUE;
+				    ++p;
+				}
+			    }
+		    }
+		}
+	    }
+	}
+	if (matched)
+	{
+#ifdef FEAT_INS_EXPAND
+	    if (action == ACTION_EXPAND)
+	    {
+		int	reuse = 0;
+		int	add_r;
+		char_u	*aux;
+
+		if (depth == -1 && lnum == curwin->w_cursor.lnum)
+		    break;
+		found = TRUE;
+		aux = p = startp;
+		if (continue_status & CONT_ADDING)
+		{
+		    p += completion_length;
+		    if (vim_iswordp(p))
+			goto exit_matched;
+		    p = find_word_start(p);
+		}
+		p = find_word_end(p);
+		i = (int)(p - aux);
+
+		if ((continue_status & CONT_ADDING) && i == completion_length)
+		{
+		    /* get the next line */
+		    /* IOSIZE > completion_length, so the STRNCPY works */
+		    STRNCPY(IObuff, aux, i);
+		    if (!(     depth < 0
+			    && lnum < end_lnum
+			    && (line = ml_get(++lnum)) != NULL)
+			&& !(	depth >= 0
+			    && !vim_fgets(line = file_line,
+						     LSIZE, files[depth].fp)))
+			goto exit_matched;
+
+		    /* we read a line, set "already" to check this "line" later
+		     * if depth >= 0 we'll increase files[depth].lnum far
+		     * bellow  -- Acevedo */
+		    already = aux = p = skipwhite(line);
+		    p = find_word_start(p);
+		    p = find_word_end(p);
+		    if (p > aux)
+		    {
+			if (*aux != ')' && IObuff[i-1] != TAB)
+			{
+			    if (IObuff[i-1] != ' ')
+				IObuff[i++] = ' ';
+			    /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/
+			    if (p_js
+				&& (IObuff[i-2] == '.'
+				    || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
+					&& (IObuff[i-2] == '?'
+					    || IObuff[i-2] == '!'))))
+				IObuff[i++] = ' ';
+			}
+			/* copy as much as posible of the new word */
+			if (p - aux >= IOSIZE - i)
+			    p = aux + IOSIZE - i - 1;
+			STRNCPY(IObuff + i, aux, p - aux);
+			i += (int)(p - aux);
+			reuse |= CONT_S_IPOS;
+		    }
+		    IObuff[i] = NUL;
+		    aux = IObuff;
+
+		    if (i == completion_length)
+			goto exit_matched;
+		}
+
+		add_r = ins_compl_add_infercase(aux, i,
+			curr_fname == curbuf->b_fname ? NULL : curr_fname,
+			dir, reuse);
+		if (add_r == OK)
+		    /* if dir was BACKWARD then honor it just once */
+		    dir = FORWARD;
+		else if (add_r == RET_ERROR)
+		    break;
+	    }
+	    else
+#endif
+		 if (action == ACTION_SHOW_ALL)
+	    {
+		found = TRUE;
+		if (!did_show)
+		    gotocmdline(TRUE);		/* cursor at status line */
+		if (curr_fname != prev_fname)
+		{
+		    if (did_show)
+			msg_putchar('\n');	/* cursor below last one */
+		    if (!got_int)		/* don't display if 'q' typed
+						    at "--more--" mesage */
+			msg_home_replace_hl(curr_fname);
+		    prev_fname = curr_fname;
+		}
+		did_show = TRUE;
+		if (!got_int)
+		    show_pat_in_path(line, type, TRUE, action,
+			    (depth == -1) ? NULL : files[depth].fp,
+			    (depth == -1) ? &lnum : &files[depth].lnum,
+			    match_count++);
+
+		/* Set matched flag for this file and all the ones that
+		 * include it */
+		for (i = 0; i <= depth; ++i)
+		    files[i].matched = TRUE;
+	    }
+	    else if (--count <= 0)
+	    {
+		found = TRUE;
+		if (depth == -1 && lnum == curwin->w_cursor.lnum
+#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
+						      && g_do_tagpreview == 0
+#endif
+						      )
+		    EMSG(_("E387: Match is on current line"));
+		else if (action == ACTION_SHOW)
+		{
+		    show_pat_in_path(line, type, did_show, action,
+			(depth == -1) ? NULL : files[depth].fp,
+			(depth == -1) ? &lnum : &files[depth].lnum, 1L);
+		    did_show = TRUE;
+		}
+		else
+		{
+#ifdef FEAT_GUI
+		    need_mouse_correct = TRUE;
+#endif
+#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
+		    /* ":psearch" uses the preview window */
+		    if (g_do_tagpreview != 0)
+		    {
+			curwin_save = curwin;
+			prepare_tagpreview();
+		    }
+#endif
+		    if (action == ACTION_SPLIT)
+		    {
+#ifdef FEAT_WINDOWS
+			if (win_split(0, 0) == FAIL)
+#endif
+			    break;
+#ifdef FEAT_SCROLLBIND
+			curwin->w_p_scb = FALSE;
+#endif
+		    }
+		    if (depth == -1)
+		    {
+			/* match in current file */
+#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
+			if (g_do_tagpreview != 0)
+			{
+			    if (getfile(0, curwin_save->w_buffer->b_fname,
+						 NULL, TRUE, lnum, FALSE) > 0)
+				break;	/* failed to jump to file */
+			}
+			else
+#endif
+			    setpcmark();
+			curwin->w_cursor.lnum = lnum;
+		    }
+		    else
+		    {
+			if (getfile(0, files[depth].name, NULL, TRUE,
+						files[depth].lnum, FALSE) > 0)
+			    break;	/* failed to jump to file */
+			/* autocommands may have changed the lnum, we don't
+			 * want that here */
+			curwin->w_cursor.lnum = files[depth].lnum;
+		    }
+		}
+		if (action != ACTION_SHOW)
+		{
+		    curwin->w_cursor.col = (colnr_T) (startp - line);
+		    curwin->w_set_curswant = TRUE;
+		}
+
+#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
+		if (g_do_tagpreview != 0
+			&& curwin != curwin_save && win_valid(curwin_save))
+		{
+		    /* Return cursor to where we were */
+		    validate_cursor();
+		    redraw_later(VALID);
+		    win_enter(curwin_save, TRUE);
+		}
+#endif
+		break;
+	    }
+#ifdef FEAT_INS_EXPAND
+exit_matched:
+#endif
+	    matched = FALSE;
+	    /* look for other matches in the rest of the line if we
+	     * are not at the end of it already */
+	    if (def_regmatch.regprog == NULL
+#ifdef FEAT_INS_EXPAND
+		    && action == ACTION_EXPAND
+		    && !(continue_status & CONT_SOL)
+#endif
+		    && *(p = startp + 1))
+		goto search_line;
+	}
+	line_breakcheck();
+#ifdef FEAT_INS_EXPAND
+	if (action == ACTION_EXPAND)
+	    ins_compl_check_keys();
+	if (got_int || completion_interrupted)
+#else
+	if (got_int)
+#endif
+	    break;
+
+	/*
+	 * Read the next line.  When reading an included file and encountering
+	 * end-of-file, close the file and continue in the file that included
+	 * it.
+	 */
+	while (depth >= 0 && !already
+		&& vim_fgets(line = file_line, LSIZE, files[depth].fp))
+	{
+	    fclose(files[depth].fp);
+	    --old_files;
+	    files[old_files].name = files[depth].name;
+	    files[old_files].matched = files[depth].matched;
+	    --depth;
+	    curr_fname = (depth == -1) ? curbuf->b_fname
+				       : files[depth].name;
+	    if (depth < depth_displayed)
+		depth_displayed = depth;
+	}
+	if (depth >= 0)		/* we could read the line */
+	    files[depth].lnum++;
+	else if (!already)
+	{
+	    if (++lnum > end_lnum)
+		break;
+	    line = ml_get(lnum);
+	}
+	already = NULL;
+    }
+    /* End of big for (;;) loop. */
+
+    /* Close any files that are still open. */
+    for (i = 0; i <= depth; i++)
+    {
+	fclose(files[i].fp);
+	vim_free(files[i].name);
+    }
+    for (i = old_files; i < max_path_depth; i++)
+	vim_free(files[i].name);
+    vim_free(files);
+
+    if (type == CHECK_PATH)
+    {
+	if (!did_show)
+	{
+	    if (action != ACTION_SHOW_ALL)
+		MSG(_("All included files were found"));
+	    else
+		MSG(_("No included files"));
+	}
+    }
+    else if (!found
+#ifdef FEAT_INS_EXPAND
+		    && action != ACTION_EXPAND
+#endif
+						)
+    {
+#ifdef FEAT_INS_EXPAND
+	if (got_int || completion_interrupted)
+#else
+	if (got_int)
+#endif
+	    EMSG(_(e_interr));
+	else if (type == FIND_DEFINE)
+	    EMSG(_("E388: Couldn't find definition"));
+	else
+	    EMSG(_("E389: Couldn't find pattern"));
+    }
+    if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
+	msg_end();
+
+fpip_end:
+    vim_free(file_line);
+    vim_free(regmatch.regprog);
+    vim_free(incl_regmatch.regprog);
+    vim_free(def_regmatch.regprog);
+
+#ifdef RISCOS
+   /* Restore previous file munging state. */
+    __riscosify_control = previous_munging;
+#endif
+}
+
+    static void
+show_pat_in_path(line, type, did_show, action, fp, lnum, count)
+    char_u  *line;
+    int	    type;
+    int	    did_show;
+    int	    action;
+    FILE    *fp;
+    linenr_T *lnum;
+    long    count;
+{
+    char_u  *p;
+
+    if (did_show)
+	msg_putchar('\n');	/* cursor below last one */
+    else
+	gotocmdline(TRUE);	/* cursor at status line */
+    if (got_int)		/* 'q' typed at "--more--" message */
+	return;
+    for (;;)
+    {
+	p = line + STRLEN(line) - 1;
+	if (fp != NULL)
+	{
+	    /* We used fgets(), so get rid of newline at end */
+	    if (p >= line && *p == '\n')
+		--p;
+	    if (p >= line && *p == '\r')
+		--p;
+	    *(p + 1) = NUL;
+	}
+	if (action == ACTION_SHOW_ALL)
+	{
+	    sprintf((char *)IObuff, "%3ld: ", count);	/* show match nr */
+	    msg_puts(IObuff);
+	    sprintf((char *)IObuff, "%4ld", *lnum);	/* show line nr */
+						/* Highlight line numbers */
+	    msg_puts_attr(IObuff, hl_attr(HLF_N));
+	    MSG_PUTS(" ");
+	}
+	msg_prt_line(line);
+	out_flush();			/* show one line at a time */
+
+	/* Definition continues until line that doesn't end with '\' */
+	if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
+	    break;
+
+	if (fp != NULL)
+	{
+	    if (vim_fgets(line, LSIZE, fp)) /* end of file */
+		break;
+	    ++*lnum;
+	}
+	else
+	{
+	    if (++*lnum > curbuf->b_ml.ml_line_count)
+		break;
+	    line = ml_get(*lnum);
+	}
+	msg_putchar('\n');
+    }
+}
+#endif
+
+#ifdef FEAT_VIMINFO
+    int
+read_viminfo_search_pattern(virp, force)
+    vir_T	*virp;
+    int		force;
+{
+    char_u	*lp;
+    int		idx = -1;
+    int		magic = FALSE;
+    int		no_scs = FALSE;
+    int		off_line = FALSE;
+    int		off_end = FALSE;
+    long	off = 0;
+    int		setlast = FALSE;
+#ifdef FEAT_SEARCH_EXTRA
+    static int	hlsearch_on = FALSE;
+#endif
+    char_u	*val;
+
+    /*
+     * Old line types:
+     * "/pat", "&pat": search/subst. pat
+     * "~/pat", "~&pat": last used search/subst. pat
+     * New line types:
+     * "~h", "~H": hlsearch highlighting off/on
+     * "~<magic><smartcase><line><end><off><last><which>pat"
+     * <magic>: 'm' off, 'M' on
+     * <smartcase>: 's' off, 'S' on
+     * <line>: 'L' line offset, 'l' char offset
+     * <end>: 'E' from end, 'e' from start
+     * <off>: decimal, offset
+     * <last>: '~' last used pattern
+     * <which>: '/' search pat, '&' subst. pat
+     */
+    lp = virp->vir_line;
+    if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M'))	/* new line type */
+    {
+	if (lp[1] == 'M')		/* magic on */
+	    magic = TRUE;
+	if (lp[2] == 's')
+	    no_scs = TRUE;
+	if (lp[3] == 'L')
+	    off_line = TRUE;
+	if (lp[4] == 'E')
+	    off_end = TRUE;
+	lp += 5;
+	off = getdigits(&lp);
+    }
+    if (lp[0] == '~')		/* use this pattern for last-used pattern */
+    {
+	setlast = TRUE;
+	lp++;
+    }
+    if (lp[0] == '/')
+	idx = RE_SEARCH;
+    else if (lp[0] == '&')
+	idx = RE_SUBST;
+#ifdef FEAT_SEARCH_EXTRA
+    else if (lp[0] == 'h')	/* ~h: 'hlsearch' highlighting off */
+	hlsearch_on = FALSE;
+    else if (lp[0] == 'H')	/* ~H: 'hlsearch' highlighting on */
+	hlsearch_on = TRUE;
+#endif
+    if (idx >= 0)
+    {
+	if (force || spats[idx].pat == NULL)
+	{
+	    val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1),
+									TRUE);
+	    if (val != NULL)
+	    {
+		set_last_search_pat(val, idx, magic, setlast);
+		vim_free(val);
+		spats[idx].no_scs = no_scs;
+		spats[idx].off.line = off_line;
+		spats[idx].off.end = off_end;
+		spats[idx].off.off = off;
+#ifdef FEAT_SEARCH_EXTRA
+		if (setlast)
+		    no_hlsearch = !hlsearch_on;
+#endif
+	    }
+	}
+    }
+    return viminfo_readline(virp);
+}
+
+    void
+write_viminfo_search_pattern(fp)
+    FILE	*fp;
+{
+    if (get_viminfo_parameter('/') != 0)
+    {
+#ifdef FEAT_SEARCH_EXTRA
+	fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c",
+	    (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H');
+#endif
+	wvsp_one(fp, RE_SEARCH, "", '/');
+	wvsp_one(fp, RE_SUBST, "Substitute ", '&');
+    }
+}
+
+    static void
+wvsp_one(fp, idx, s, sc)
+    FILE	*fp;	/* file to write to */
+    int		idx;	/* spats[] index */
+    char	*s;	/* search pat */
+    int		sc;	/* dir char */
+{
+    if (spats[idx].pat != NULL)
+    {
+	fprintf(fp, "\n# Last %sSearch Pattern:\n~", s);
+	/* off.dir is not stored, it's reset to forward */
+	fprintf(fp, "%c%c%c%c%ld%s%c",
+		spats[idx].magic    ? 'M' : 'm',	/* magic */
+		spats[idx].no_scs   ? 's' : 'S',	/* smartcase */
+		spats[idx].off.line ? 'L' : 'l',	/* line offset */
+		spats[idx].off.end  ? 'E' : 'e',	/* offset from end */
+		spats[idx].off.off,			/* offset */
+		last_idx == idx	    ? "~" : "",		/* last used pat */
+		sc);
+	viminfo_writestring(fp, spats[idx].pat);
+    }
+}
+#endif /* FEAT_VIMINFO */