xref: /vim-8.2.3635/src/search.c (revision 69a7cb47)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 /*
10  * search.c: code for normal mode searching commands
11  */
12 
13 #include "vim.h"
14 
15 static void save_re_pat __ARGS((int idx, char_u *pat, int magic));
16 #ifdef FEAT_EVAL
17 static int first_submatch __ARGS((regmmatch_T *rp));
18 #endif
19 static int check_prevcol __ARGS((char_u *linep, int col, int ch, int *prevcol));
20 static int inmacro __ARGS((char_u *, char_u *));
21 static int check_linecomment __ARGS((char_u *line));
22 static int cls __ARGS((void));
23 static int skip_chars __ARGS((int, int));
24 #ifdef FEAT_TEXTOBJ
25 static void back_in_line __ARGS((void));
26 static void find_first_blank __ARGS((pos_T *));
27 static void findsent_forward __ARGS((long count, int at_start_sent));
28 #endif
29 #ifdef FEAT_FIND_ID
30 static void show_pat_in_path __ARGS((char_u *, int,
31 					 int, int, FILE *, linenr_T *, long));
32 #endif
33 #ifdef FEAT_VIMINFO
34 static void wvsp_one __ARGS((FILE *fp, int idx, char *s, int sc));
35 #endif
36 
37 static char_u *top_bot_msg = (char_u *)N_("search hit TOP, continuing at BOTTOM");
38 static char_u *bot_top_msg = (char_u *)N_("search hit BOTTOM, continuing at TOP");
39 
40 /*
41  * This file contains various searching-related routines. These fall into
42  * three groups:
43  * 1. string searches (for /, ?, n, and N)
44  * 2. character searches within a single line (for f, F, t, T, etc)
45  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
46  */
47 
48 /*
49  * String searches
50  *
51  * The string search functions are divided into two levels:
52  * lowest:  searchit(); uses an pos_T for starting position and found match.
53  * Highest: do_search(); uses curwin->w_cursor; calls searchit().
54  *
55  * The last search pattern is remembered for repeating the same search.
56  * This pattern is shared between the :g, :s, ? and / commands.
57  * This is in search_regcomp().
58  *
59  * The actual string matching is done using a heavily modified version of
60  * Henry Spencer's regular expression library.  See regexp.c.
61  */
62 
63 /* The offset for a search command is store in a soff struct */
64 /* Note: only spats[0].off is really used */
65 struct soffset
66 {
67     int		dir;		/* search direction */
68     int		line;		/* search has line offset */
69     int		end;		/* search set cursor at end */
70     long	off;		/* line or char offset */
71 };
72 
73 /* A search pattern and its attributes are stored in a spat struct */
74 struct spat
75 {
76     char_u	    *pat;	/* the pattern (in allocated memory) or NULL */
77     int		    magic;	/* magicness of the pattern */
78     int		    no_scs;	/* no smarcase for this pattern */
79     struct soffset  off;
80 };
81 
82 /*
83  * Two search patterns are remembered: One for the :substitute command and
84  * one for other searches.  last_idx points to the one that was used the last
85  * time.
86  */
87 static struct spat spats[2] =
88 {
89     {NULL, TRUE, FALSE, {'/', 0, 0, 0L}},	/* last used search pat */
90     {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}	/* last used substitute pat */
91 };
92 
93 static int last_idx = 0;	/* index in spats[] for RE_LAST */
94 
95 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
96 /* copy of spats[], for keeping the search patterns while executing autocmds */
97 static struct spat  saved_spats[2];
98 static int	    saved_last_idx = 0;
99 # ifdef FEAT_SEARCH_EXTRA
100 static int	    saved_no_hlsearch = 0;
101 # endif
102 #endif
103 
104 static char_u	    *mr_pattern = NULL;	/* pattern used by search_regcomp() */
105 #ifdef FEAT_RIGHTLEFT
106 static int	    mr_pattern_alloced = FALSE; /* mr_pattern was allocated */
107 static char_u	    *reverse_text __ARGS((char_u *s));
108 #endif
109 
110 #ifdef FEAT_FIND_ID
111 /*
112  * Type used by find_pattern_in_path() to remember which included files have
113  * been searched already.
114  */
115 typedef struct SearchedFile
116 {
117     FILE	*fp;		/* File pointer */
118     char_u	*name;		/* Full name of file */
119     linenr_T	lnum;		/* Line we were up to in file */
120     int		matched;	/* Found a match in this file */
121 } SearchedFile;
122 #endif
123 
124 /*
125  * translate search pattern for vim_regcomp()
126  *
127  * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd)
128  * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command)
129  * pat_save == RE_BOTH: save pat in both patterns (:global command)
130  * pat_use  == RE_SEARCH: use previous search pattern if "pat" is NULL
131  * pat_use  == RE_SUBST: use previous sustitute pattern if "pat" is NULL
132  * pat_use  == RE_LAST: use last used pattern if "pat" is NULL
133  * options & SEARCH_HIS: put search string in history
134  * options & SEARCH_KEEP: keep previous search pattern
135  *
136  * returns FAIL if failed, OK otherwise.
137  */
138     int
139 search_regcomp(pat, pat_save, pat_use, options, regmatch)
140     char_u	*pat;
141     int		pat_save;
142     int		pat_use;
143     int		options;
144     regmmatch_T	*regmatch;	/* return: pattern and ignore-case flag */
145 {
146     int		magic;
147     int		i;
148 
149     rc_did_emsg = FALSE;
150     magic = p_magic;
151 
152     /*
153      * If no pattern given, use a previously defined pattern.
154      */
155     if (pat == NULL || *pat == NUL)
156     {
157 	if (pat_use == RE_LAST)
158 	    i = last_idx;
159 	else
160 	    i = pat_use;
161 	if (spats[i].pat == NULL)	/* pattern was never defined */
162 	{
163 	    if (pat_use == RE_SUBST)
164 		EMSG(_(e_nopresub));
165 	    else
166 		EMSG(_(e_noprevre));
167 	    rc_did_emsg = TRUE;
168 	    return FAIL;
169 	}
170 	pat = spats[i].pat;
171 	magic = spats[i].magic;
172 	no_smartcase = spats[i].no_scs;
173     }
174 #ifdef FEAT_CMDHIST
175     else if (options & SEARCH_HIS)	/* put new pattern in history */
176 	add_to_history(HIST_SEARCH, pat, TRUE, NUL);
177 #endif
178 
179 #ifdef FEAT_RIGHTLEFT
180     if (mr_pattern_alloced)
181     {
182 	vim_free(mr_pattern);
183 	mr_pattern_alloced = FALSE;
184     }
185 
186     if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
187     {
188 	char_u *rev_pattern;
189 
190 	rev_pattern = reverse_text(pat);
191 	if (rev_pattern == NULL)
192 	    mr_pattern = pat;	    /* out of memory, keep normal pattern. */
193 	else
194 	{
195 	    mr_pattern = rev_pattern;
196 	    mr_pattern_alloced = TRUE;
197 	}
198     }
199     else
200 #endif
201 	mr_pattern = pat;
202 
203     /*
204      * Save the currently used pattern in the appropriate place,
205      * unless the pattern should not be remembered.
206      */
207     if (!(options & SEARCH_KEEP))
208     {
209 	/* search or global command */
210 	if (pat_save == RE_SEARCH || pat_save == RE_BOTH)
211 	    save_re_pat(RE_SEARCH, pat, magic);
212 	/* substitute or global command */
213 	if (pat_save == RE_SUBST || pat_save == RE_BOTH)
214 	    save_re_pat(RE_SUBST, pat, magic);
215     }
216 
217     regmatch->rmm_ic = ignorecase(pat);
218     regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0);
219     if (regmatch->regprog == NULL)
220 	return FAIL;
221     return OK;
222 }
223 
224 /*
225  * Get search pattern used by search_regcomp().
226  */
227     char_u *
228 get_search_pat()
229 {
230     return mr_pattern;
231 }
232 
233 #ifdef FEAT_RIGHTLEFT
234 /*
235  * Reverse text into allocated memory.
236  * Returns the allocated string, NULL when out of memory.
237  */
238     static char_u *
239 reverse_text(s)
240     char_u *s;
241 {
242     unsigned	len;
243     unsigned	s_i, rev_i;
244     char_u	*rev;
245 
246     /*
247      * Reverse the pattern.
248      */
249     len = (unsigned)STRLEN(s);
250     rev = alloc(len + 1);
251     if (rev != NULL)
252     {
253 	rev_i = len;
254 	for (s_i = 0; s_i < len; ++s_i)
255 	{
256 # ifdef FEAT_MBYTE
257 	    if (has_mbyte)
258 	    {
259 		int	mb_len;
260 
261 		mb_len = (*mb_ptr2len_check)(s + s_i);
262 		rev_i -= mb_len;
263 		mch_memmove(rev + rev_i, s + s_i, mb_len);
264 		s_i += mb_len - 1;
265 	    }
266 	    else
267 # endif
268 		rev[--rev_i] = s[s_i];
269 
270 	}
271 	rev[len] = NUL;
272     }
273     return rev;
274 }
275 #endif
276 
277     static void
278 save_re_pat(idx, pat, magic)
279     int		idx;
280     char_u	*pat;
281     int		magic;
282 {
283     if (spats[idx].pat != pat)
284     {
285 	vim_free(spats[idx].pat);
286 	spats[idx].pat = vim_strsave(pat);
287 	spats[idx].magic = magic;
288 	spats[idx].no_scs = no_smartcase;
289 	last_idx = idx;
290 #ifdef FEAT_SEARCH_EXTRA
291 	/* If 'hlsearch' set and search pat changed: need redraw. */
292 	if (p_hls)
293 	    redraw_all_later(NOT_VALID);
294 	no_hlsearch = FALSE;
295 #endif
296     }
297 }
298 
299 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
300 /*
301  * Save the search patterns, so they can be restored later.
302  * Used before/after executing autocommands and user functions.
303  */
304 static int save_level = 0;
305 
306     void
307 save_search_patterns()
308 {
309     if (save_level++ == 0)
310     {
311 	saved_spats[0] = spats[0];
312 	if (spats[0].pat != NULL)
313 	    saved_spats[0].pat = vim_strsave(spats[0].pat);
314 	saved_spats[1] = spats[1];
315 	if (spats[1].pat != NULL)
316 	    saved_spats[1].pat = vim_strsave(spats[1].pat);
317 	saved_last_idx = last_idx;
318 # ifdef FEAT_SEARCH_EXTRA
319 	saved_no_hlsearch = no_hlsearch;
320 # endif
321     }
322 }
323 
324     void
325 restore_search_patterns()
326 {
327     if (--save_level == 0)
328     {
329 	vim_free(spats[0].pat);
330 	spats[0] = saved_spats[0];
331 	vim_free(spats[1].pat);
332 	spats[1] = saved_spats[1];
333 	last_idx = saved_last_idx;
334 # ifdef FEAT_SEARCH_EXTRA
335 	no_hlsearch = saved_no_hlsearch;
336 # endif
337     }
338 }
339 #endif
340 
341 /*
342  * Return TRUE when case should be ignored for search pattern "pat".
343  * Uses the 'ignorecase' and 'smartcase' options.
344  */
345     int
346 ignorecase(pat)
347     char_u	*pat;
348 {
349     char_u	*p;
350     int		ic;
351 
352     ic = p_ic;
353     if (ic && !no_smartcase && p_scs
354 #ifdef FEAT_INS_EXPAND
355 				&& !(ctrl_x_mode && curbuf->b_p_inf)
356 #endif
357 								    )
358     {
359 	/* don't ignore case if pattern has uppercase */
360 	for (p = pat; *p; )
361 	{
362 #ifdef FEAT_MBYTE
363 	    int		l;
364 
365 	    if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
366 	    {
367 		if (enc_utf8 && utf_isupper(utf_ptr2char(p)))
368 		{
369 		    ic = FALSE;
370 		    break;
371 		}
372 		p += l;
373 	    }
374 	    else
375 #endif
376 		if (*p == '\\' && p[1] != NUL)	/* skip "\S" et al. */
377 		    p += 2;
378 		else if (isupper(*p++))
379 		{
380 		    ic = FALSE;
381 		    break;
382 		}
383 	}
384     }
385     no_smartcase = FALSE;
386 
387     return ic;
388 }
389 
390     char_u *
391 last_search_pat()
392 {
393     return spats[last_idx].pat;
394 }
395 
396 /*
397  * Reset search direction to forward.  For "gd" and "gD" commands.
398  */
399     void
400 reset_search_dir()
401 {
402     spats[0].off.dir = '/';
403 }
404 
405 #if defined(FEAT_EVAL) || defined(FEAT_VIMINFO)
406 /*
407  * Set the last search pattern.  For ":let @/ =" and viminfo.
408  * Also set the saved search pattern, so that this works in an autocommand.
409  */
410     void
411 set_last_search_pat(s, idx, magic, setlast)
412     char_u	*s;
413     int		idx;
414     int		magic;
415     int		setlast;
416 {
417     vim_free(spats[idx].pat);
418     /* An empty string means that nothing should be matched. */
419     if (*s == NUL)
420 	spats[idx].pat = NULL;
421     else
422 	spats[idx].pat = vim_strsave(s);
423     spats[idx].magic = magic;
424     spats[idx].no_scs = FALSE;
425     spats[idx].off.dir = '/';
426     spats[idx].off.line = FALSE;
427     spats[idx].off.end = FALSE;
428     spats[idx].off.off = 0;
429     if (setlast)
430 	last_idx = idx;
431     if (save_level)
432     {
433 	vim_free(saved_spats[idx].pat);
434 	saved_spats[idx] = spats[0];
435 	if (spats[idx].pat == NULL)
436 	    saved_spats[idx].pat = NULL;
437 	else
438 	    saved_spats[idx].pat = vim_strsave(spats[idx].pat);
439 	saved_last_idx = last_idx;
440     }
441 # ifdef FEAT_SEARCH_EXTRA
442     /* If 'hlsearch' set and search pat changed: need redraw. */
443     if (p_hls && idx == last_idx && !no_hlsearch)
444 	redraw_all_later(NOT_VALID);
445 # endif
446 }
447 #endif
448 
449 #ifdef FEAT_SEARCH_EXTRA
450 /*
451  * Get a regexp program for the last used search pattern.
452  * This is used for highlighting all matches in a window.
453  * Values returned in regmatch->regprog and regmatch->rmm_ic.
454  */
455     void
456 last_pat_prog(regmatch)
457     regmmatch_T	*regmatch;
458 {
459     if (spats[last_idx].pat == NULL)
460     {
461 	regmatch->regprog = NULL;
462 	return;
463     }
464     ++emsg_off;		/* So it doesn't beep if bad expr */
465     (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
466     --emsg_off;
467 }
468 #endif
469 
470 /*
471  * lowest level search function.
472  * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'.
473  * Start at position 'pos' and return the found position in 'pos'.
474  *
475  * if (options & SEARCH_MSG) == 0 don't give any messages
476  * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
477  * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
478  * if (options & SEARCH_HIS) put search pattern in history
479  * if (options & SEARCH_END) return position at end of match
480  * if (options & SEARCH_START) accept match at pos itself
481  * if (options & SEARCH_KEEP) keep previous search pattern
482  * if (options & SEARCH_FOLD) match only once in a closed fold
483  * if (options & SEARCH_PEEK) check for typed char, cancel search
484  *
485  * Return FAIL (zero) for failure, non-zero for success.
486  * When FEAT_EVAL is defined, returns the index of the first matching
487  * subpattern plus one; one if there was none.
488  */
489     int
490 searchit(win, buf, pos, dir, pat, count, options, pat_use)
491     win_T	*win;		/* window to search in; can be NULL for a
492 				   buffer without a window! */
493     buf_T	*buf;
494     pos_T	*pos;
495     int		dir;
496     char_u	*pat;
497     long	count;
498     int		options;
499     int		pat_use;
500 {
501     int		found;
502     linenr_T	lnum;		/* no init to shut up Apollo cc */
503     regmmatch_T	regmatch;
504     char_u	*ptr;
505     colnr_T	matchcol;
506     colnr_T	startcol;
507     lpos_T	endpos;
508     int		loop;
509     pos_T	start_pos;
510     int		at_first_line;
511     int		extra_col;
512     int		match_ok;
513     long	nmatched;
514     int		submatch = 0;
515     linenr_T	first_lnum;
516 #ifdef FEAT_SEARCH_EXTRA
517     int		break_loop = FALSE;
518 #else
519 # define break_loop FALSE
520 #endif
521 
522     if (search_regcomp(pat, RE_SEARCH, pat_use,
523 		   (options & (SEARCH_HIS + SEARCH_KEEP)), &regmatch) == FAIL)
524     {
525 	if ((options & SEARCH_MSG) && !rc_did_emsg)
526 	    EMSG2(_("E383: Invalid search string: %s"), mr_pattern);
527 	return FAIL;
528     }
529 
530     if (options & SEARCH_START)
531 	extra_col = 0;
532 #ifdef FEAT_MBYTE
533     /* Watch out for the "col" being MAXCOL - 2, used in a closed fold. */
534     else if (has_mbyte && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
535 						     && pos->col < MAXCOL - 2)
536 	extra_col = (*mb_ptr2len_check)(ml_get_buf(buf, pos->lnum, FALSE)
537 								  + pos->col);
538 #endif
539     else
540 	extra_col = 1;
541 
542 /*
543  * find the string
544  */
545     called_emsg = FALSE;
546     do	/* loop for count */
547     {
548 	start_pos = *pos;	/* remember start pos for detecting no match */
549 	found = 0;		/* default: not found */
550 	at_first_line = TRUE;	/* default: start in first line */
551 	if (pos->lnum == 0)	/* correct lnum for when starting in line 0 */
552 	{
553 	    pos->lnum = 1;
554 	    pos->col = 0;
555 	    at_first_line = FALSE;  /* not in first line now */
556 	}
557 
558 	/*
559 	 * Start searching in current line, unless searching backwards and
560 	 * we're in column 0.
561 	 */
562 	if (dir == BACKWARD && start_pos.col == 0)
563 	{
564 	    lnum = pos->lnum - 1;
565 	    at_first_line = FALSE;
566 	}
567 	else
568 	    lnum = pos->lnum;
569 
570 	for (loop = 0; loop <= 1; ++loop)   /* loop twice if 'wrapscan' set */
571 	{
572 	    for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
573 					   lnum += dir, at_first_line = FALSE)
574 	    {
575 		/*
576 		 * Look for a match somewhere in the line.
577 		 */
578 		first_lnum = lnum;
579 		nmatched = vim_regexec_multi(&regmatch, win, buf,
580 							    lnum, (colnr_T)0);
581 		/* Abort searching on an error (e.g., out of stack). */
582 		if (called_emsg)
583 		    break;
584 		if (nmatched > 0)
585 		{
586 		    /* match may actually be in another line when using \zs */
587 		    lnum += regmatch.startpos[0].lnum;
588 		    ptr = ml_get_buf(buf, lnum, FALSE);
589 		    startcol = regmatch.startpos[0].col;
590 		    endpos = regmatch.endpos[0];
591 # ifdef FEAT_EVAL
592 		    submatch = first_submatch(&regmatch);
593 # endif
594 
595 		    /*
596 		     * Forward search in the first line: match should be after
597 		     * the start position. If not, continue at the end of the
598 		     * match (this is vi compatible) or on the next char.
599 		     */
600 		    if (dir == FORWARD && at_first_line)
601 		    {
602 			match_ok = TRUE;
603 			/*
604 			 * When match lands on a NUL the cursor will be put
605 			 * one back afterwards, compare with that position,
606 			 * otherwise "/$" will get stuck on end of line.
607 			 */
608 			while ((options & SEARCH_END)
609 				?  (nmatched == 1
610 				    && (int)endpos.col - 1
611 					     < (int)start_pos.col + extra_col)
612 				: ((int)startcol - (ptr[startcol] == NUL)
613 					    < (int)start_pos.col + extra_col))
614 			{
615 			    /*
616 			     * If vi-compatible searching, continue at the end
617 			     * of the match, otherwise continue one position
618 			     * forward.
619 			     */
620 			    if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
621 			    {
622 				if (nmatched > 1)
623 				{
624 				    /* end is in next line, thus no match in
625 				     * this line */
626 				    match_ok = FALSE;
627 				    break;
628 				}
629 				matchcol = endpos.col;
630 				/* for empty match: advance one char */
631 				if (matchcol == startcol
632 						      && ptr[matchcol] != NUL)
633 				{
634 #ifdef FEAT_MBYTE
635 				    if (has_mbyte)
636 					matchcol +=
637 					  (*mb_ptr2len_check)(ptr + matchcol);
638 				    else
639 #endif
640 					++matchcol;
641 				}
642 			    }
643 			    else
644 			    {
645 				matchcol = startcol;
646 				if (ptr[matchcol] != NUL)
647 				{
648 #ifdef FEAT_MBYTE
649 				    if (has_mbyte)
650 					matchcol += (*mb_ptr2len_check)(ptr
651 								  + matchcol);
652 				    else
653 #endif
654 					++matchcol;
655 				}
656 			    }
657 			    if (ptr[matchcol] == NUL
658 				    || (nmatched = vim_regexec_multi(&regmatch,
659 					      win, buf, lnum, matchcol)) == 0)
660 			    {
661 				match_ok = FALSE;
662 				break;
663 			    }
664 			    startcol = regmatch.startpos[0].col;
665 			    endpos = regmatch.endpos[0];
666 # ifdef FEAT_EVAL
667 			    submatch = first_submatch(&regmatch);
668 # endif
669 
670 			    /* Need to get the line pointer again, a
671 			     * multi-line search may have made it invalid. */
672 			    ptr = ml_get_buf(buf, lnum, FALSE);
673 			}
674 			if (!match_ok)
675 			    continue;
676 		    }
677 		    if (dir == BACKWARD)
678 		    {
679 			/*
680 			 * Now, if there are multiple matches on this line,
681 			 * we have to get the last one. Or the last one before
682 			 * the cursor, if we're on that line.
683 			 * When putting the new cursor at the end, compare
684 			 * relative to the end of the match.
685 			 */
686 			match_ok = FALSE;
687 			for (;;)
688 			{
689 			    if (!at_first_line
690 				    || ((options & SEARCH_END)
691 					?  (nmatched == 1
692 					    && (int)regmatch.endpos[0].col - 1
693 								   + extra_col
694 							<= (int)start_pos.col)
695 					: ((int)regmatch.startpos[0].col
696 								   + extra_col
697 						      <= (int)start_pos.col)))
698 			    {
699 				/* Remember this position, we use it if it's
700 				 * the last match in the line. */
701 				match_ok = TRUE;
702 				startcol = regmatch.startpos[0].col;
703 				endpos = regmatch.endpos[0];
704 # ifdef FEAT_EVAL
705 				submatch = first_submatch(&regmatch);
706 # endif
707 			    }
708 			    else
709 				break;
710 
711 			    /*
712 			     * We found a valid match, now check if there is
713 			     * another one after it.
714 			     * If vi-compatible searching, continue at the end
715 			     * of the match, otherwise continue one position
716 			     * forward.
717 			     */
718 			    if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
719 			    {
720 				if (nmatched > 1)
721 				    break;
722 				matchcol = endpos.col;
723 				/* for empty match: advance one char */
724 				if (matchcol == startcol
725 						      && ptr[matchcol] != NUL)
726 				{
727 #ifdef FEAT_MBYTE
728 				    if (has_mbyte)
729 					matchcol +=
730 					  (*mb_ptr2len_check)(ptr + matchcol);
731 				    else
732 #endif
733 					++matchcol;
734 				}
735 			    }
736 			    else
737 			    {
738 				matchcol = startcol;
739 				if (ptr[matchcol] != NUL)
740 				{
741 #ifdef FEAT_MBYTE
742 				    if (has_mbyte)
743 					matchcol +=
744 					  (*mb_ptr2len_check)(ptr + matchcol);
745 				    else
746 #endif
747 					++matchcol;
748 				}
749 			    }
750 			    if (ptr[matchcol] == NUL
751 				    || (nmatched = vim_regexec_multi(&regmatch,
752 					      win, buf, lnum, matchcol)) == 0)
753 				break;
754 
755 			    /* Need to get the line pointer again, a
756 			     * multi-line search may have made it invalid. */
757 			    ptr = ml_get_buf(buf, lnum, FALSE);
758 			}
759 
760 			/*
761 			 * If there is only a match after the cursor, skip
762 			 * this match.
763 			 */
764 			if (!match_ok)
765 			    continue;
766 		    }
767 
768 		    if (options & SEARCH_END && !(options & SEARCH_NOOF))
769 		    {
770 			pos->lnum = endpos.lnum + first_lnum;
771 			pos->col = endpos.col - 1;
772 		    }
773 		    else
774 		    {
775 			pos->lnum = lnum;
776 			pos->col = startcol;
777 		    }
778 #ifdef FEAT_VIRTUALEDIT
779 		    pos->coladd = 0;
780 #endif
781 		    found = 1;
782 
783 		    /* Set variables used for 'incsearch' highlighting. */
784 		    search_match_lines = endpos.lnum - (lnum - first_lnum);
785 		    search_match_endcol = endpos.col;
786 		    break;
787 		}
788 		line_breakcheck();	/* stop if ctrl-C typed */
789 		if (got_int)
790 		    break;
791 
792 #ifdef FEAT_SEARCH_EXTRA
793 		/* Cancel searching if a character was typed.  Used for
794 		 * 'incsearch'.  Don't check too often, that would slowdown
795 		 * searching too much. */
796 		if ((options & SEARCH_PEEK)
797 			&& ((lnum - pos->lnum) & 0x3f) == 0
798 			&& char_avail())
799 		{
800 		    break_loop = TRUE;
801 		    break;
802 		}
803 #endif
804 
805 		if (loop && lnum == start_pos.lnum)
806 		    break;	    /* if second loop, stop where started */
807 	    }
808 	    at_first_line = FALSE;
809 
810 	    /*
811 	     * Stop the search if wrapscan isn't set, after an interrupt,
812 	     * after a match and after looping twice.
813 	     */
814 	    if (!p_ws || got_int || called_emsg || break_loop || found || loop)
815 		break;
816 
817 	    /*
818 	     * If 'wrapscan' is set we continue at the other end of the file.
819 	     * If 'shortmess' does not contain 's', we give a message.
820 	     * This message is also remembered in keep_msg for when the screen
821 	     * is redrawn. The keep_msg is cleared whenever another message is
822 	     * written.
823 	     */
824 	    if (dir == BACKWARD)    /* start second loop at the other end */
825 	    {
826 		lnum = buf->b_ml.ml_line_count;
827 		if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
828 		    give_warning((char_u *)_(top_bot_msg), TRUE);
829 	    }
830 	    else
831 	    {
832 		lnum = 1;
833 		if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
834 		    give_warning((char_u *)_(bot_top_msg), TRUE);
835 	    }
836 	}
837 	if (got_int || called_emsg || break_loop)
838 	    break;
839     }
840     while (--count > 0 && found);   /* stop after count matches or no match */
841 
842     vim_free(regmatch.regprog);
843 
844     if (!found)		    /* did not find it */
845     {
846 	if (got_int)
847 	    EMSG(_(e_interr));
848 	else if ((options & SEARCH_MSG) == SEARCH_MSG)
849 	{
850 	    if (p_ws)
851 		EMSG2(_(e_patnotf2), mr_pattern);
852 	    else if (lnum == 0)
853 		EMSG2(_("E384: search hit TOP without match for: %s"),
854 								  mr_pattern);
855 	    else
856 		EMSG2(_("E385: search hit BOTTOM without match for: %s"),
857 								  mr_pattern);
858 	}
859 	return FAIL;
860     }
861 
862     return submatch + 1;
863 }
864 
865 #ifdef FEAT_EVAL
866 /*
867  * Return the number of the first subpat that matched.
868  */
869     static int
870 first_submatch(rp)
871     regmmatch_T	*rp;
872 {
873     int		submatch;
874 
875     for (submatch = 1; ; ++submatch)
876     {
877 	if (rp->startpos[submatch].lnum >= 0)
878 	    break;
879 	if (submatch == 9)
880 	{
881 	    submatch = 0;
882 	    break;
883 	}
884     }
885     return submatch;
886 }
887 #endif
888 
889 /*
890  * Highest level string search function.
891  * Search for the 'count'th occurence of pattern 'pat' in direction 'dirc'
892  *		  If 'dirc' is 0: use previous dir.
893  *    If 'pat' is NULL or empty : use previous string.
894  *    If 'options & SEARCH_REV' : go in reverse of previous dir.
895  *    If 'options & SEARCH_ECHO': echo the search command and handle options
896  *    If 'options & SEARCH_MSG' : may give error message
897  *    If 'options & SEARCH_OPT' : interpret optional flags
898  *    If 'options & SEARCH_HIS' : put search pattern in history
899  *    If 'options & SEARCH_NOOF': don't add offset to position
900  *    If 'options & SEARCH_MARK': set previous context mark
901  *    If 'options & SEARCH_KEEP': keep previous search pattern
902  *    If 'options & SEARCH_START': accept match at curpos itself
903  *    If 'options & SEARCH_PEEK': check for typed char, cancel search
904  *
905  * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
906  * makes the movement linewise without moving the match position.
907  *
908  * return 0 for failure, 1 for found, 2 for found and line offset added
909  */
910     int
911 do_search(oap, dirc, pat, count, options)
912     oparg_T	    *oap;	/* can be NULL */
913     int		    dirc;	/* '/' or '?' */
914     char_u	   *pat;
915     long	    count;
916     int		    options;
917 {
918     pos_T	    pos;	/* position of the last match */
919     char_u	    *searchstr;
920     struct soffset  old_off;
921     int		    retval;	/* Return value */
922     char_u	    *p;
923     long	    c;
924     char_u	    *dircp;
925     char_u	    *strcopy = NULL;
926     char_u	    *ps;
927 
928     /*
929      * A line offset is not remembered, this is vi compatible.
930      */
931     if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
932     {
933 	spats[0].off.line = FALSE;
934 	spats[0].off.off = 0;
935     }
936 
937     /*
938      * Save the values for when (options & SEARCH_KEEP) is used.
939      * (there is no "if ()" around this because gcc wants them initialized)
940      */
941     old_off = spats[0].off;
942 
943     pos = curwin->w_cursor;	/* start searching at the cursor position */
944 
945     /*
946      * Find out the direction of the search.
947      */
948     if (dirc == 0)
949 	dirc = spats[0].off.dir;
950     else
951 	spats[0].off.dir = dirc;
952     if (options & SEARCH_REV)
953     {
954 #ifdef WIN32
955 	/* There is a bug in the Visual C++ 2.2 compiler which means that
956 	 * dirc always ends up being '/' */
957 	dirc = (dirc == '/')  ?  '?'  :  '/';
958 #else
959 	if (dirc == '/')
960 	    dirc = '?';
961 	else
962 	    dirc = '/';
963 #endif
964     }
965 
966 #ifdef FEAT_FOLDING
967     /* If the cursor is in a closed fold, don't find another match in the same
968      * fold. */
969     if (dirc == '/')
970     {
971 	if (hasFolding(pos.lnum, NULL, &pos.lnum))
972 	    pos.col = MAXCOL - 2;	/* avoid overflow when adding 1 */
973     }
974     else
975     {
976 	if (hasFolding(pos.lnum, &pos.lnum, NULL))
977 	    pos.col = 0;
978     }
979 #endif
980 
981 #ifdef FEAT_SEARCH_EXTRA
982     /*
983      * Turn 'hlsearch' highlighting back on.
984      */
985     if (no_hlsearch && !(options & SEARCH_KEEP))
986     {
987 	redraw_all_later(NOT_VALID);
988 	no_hlsearch = FALSE;
989     }
990 #endif
991 
992     /*
993      * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
994      */
995     for (;;)
996     {
997 	searchstr = pat;
998 	dircp = NULL;
999 					    /* use previous pattern */
1000 	if (pat == NULL || *pat == NUL || *pat == dirc)
1001 	{
1002 	    if (spats[RE_SEARCH].pat == NULL)	    /* no previous pattern */
1003 	    {
1004 		EMSG(_(e_noprevre));
1005 		retval = 0;
1006 		goto end_do_search;
1007 	    }
1008 	    /* make search_regcomp() use spats[RE_SEARCH].pat */
1009 	    searchstr = (char_u *)"";
1010 	}
1011 
1012 	if (pat != NULL && *pat != NUL)	/* look for (new) offset */
1013 	{
1014 	    /*
1015 	     * Find end of regular expression.
1016 	     * If there is a matching '/' or '?', toss it.
1017 	     */
1018 	    ps = strcopy;
1019 	    p = skip_regexp(pat, dirc, (int)p_magic, &strcopy);
1020 	    if (strcopy != ps)
1021 	    {
1022 		/* made a copy of "pat" to change "\?" to "?" */
1023 		searchcmdlen += STRLEN(pat) - STRLEN(strcopy);
1024 		pat = strcopy;
1025 		searchstr = strcopy;
1026 	    }
1027 	    if (*p == dirc)
1028 	    {
1029 		dircp = p;	/* remember where we put the NUL */
1030 		*p++ = NUL;
1031 	    }
1032 	    spats[0].off.line = FALSE;
1033 	    spats[0].off.end = FALSE;
1034 	    spats[0].off.off = 0;
1035 	    /*
1036 	     * Check for a line offset or a character offset.
1037 	     * For get_address (echo off) we don't check for a character
1038 	     * offset, because it is meaningless and the 's' could be a
1039 	     * substitute command.
1040 	     */
1041 	    if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p))
1042 		spats[0].off.line = TRUE;
1043 	    else if ((options & SEARCH_OPT) &&
1044 					(*p == 'e' || *p == 's' || *p == 'b'))
1045 	    {
1046 		if (*p == 'e')		/* end */
1047 		    spats[0].off.end = SEARCH_END;
1048 		++p;
1049 	    }
1050 	    if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-')  /* got an offset */
1051 	    {
1052 					    /* 'nr' or '+nr' or '-nr' */
1053 		if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1)))
1054 		    spats[0].off.off = atol((char *)p);
1055 		else if (*p == '-')	    /* single '-' */
1056 		    spats[0].off.off = -1;
1057 		else			    /* single '+' */
1058 		    spats[0].off.off = 1;
1059 		++p;
1060 		while (VIM_ISDIGIT(*p))	    /* skip number */
1061 		    ++p;
1062 	    }
1063 
1064 	    /* compute length of search command for get_address() */
1065 	    searchcmdlen += (int)(p - pat);
1066 
1067 	    pat = p;			    /* put pat after search command */
1068 	}
1069 
1070 	if ((options & SEARCH_ECHO) && messaging()
1071 					    && !cmd_silent && msg_silent == 0)
1072 	{
1073 	    char_u	*msgbuf;
1074 	    char_u	*trunc;
1075 
1076 	    if (*searchstr == NUL)
1077 		p = spats[last_idx].pat;
1078 	    else
1079 		p = searchstr;
1080 	    msgbuf = alloc((unsigned)(STRLEN(p) + 40));
1081 	    if (msgbuf != NULL)
1082 	    {
1083 		msgbuf[0] = dirc;
1084 		STRCPY(msgbuf + 1, p);
1085 		if (spats[0].off.line || spats[0].off.end || spats[0].off.off)
1086 		{
1087 		    p = msgbuf + STRLEN(msgbuf);
1088 		    *p++ = dirc;
1089 		    if (spats[0].off.end)
1090 			*p++ = 'e';
1091 		    else if (!spats[0].off.line)
1092 			*p++ = 's';
1093 		    if (spats[0].off.off > 0 || spats[0].off.line)
1094 			*p++ = '+';
1095 		    if (spats[0].off.off != 0 || spats[0].off.line)
1096 			sprintf((char *)p, "%ld", spats[0].off.off);
1097 		    else
1098 			*p = NUL;
1099 		}
1100 
1101 		msg_start();
1102 		trunc = msg_strtrunc(msgbuf);
1103 
1104 #ifdef FEAT_RIGHTLEFT
1105 		/* The search pattern could be shown on the right in rightleft
1106 		 * mode, but the 'ruler' and 'showcmd' area use it too, thus
1107 		 * it would be blanked out again very soon.  Show it on the
1108 		 * left, but do reverse the text. */
1109 		if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
1110 		{
1111 		    char_u *r;
1112 
1113 		    r = reverse_text(trunc != NULL ? trunc : msgbuf);
1114 		    if (r != NULL)
1115 		    {
1116 			vim_free(trunc);
1117 			trunc = r;
1118 		    }
1119 		}
1120 #endif
1121 		if (trunc != NULL)
1122 		{
1123 		    msg_outtrans(trunc);
1124 		    vim_free(trunc);
1125 		}
1126 		else
1127 		    msg_outtrans(msgbuf);
1128 		msg_clr_eos();
1129 		msg_check();
1130 		vim_free(msgbuf);
1131 
1132 		gotocmdline(FALSE);
1133 		out_flush();
1134 		msg_nowait = TRUE;	    /* don't wait for this message */
1135 	    }
1136 	}
1137 
1138 	/*
1139 	 * If there is a character offset, subtract it from the current
1140 	 * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
1141 	 * Skip this if pos.col is near MAXCOL (closed fold).
1142 	 * This is not done for a line offset, because then we would not be vi
1143 	 * compatible.
1144 	 */
1145 	if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2)
1146 	{
1147 	    if (spats[0].off.off > 0)
1148 	    {
1149 		for (c = spats[0].off.off; c; --c)
1150 		    if (decl(&pos) == -1)
1151 			break;
1152 		if (c)			/* at start of buffer */
1153 		{
1154 		    pos.lnum = 0;	/* allow lnum == 0 here */
1155 		    pos.col = MAXCOL;
1156 		}
1157 	    }
1158 	    else
1159 	    {
1160 		for (c = spats[0].off.off; c; ++c)
1161 		    if (incl(&pos) == -1)
1162 			break;
1163 		if (c)			/* at end of buffer */
1164 		{
1165 		    pos.lnum = curbuf->b_ml.ml_line_count + 1;
1166 		    pos.col = 0;
1167 		}
1168 	    }
1169 	}
1170 
1171 #ifdef FEAT_FKMAP	/* when in Farsi mode, reverse the character flow */
1172 	if (p_altkeymap && curwin->w_p_rl)
1173 	     lrFswap(searchstr,0);
1174 #endif
1175 
1176 	c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD,
1177 		searchstr, count, spats[0].off.end + (options &
1178 		       (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS
1179 			+ SEARCH_MSG + SEARCH_START
1180 			+ ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF))),
1181 		RE_LAST);
1182 
1183 	if (dircp != NULL)
1184 	    *dircp = dirc;	/* restore second '/' or '?' for normal_cmd() */
1185 	if (c == FAIL)
1186 	{
1187 	    retval = 0;
1188 	    goto end_do_search;
1189 	}
1190 	if (spats[0].off.end && oap != NULL)
1191 	    oap->inclusive = TRUE;  /* 'e' includes last character */
1192 
1193 	retval = 1;		    /* pattern found */
1194 
1195 	/*
1196 	 * Add character and/or line offset
1197 	 */
1198 	if (!(options & SEARCH_NOOF) || *pat == ';')
1199 	{
1200 	    if (spats[0].off.line)	/* Add the offset to the line number. */
1201 	    {
1202 		c = pos.lnum + spats[0].off.off;
1203 		if (c < 1)
1204 		    pos.lnum = 1;
1205 		else if (c > curbuf->b_ml.ml_line_count)
1206 		    pos.lnum = curbuf->b_ml.ml_line_count;
1207 		else
1208 		    pos.lnum = c;
1209 		pos.col = 0;
1210 
1211 		retval = 2;	    /* pattern found, line offset added */
1212 	    }
1213 	    else if (pos.col < MAXCOL - 2)	/* just in case */
1214 	    {
1215 		/* to the right, check for end of file */
1216 		if (spats[0].off.off > 0)
1217 		{
1218 		    for (c = spats[0].off.off; c; --c)
1219 			if (incl(&pos) == -1)
1220 			    break;
1221 		}
1222 		/* to the left, check for start of file */
1223 		else
1224 		{
1225 		    if ((c = pos.col + spats[0].off.off) >= 0)
1226 			pos.col = c;
1227 		    else
1228 			for (c = spats[0].off.off; c; ++c)
1229 			    if (decl(&pos) == -1)
1230 				break;
1231 		}
1232 	    }
1233 	}
1234 
1235 	/*
1236 	 * The search command can be followed by a ';' to do another search.
1237 	 * For example: "/pat/;/foo/+3;?bar"
1238 	 * This is like doing another search command, except:
1239 	 * - The remembered direction '/' or '?' is from the first search.
1240 	 * - When an error happens the cursor isn't moved at all.
1241 	 * Don't do this when called by get_address() (it handles ';' itself).
1242 	 */
1243 	if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';')
1244 	    break;
1245 
1246 	dirc = *++pat;
1247 	if (dirc != '?' && dirc != '/')
1248 	{
1249 	    retval = 0;
1250 	    EMSG(_("E386: Expected '?' or '/'  after ';'"));
1251 	    goto end_do_search;
1252 	}
1253 	++pat;
1254     }
1255 
1256     if (options & SEARCH_MARK)
1257 	setpcmark();
1258     curwin->w_cursor = pos;
1259     curwin->w_set_curswant = TRUE;
1260 
1261 end_do_search:
1262     if (options & SEARCH_KEEP)
1263 	spats[0].off = old_off;
1264     vim_free(strcopy);
1265 
1266     return retval;
1267 }
1268 
1269 #if defined(FEAT_INS_EXPAND) || defined(PROTO)
1270 /*
1271  * search_for_exact_line(buf, pos, dir, pat)
1272  *
1273  * Search for a line starting with the given pattern (ignoring leading
1274  * white-space), starting from pos and going in direction dir.	pos will
1275  * contain the position of the match found.    Blank lines match only if
1276  * ADDING is set.  if p_ic is set then the pattern must be in lowercase.
1277  * Return OK for success, or FAIL if no line found.
1278  */
1279     int
1280 search_for_exact_line(buf, pos, dir, pat)
1281     buf_T	*buf;
1282     pos_T	*pos;
1283     int		dir;
1284     char_u	*pat;
1285 {
1286     linenr_T	start = 0;
1287     char_u	*ptr;
1288     char_u	*p;
1289 
1290     if (buf->b_ml.ml_line_count == 0)
1291 	return FAIL;
1292     for (;;)
1293     {
1294 	pos->lnum += dir;
1295 	if (pos->lnum < 1)
1296 	{
1297 	    if (p_ws)
1298 	    {
1299 		pos->lnum = buf->b_ml.ml_line_count;
1300 		if (!shortmess(SHM_SEARCH))
1301 		    give_warning((char_u *)_(top_bot_msg), TRUE);
1302 	    }
1303 	    else
1304 	    {
1305 		pos->lnum = 1;
1306 		break;
1307 	    }
1308 	}
1309 	else if (pos->lnum > buf->b_ml.ml_line_count)
1310 	{
1311 	    if (p_ws)
1312 	    {
1313 		pos->lnum = 1;
1314 		if (!shortmess(SHM_SEARCH))
1315 		    give_warning((char_u *)_(bot_top_msg), TRUE);
1316 	    }
1317 	    else
1318 	    {
1319 		pos->lnum = 1;
1320 		break;
1321 	    }
1322 	}
1323 	if (pos->lnum == start)
1324 	    break;
1325 	if (start == 0)
1326 	    start = pos->lnum;
1327 	ptr = ml_get_buf(buf, pos->lnum, FALSE);
1328 	p = skipwhite(ptr);
1329 	pos->col = (colnr_T) (p - ptr);
1330 
1331 	/* when adding lines the matching line may be empty but it is not
1332 	 * ignored because we are interested in the next line -- Acevedo */
1333 	if ((continue_status & CONT_ADDING) && !(continue_status & CONT_SOL))
1334 	{
1335 	    if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0)
1336 		return OK;
1337 	}
1338 	else if (*p != NUL)	/* ignore empty lines */
1339 	{	/* expanding lines or words */
1340 	    if ((p_ic ? MB_STRNICMP(p, pat, completion_length)
1341 				   : STRNCMP(p, pat, completion_length)) == 0)
1342 		return OK;
1343 	}
1344     }
1345     return FAIL;
1346 }
1347 #endif /* FEAT_INS_EXPAND */
1348 
1349 /*
1350  * Character Searches
1351  */
1352 
1353 /*
1354  * Search for a character in a line.  If "t_cmd" is FALSE, move to the
1355  * position of the character, otherwise move to just before the char.
1356  * Do this "cap->count1" times.
1357  * Return FAIL or OK.
1358  */
1359     int
1360 searchc(cap, t_cmd)
1361     cmdarg_T	*cap;
1362     int		t_cmd;
1363 {
1364     int			c = cap->nchar;	/* char to search for */
1365     int			dir = cap->arg;	/* TRUE for searching forward */
1366     long		count = cap->count1;	/* repeat count */
1367     static int		lastc = NUL;	/* last character searched for */
1368     static int		lastcdir;	/* last direction of character search */
1369     static int		last_t_cmd;	/* last search t_cmd */
1370     int			col;
1371     char_u		*p;
1372     int			len;
1373 #ifdef FEAT_MBYTE
1374     static char_u	bytes[MB_MAXBYTES];
1375     static int		bytelen = 1;	/* >1 for multi-byte char */
1376 #endif
1377 
1378     if (c != NUL)	/* normal search: remember args for repeat */
1379     {
1380 	if (!KeyStuffed)    /* don't remember when redoing */
1381 	{
1382 	    lastc = c;
1383 	    lastcdir = dir;
1384 	    last_t_cmd = t_cmd;
1385 #ifdef FEAT_MBYTE
1386 	    bytelen = (*mb_char2bytes)(c, bytes);
1387 	    if (cap->ncharC1 != 0)
1388 	    {
1389 		bytelen += (*mb_char2bytes)(cap->ncharC1, bytes + bytelen);
1390 		if (cap->ncharC2 != 0)
1391 		    bytelen += (*mb_char2bytes)(cap->ncharC2, bytes + bytelen);
1392 	    }
1393 #endif
1394 	}
1395     }
1396     else		/* repeat previous search */
1397     {
1398 	if (lastc == NUL)
1399 	    return FAIL;
1400 	if (dir)	/* repeat in opposite direction */
1401 	    dir = -lastcdir;
1402 	else
1403 	    dir = lastcdir;
1404 	t_cmd = last_t_cmd;
1405 	c = lastc;
1406 	/* For multi-byte re-use last bytes[] and bytelen. */
1407     }
1408 
1409     p = ml_get_curline();
1410     col = curwin->w_cursor.col;
1411     len = (int)STRLEN(p);
1412 
1413     while (count--)
1414     {
1415 #ifdef FEAT_MBYTE
1416 	if (has_mbyte)
1417 	{
1418 	    for (;;)
1419 	    {
1420 		if (dir > 0)
1421 		{
1422 		    col += (*mb_ptr2len_check)(p + col);
1423 		    if (col >= len)
1424 			return FAIL;
1425 		}
1426 		else
1427 		{
1428 		    if (col == 0)
1429 			return FAIL;
1430 		    col -= (*mb_head_off)(p, p + col - 1) + 1;
1431 		}
1432 		if (bytelen == 1)
1433 		{
1434 		    if (p[col] == c)
1435 			break;
1436 		}
1437 		else
1438 		{
1439 		    if (vim_memcmp(p + col, bytes, bytelen) == 0)
1440 			break;
1441 		}
1442 	    }
1443 	}
1444 	else
1445 #endif
1446 	{
1447 	    for (;;)
1448 	    {
1449 		if ((col += dir) < 0 || col >= len)
1450 		    return FAIL;
1451 		if (p[col] == c)
1452 		    break;
1453 	    }
1454 	}
1455     }
1456 
1457     if (t_cmd)
1458     {
1459 	/* backup to before the character (possibly double-byte) */
1460 	col -= dir;
1461 #ifdef FEAT_MBYTE
1462 	if (has_mbyte)
1463 	{
1464 	    if (dir < 0)
1465 		/* Landed on the search char which is bytelen long */
1466 		col += bytelen - 1;
1467 	    else
1468 		/* To previous char, which may be multi-byte. */
1469 		col -= (*mb_head_off)(p, p + col);
1470 	}
1471 #endif
1472     }
1473     curwin->w_cursor.col = col;
1474 
1475     return OK;
1476 }
1477 
1478 /*
1479  * "Other" Searches
1480  */
1481 
1482 /*
1483  * findmatch - find the matching paren or brace
1484  *
1485  * Improvement over vi: Braces inside quotes are ignored.
1486  */
1487     pos_T *
1488 findmatch(oap, initc)
1489     oparg_T   *oap;
1490     int	    initc;
1491 {
1492     return findmatchlimit(oap, initc, 0, 0);
1493 }
1494 
1495 /*
1496  * Return TRUE if the character before "linep[col]" equals "ch".
1497  * Return FALSE if "col" is zero.
1498  * Update "*prevcol" to the column of the previous character, unless "prevcol"
1499  * is NULL.
1500  * Handles multibyte string correctly.
1501  */
1502     static int
1503 check_prevcol(linep, col, ch, prevcol)
1504     char_u	*linep;
1505     int		col;
1506     int		ch;
1507     int		*prevcol;
1508 {
1509     --col;
1510 #ifdef FEAT_MBYTE
1511     if (col > 0 && has_mbyte)
1512 	col -= (*mb_head_off)(linep, linep + col);
1513 #endif
1514     if (prevcol)
1515 	*prevcol = col;
1516     return (col >= 0 && linep[col] == ch) ? TRUE : FALSE;
1517 }
1518 
1519 /*
1520  * findmatchlimit -- find the matching paren or brace, if it exists within
1521  * maxtravel lines of here.  A maxtravel of 0 means search until falling off
1522  * the edge of the file.
1523  *
1524  * "initc" is the character to find a match for.  NUL means to find the
1525  * character at or after the cursor.
1526  *
1527  * flags: FM_BACKWARD	search backwards (when initc is '/', '*' or '#')
1528  *	  FM_FORWARD	search forwards (when initc is '/', '*' or '#')
1529  *	  FM_BLOCKSTOP	stop at start/end of block ({ or } in column 0)
1530  *	  FM_SKIPCOMM	skip comments (not implemented yet!)
1531  */
1532 
1533     pos_T *
1534 findmatchlimit(oap, initc, flags, maxtravel)
1535     oparg_T	*oap;
1536     int		initc;
1537     int		flags;
1538     int		maxtravel;
1539 {
1540     static pos_T pos;			/* current search position */
1541     int		findc = 0;		/* matching brace */
1542     int		c;
1543     int		count = 0;		/* cumulative number of braces */
1544     int		backwards = FALSE;	/* init for gcc */
1545     int		inquote = FALSE;	/* TRUE when inside quotes */
1546     char_u	*linep;			/* pointer to current line */
1547     char_u	*ptr;
1548     int		do_quotes;		/* check for quotes in current line */
1549     int		at_start;		/* do_quotes value at start position */
1550     int		hash_dir = 0;		/* Direction searched for # things */
1551     int		comment_dir = 0;	/* Direction searched for comments */
1552     pos_T	match_pos;		/* Where last slash-star was found */
1553     int		start_in_quotes;	/* start position is in quotes */
1554     int		traveled = 0;		/* how far we've searched so far */
1555     int		ignore_cend = FALSE;    /* ignore comment end */
1556     int		cpo_match;		/* vi compatible matching */
1557     int		cpo_bsl;		/* don't recognize backslashes */
1558     int		match_escaped = 0;	/* search for escaped match */
1559     int		dir;			/* Direction to search */
1560     int		comment_col = MAXCOL;   /* start of / / comment */
1561 
1562     pos = curwin->w_cursor;
1563     linep = ml_get(pos.lnum);
1564 
1565     cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
1566     cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL);
1567 
1568     /* Direction to search when initc is '/', '*' or '#' */
1569     if (flags & FM_BACKWARD)
1570 	dir = BACKWARD;
1571     else if (flags & FM_FORWARD)
1572 	dir = FORWARD;
1573     else
1574 	dir = 0;
1575 
1576     /*
1577      * if initc given, look in the table for the matching character
1578      * '/' and '*' are special cases: look for start or end of comment.
1579      * When '/' is used, we ignore running backwards into an star-slash, for
1580      * "[*" command, we just want to find any comment.
1581      */
1582     if (initc == '/' || initc == '*')
1583     {
1584 	comment_dir = dir;
1585 	if (initc == '/')
1586 	    ignore_cend = TRUE;
1587 	backwards = (dir == FORWARD) ? FALSE : TRUE;
1588 	initc = NUL;
1589     }
1590     else if (initc != '#' && initc != NUL)
1591     {
1592 	/* 'matchpairs' is "x:y,x:y" */
1593 	for (ptr = curbuf->b_p_mps; *ptr; ptr += 2)
1594 	{
1595 	    if (*ptr == initc)
1596 	    {
1597 		findc = initc;
1598 		initc = ptr[2];
1599 		backwards = TRUE;
1600 		break;
1601 	    }
1602 	    ptr += 2;
1603 	    if (*ptr == initc)
1604 	    {
1605 		findc = initc;
1606 		initc = ptr[-2];
1607 		backwards = FALSE;
1608 		break;
1609 	    }
1610 	    if (ptr[1] != ',')
1611 		break;
1612 	}
1613 	if (!findc)		/* invalid initc! */
1614 	    return NULL;
1615     }
1616     /*
1617      * Either initc is '#', or no initc was given and we need to look under the
1618      * cursor.
1619      */
1620     else
1621     {
1622 	if (initc == '#')
1623 	{
1624 	    hash_dir = dir;
1625 	}
1626 	else
1627 	{
1628 	    /*
1629 	     * initc was not given, must look for something to match under
1630 	     * or near the cursor.
1631 	     * Only check for special things when 'cpo' doesn't have '%'.
1632 	     */
1633 	    if (!cpo_match)
1634 	    {
1635 		/* Are we before or at #if, #else etc.? */
1636 		ptr = skipwhite(linep);
1637 		if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep))
1638 		{
1639 		    ptr = skipwhite(ptr + 1);
1640 		    if (   STRNCMP(ptr, "if", 2) == 0
1641 			|| STRNCMP(ptr, "endif", 5) == 0
1642 			|| STRNCMP(ptr, "el", 2) == 0)
1643 			hash_dir = 1;
1644 		}
1645 
1646 		/* Are we on a comment? */
1647 		else if (linep[pos.col] == '/')
1648 		{
1649 		    if (linep[pos.col + 1] == '*')
1650 		    {
1651 			comment_dir = FORWARD;
1652 			backwards = FALSE;
1653 			pos.col++;
1654 		    }
1655 		    else if (pos.col > 0 && linep[pos.col - 1] == '*')
1656 		    {
1657 			comment_dir = BACKWARD;
1658 			backwards = TRUE;
1659 			pos.col--;
1660 		    }
1661 		}
1662 		else if (linep[pos.col] == '*')
1663 		{
1664 		    if (linep[pos.col + 1] == '/')
1665 		    {
1666 			comment_dir = BACKWARD;
1667 			backwards = TRUE;
1668 		    }
1669 		    else if (pos.col > 0 && linep[pos.col - 1] == '/')
1670 		    {
1671 			comment_dir = FORWARD;
1672 			backwards = FALSE;
1673 		    }
1674 		}
1675 	    }
1676 
1677 	    /*
1678 	     * If we are not on a comment or the # at the start of a line, then
1679 	     * look for brace anywhere on this line after the cursor.
1680 	     */
1681 	    if (!hash_dir && !comment_dir)
1682 	    {
1683 		/*
1684 		 * Find the brace under or after the cursor.
1685 		 * If beyond the end of the line, use the last character in
1686 		 * the line.
1687 		 */
1688 		if (linep[pos.col] == NUL && pos.col)
1689 		    --pos.col;
1690 		for (;;)
1691 		{
1692 		    initc = linep[pos.col];
1693 		    if (initc == NUL)
1694 			break;
1695 
1696 		    for (ptr = curbuf->b_p_mps; *ptr; ++ptr)
1697 		    {
1698 			if (*ptr == initc)
1699 			{
1700 			    findc = ptr[2];
1701 			    backwards = FALSE;
1702 			    break;
1703 			}
1704 			ptr += 2;
1705 			if (*ptr == initc)
1706 			{
1707 			    findc = ptr[-2];
1708 			    backwards = TRUE;
1709 			    break;
1710 			}
1711 			if (!*++ptr)
1712 			    break;
1713 		    }
1714 		    if (findc)
1715 			break;
1716 #ifdef FEAT_MBYTE
1717 		    if (has_mbyte)
1718 			pos.col += (*mb_ptr2len_check)(linep + pos.col);
1719 		    else
1720 #endif
1721 			++pos.col;
1722 		}
1723 		if (!findc)
1724 		{
1725 		    /* no brace in the line, maybe use "  #if" then */
1726 		    if (!cpo_match && *skipwhite(linep) == '#')
1727 			hash_dir = 1;
1728 		    else
1729 			return NULL;
1730 		}
1731 		else if (!cpo_bsl)
1732 		{
1733 		    int col, bslcnt = 0;
1734 
1735 		    /* Set "match_escaped" if there are an odd number of
1736 		     * backslashes. */
1737 		    for (col = pos.col; check_prevcol(linep, col, '\\', &col);)
1738 			bslcnt++;
1739 		    match_escaped = (bslcnt & 1);
1740 		}
1741 	    }
1742 	}
1743 	if (hash_dir)
1744 	{
1745 	    /*
1746 	     * Look for matching #if, #else, #elif, or #endif
1747 	     */
1748 	    if (oap != NULL)
1749 		oap->motion_type = MLINE;   /* Linewise for this case only */
1750 	    if (initc != '#')
1751 	    {
1752 		ptr = skipwhite(skipwhite(linep) + 1);
1753 		if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0)
1754 		    hash_dir = 1;
1755 		else if (STRNCMP(ptr, "endif", 5) == 0)
1756 		    hash_dir = -1;
1757 		else
1758 		    return NULL;
1759 	    }
1760 	    pos.col = 0;
1761 	    while (!got_int)
1762 	    {
1763 		if (hash_dir > 0)
1764 		{
1765 		    if (pos.lnum == curbuf->b_ml.ml_line_count)
1766 			break;
1767 		}
1768 		else if (pos.lnum == 1)
1769 		    break;
1770 		pos.lnum += hash_dir;
1771 		linep = ml_get(pos.lnum);
1772 		line_breakcheck();	/* check for CTRL-C typed */
1773 		ptr = skipwhite(linep);
1774 		if (*ptr != '#')
1775 		    continue;
1776 		pos.col = (colnr_T) (ptr - linep);
1777 		ptr = skipwhite(ptr + 1);
1778 		if (hash_dir > 0)
1779 		{
1780 		    if (STRNCMP(ptr, "if", 2) == 0)
1781 			count++;
1782 		    else if (STRNCMP(ptr, "el", 2) == 0)
1783 		    {
1784 			if (count == 0)
1785 			    return &pos;
1786 		    }
1787 		    else if (STRNCMP(ptr, "endif", 5) == 0)
1788 		    {
1789 			if (count == 0)
1790 			    return &pos;
1791 			count--;
1792 		    }
1793 		}
1794 		else
1795 		{
1796 		    if (STRNCMP(ptr, "if", 2) == 0)
1797 		    {
1798 			if (count == 0)
1799 			    return &pos;
1800 			count--;
1801 		    }
1802 		    else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0)
1803 		    {
1804 			if (count == 0)
1805 			    return &pos;
1806 		    }
1807 		    else if (STRNCMP(ptr, "endif", 5) == 0)
1808 			count++;
1809 		}
1810 	    }
1811 	    return NULL;
1812 	}
1813     }
1814 
1815 #ifdef FEAT_RIGHTLEFT
1816     /* This is just guessing: when 'rightleft' is set, search for a maching
1817      * paren/brace in the other direction. */
1818     if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL)
1819 	backwards = !backwards;
1820 #endif
1821 
1822     do_quotes = -1;
1823     start_in_quotes = MAYBE;
1824     /* backward search: Check if this line contains a single-line comment */
1825     if (backwards && comment_dir)
1826 	comment_col = check_linecomment(linep);
1827     while (!got_int)
1828     {
1829 	/*
1830 	 * Go to the next position, forward or backward. We could use
1831 	 * inc() and dec() here, but that is much slower
1832 	 */
1833 	if (backwards)
1834 	{
1835 	    if (pos.col == 0)		/* at start of line, go to prev. one */
1836 	    {
1837 		if (pos.lnum == 1)	/* start of file */
1838 		    break;
1839 		--pos.lnum;
1840 
1841 		if (maxtravel && traveled++ > maxtravel)
1842 		    break;
1843 
1844 		linep = ml_get(pos.lnum);
1845 		pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */
1846 		do_quotes = -1;
1847 		line_breakcheck();
1848 
1849 		/* Check if this line contains a single-line comment */
1850 		if (comment_dir)
1851 		    comment_col = check_linecomment(linep);
1852 	    }
1853 	    else
1854 	    {
1855 		--pos.col;
1856 #ifdef FEAT_MBYTE
1857 		if (has_mbyte)
1858 		    pos.col -= (*mb_head_off)(linep, linep + pos.col);
1859 #endif
1860 	    }
1861 	}
1862 	else				/* forward search */
1863 	{
1864 	    if (linep[pos.col] == NUL)	/* at end of line, go to next one */
1865 	    {
1866 		if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
1867 		    break;
1868 		++pos.lnum;
1869 
1870 		if (maxtravel && traveled++ > maxtravel)
1871 		    break;
1872 
1873 		linep = ml_get(pos.lnum);
1874 		pos.col = 0;
1875 		do_quotes = -1;
1876 		line_breakcheck();
1877 	    }
1878 	    else
1879 	    {
1880 #ifdef FEAT_MBYTE
1881 		if (has_mbyte)
1882 		    pos.col += (*mb_ptr2len_check)(linep + pos.col);
1883 		else
1884 #endif
1885 		    ++pos.col;
1886 	    }
1887 	}
1888 
1889 	/*
1890 	 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
1891 	 */
1892 	if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
1893 					 (linep[0] == '{' || linep[0] == '}'))
1894 	{
1895 	    if (linep[0] == findc && count == 0)	/* match! */
1896 		return &pos;
1897 	    break;					/* out of scope */
1898 	}
1899 
1900 	if (comment_dir)
1901 	{
1902 	    /* Note: comments do not nest, and we ignore quotes in them */
1903 	    /* TODO: ignore comment brackets inside strings */
1904 	    if (comment_dir == FORWARD)
1905 	    {
1906 		if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
1907 		{
1908 		    pos.col++;
1909 		    return &pos;
1910 		}
1911 	    }
1912 	    else    /* Searching backwards */
1913 	    {
1914 		/*
1915 		 * A comment may contain / * or / /, it may also start or end
1916 		 * with / * /.	Ignore a / * after / /.
1917 		 */
1918 		if (pos.col == 0)
1919 		    continue;
1920 		else if (  linep[pos.col - 1] == '/'
1921 			&& linep[pos.col] == '*'
1922 			&& (int)pos.col < comment_col)
1923 		{
1924 		    count++;
1925 		    match_pos = pos;
1926 		    match_pos.col--;
1927 		}
1928 		else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
1929 		{
1930 		    if (count > 0)
1931 			pos = match_pos;
1932 		    else if (pos.col > 1 && linep[pos.col - 2] == '/'
1933 					       && (int)pos.col <= comment_col)
1934 			pos.col -= 2;
1935 		    else if (ignore_cend)
1936 			continue;
1937 		    else
1938 			return NULL;
1939 		    return &pos;
1940 		}
1941 	    }
1942 	    continue;
1943 	}
1944 
1945 	/*
1946 	 * If smart matching ('cpoptions' does not contain '%'), braces inside
1947 	 * of quotes are ignored, but only if there is an even number of
1948 	 * quotes in the line.
1949 	 */
1950 	if (cpo_match)
1951 	    do_quotes = 0;
1952 	else if (do_quotes == -1)
1953 	{
1954 	    /*
1955 	     * Count the number of quotes in the line, skipping \" and '"'.
1956 	     * Watch out for "\\".
1957 	     */
1958 	    at_start = do_quotes;
1959 	    for (ptr = linep; *ptr; ++ptr)
1960 	    {
1961 		if (ptr == linep + pos.col + backwards)
1962 		    at_start = (do_quotes & 1);
1963 		if (*ptr == '"'
1964 			&& (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
1965 		    ++do_quotes;
1966 		if (*ptr == '\\' && ptr[1] != NUL)
1967 		    ++ptr;
1968 	    }
1969 	    do_quotes &= 1;	    /* result is 1 with even number of quotes */
1970 
1971 	    /*
1972 	     * If we find an uneven count, check current line and previous
1973 	     * one for a '\' at the end.
1974 	     */
1975 	    if (!do_quotes)
1976 	    {
1977 		inquote = FALSE;
1978 		if (ptr[-1] == '\\')
1979 		{
1980 		    do_quotes = 1;
1981 		    if (start_in_quotes == MAYBE)
1982 		    {
1983 			/* Do we need to use at_start here? */
1984 			inquote = TRUE;
1985 			start_in_quotes = TRUE;
1986 		    }
1987 		    else if (backwards)
1988 			inquote = TRUE;
1989 		}
1990 		if (pos.lnum > 1)
1991 		{
1992 		    ptr = ml_get(pos.lnum - 1);
1993 		    if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
1994 		    {
1995 			do_quotes = 1;
1996 			if (start_in_quotes == MAYBE)
1997 			{
1998 			    inquote = at_start;
1999 			    if (inquote)
2000 				start_in_quotes = TRUE;
2001 			}
2002 			else if (!backwards)
2003 			    inquote = TRUE;
2004 		    }
2005 		}
2006 	    }
2007 	}
2008 	if (start_in_quotes == MAYBE)
2009 	    start_in_quotes = FALSE;
2010 
2011 	/*
2012 	 * If 'smartmatch' is set:
2013 	 *   Things inside quotes are ignored by setting 'inquote'.  If we
2014 	 *   find a quote without a preceding '\' invert 'inquote'.  At the
2015 	 *   end of a line not ending in '\' we reset 'inquote'.
2016 	 *
2017 	 *   In lines with an uneven number of quotes (without preceding '\')
2018 	 *   we do not know which part to ignore. Therefore we only set
2019 	 *   inquote if the number of quotes in a line is even, unless this
2020 	 *   line or the previous one ends in a '\'.  Complicated, isn't it?
2021 	 */
2022 	switch (c = linep[pos.col])
2023 	{
2024 	case NUL:
2025 	    /* at end of line without trailing backslash, reset inquote */
2026 	    if (pos.col == 0 || linep[pos.col - 1] != '\\')
2027 	    {
2028 		inquote = FALSE;
2029 		start_in_quotes = FALSE;
2030 	    }
2031 	    break;
2032 
2033 	case '"':
2034 	    /* a quote that is preceded with an odd number of backslashes is
2035 	     * ignored */
2036 	    if (do_quotes)
2037 	    {
2038 		int col;
2039 
2040 		for (col = pos.col - 1; col >= 0; --col)
2041 		    if (linep[col] != '\\')
2042 			break;
2043 		if ((((int)pos.col - 1 - col) & 1) == 0)
2044 		{
2045 		    inquote = !inquote;
2046 		    start_in_quotes = FALSE;
2047 		}
2048 	    }
2049 	    break;
2050 
2051 	/*
2052 	 * If smart matching ('cpoptions' does not contain '%'):
2053 	 *   Skip things in single quotes: 'x' or '\x'.  Be careful for single
2054 	 *   single quotes, eg jon's.  Things like '\233' or '\x3f' are not
2055 	 *   skipped, there is never a brace in them.
2056 	 *   Ignore this when finding matches for `'.
2057 	 */
2058 	case '\'':
2059 	    if (!cpo_match && initc != '\'' && findc != '\'')
2060 	    {
2061 		if (backwards)
2062 		{
2063 		    if (pos.col > 1)
2064 		    {
2065 			if (linep[pos.col - 2] == '\'')
2066 			{
2067 			    pos.col -= 2;
2068 			    break;
2069 			}
2070 			else if (linep[pos.col - 2] == '\\' &&
2071 				    pos.col > 2 && linep[pos.col - 3] == '\'')
2072 			{
2073 			    pos.col -= 3;
2074 			    break;
2075 			}
2076 		    }
2077 		}
2078 		else if (linep[pos.col + 1])	/* forward search */
2079 		{
2080 		    if (linep[pos.col + 1] == '\\' &&
2081 			    linep[pos.col + 2] && linep[pos.col + 3] == '\'')
2082 		    {
2083 			pos.col += 3;
2084 			break;
2085 		    }
2086 		    else if (linep[pos.col + 2] == '\'')
2087 		    {
2088 			pos.col += 2;
2089 			break;
2090 		    }
2091 		}
2092 	    }
2093 	    /* FALLTHROUGH */
2094 
2095 	default:
2096 #ifdef FEAT_LISP
2097 	    /* For Lisp skip over backslashed (), {} and []. */
2098 	    if (curbuf->b_p_lisp
2099 		    && vim_strchr((char_u *)"(){}[]", c) != NULL
2100 		    && pos.col > 0
2101 		    && check_prevcol(linep, pos.col, '\\', NULL))
2102 		break;
2103 #endif
2104 
2105 	    /* Check for match outside of quotes, and inside of
2106 	     * quotes when the start is also inside of quotes. */
2107 	    if ((!inquote || start_in_quotes == TRUE)
2108 		    && (c == initc || c == findc))
2109 	    {
2110 		int	col, bslcnt = 0;
2111 
2112 		if (!cpo_bsl)
2113 		{
2114 		    for (col = pos.col; check_prevcol(linep, col, '\\', &col);)
2115 			bslcnt++;
2116 		}
2117 		/* Only accept a match when 'M' is in 'cpo' or when ecaping is
2118 		 * what we expect. */
2119 		if (cpo_bsl || (bslcnt & 1) == match_escaped)
2120 		{
2121 		    if (c == initc)
2122 			count++;
2123 		    else
2124 		    {
2125 			if (count == 0)
2126 			    return &pos;
2127 			count--;
2128 		    }
2129 		}
2130 	    }
2131 	}
2132     }
2133 
2134     if (comment_dir == BACKWARD && count > 0)
2135     {
2136 	pos = match_pos;
2137 	return &pos;
2138     }
2139     return (pos_T *)NULL;	/* never found it */
2140 }
2141 
2142 /*
2143  * Check if line[] contains a / / comment.
2144  * Return MAXCOL if not, otherwise return the column.
2145  * TODO: skip strings.
2146  */
2147     static int
2148 check_linecomment(line)
2149     char_u	*line;
2150 {
2151     char_u  *p;
2152 
2153     p = line;
2154     while ((p = vim_strchr(p, '/')) != NULL)
2155     {
2156 	if (p[1] == '/')
2157 	    break;
2158 	++p;
2159     }
2160 
2161     if (p == NULL)
2162 	return MAXCOL;
2163     return (int)(p - line);
2164 }
2165 
2166 /*
2167  * Move cursor briefly to character matching the one under the cursor.
2168  * Used for Insert mode and "r" command.
2169  * Show the match only if it is visible on the screen.
2170  * If there isn't a match, then beep.
2171  */
2172     void
2173 showmatch(c)
2174     int		c;	    /* char to show match for */
2175 {
2176     pos_T	*lpos, save_cursor;
2177     pos_T	mpos;
2178     colnr_T	vcol;
2179     long	save_so;
2180     long	save_siso;
2181 #ifdef CURSOR_SHAPE
2182     int		save_state;
2183 #endif
2184     colnr_T	save_dollar_vcol;
2185     char_u	*p;
2186 
2187     /*
2188      * Only show match for chars in the 'matchpairs' option.
2189      */
2190     /* 'matchpairs' is "x:y,x:y" */
2191     for (p = curbuf->b_p_mps; *p != NUL; p += 2)
2192     {
2193 #ifdef FEAT_RIGHTLEFT
2194 	if (*p == c && (curwin->w_p_rl ^ p_ri))
2195 	    break;
2196 #endif
2197 	p += 2;
2198 	if (*p == c
2199 #ifdef FEAT_RIGHTLEFT
2200 		&& !(curwin->w_p_rl ^ p_ri)
2201 #endif
2202 	   )
2203 	    break;
2204 	if (p[1] != ',')
2205 	    return;
2206     }
2207 
2208     if ((lpos = findmatch(NULL, NUL)) == NULL)	    /* no match, so beep */
2209 	vim_beep();
2210     else if (lpos->lnum >= curwin->w_topline)
2211     {
2212 	if (!curwin->w_p_wrap)
2213 	    getvcol(curwin, lpos, NULL, &vcol, NULL);
2214 	if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol
2215 			       && vcol < curwin->w_leftcol + W_WIDTH(curwin)))
2216 	{
2217 	    mpos = *lpos;    /* save the pos, update_screen() may change it */
2218 	    save_cursor = curwin->w_cursor;
2219 	    save_so = p_so;
2220 	    save_siso = p_siso;
2221 	    /* Handle "$" in 'cpo': If the ')' is typed on top of the "$",
2222 	     * stop displaying the "$". */
2223 	    if (dollar_vcol > 0 && dollar_vcol == curwin->w_virtcol)
2224 		dollar_vcol = 0;
2225 	    ++curwin->w_virtcol;	/* do display ')' just before "$" */
2226 	    update_screen(VALID);	/* show the new char first */
2227 
2228 	    save_dollar_vcol = dollar_vcol;
2229 #ifdef CURSOR_SHAPE
2230 	    save_state = State;
2231 	    State = SHOWMATCH;
2232 	    ui_cursor_shape();		/* may show different cursor shape */
2233 #endif
2234 	    curwin->w_cursor = mpos;	/* move to matching char */
2235 	    p_so = 0;			/* don't use 'scrolloff' here */
2236 	    p_siso = 0;			/* don't use 'sidescrolloff' here */
2237 	    showruler(FALSE);
2238 	    setcursor();
2239 	    cursor_on();		/* make sure that the cursor is shown */
2240 	    out_flush();
2241 #ifdef FEAT_GUI
2242 	    if (gui.in_use)
2243 	    {
2244 		gui_update_cursor(TRUE, FALSE);
2245 		gui_mch_flush();
2246 	    }
2247 #endif
2248 	    /* Restore dollar_vcol(), because setcursor() may call curs_rows()
2249 	     * which resets it if the matching position is in a previous line
2250 	     * and has a higher column number. */
2251 	    dollar_vcol = save_dollar_vcol;
2252 
2253 	    /*
2254 	     * brief pause, unless 'm' is present in 'cpo' and a character is
2255 	     * available.
2256 	     */
2257 	    if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
2258 		ui_delay(p_mat * 100L, TRUE);
2259 	    else if (!char_avail())
2260 		ui_delay(p_mat * 100L, FALSE);
2261 	    curwin->w_cursor = save_cursor;	/* restore cursor position */
2262 	    p_so = save_so;
2263 	    p_siso = save_siso;
2264 #ifdef CURSOR_SHAPE
2265 	    State = save_state;
2266 	    ui_cursor_shape();		/* may show different cursor shape */
2267 #endif
2268 	}
2269     }
2270 }
2271 
2272 /*
2273  * findsent(dir, count) - Find the start of the next sentence in direction
2274  * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
2275  * space or a line break. Also stop at an empty line.
2276  * Return OK if the next sentence was found.
2277  */
2278     int
2279 findsent(dir, count)
2280     int		dir;
2281     long	count;
2282 {
2283     pos_T	pos, tpos;
2284     int		c;
2285     int		(*func) __ARGS((pos_T *));
2286     int		startlnum;
2287     int		noskip = FALSE;	    /* do not skip blanks */
2288     int		cpo_J;
2289 
2290     pos = curwin->w_cursor;
2291     if (dir == FORWARD)
2292 	func = incl;
2293     else
2294 	func = decl;
2295 
2296     while (count--)
2297     {
2298 	/*
2299 	 * if on an empty line, skip upto a non-empty line
2300 	 */
2301 	if (gchar_pos(&pos) == NUL)
2302 	{
2303 	    do
2304 		if ((*func)(&pos) == -1)
2305 		    break;
2306 	    while (gchar_pos(&pos) == NUL);
2307 	    if (dir == FORWARD)
2308 		goto found;
2309 	}
2310 	/*
2311 	 * if on the start of a paragraph or a section and searching forward,
2312 	 * go to the next line
2313 	 */
2314 	else if (dir == FORWARD && pos.col == 0 &&
2315 						startPS(pos.lnum, NUL, FALSE))
2316 	{
2317 	    if (pos.lnum == curbuf->b_ml.ml_line_count)
2318 		return FAIL;
2319 	    ++pos.lnum;
2320 	    goto found;
2321 	}
2322 	else if (dir == BACKWARD)
2323 	    decl(&pos);
2324 
2325 	/* go back to the previous non-blank char */
2326 	while ((c = gchar_pos(&pos)) == ' ' || c == '\t' ||
2327 	     (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
2328 	{
2329 	    if (decl(&pos) == -1)
2330 		break;
2331 	    /* when going forward: Stop in front of empty line */
2332 	    if (lineempty(pos.lnum) && dir == FORWARD)
2333 	    {
2334 		incl(&pos);
2335 		goto found;
2336 	    }
2337 	}
2338 
2339 	/* remember the line where the search started */
2340 	startlnum = pos.lnum;
2341 	cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
2342 
2343 	for (;;)		/* find end of sentence */
2344 	{
2345 	    c = gchar_pos(&pos);
2346 	    if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
2347 	    {
2348 		if (dir == BACKWARD && pos.lnum != startlnum)
2349 		    ++pos.lnum;
2350 		break;
2351 	    }
2352 	    if (c == '.' || c == '!' || c == '?')
2353 	    {
2354 		tpos = pos;
2355 		do
2356 		    if ((c = inc(&tpos)) == -1)
2357 			break;
2358 		while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
2359 			!= NULL);
2360 		if (c == -1  || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
2361 		    || (cpo_J && (c == ' ' && inc(&tpos) >= 0
2362 			      && gchar_pos(&tpos) == ' ')))
2363 		{
2364 		    pos = tpos;
2365 		    if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */
2366 			inc(&pos);
2367 		    break;
2368 		}
2369 	    }
2370 	    if ((*func)(&pos) == -1)
2371 	    {
2372 		if (count)
2373 		    return FAIL;
2374 		noskip = TRUE;
2375 		break;
2376 	    }
2377 	}
2378 found:
2379 	    /* skip white space */
2380 	while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
2381 	    if (incl(&pos) == -1)
2382 		break;
2383     }
2384 
2385     setpcmark();
2386     curwin->w_cursor = pos;
2387     return OK;
2388 }
2389 
2390 /*
2391  * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
2392  * Paragraphs are currently supposed to be separated by empty lines.
2393  * Return TRUE if the next paragraph was found.
2394  * If 'what' is '{' or '}' we go to the next section.
2395  * If 'both' is TRUE also stop at '}'.
2396  */
2397     int
2398 findpar(oap, dir, count, what, both)
2399     oparg_T	    *oap;
2400     int		    dir;
2401     long	    count;
2402     int		    what;
2403     int		    both;
2404 {
2405     linenr_T	curr;
2406     int		did_skip;   /* TRUE after separating lines have been skipped */
2407     int		first;	    /* TRUE on first line */
2408 #ifdef FEAT_FOLDING
2409     linenr_T	fold_first; /* first line of a closed fold */
2410     linenr_T	fold_last;  /* last line of a closed fold */
2411     int		fold_skipped; /* TRUE if a closed fold was skipped this
2412 				 iteration */
2413 #endif
2414 
2415     curr = curwin->w_cursor.lnum;
2416 
2417     while (count--)
2418     {
2419 	did_skip = FALSE;
2420 	for (first = TRUE; ; first = FALSE)
2421 	{
2422 	    if (*ml_get(curr) != NUL)
2423 		did_skip = TRUE;
2424 
2425 #ifdef FEAT_FOLDING
2426 	    /* skip folded lines */
2427 	    fold_skipped = FALSE;
2428 	    if (first && hasFolding(curr, &fold_first, &fold_last))
2429 	    {
2430 		curr = ((dir > 0) ? fold_last : fold_first) + dir;
2431 		fold_skipped = TRUE;
2432 	    }
2433 #endif
2434 
2435 	    if (!first && did_skip && startPS(curr, what, both))
2436 		break;
2437 
2438 #ifdef FEAT_FOLDING
2439 	    if (fold_skipped)
2440 		curr -= dir;
2441 #endif
2442 	    if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
2443 	    {
2444 		if (count)
2445 		    return FALSE;
2446 		curr -= dir;
2447 		break;
2448 	    }
2449 	}
2450     }
2451     setpcmark();
2452     if (both && *ml_get(curr) == '}')	/* include line with '}' */
2453 	++curr;
2454     curwin->w_cursor.lnum = curr;
2455     if (curr == curbuf->b_ml.ml_line_count && what != '}')
2456     {
2457 	if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0)
2458 	{
2459 	    --curwin->w_cursor.col;
2460 	    oap->inclusive = TRUE;
2461 	}
2462     }
2463     else
2464 	curwin->w_cursor.col = 0;
2465     return TRUE;
2466 }
2467 
2468 /*
2469  * check if the string 's' is a nroff macro that is in option 'opt'
2470  */
2471     static int
2472 inmacro(opt, s)
2473     char_u	*opt;
2474     char_u	*s;
2475 {
2476     char_u	*macro;
2477 
2478     for (macro = opt; macro[0]; ++macro)
2479     {
2480 	/* Accept two characters in the option being equal to two characters
2481 	 * in the line.  A space in the option matches with a space in the
2482 	 * line or the line having ended. */
2483 	if (       (macro[0] == s[0]
2484 		    || (macro[0] == ' '
2485 			&& (s[0] == NUL || s[0] == ' ')))
2486 		&& (macro[1] == s[1]
2487 		    || ((macro[1] == NUL || macro[1] == ' ')
2488 			&& (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
2489 	    break;
2490 	++macro;
2491 	if (macro[0] == NUL)
2492 	    break;
2493     }
2494     return (macro[0] != NUL);
2495 }
2496 
2497 /*
2498  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
2499  * If 'para' is '{' or '}' only check for sections.
2500  * If 'both' is TRUE also stop at '}'
2501  */
2502     int
2503 startPS(lnum, para, both)
2504     linenr_T	lnum;
2505     int		para;
2506     int		both;
2507 {
2508     char_u	*s;
2509 
2510     s = ml_get(lnum);
2511     if (*s == para || *s == '\f' || (both && *s == '}'))
2512 	return TRUE;
2513     if (*s == '.' && (inmacro(p_sections, s + 1) ||
2514 					   (!para && inmacro(p_para, s + 1))))
2515 	return TRUE;
2516     return FALSE;
2517 }
2518 
2519 /*
2520  * The following routines do the word searches performed by the 'w', 'W',
2521  * 'b', 'B', 'e', and 'E' commands.
2522  */
2523 
2524 /*
2525  * To perform these searches, characters are placed into one of three
2526  * classes, and transitions between classes determine word boundaries.
2527  *
2528  * The classes are:
2529  *
2530  * 0 - white space
2531  * 1 - punctuation
2532  * 2 or higher - keyword characters (letters, digits and underscore)
2533  */
2534 
2535 static int	cls_bigword;	/* TRUE for "W", "B" or "E" */
2536 
2537 /*
2538  * cls() - returns the class of character at curwin->w_cursor
2539  *
2540  * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
2541  * from class 2 and higher are reported as class 1 since only white space
2542  * boundaries are of interest.
2543  */
2544     static int
2545 cls()
2546 {
2547     int	    c;
2548 
2549     c = gchar_cursor();
2550 #ifdef FEAT_FKMAP	/* when 'akm' (Farsi mode), take care of Farsi blank */
2551     if (p_altkeymap && c == F_BLANK)
2552 	return 0;
2553 #endif
2554     if (c == ' ' || c == '\t' || c == NUL)
2555 	return 0;
2556 #ifdef FEAT_MBYTE
2557     if (enc_dbcs != 0 && c > 0xFF)
2558     {
2559 	/* If cls_bigword, report multi-byte chars as class 1. */
2560 	if (enc_dbcs == DBCS_KOR && cls_bigword)
2561 	    return 1;
2562 
2563 	/* process code leading/trailing bytes */
2564 	return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
2565     }
2566     if (enc_utf8)
2567     {
2568 	c = utf_class(c);
2569 	if (c != 0 && cls_bigword)
2570 	    return 1;
2571 	return c;
2572     }
2573 #endif
2574 
2575     /* If cls_bigword is TRUE, report all non-blanks as class 1. */
2576     if (cls_bigword)
2577 	return 1;
2578 
2579     if (vim_iswordc(c))
2580 	return 2;
2581     return 1;
2582 }
2583 
2584 
2585 /*
2586  * fwd_word(count, type, eol) - move forward one word
2587  *
2588  * Returns FAIL if the cursor was already at the end of the file.
2589  * If eol is TRUE, last word stops at end of line (for operators).
2590  */
2591     int
2592 fwd_word(count, bigword, eol)
2593     long	count;
2594     int		bigword;    /* "W", "E" or "B" */
2595     int		eol;
2596 {
2597     int		sclass;	    /* starting class */
2598     int		i;
2599     int		last_line;
2600 
2601 #ifdef FEAT_VIRTUALEDIT
2602     curwin->w_cursor.coladd = 0;
2603 #endif
2604     cls_bigword = bigword;
2605     while (--count >= 0)
2606     {
2607 #ifdef FEAT_FOLDING
2608 	/* When inside a range of folded lines, move to the last char of the
2609 	 * last line. */
2610 	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
2611 	    coladvance((colnr_T)MAXCOL);
2612 #endif
2613 	sclass = cls();
2614 
2615 	/*
2616 	 * We always move at least one character, unless on the last
2617 	 * character in the buffer.
2618 	 */
2619 	last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
2620 	i = inc_cursor();
2621 	if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */
2622 	    return FAIL;
2623 	if (i == 1 && eol && count == 0)      /* started at last char in line */
2624 	    return OK;
2625 
2626 	/*
2627 	 * Go one char past end of current word (if any)
2628 	 */
2629 	if (sclass != 0)
2630 	    while (cls() == sclass)
2631 	    {
2632 		i = inc_cursor();
2633 		if (i == -1 || (i >= 1 && eol && count == 0))
2634 		    return OK;
2635 	    }
2636 
2637 	/*
2638 	 * go to next non-white
2639 	 */
2640 	while (cls() == 0)
2641 	{
2642 	    /*
2643 	     * We'll stop if we land on a blank line
2644 	     */
2645 	    if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
2646 		break;
2647 
2648 	    i = inc_cursor();
2649 	    if (i == -1 || (i >= 1 && eol && count == 0))
2650 		return OK;
2651 	}
2652     }
2653     return OK;
2654 }
2655 
2656 /*
2657  * bck_word() - move backward 'count' words
2658  *
2659  * If stop is TRUE and we are already on the start of a word, move one less.
2660  *
2661  * Returns FAIL if top of the file was reached.
2662  */
2663     int
2664 bck_word(count, bigword, stop)
2665     long	count;
2666     int		bigword;
2667     int		stop;
2668 {
2669     int		sclass;	    /* starting class */
2670 
2671 #ifdef FEAT_VIRTUALEDIT
2672     curwin->w_cursor.coladd = 0;
2673 #endif
2674     cls_bigword = bigword;
2675     while (--count >= 0)
2676     {
2677 #ifdef FEAT_FOLDING
2678 	/* When inside a range of folded lines, move to the first char of the
2679 	 * first line. */
2680 	if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
2681 	    curwin->w_cursor.col = 0;
2682 #endif
2683 	sclass = cls();
2684 	if (dec_cursor() == -1)		/* started at start of file */
2685 	    return FAIL;
2686 
2687 	if (!stop || sclass == cls() || sclass == 0)
2688 	{
2689 	    /*
2690 	     * Skip white space before the word.
2691 	     * Stop on an empty line.
2692 	     */
2693 	    while (cls() == 0)
2694 	    {
2695 		if (curwin->w_cursor.col == 0
2696 				      && lineempty(curwin->w_cursor.lnum))
2697 		    goto finished;
2698 		if (dec_cursor() == -1) /* hit start of file, stop here */
2699 		    return OK;
2700 	    }
2701 
2702 	    /*
2703 	     * Move backward to start of this word.
2704 	     */
2705 	    if (skip_chars(cls(), BACKWARD))
2706 		return OK;
2707 	}
2708 
2709 	inc_cursor();			/* overshot - forward one */
2710 finished:
2711 	stop = FALSE;
2712     }
2713     return OK;
2714 }
2715 
2716 /*
2717  * end_word() - move to the end of the word
2718  *
2719  * There is an apparent bug in the 'e' motion of the real vi. At least on the
2720  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
2721  * motion crosses blank lines. When the real vi crosses a blank line in an
2722  * 'e' motion, the cursor is placed on the FIRST character of the next
2723  * non-blank line. The 'E' command, however, works correctly. Since this
2724  * appears to be a bug, I have not duplicated it here.
2725  *
2726  * Returns FAIL if end of the file was reached.
2727  *
2728  * If stop is TRUE and we are already on the end of a word, move one less.
2729  * If empty is TRUE stop on an empty line.
2730  */
2731     int
2732 end_word(count, bigword, stop, empty)
2733     long	count;
2734     int		bigword;
2735     int		stop;
2736     int		empty;
2737 {
2738     int		sclass;	    /* starting class */
2739 
2740 #ifdef FEAT_VIRTUALEDIT
2741     curwin->w_cursor.coladd = 0;
2742 #endif
2743     cls_bigword = bigword;
2744     while (--count >= 0)
2745     {
2746 #ifdef FEAT_FOLDING
2747 	/* When inside a range of folded lines, move to the last char of the
2748 	 * last line. */
2749 	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
2750 	    coladvance((colnr_T)MAXCOL);
2751 #endif
2752 	sclass = cls();
2753 	if (inc_cursor() == -1)
2754 	    return FAIL;
2755 
2756 	/*
2757 	 * If we're in the middle of a word, we just have to move to the end
2758 	 * of it.
2759 	 */
2760 	if (cls() == sclass && sclass != 0)
2761 	{
2762 	    /*
2763 	     * Move forward to end of the current word
2764 	     */
2765 	    if (skip_chars(sclass, FORWARD))
2766 		return FAIL;
2767 	}
2768 	else if (!stop || sclass == 0)
2769 	{
2770 	    /*
2771 	     * We were at the end of a word. Go to the end of the next word.
2772 	     * First skip white space, if 'empty' is TRUE, stop at empty line.
2773 	     */
2774 	    while (cls() == 0)
2775 	    {
2776 		if (empty && curwin->w_cursor.col == 0
2777 					  && lineempty(curwin->w_cursor.lnum))
2778 		    goto finished;
2779 		if (inc_cursor() == -1)	    /* hit end of file, stop here */
2780 		    return FAIL;
2781 	    }
2782 
2783 	    /*
2784 	     * Move forward to the end of this word.
2785 	     */
2786 	    if (skip_chars(cls(), FORWARD))
2787 		return FAIL;
2788 	}
2789 	dec_cursor();			/* overshot - one char backward */
2790 finished:
2791 	stop = FALSE;			/* we move only one word less */
2792     }
2793     return OK;
2794 }
2795 
2796 /*
2797  * Move back to the end of the word.
2798  *
2799  * Returns FAIL if start of the file was reached.
2800  */
2801     int
2802 bckend_word(count, bigword, eol)
2803     long	count;
2804     int		bigword;    /* TRUE for "B" */
2805     int		eol;	    /* TRUE: stop at end of line. */
2806 {
2807     int		sclass;	    /* starting class */
2808     int		i;
2809 
2810 #ifdef FEAT_VIRTUALEDIT
2811     curwin->w_cursor.coladd = 0;
2812 #endif
2813     cls_bigword = bigword;
2814     while (--count >= 0)
2815     {
2816 	sclass = cls();
2817 	if ((i = dec_cursor()) == -1)
2818 	    return FAIL;
2819 	if (eol && i == 1)
2820 	    return OK;
2821 
2822 	/*
2823 	 * Move backward to before the start of this word.
2824 	 */
2825 	if (sclass != 0)
2826 	{
2827 	    while (cls() == sclass)
2828 		if ((i = dec_cursor()) == -1 || (eol && i == 1))
2829 		    return OK;
2830 	}
2831 
2832 	/*
2833 	 * Move backward to end of the previous word
2834 	 */
2835 	while (cls() == 0)
2836 	{
2837 	    if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
2838 		break;
2839 	    if ((i = dec_cursor()) == -1 || (eol && i == 1))
2840 		return OK;
2841 	}
2842     }
2843     return OK;
2844 }
2845 
2846 /*
2847  * Skip a row of characters of the same class.
2848  * Return TRUE when end-of-file reached, FALSE otherwise.
2849  */
2850     static int
2851 skip_chars(cclass, dir)
2852     int		cclass;
2853     int		dir;
2854 {
2855     while (cls() == cclass)
2856 	if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
2857 	    return TRUE;
2858     return FALSE;
2859 }
2860 
2861 #ifdef FEAT_TEXTOBJ
2862 /*
2863  * Go back to the start of the word or the start of white space
2864  */
2865     static void
2866 back_in_line()
2867 {
2868     int		sclass;		    /* starting class */
2869 
2870     sclass = cls();
2871     for (;;)
2872     {
2873 	if (curwin->w_cursor.col == 0)	    /* stop at start of line */
2874 	    break;
2875 	dec_cursor();
2876 	if (cls() != sclass)		    /* stop at start of word */
2877 	{
2878 	    inc_cursor();
2879 	    break;
2880 	}
2881     }
2882 }
2883 
2884     static void
2885 find_first_blank(posp)
2886     pos_T    *posp;
2887 {
2888     int	    c;
2889 
2890     while (decl(posp) != -1)
2891     {
2892 	c = gchar_pos(posp);
2893 	if (!vim_iswhite(c))
2894 	{
2895 	    incl(posp);
2896 	    break;
2897 	}
2898     }
2899 }
2900 
2901 /*
2902  * Skip count/2 sentences and count/2 separating white spaces.
2903  */
2904     static void
2905 findsent_forward(count, at_start_sent)
2906     long    count;
2907     int	    at_start_sent;	/* cursor is at start of sentence */
2908 {
2909     while (count--)
2910     {
2911 	findsent(FORWARD, 1L);
2912 	if (at_start_sent)
2913 	    find_first_blank(&curwin->w_cursor);
2914 	if (count == 0 || at_start_sent)
2915 	    decl(&curwin->w_cursor);
2916 	at_start_sent = !at_start_sent;
2917     }
2918 }
2919 
2920 /*
2921  * Find word under cursor, cursor at end.
2922  * Used while an operator is pending, and in Visual mode.
2923  */
2924     int
2925 current_word(oap, count, include, bigword)
2926     oparg_T	*oap;
2927     long	count;
2928     int		include;	/* TRUE: include word and white space */
2929     int		bigword;	/* FALSE == word, TRUE == WORD */
2930 {
2931     pos_T	start_pos;
2932     pos_T	pos;
2933     int		inclusive = TRUE;
2934     int		include_white = FALSE;
2935 
2936     cls_bigword = bigword;
2937 
2938 #ifdef FEAT_VISUAL
2939     /* Correct cursor when 'selection' is exclusive */
2940     if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor))
2941 	dec_cursor();
2942 
2943     /*
2944      * When Visual mode is not active, or when the VIsual area is only one
2945      * character, select the word and/or white space under the cursor.
2946      */
2947     if (!VIsual_active || equalpos(curwin->w_cursor, VIsual))
2948 #endif
2949     {
2950 	/*
2951 	 * Go to start of current word or white space.
2952 	 */
2953 	back_in_line();
2954 	start_pos = curwin->w_cursor;
2955 
2956 	/*
2957 	 * If the start is on white space, and white space should be included
2958 	 * ("	word"), or start is not on white space, and white space should
2959 	 * not be included ("word"), find end of word.
2960 	 */
2961 	if ((cls() == 0) == include)
2962 	{
2963 	    if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
2964 		return FAIL;
2965 	}
2966 	else
2967 	{
2968 	    /*
2969 	     * If the start is not on white space, and white space should be
2970 	     * included ("word	 "), or start is on white space and white
2971 	     * space should not be included ("	 "), find start of word.
2972 	     * If we end up in the first column of the next line (single char
2973 	     * word) back up to end of the line.
2974 	     */
2975 	    fwd_word(1L, bigword, TRUE);
2976 	    if (curwin->w_cursor.col == 0)
2977 		decl(&curwin->w_cursor);
2978 	    else
2979 		oneleft();
2980 
2981 	    if (include)
2982 		include_white = TRUE;
2983 	}
2984 
2985 #ifdef FEAT_VISUAL
2986 	if (VIsual_active)
2987 	{
2988 	    /* should do something when inclusive == FALSE ! */
2989 	    VIsual = start_pos;
2990 	    redraw_curbuf_later(INVERTED);	/* update the inversion */
2991 	}
2992 	else
2993 #endif
2994 	{
2995 	    oap->start = start_pos;
2996 	    oap->motion_type = MCHAR;
2997 	}
2998 	--count;
2999     }
3000 
3001     /*
3002      * When count is still > 0, extend with more objects.
3003      */
3004     while (count > 0)
3005     {
3006 	inclusive = TRUE;
3007 #ifdef FEAT_VISUAL
3008 	if (VIsual_active && lt(curwin->w_cursor, VIsual))
3009 	{
3010 	    /*
3011 	     * In Visual mode, with cursor at start: move cursor back.
3012 	     */
3013 	    if (decl(&curwin->w_cursor) == -1)
3014 		return FAIL;
3015 	    if (include != (cls() != 0))
3016 	    {
3017 		if (bck_word(1L, bigword, TRUE) == FAIL)
3018 		    return FAIL;
3019 	    }
3020 	    else
3021 	    {
3022 		if (bckend_word(1L, bigword, TRUE) == FAIL)
3023 		    return FAIL;
3024 		(void)incl(&curwin->w_cursor);
3025 	    }
3026 	}
3027 	else
3028 #endif
3029 	{
3030 	    /*
3031 	     * Move cursor forward one word and/or white area.
3032 	     */
3033 	    if (incl(&curwin->w_cursor) == -1)
3034 		return FAIL;
3035 	    if (include != (cls() == 0))
3036 	    {
3037 		if (fwd_word(1L, bigword, TRUE) == FAIL)
3038 		    return FAIL;
3039 		/*
3040 		 * If end is just past a new-line, we don't want to include
3041 		 * the first character on the line
3042 		 */
3043 		if (oneleft() == FAIL)	/* put cursor on last char of white */
3044 		    inclusive = FALSE;
3045 	    }
3046 	    else
3047 	    {
3048 		if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
3049 		    return FAIL;
3050 	    }
3051 	}
3052 	--count;
3053     }
3054 
3055     if (include_white && (cls() != 0
3056 		 || (curwin->w_cursor.col == 0 && !inclusive)))
3057     {
3058 	/*
3059 	 * If we don't include white space at the end, move the start
3060 	 * to include some white space there. This makes "daw" work
3061 	 * better on the last word in a sentence (and "2daw" on last-but-one
3062 	 * word).  Also when "2daw" deletes "word." at the end of the line
3063 	 * (cursor is at start of next line).
3064 	 * But don't delete white space at start of line (indent).
3065 	 */
3066 	pos = curwin->w_cursor;	/* save cursor position */
3067 	curwin->w_cursor = start_pos;
3068 	if (oneleft() == OK)
3069 	{
3070 	    back_in_line();
3071 	    if (cls() == 0 && curwin->w_cursor.col > 0)
3072 	    {
3073 #ifdef FEAT_VISUAL
3074 		if (VIsual_active)
3075 		    VIsual = curwin->w_cursor;
3076 		else
3077 #endif
3078 		    oap->start = curwin->w_cursor;
3079 	    }
3080 	}
3081 	curwin->w_cursor = pos;	/* put cursor back at end */
3082     }
3083 
3084 #ifdef FEAT_VISUAL
3085     if (VIsual_active)
3086     {
3087 	if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor))
3088 	    inc_cursor();
3089 	if (VIsual_mode == 'V')
3090 	{
3091 	    VIsual_mode = 'v';
3092 	    redraw_cmdline = TRUE;		/* show mode later */
3093 	}
3094     }
3095     else
3096 #endif
3097 	oap->inclusive = inclusive;
3098 
3099     return OK;
3100 }
3101 
3102 /*
3103  * Find sentence(s) under the cursor, cursor at end.
3104  * When Visual active, extend it by one or more sentences.
3105  */
3106     int
3107 current_sent(oap, count, include)
3108     oparg_T	*oap;
3109     long	count;
3110     int		include;
3111 {
3112     pos_T	start_pos;
3113     pos_T	pos;
3114     int		start_blank;
3115     int		c;
3116     int		at_start_sent;
3117     long	ncount;
3118 
3119     start_pos = curwin->w_cursor;
3120     pos = start_pos;
3121     findsent(FORWARD, 1L);	/* Find start of next sentence. */
3122 
3123 #ifdef FEAT_VISUAL
3124     /*
3125      * When visual area is bigger than one character: Extend it.
3126      */
3127     if (VIsual_active && !equalpos(start_pos, VIsual))
3128     {
3129 extend:
3130 	if (lt(start_pos, VIsual))
3131 	{
3132 	    /*
3133 	     * Cursor at start of Visual area.
3134 	     * Find out where we are:
3135 	     * - in the white space before a sentence
3136 	     * - in a sentence or just after it
3137 	     * - at the start of a sentence
3138 	     */
3139 	    at_start_sent = TRUE;
3140 	    decl(&pos);
3141 	    while (lt(pos, curwin->w_cursor))
3142 	    {
3143 		c = gchar_pos(&pos);
3144 		if (!vim_iswhite(c))
3145 		{
3146 		    at_start_sent = FALSE;
3147 		    break;
3148 		}
3149 		incl(&pos);
3150 	    }
3151 	    if (!at_start_sent)
3152 	    {
3153 		findsent(BACKWARD, 1L);
3154 		if (equalpos(curwin->w_cursor, start_pos))
3155 		    at_start_sent = TRUE;  /* exactly at start of sentence */
3156 		else
3157 		    /* inside a sentence, go to its end (start of next) */
3158 		    findsent(FORWARD, 1L);
3159 	    }
3160 	    if (include)	/* "as" gets twice as much as "is" */
3161 		count *= 2;
3162 	    while (count--)
3163 	    {
3164 		if (at_start_sent)
3165 		    find_first_blank(&curwin->w_cursor);
3166 		c = gchar_cursor();
3167 		if (!at_start_sent || (!include && !vim_iswhite(c)))
3168 		    findsent(BACKWARD, 1L);
3169 		at_start_sent = !at_start_sent;
3170 	    }
3171 	}
3172 	else
3173 	{
3174 	    /*
3175 	     * Cursor at end of Visual area.
3176 	     * Find out where we are:
3177 	     * - just before a sentence
3178 	     * - just before or in the white space before a sentence
3179 	     * - in a sentence
3180 	     */
3181 	    incl(&pos);
3182 	    at_start_sent = TRUE;
3183 	    if (!equalpos(pos, curwin->w_cursor)) /* not just before a sentence */
3184 	    {
3185 		at_start_sent = FALSE;
3186 		while (lt(pos, curwin->w_cursor))
3187 		{
3188 		    c = gchar_pos(&pos);
3189 		    if (!vim_iswhite(c))
3190 		    {
3191 			at_start_sent = TRUE;
3192 			break;
3193 		    }
3194 		    incl(&pos);
3195 		}
3196 		if (at_start_sent)	/* in the sentence */
3197 		    findsent(BACKWARD, 1L);
3198 		else		/* in/before white before a sentence */
3199 		    curwin->w_cursor = start_pos;
3200 	    }
3201 
3202 	    if (include)	/* "as" gets twice as much as "is" */
3203 		count *= 2;
3204 	    findsent_forward(count, at_start_sent);
3205 	    if (*p_sel == 'e')
3206 		++curwin->w_cursor.col;
3207 	}
3208 	return OK;
3209     }
3210 #endif
3211 
3212     /*
3213      * If cursor started on blank, check if it is just before the start of the
3214      * next sentence.
3215      */
3216     while (c = gchar_pos(&pos), vim_iswhite(c))	/* vim_iswhite() is a macro */
3217 	incl(&pos);
3218     if (equalpos(pos, curwin->w_cursor))
3219     {
3220 	start_blank = TRUE;
3221 	find_first_blank(&start_pos);	/* go back to first blank */
3222     }
3223     else
3224     {
3225 	start_blank = FALSE;
3226 	findsent(BACKWARD, 1L);
3227 	start_pos = curwin->w_cursor;
3228     }
3229     if (include)
3230 	ncount = count * 2;
3231     else
3232     {
3233 	ncount = count;
3234 	if (start_blank)
3235 	    --ncount;
3236     }
3237     if (ncount)
3238 	findsent_forward(ncount, TRUE);
3239     else
3240 	decl(&curwin->w_cursor);
3241 
3242     if (include)
3243     {
3244 	/*
3245 	 * If the blank in front of the sentence is included, exclude the
3246 	 * blanks at the end of the sentence, go back to the first blank.
3247 	 * If there are no trailing blanks, try to include leading blanks.
3248 	 */
3249 	if (start_blank)
3250 	{
3251 	    find_first_blank(&curwin->w_cursor);
3252 	    c = gchar_pos(&curwin->w_cursor);	/* vim_iswhite() is a macro */
3253 	    if (vim_iswhite(c))
3254 		decl(&curwin->w_cursor);
3255 	}
3256 	else if (c = gchar_cursor(), !vim_iswhite(c))
3257 	    find_first_blank(&start_pos);
3258     }
3259 
3260 #ifdef FEAT_VISUAL
3261     if (VIsual_active)
3262     {
3263 	/* avoid getting stuck with "is" on a single space before a sent. */
3264 	if (equalpos(start_pos, curwin->w_cursor))
3265 	    goto extend;
3266 	if (*p_sel == 'e')
3267 	    ++curwin->w_cursor.col;
3268 	VIsual = start_pos;
3269 	VIsual_mode = 'v';
3270 	redraw_curbuf_later(INVERTED);	/* update the inversion */
3271     }
3272     else
3273 #endif
3274     {
3275 	/* include a newline after the sentence, if there is one */
3276 	if (incl(&curwin->w_cursor) == -1)
3277 	    oap->inclusive = TRUE;
3278 	else
3279 	    oap->inclusive = FALSE;
3280 	oap->start = start_pos;
3281 	oap->motion_type = MCHAR;
3282     }
3283     return OK;
3284 }
3285 
3286     int
3287 current_block(oap, count, include, what, other)
3288     oparg_T	*oap;
3289     long	count;
3290     int		include;	/* TRUE == include white space */
3291     int		what;		/* '(', '{', etc. */
3292     int		other;		/* ')', '}', etc. */
3293 {
3294     pos_T	old_pos;
3295     pos_T	*pos = NULL;
3296     pos_T	start_pos;
3297     pos_T	*end_pos;
3298     pos_T	old_start, old_end;
3299     char_u	*save_cpo;
3300     int		sol = FALSE;	/* { at start of line */
3301 
3302     old_pos = curwin->w_cursor;
3303     old_end = curwin->w_cursor;		    /* remember where we started */
3304     old_start = old_end;
3305 
3306     /*
3307      * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
3308      */
3309 #ifdef FEAT_VISUAL
3310     if (!VIsual_active || equalpos(VIsual, curwin->w_cursor))
3311 #endif
3312     {
3313 	setpcmark();
3314 	if (what == '{')		    /* ignore indent */
3315 	    while (inindent(1))
3316 		if (inc_cursor() != 0)
3317 		    break;
3318 	if (gchar_cursor() == what)	    /* cursor on '(' or '{' */
3319 	    ++curwin->w_cursor.col;
3320     }
3321 #ifdef FEAT_VISUAL
3322     else if (lt(VIsual, curwin->w_cursor))
3323     {
3324 	old_start = VIsual;
3325 	curwin->w_cursor = VIsual;	    /* cursor at low end of Visual */
3326     }
3327     else
3328 	old_end = VIsual;
3329 #endif
3330 
3331     /*
3332      * Search backwards for unclosed '(', '{', etc..
3333      * Put this position in start_pos.
3334      * Ignory quotes here.
3335      */
3336     save_cpo = p_cpo;
3337     p_cpo = (char_u *)"%";
3338     while (count-- > 0)
3339     {
3340 	if ((pos = findmatch(NULL, what)) == NULL)
3341 	    break;
3342 	curwin->w_cursor = *pos;
3343 	start_pos = *pos;   /* the findmatch for end_pos will overwrite *pos */
3344     }
3345     p_cpo = save_cpo;
3346 
3347     /*
3348      * Search for matching ')', '}', etc.
3349      * Put this position in curwin->w_cursor.
3350      */
3351     if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
3352     {
3353 	curwin->w_cursor = old_pos;
3354 	return FAIL;
3355     }
3356     curwin->w_cursor = *end_pos;
3357 
3358     /*
3359      * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
3360      * If the ending '}' is only preceded by indent, skip that indent.
3361      * But only if the resulting area is not smaller than what we started with.
3362      */
3363     while (!include)
3364     {
3365 	incl(&start_pos);
3366 	sol = (curwin->w_cursor.col == 0);
3367 	decl(&curwin->w_cursor);
3368 	if (what == '{')
3369 	    while (inindent(1))
3370 	    {
3371 		sol = TRUE;
3372 		if (decl(&curwin->w_cursor) != 0)
3373 		    break;
3374 	    }
3375 #ifdef FEAT_VISUAL
3376 	/*
3377 	 * In Visual mode, when the resulting area is not bigger than what we
3378 	 * started with, extend it to the next block, and then exclude again.
3379 	 */
3380 	if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
3381 		&& VIsual_active)
3382 	{
3383 	    curwin->w_cursor = old_start;
3384 	    decl(&curwin->w_cursor);
3385 	    if ((pos = findmatch(NULL, what)) == NULL)
3386 	    {
3387 		curwin->w_cursor = old_pos;
3388 		return FAIL;
3389 	    }
3390 	    start_pos = *pos;
3391 	    curwin->w_cursor = *pos;
3392 	    if ((end_pos = findmatch(NULL, other)) == NULL)
3393 	    {
3394 		curwin->w_cursor = old_pos;
3395 		return FAIL;
3396 	    }
3397 	    curwin->w_cursor = *end_pos;
3398 	}
3399 	else
3400 #endif
3401 	    break;
3402     }
3403 
3404 #ifdef FEAT_VISUAL
3405     if (VIsual_active)
3406     {
3407 	if (*p_sel == 'e')
3408 	    ++curwin->w_cursor.col;
3409 	if (sol)
3410 	    inc(&curwin->w_cursor);	/* include the line break */
3411 	VIsual = start_pos;
3412 	VIsual_mode = 'v';
3413 	redraw_curbuf_later(INVERTED);	/* update the inversion */
3414 	showmode();
3415     }
3416     else
3417 #endif
3418     {
3419 	oap->start = start_pos;
3420 	oap->motion_type = MCHAR;
3421 	if (sol)
3422 	{
3423 	    incl(&curwin->w_cursor);
3424 	    oap->inclusive = FALSE;
3425 	}
3426 	else
3427 	    oap->inclusive = TRUE;
3428     }
3429 
3430     return OK;
3431 }
3432 
3433     int
3434 current_par(oap, count, include, type)
3435     oparg_T	*oap;
3436     long	count;
3437     int		include;	/* TRUE == include white space */
3438     int		type;		/* 'p' for paragraph, 'S' for section */
3439 {
3440     linenr_T	start_lnum;
3441     linenr_T	end_lnum;
3442     int		white_in_front;
3443     int		dir;
3444     int		start_is_white;
3445     int		prev_start_is_white;
3446     int		retval = OK;
3447     int		do_white = FALSE;
3448     int		t;
3449     int		i;
3450 
3451     if (type == 'S')	    /* not implemented yet */
3452 	return FAIL;
3453 
3454     start_lnum = curwin->w_cursor.lnum;
3455 
3456 #ifdef FEAT_VISUAL
3457     /*
3458      * When visual area is more than one line: extend it.
3459      */
3460     if (VIsual_active && start_lnum != VIsual.lnum)
3461     {
3462 extend:
3463 	if (start_lnum < VIsual.lnum)
3464 	    dir = BACKWARD;
3465 	else
3466 	    dir = FORWARD;
3467 	for (i = count; --i >= 0; )
3468 	{
3469 	    if (start_lnum ==
3470 			   (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
3471 	    {
3472 		retval = FAIL;
3473 		break;
3474 	    }
3475 
3476 	    prev_start_is_white = -1;
3477 	    for (t = 0; t < 2; ++t)
3478 	    {
3479 		start_lnum += dir;
3480 		start_is_white = linewhite(start_lnum);
3481 		if (prev_start_is_white == start_is_white)
3482 		{
3483 		    start_lnum -= dir;
3484 		    break;
3485 		}
3486 		for (;;)
3487 		{
3488 		    if (start_lnum == (dir == BACKWARD
3489 					    ? 1 : curbuf->b_ml.ml_line_count))
3490 			break;
3491 		    if (start_is_white != linewhite(start_lnum + dir)
3492 			    || (!start_is_white
3493 				    && startPS(start_lnum + (dir > 0
3494 							     ? 1 : 0), 0, 0)))
3495 			break;
3496 		    start_lnum += dir;
3497 		}
3498 		if (!include)
3499 		    break;
3500 		if (start_lnum == (dir == BACKWARD
3501 					    ? 1 : curbuf->b_ml.ml_line_count))
3502 		    break;
3503 		prev_start_is_white = start_is_white;
3504 	    }
3505 	}
3506 	curwin->w_cursor.lnum = start_lnum;
3507 	curwin->w_cursor.col = 0;
3508 	return retval;
3509     }
3510 #endif
3511 
3512     /*
3513      * First move back to the start_lnum of the paragraph or white lines
3514      */
3515     white_in_front = linewhite(start_lnum);
3516     while (start_lnum > 1)
3517     {
3518 	if (white_in_front)	    /* stop at first white line */
3519 	{
3520 	    if (!linewhite(start_lnum - 1))
3521 		break;
3522 	}
3523 	else		/* stop at first non-white line of start of paragraph */
3524 	{
3525 	    if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
3526 		break;
3527 	}
3528 	--start_lnum;
3529     }
3530 
3531     /*
3532      * Move past the end of any white lines.
3533      */
3534     end_lnum = start_lnum;
3535     while (linewhite(end_lnum) && end_lnum < curbuf->b_ml.ml_line_count)
3536 	    ++end_lnum;
3537 
3538     --end_lnum;
3539     i = count;
3540     if (!include && white_in_front)
3541 	--i;
3542     while (i--)
3543     {
3544 	if (end_lnum == curbuf->b_ml.ml_line_count)
3545 	    return FAIL;
3546 
3547 	if (!include)
3548 	    do_white = linewhite(end_lnum + 1);
3549 
3550 	if (include || !do_white)
3551 	{
3552 	    ++end_lnum;
3553 	    /*
3554 	     * skip to end of paragraph
3555 	     */
3556 	    while (end_lnum < curbuf->b_ml.ml_line_count
3557 		    && !linewhite(end_lnum + 1)
3558 		    && !startPS(end_lnum + 1, 0, 0))
3559 		++end_lnum;
3560 	}
3561 
3562 	if (i == 0 && white_in_front && include)
3563 	    break;
3564 
3565 	/*
3566 	 * skip to end of white lines after paragraph
3567 	 */
3568 	if (include || do_white)
3569 	    while (end_lnum < curbuf->b_ml.ml_line_count
3570 						   && linewhite(end_lnum + 1))
3571 		++end_lnum;
3572     }
3573 
3574     /*
3575      * If there are no empty lines at the end, try to find some empty lines at
3576      * the start (unless that has been done already).
3577      */
3578     if (!white_in_front && !linewhite(end_lnum) && include)
3579 	while (start_lnum > 1 && linewhite(start_lnum - 1))
3580 	    --start_lnum;
3581 
3582 #ifdef FEAT_VISUAL
3583     if (VIsual_active)
3584     {
3585 	/* Problem: when doing "Vipipip" nothing happens in a single white
3586 	 * line, we get stuck there.  Trap this here. */
3587 	if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
3588 	    goto extend;
3589 	VIsual.lnum = start_lnum;
3590 	VIsual_mode = 'V';
3591 	redraw_curbuf_later(INVERTED);	/* update the inversion */
3592 	showmode();
3593     }
3594     else
3595 #endif
3596     {
3597 	oap->start.lnum = start_lnum;
3598 	oap->start.col = 0;
3599 	oap->motion_type = MLINE;
3600     }
3601     curwin->w_cursor.lnum = end_lnum;
3602     curwin->w_cursor.col = 0;
3603 
3604     return OK;
3605 }
3606 #endif
3607 
3608 #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \
3609 	|| defined(PROTO)
3610 /*
3611  * return TRUE if line 'lnum' is empty or has white chars only.
3612  */
3613     int
3614 linewhite(lnum)
3615     linenr_T	lnum;
3616 {
3617     char_u  *p;
3618 
3619     p = skipwhite(ml_get(lnum));
3620     return (*p == NUL);
3621 }
3622 #endif
3623 
3624 #if defined(FEAT_FIND_ID) || defined(PROTO)
3625 /*
3626  * Find identifiers or defines in included files.
3627  * if p_ic && (continue_status & CONT_SOL) then ptr must be in lowercase.
3628  */
3629 /*ARGSUSED*/
3630     void
3631 find_pattern_in_path(ptr, dir, len, whole, skip_comments,
3632 				    type, count, action, start_lnum, end_lnum)
3633     char_u	*ptr;		/* pointer to search pattern */
3634     int		dir;		/* direction of expansion */
3635     int		len;		/* length of search pattern */
3636     int		whole;		/* match whole words only */
3637     int		skip_comments;	/* don't match inside comments */
3638     int		type;		/* Type of search; are we looking for a type?
3639 				   a macro? */
3640     long	count;
3641     int		action;		/* What to do when we find it */
3642     linenr_T	start_lnum;	/* first line to start searching */
3643     linenr_T	end_lnum;	/* last line for searching */
3644 {
3645     SearchedFile *files;		/* Stack of included files */
3646     SearchedFile *bigger;		/* When we need more space */
3647     int		max_path_depth = 50;
3648     long	match_count = 1;
3649 
3650     char_u	*pat;
3651     char_u	*new_fname;
3652     char_u	*curr_fname = curbuf->b_fname;
3653     char_u	*prev_fname = NULL;
3654     linenr_T	lnum;
3655     int		depth;
3656     int		depth_displayed;	/* For type==CHECK_PATH */
3657     int		old_files;
3658     int		already_searched;
3659     char_u	*file_line;
3660     char_u	*line;
3661     char_u	*p;
3662     char_u	save_char;
3663     int		define_matched;
3664     regmatch_T	regmatch;
3665     regmatch_T	incl_regmatch;
3666     regmatch_T	def_regmatch;
3667     int		matched = FALSE;
3668     int		did_show = FALSE;
3669     int		found = FALSE;
3670     int		i;
3671     char_u	*already = NULL;
3672     char_u	*startp = NULL;
3673 #ifdef RISCOS
3674     int		previous_munging = __riscosify_control;
3675 #endif
3676 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
3677     win_T	*curwin_save = NULL;
3678 #endif
3679 
3680     regmatch.regprog = NULL;
3681     incl_regmatch.regprog = NULL;
3682     def_regmatch.regprog = NULL;
3683 
3684     file_line = alloc(LSIZE);
3685     if (file_line == NULL)
3686 	return;
3687 
3688 #ifdef RISCOS
3689     /* UnixLib knows best how to munge c file names - turn munging back on. */
3690     __riscosify_control = __RISCOSIFY_LONG_TRUNCATE;
3691 #endif
3692 
3693     if (type != CHECK_PATH && type != FIND_DEFINE
3694 #ifdef FEAT_INS_EXPAND
3695 	/* when CONT_SOL is set compare "ptr" with the beginning of the line
3696 	 * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */
3697 	    && !(continue_status & CONT_SOL)
3698 #endif
3699        )
3700     {
3701 	pat = alloc(len + 5);
3702 	if (pat == NULL)
3703 	    goto fpip_end;
3704 	sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
3705 	/* ignore case according to p_ic, p_scs and pat */
3706 	regmatch.rm_ic = ignorecase(pat);
3707 	regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
3708 	vim_free(pat);
3709 	if (regmatch.regprog == NULL)
3710 	    goto fpip_end;
3711     }
3712     if (*curbuf->b_p_inc != NUL || *p_inc != NUL)
3713     {
3714 	incl_regmatch.regprog = vim_regcomp(*curbuf->b_p_inc == NUL
3715 			   ? p_inc : curbuf->b_p_inc, p_magic ? RE_MAGIC : 0);
3716 	if (incl_regmatch.regprog == NULL)
3717 	    goto fpip_end;
3718 	incl_regmatch.rm_ic = FALSE;	/* don't ignore case in incl. pat. */
3719     }
3720     if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL))
3721     {
3722 	def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
3723 			   ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0);
3724 	if (def_regmatch.regprog == NULL)
3725 	    goto fpip_end;
3726 	def_regmatch.rm_ic = FALSE;	/* don't ignore case in define pat. */
3727     }
3728     files = (SearchedFile *)lalloc_clear((long_u)
3729 			       (max_path_depth * sizeof(SearchedFile)), TRUE);
3730     if (files == NULL)
3731 	goto fpip_end;
3732     old_files = max_path_depth;
3733     depth = depth_displayed = -1;
3734 
3735     lnum = start_lnum;
3736     if (end_lnum > curbuf->b_ml.ml_line_count)
3737 	end_lnum = curbuf->b_ml.ml_line_count;
3738     if (lnum > end_lnum)		/* do at least one line */
3739 	lnum = end_lnum;
3740     line = ml_get(lnum);
3741 
3742     for (;;)
3743     {
3744 	if (incl_regmatch.regprog != NULL
3745 		&& vim_regexec(&incl_regmatch, line, (colnr_T)0))
3746 	{
3747 	    new_fname = file_name_in_line(incl_regmatch.endp[0],
3748 		    0, FNAME_EXP|FNAME_INCL|FNAME_REL, 1L,
3749 		    curr_fname == curbuf->b_fname
3750 					     ? curbuf->b_ffname : curr_fname);
3751 	    already_searched = FALSE;
3752 	    if (new_fname != NULL)
3753 	    {
3754 		/* Check whether we have already searched in this file */
3755 		for (i = 0;; i++)
3756 		{
3757 		    if (i == depth + 1)
3758 			i = old_files;
3759 		    if (i == max_path_depth)
3760 			break;
3761 		    if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME)
3762 		    {
3763 			if (type != CHECK_PATH &&
3764 				action == ACTION_SHOW_ALL && files[i].matched)
3765 			{
3766 			    msg_putchar('\n');	    /* cursor below last one */
3767 			    if (!got_int)	    /* don't display if 'q'
3768 						       typed at "--more--"
3769 						       mesage */
3770 			    {
3771 				msg_home_replace_hl(new_fname);
3772 				MSG_PUTS(_(" (includes previously listed match)"));
3773 				prev_fname = NULL;
3774 			    }
3775 			}
3776 			vim_free(new_fname);
3777 			new_fname = NULL;
3778 			already_searched = TRUE;
3779 			break;
3780 		    }
3781 		}
3782 	    }
3783 
3784 	    if (type == CHECK_PATH && (action == ACTION_SHOW_ALL
3785 				 || (new_fname == NULL && !already_searched)))
3786 	    {
3787 		if (did_show)
3788 		    msg_putchar('\n');	    /* cursor below last one */
3789 		else
3790 		{
3791 		    gotocmdline(TRUE);	    /* cursor at status line */
3792 		    MSG_PUTS_TITLE(_("--- Included files "));
3793 		    if (action != ACTION_SHOW_ALL)
3794 			MSG_PUTS_TITLE(_("not found "));
3795 		    MSG_PUTS_TITLE(_("in path ---\n"));
3796 		}
3797 		did_show = TRUE;
3798 		while (depth_displayed < depth && !got_int)
3799 		{
3800 		    ++depth_displayed;
3801 		    for (i = 0; i < depth_displayed; i++)
3802 			MSG_PUTS("  ");
3803 		    msg_home_replace(files[depth_displayed].name);
3804 		    MSG_PUTS(" -->\n");
3805 		}
3806 		if (!got_int)		    /* don't display if 'q' typed
3807 					       for "--more--" message */
3808 		{
3809 		    for (i = 0; i <= depth_displayed; i++)
3810 			MSG_PUTS("  ");
3811 		    if (new_fname != NULL)
3812 		    {
3813 			/* using "new_fname" is more reliable, e.g., when
3814 			 * 'includeexpr' is set. */
3815 			msg_outtrans_attr(new_fname, hl_attr(HLF_D));
3816 		    }
3817 		    else
3818 		    {
3819 			/*
3820 			 * Isolate the file name.
3821 			 * Include the surrounding "" or <> if present.
3822 			 */
3823 			for (p = incl_regmatch.endp[0]; !vim_isfilec(*p); p++)
3824 			    ;
3825 			for (i = 0; vim_isfilec(p[i]); i++)
3826 			    ;
3827 			if (i == 0)
3828 			{
3829 			    /* Nothing found, use the rest of the line. */
3830 			    p = incl_regmatch.endp[0];
3831 			    i = STRLEN(p);
3832 			}
3833 			else
3834 			{
3835 			    if (p[-1] == '"' || p[-1] == '<')
3836 			    {
3837 				--p;
3838 				++i;
3839 			    }
3840 			    if (p[i] == '"' || p[i] == '>')
3841 				++i;
3842 			}
3843 			save_char = p[i];
3844 			p[i] = NUL;
3845 			msg_outtrans_attr(p, hl_attr(HLF_D));
3846 			p[i] = save_char;
3847 		    }
3848 
3849 		    if (new_fname == NULL && action == ACTION_SHOW_ALL)
3850 		    {
3851 			if (already_searched)
3852 			    MSG_PUTS(_("  (Already listed)"));
3853 			else
3854 			    MSG_PUTS(_("  NOT FOUND"));
3855 		    }
3856 		}
3857 		out_flush();	    /* output each line directly */
3858 	    }
3859 
3860 	    if (new_fname != NULL)
3861 	    {
3862 		/* Push the new file onto the file stack */
3863 		if (depth + 1 == old_files)
3864 		{
3865 		    bigger = (SearchedFile *)lalloc((long_u)(
3866 			    max_path_depth * 2 * sizeof(SearchedFile)), TRUE);
3867 		    if (bigger != NULL)
3868 		    {
3869 			for (i = 0; i <= depth; i++)
3870 			    bigger[i] = files[i];
3871 			for (i = depth + 1; i < old_files + max_path_depth; i++)
3872 			{
3873 			    bigger[i].fp = NULL;
3874 			    bigger[i].name = NULL;
3875 			    bigger[i].lnum = 0;
3876 			    bigger[i].matched = FALSE;
3877 			}
3878 			for (i = old_files; i < max_path_depth; i++)
3879 			    bigger[i + max_path_depth] = files[i];
3880 			old_files += max_path_depth;
3881 			max_path_depth *= 2;
3882 			vim_free(files);
3883 			files = bigger;
3884 		    }
3885 		}
3886 		if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r"))
3887 								    == NULL)
3888 		    vim_free(new_fname);
3889 		else
3890 		{
3891 		    if (++depth == old_files)
3892 		    {
3893 			/*
3894 			 * lalloc() for 'bigger' must have failed above.  We
3895 			 * will forget one of our already visited files now.
3896 			 */
3897 			vim_free(files[old_files].name);
3898 			++old_files;
3899 		    }
3900 		    files[depth].name = curr_fname = new_fname;
3901 		    files[depth].lnum = 0;
3902 		    files[depth].matched = FALSE;
3903 #ifdef FEAT_INS_EXPAND
3904 		    if (action == ACTION_EXPAND)
3905 		    {
3906 			sprintf((char*)IObuff, _("Scanning included file: %s"),
3907 			    (char *)new_fname);
3908 			msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R));
3909 		    }
3910 #endif
3911 		}
3912 	    }
3913 	}
3914 	else
3915 	{
3916 	    /*
3917 	     * Check if the line is a define (type == FIND_DEFINE)
3918 	     */
3919 	    p = line;
3920 search_line:
3921 	    define_matched = FALSE;
3922 	    if (def_regmatch.regprog != NULL
3923 			      && vim_regexec(&def_regmatch, line, (colnr_T)0))
3924 	    {
3925 		/*
3926 		 * Pattern must be first identifier after 'define', so skip
3927 		 * to that position before checking for match of pattern.  Also
3928 		 * don't let it match beyond the end of this identifier.
3929 		 */
3930 		p = def_regmatch.endp[0];
3931 		while (*p && !vim_iswordc(*p))
3932 		    p++;
3933 		define_matched = TRUE;
3934 	    }
3935 
3936 	    /*
3937 	     * Look for a match.  Don't do this if we are looking for a
3938 	     * define and this line didn't match define_prog above.
3939 	     */
3940 	    if (def_regmatch.regprog == NULL || define_matched)
3941 	    {
3942 		if (define_matched
3943 #ifdef FEAT_INS_EXPAND
3944 			|| (continue_status & CONT_SOL)
3945 #endif
3946 		    )
3947 		{
3948 		    /* compare the first "len" chars from "ptr" */
3949 		    startp = skipwhite(p);
3950 		    if (p_ic)
3951 			matched = !MB_STRNICMP(startp, ptr, len);
3952 		    else
3953 			matched = !STRNCMP(startp, ptr, len);
3954 		    if (matched && define_matched && whole
3955 						  && vim_iswordc(startp[len]))
3956 			matched = FALSE;
3957 		}
3958 		else if (regmatch.regprog != NULL
3959 			 && vim_regexec(&regmatch, line, (colnr_T)(p - line)))
3960 		{
3961 		    matched = TRUE;
3962 		    startp = regmatch.startp[0];
3963 		    /*
3964 		     * Check if the line is not a comment line (unless we are
3965 		     * looking for a define).  A line starting with "# define"
3966 		     * is not considered to be a comment line.
3967 		     */
3968 		    if (!define_matched && skip_comments)
3969 		    {
3970 #ifdef FEAT_COMMENTS
3971 			if ((*line != '#' ||
3972 				STRNCMP(skipwhite(line + 1), "define", 6) != 0)
3973 				&& get_leader_len(line, NULL, FALSE))
3974 			    matched = FALSE;
3975 
3976 			/*
3977 			 * Also check for a "/ *" or "/ /" before the match.
3978 			 * Skips lines like "int backwards;  / * normal index
3979 			 * * /" when looking for "normal".
3980 			 * Note: Doesn't skip "/ *" in comments.
3981 			 */
3982 			p = skipwhite(line);
3983 			if (matched
3984 				|| (p[0] == '/' && p[1] == '*') || p[0] == '*')
3985 #endif
3986 			    for (p = line; *p && p < startp; ++p)
3987 			    {
3988 				if (matched
3989 					&& p[0] == '/'
3990 					&& (p[1] == '*' || p[1] == '/'))
3991 				{
3992 				    matched = FALSE;
3993 				    /* After "//" all text is comment */
3994 				    if (p[1] == '/')
3995 					break;
3996 				    ++p;
3997 				}
3998 				else if (!matched && p[0] == '*' && p[1] == '/')
3999 				{
4000 				    /* Can find match after "* /". */
4001 				    matched = TRUE;
4002 				    ++p;
4003 				}
4004 			    }
4005 		    }
4006 		}
4007 	    }
4008 	}
4009 	if (matched)
4010 	{
4011 #ifdef FEAT_INS_EXPAND
4012 	    if (action == ACTION_EXPAND)
4013 	    {
4014 		int	reuse = 0;
4015 		int	add_r;
4016 		char_u	*aux;
4017 
4018 		if (depth == -1 && lnum == curwin->w_cursor.lnum)
4019 		    break;
4020 		found = TRUE;
4021 		aux = p = startp;
4022 		if (continue_status & CONT_ADDING)
4023 		{
4024 		    p += completion_length;
4025 		    if (vim_iswordp(p))
4026 			goto exit_matched;
4027 		    p = find_word_start(p);
4028 		}
4029 		p = find_word_end(p);
4030 		i = (int)(p - aux);
4031 
4032 		if ((continue_status & CONT_ADDING) && i == completion_length)
4033 		{
4034 		    /* get the next line */
4035 		    /* IOSIZE > completion_length, so the STRNCPY works */
4036 		    STRNCPY(IObuff, aux, i);
4037 		    if (!(     depth < 0
4038 			    && lnum < end_lnum
4039 			    && (line = ml_get(++lnum)) != NULL)
4040 			&& !(	depth >= 0
4041 			    && !vim_fgets(line = file_line,
4042 						     LSIZE, files[depth].fp)))
4043 			goto exit_matched;
4044 
4045 		    /* we read a line, set "already" to check this "line" later
4046 		     * if depth >= 0 we'll increase files[depth].lnum far
4047 		     * bellow  -- Acevedo */
4048 		    already = aux = p = skipwhite(line);
4049 		    p = find_word_start(p);
4050 		    p = find_word_end(p);
4051 		    if (p > aux)
4052 		    {
4053 			if (*aux != ')' && IObuff[i-1] != TAB)
4054 			{
4055 			    if (IObuff[i-1] != ' ')
4056 				IObuff[i++] = ' ';
4057 			    /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/
4058 			    if (p_js
4059 				&& (IObuff[i-2] == '.'
4060 				    || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
4061 					&& (IObuff[i-2] == '?'
4062 					    || IObuff[i-2] == '!'))))
4063 				IObuff[i++] = ' ';
4064 			}
4065 			/* copy as much as posible of the new word */
4066 			if (p - aux >= IOSIZE - i)
4067 			    p = aux + IOSIZE - i - 1;
4068 			STRNCPY(IObuff + i, aux, p - aux);
4069 			i += (int)(p - aux);
4070 			reuse |= CONT_S_IPOS;
4071 		    }
4072 		    IObuff[i] = NUL;
4073 		    aux = IObuff;
4074 
4075 		    if (i == completion_length)
4076 			goto exit_matched;
4077 		}
4078 
4079 		add_r = ins_compl_add_infercase(aux, i,
4080 			curr_fname == curbuf->b_fname ? NULL : curr_fname,
4081 			dir, reuse);
4082 		if (add_r == OK)
4083 		    /* if dir was BACKWARD then honor it just once */
4084 		    dir = FORWARD;
4085 		else if (add_r == RET_ERROR)
4086 		    break;
4087 	    }
4088 	    else
4089 #endif
4090 		 if (action == ACTION_SHOW_ALL)
4091 	    {
4092 		found = TRUE;
4093 		if (!did_show)
4094 		    gotocmdline(TRUE);		/* cursor at status line */
4095 		if (curr_fname != prev_fname)
4096 		{
4097 		    if (did_show)
4098 			msg_putchar('\n');	/* cursor below last one */
4099 		    if (!got_int)		/* don't display if 'q' typed
4100 						    at "--more--" mesage */
4101 			msg_home_replace_hl(curr_fname);
4102 		    prev_fname = curr_fname;
4103 		}
4104 		did_show = TRUE;
4105 		if (!got_int)
4106 		    show_pat_in_path(line, type, TRUE, action,
4107 			    (depth == -1) ? NULL : files[depth].fp,
4108 			    (depth == -1) ? &lnum : &files[depth].lnum,
4109 			    match_count++);
4110 
4111 		/* Set matched flag for this file and all the ones that
4112 		 * include it */
4113 		for (i = 0; i <= depth; ++i)
4114 		    files[i].matched = TRUE;
4115 	    }
4116 	    else if (--count <= 0)
4117 	    {
4118 		found = TRUE;
4119 		if (depth == -1 && lnum == curwin->w_cursor.lnum
4120 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4121 						      && g_do_tagpreview == 0
4122 #endif
4123 						      )
4124 		    EMSG(_("E387: Match is on current line"));
4125 		else if (action == ACTION_SHOW)
4126 		{
4127 		    show_pat_in_path(line, type, did_show, action,
4128 			(depth == -1) ? NULL : files[depth].fp,
4129 			(depth == -1) ? &lnum : &files[depth].lnum, 1L);
4130 		    did_show = TRUE;
4131 		}
4132 		else
4133 		{
4134 #ifdef FEAT_GUI
4135 		    need_mouse_correct = TRUE;
4136 #endif
4137 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4138 		    /* ":psearch" uses the preview window */
4139 		    if (g_do_tagpreview != 0)
4140 		    {
4141 			curwin_save = curwin;
4142 			prepare_tagpreview();
4143 		    }
4144 #endif
4145 		    if (action == ACTION_SPLIT)
4146 		    {
4147 #ifdef FEAT_WINDOWS
4148 			if (win_split(0, 0) == FAIL)
4149 #endif
4150 			    break;
4151 #ifdef FEAT_SCROLLBIND
4152 			curwin->w_p_scb = FALSE;
4153 #endif
4154 		    }
4155 		    if (depth == -1)
4156 		    {
4157 			/* match in current file */
4158 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4159 			if (g_do_tagpreview != 0)
4160 			{
4161 			    if (getfile(0, curwin_save->w_buffer->b_fname,
4162 						 NULL, TRUE, lnum, FALSE) > 0)
4163 				break;	/* failed to jump to file */
4164 			}
4165 			else
4166 #endif
4167 			    setpcmark();
4168 			curwin->w_cursor.lnum = lnum;
4169 		    }
4170 		    else
4171 		    {
4172 			if (getfile(0, files[depth].name, NULL, TRUE,
4173 						files[depth].lnum, FALSE) > 0)
4174 			    break;	/* failed to jump to file */
4175 			/* autocommands may have changed the lnum, we don't
4176 			 * want that here */
4177 			curwin->w_cursor.lnum = files[depth].lnum;
4178 		    }
4179 		}
4180 		if (action != ACTION_SHOW)
4181 		{
4182 		    curwin->w_cursor.col = (colnr_T) (startp - line);
4183 		    curwin->w_set_curswant = TRUE;
4184 		}
4185 
4186 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4187 		if (g_do_tagpreview != 0
4188 			&& curwin != curwin_save && win_valid(curwin_save))
4189 		{
4190 		    /* Return cursor to where we were */
4191 		    validate_cursor();
4192 		    redraw_later(VALID);
4193 		    win_enter(curwin_save, TRUE);
4194 		}
4195 #endif
4196 		break;
4197 	    }
4198 #ifdef FEAT_INS_EXPAND
4199 exit_matched:
4200 #endif
4201 	    matched = FALSE;
4202 	    /* look for other matches in the rest of the line if we
4203 	     * are not at the end of it already */
4204 	    if (def_regmatch.regprog == NULL
4205 #ifdef FEAT_INS_EXPAND
4206 		    && action == ACTION_EXPAND
4207 		    && !(continue_status & CONT_SOL)
4208 #endif
4209 		    && *(p = startp + 1))
4210 		goto search_line;
4211 	}
4212 	line_breakcheck();
4213 #ifdef FEAT_INS_EXPAND
4214 	if (action == ACTION_EXPAND)
4215 	    ins_compl_check_keys();
4216 	if (got_int || completion_interrupted)
4217 #else
4218 	if (got_int)
4219 #endif
4220 	    break;
4221 
4222 	/*
4223 	 * Read the next line.  When reading an included file and encountering
4224 	 * end-of-file, close the file and continue in the file that included
4225 	 * it.
4226 	 */
4227 	while (depth >= 0 && !already
4228 		&& vim_fgets(line = file_line, LSIZE, files[depth].fp))
4229 	{
4230 	    fclose(files[depth].fp);
4231 	    --old_files;
4232 	    files[old_files].name = files[depth].name;
4233 	    files[old_files].matched = files[depth].matched;
4234 	    --depth;
4235 	    curr_fname = (depth == -1) ? curbuf->b_fname
4236 				       : files[depth].name;
4237 	    if (depth < depth_displayed)
4238 		depth_displayed = depth;
4239 	}
4240 	if (depth >= 0)		/* we could read the line */
4241 	    files[depth].lnum++;
4242 	else if (!already)
4243 	{
4244 	    if (++lnum > end_lnum)
4245 		break;
4246 	    line = ml_get(lnum);
4247 	}
4248 	already = NULL;
4249     }
4250     /* End of big for (;;) loop. */
4251 
4252     /* Close any files that are still open. */
4253     for (i = 0; i <= depth; i++)
4254     {
4255 	fclose(files[i].fp);
4256 	vim_free(files[i].name);
4257     }
4258     for (i = old_files; i < max_path_depth; i++)
4259 	vim_free(files[i].name);
4260     vim_free(files);
4261 
4262     if (type == CHECK_PATH)
4263     {
4264 	if (!did_show)
4265 	{
4266 	    if (action != ACTION_SHOW_ALL)
4267 		MSG(_("All included files were found"));
4268 	    else
4269 		MSG(_("No included files"));
4270 	}
4271     }
4272     else if (!found
4273 #ifdef FEAT_INS_EXPAND
4274 		    && action != ACTION_EXPAND
4275 #endif
4276 						)
4277     {
4278 #ifdef FEAT_INS_EXPAND
4279 	if (got_int || completion_interrupted)
4280 #else
4281 	if (got_int)
4282 #endif
4283 	    EMSG(_(e_interr));
4284 	else if (type == FIND_DEFINE)
4285 	    EMSG(_("E388: Couldn't find definition"));
4286 	else
4287 	    EMSG(_("E389: Couldn't find pattern"));
4288     }
4289     if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
4290 	msg_end();
4291 
4292 fpip_end:
4293     vim_free(file_line);
4294     vim_free(regmatch.regprog);
4295     vim_free(incl_regmatch.regprog);
4296     vim_free(def_regmatch.regprog);
4297 
4298 #ifdef RISCOS
4299    /* Restore previous file munging state. */
4300     __riscosify_control = previous_munging;
4301 #endif
4302 }
4303 
4304     static void
4305 show_pat_in_path(line, type, did_show, action, fp, lnum, count)
4306     char_u  *line;
4307     int	    type;
4308     int	    did_show;
4309     int	    action;
4310     FILE    *fp;
4311     linenr_T *lnum;
4312     long    count;
4313 {
4314     char_u  *p;
4315 
4316     if (did_show)
4317 	msg_putchar('\n');	/* cursor below last one */
4318     else
4319 	gotocmdline(TRUE);	/* cursor at status line */
4320     if (got_int)		/* 'q' typed at "--more--" message */
4321 	return;
4322     for (;;)
4323     {
4324 	p = line + STRLEN(line) - 1;
4325 	if (fp != NULL)
4326 	{
4327 	    /* We used fgets(), so get rid of newline at end */
4328 	    if (p >= line && *p == '\n')
4329 		--p;
4330 	    if (p >= line && *p == '\r')
4331 		--p;
4332 	    *(p + 1) = NUL;
4333 	}
4334 	if (action == ACTION_SHOW_ALL)
4335 	{
4336 	    sprintf((char *)IObuff, "%3ld: ", count);	/* show match nr */
4337 	    msg_puts(IObuff);
4338 	    sprintf((char *)IObuff, "%4ld", *lnum);	/* show line nr */
4339 						/* Highlight line numbers */
4340 	    msg_puts_attr(IObuff, hl_attr(HLF_N));
4341 	    MSG_PUTS(" ");
4342 	}
4343 	msg_prt_line(line);
4344 	out_flush();			/* show one line at a time */
4345 
4346 	/* Definition continues until line that doesn't end with '\' */
4347 	if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
4348 	    break;
4349 
4350 	if (fp != NULL)
4351 	{
4352 	    if (vim_fgets(line, LSIZE, fp)) /* end of file */
4353 		break;
4354 	    ++*lnum;
4355 	}
4356 	else
4357 	{
4358 	    if (++*lnum > curbuf->b_ml.ml_line_count)
4359 		break;
4360 	    line = ml_get(*lnum);
4361 	}
4362 	msg_putchar('\n');
4363     }
4364 }
4365 #endif
4366 
4367 #ifdef FEAT_VIMINFO
4368     int
4369 read_viminfo_search_pattern(virp, force)
4370     vir_T	*virp;
4371     int		force;
4372 {
4373     char_u	*lp;
4374     int		idx = -1;
4375     int		magic = FALSE;
4376     int		no_scs = FALSE;
4377     int		off_line = FALSE;
4378     int		off_end = FALSE;
4379     long	off = 0;
4380     int		setlast = FALSE;
4381 #ifdef FEAT_SEARCH_EXTRA
4382     static int	hlsearch_on = FALSE;
4383 #endif
4384     char_u	*val;
4385 
4386     /*
4387      * Old line types:
4388      * "/pat", "&pat": search/subst. pat
4389      * "~/pat", "~&pat": last used search/subst. pat
4390      * New line types:
4391      * "~h", "~H": hlsearch highlighting off/on
4392      * "~<magic><smartcase><line><end><off><last><which>pat"
4393      * <magic>: 'm' off, 'M' on
4394      * <smartcase>: 's' off, 'S' on
4395      * <line>: 'L' line offset, 'l' char offset
4396      * <end>: 'E' from end, 'e' from start
4397      * <off>: decimal, offset
4398      * <last>: '~' last used pattern
4399      * <which>: '/' search pat, '&' subst. pat
4400      */
4401     lp = virp->vir_line;
4402     if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M'))	/* new line type */
4403     {
4404 	if (lp[1] == 'M')		/* magic on */
4405 	    magic = TRUE;
4406 	if (lp[2] == 's')
4407 	    no_scs = TRUE;
4408 	if (lp[3] == 'L')
4409 	    off_line = TRUE;
4410 	if (lp[4] == 'E')
4411 	    off_end = SEARCH_END;
4412 	lp += 5;
4413 	off = getdigits(&lp);
4414     }
4415     if (lp[0] == '~')		/* use this pattern for last-used pattern */
4416     {
4417 	setlast = TRUE;
4418 	lp++;
4419     }
4420     if (lp[0] == '/')
4421 	idx = RE_SEARCH;
4422     else if (lp[0] == '&')
4423 	idx = RE_SUBST;
4424 #ifdef FEAT_SEARCH_EXTRA
4425     else if (lp[0] == 'h')	/* ~h: 'hlsearch' highlighting off */
4426 	hlsearch_on = FALSE;
4427     else if (lp[0] == 'H')	/* ~H: 'hlsearch' highlighting on */
4428 	hlsearch_on = TRUE;
4429 #endif
4430     if (idx >= 0)
4431     {
4432 	if (force || spats[idx].pat == NULL)
4433 	{
4434 	    val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1),
4435 									TRUE);
4436 	    if (val != NULL)
4437 	    {
4438 		set_last_search_pat(val, idx, magic, setlast);
4439 		vim_free(val);
4440 		spats[idx].no_scs = no_scs;
4441 		spats[idx].off.line = off_line;
4442 		spats[idx].off.end = off_end;
4443 		spats[idx].off.off = off;
4444 #ifdef FEAT_SEARCH_EXTRA
4445 		if (setlast)
4446 		    no_hlsearch = !hlsearch_on;
4447 #endif
4448 	    }
4449 	}
4450     }
4451     return viminfo_readline(virp);
4452 }
4453 
4454     void
4455 write_viminfo_search_pattern(fp)
4456     FILE	*fp;
4457 {
4458     if (get_viminfo_parameter('/') != 0)
4459     {
4460 #ifdef FEAT_SEARCH_EXTRA
4461 	fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c",
4462 	    (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H');
4463 #endif
4464 	wvsp_one(fp, RE_SEARCH, "", '/');
4465 	wvsp_one(fp, RE_SUBST, "Substitute ", '&');
4466     }
4467 }
4468 
4469     static void
4470 wvsp_one(fp, idx, s, sc)
4471     FILE	*fp;	/* file to write to */
4472     int		idx;	/* spats[] index */
4473     char	*s;	/* search pat */
4474     int		sc;	/* dir char */
4475 {
4476     if (spats[idx].pat != NULL)
4477     {
4478 	fprintf(fp, "\n# Last %sSearch Pattern:\n~", s);
4479 	/* off.dir is not stored, it's reset to forward */
4480 	fprintf(fp, "%c%c%c%c%ld%s%c",
4481 		spats[idx].magic    ? 'M' : 'm',	/* magic */
4482 		spats[idx].no_scs   ? 's' : 'S',	/* smartcase */
4483 		spats[idx].off.line ? 'L' : 'l',	/* line offset */
4484 		spats[idx].off.end  ? 'E' : 'e',	/* offset from end */
4485 		spats[idx].off.off,			/* offset */
4486 		last_idx == idx	    ? "~" : "",		/* last used pat */
4487 		sc);
4488 	viminfo_writestring(fp, spats[idx].pat);
4489     }
4490 }
4491 #endif /* FEAT_VIMINFO */
4492