xref: /vim-8.2.3635/src/search.c (revision ed20346f)
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     {
3057 	/*
3058 	 * If we don't include white space at the end, move the start
3059 	 * to include some white space there. This makes "daw" work
3060 	 * better on the last word in a sentence (and "2daw" on last-but-one
3061 	 * word).  But don't delete white space at start of line (indent).
3062 	 */
3063 	pos = curwin->w_cursor;	/* save cursor position */
3064 	curwin->w_cursor = start_pos;
3065 	if (oneleft() == OK)
3066 	{
3067 	    back_in_line();
3068 	    if (cls() == 0 && curwin->w_cursor.col > 0)
3069 	    {
3070 #ifdef FEAT_VISUAL
3071 		if (VIsual_active)
3072 		    VIsual = curwin->w_cursor;
3073 		else
3074 #endif
3075 		    oap->start = curwin->w_cursor;
3076 	    }
3077 	}
3078 	curwin->w_cursor = pos;	/* put cursor back at end */
3079     }
3080 
3081 #ifdef FEAT_VISUAL
3082     if (VIsual_active)
3083     {
3084 	if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor))
3085 	    inc_cursor();
3086 	if (VIsual_mode == 'V')
3087 	{
3088 	    VIsual_mode = 'v';
3089 	    redraw_cmdline = TRUE;		/* show mode later */
3090 	}
3091     }
3092     else
3093 #endif
3094 	oap->inclusive = inclusive;
3095 
3096     return OK;
3097 }
3098 
3099 /*
3100  * Find sentence(s) under the cursor, cursor at end.
3101  * When Visual active, extend it by one or more sentences.
3102  */
3103     int
3104 current_sent(oap, count, include)
3105     oparg_T	*oap;
3106     long	count;
3107     int		include;
3108 {
3109     pos_T	start_pos;
3110     pos_T	pos;
3111     int		start_blank;
3112     int		c;
3113     int		at_start_sent;
3114     long	ncount;
3115 
3116     start_pos = curwin->w_cursor;
3117     pos = start_pos;
3118     findsent(FORWARD, 1L);	/* Find start of next sentence. */
3119 
3120 #ifdef FEAT_VISUAL
3121     /*
3122      * When visual area is bigger than one character: Extend it.
3123      */
3124     if (VIsual_active && !equalpos(start_pos, VIsual))
3125     {
3126 extend:
3127 	if (lt(start_pos, VIsual))
3128 	{
3129 	    /*
3130 	     * Cursor at start of Visual area.
3131 	     * Find out where we are:
3132 	     * - in the white space before a sentence
3133 	     * - in a sentence or just after it
3134 	     * - at the start of a sentence
3135 	     */
3136 	    at_start_sent = TRUE;
3137 	    decl(&pos);
3138 	    while (lt(pos, curwin->w_cursor))
3139 	    {
3140 		c = gchar_pos(&pos);
3141 		if (!vim_iswhite(c))
3142 		{
3143 		    at_start_sent = FALSE;
3144 		    break;
3145 		}
3146 		incl(&pos);
3147 	    }
3148 	    if (!at_start_sent)
3149 	    {
3150 		findsent(BACKWARD, 1L);
3151 		if (equalpos(curwin->w_cursor, start_pos))
3152 		    at_start_sent = TRUE;  /* exactly at start of sentence */
3153 		else
3154 		    /* inside a sentence, go to its end (start of next) */
3155 		    findsent(FORWARD, 1L);
3156 	    }
3157 	    if (include)	/* "as" gets twice as much as "is" */
3158 		count *= 2;
3159 	    while (count--)
3160 	    {
3161 		if (at_start_sent)
3162 		    find_first_blank(&curwin->w_cursor);
3163 		c = gchar_cursor();
3164 		if (!at_start_sent || (!include && !vim_iswhite(c)))
3165 		    findsent(BACKWARD, 1L);
3166 		at_start_sent = !at_start_sent;
3167 	    }
3168 	}
3169 	else
3170 	{
3171 	    /*
3172 	     * Cursor at end of Visual area.
3173 	     * Find out where we are:
3174 	     * - just before a sentence
3175 	     * - just before or in the white space before a sentence
3176 	     * - in a sentence
3177 	     */
3178 	    incl(&pos);
3179 	    at_start_sent = TRUE;
3180 	    if (!equalpos(pos, curwin->w_cursor)) /* not just before a sentence */
3181 	    {
3182 		at_start_sent = FALSE;
3183 		while (lt(pos, curwin->w_cursor))
3184 		{
3185 		    c = gchar_pos(&pos);
3186 		    if (!vim_iswhite(c))
3187 		    {
3188 			at_start_sent = TRUE;
3189 			break;
3190 		    }
3191 		    incl(&pos);
3192 		}
3193 		if (at_start_sent)	/* in the sentence */
3194 		    findsent(BACKWARD, 1L);
3195 		else		/* in/before white before a sentence */
3196 		    curwin->w_cursor = start_pos;
3197 	    }
3198 
3199 	    if (include)	/* "as" gets twice as much as "is" */
3200 		count *= 2;
3201 	    findsent_forward(count, at_start_sent);
3202 	    if (*p_sel == 'e')
3203 		++curwin->w_cursor.col;
3204 	}
3205 	return OK;
3206     }
3207 #endif
3208 
3209     /*
3210      * If cursor started on blank, check if it is just before the start of the
3211      * next sentence.
3212      */
3213     while (c = gchar_pos(&pos), vim_iswhite(c))	/* vim_iswhite() is a macro */
3214 	incl(&pos);
3215     if (equalpos(pos, curwin->w_cursor))
3216     {
3217 	start_blank = TRUE;
3218 	find_first_blank(&start_pos);	/* go back to first blank */
3219     }
3220     else
3221     {
3222 	start_blank = FALSE;
3223 	findsent(BACKWARD, 1L);
3224 	start_pos = curwin->w_cursor;
3225     }
3226     if (include)
3227 	ncount = count * 2;
3228     else
3229     {
3230 	ncount = count;
3231 	if (start_blank)
3232 	    --ncount;
3233     }
3234     if (ncount)
3235 	findsent_forward(ncount, TRUE);
3236     else
3237 	decl(&curwin->w_cursor);
3238 
3239     if (include)
3240     {
3241 	/*
3242 	 * If the blank in front of the sentence is included, exclude the
3243 	 * blanks at the end of the sentence, go back to the first blank.
3244 	 * If there are no trailing blanks, try to include leading blanks.
3245 	 */
3246 	if (start_blank)
3247 	{
3248 	    find_first_blank(&curwin->w_cursor);
3249 	    c = gchar_pos(&curwin->w_cursor);	/* vim_iswhite() is a macro */
3250 	    if (vim_iswhite(c))
3251 		decl(&curwin->w_cursor);
3252 	}
3253 	else if (c = gchar_cursor(), !vim_iswhite(c))
3254 	    find_first_blank(&start_pos);
3255     }
3256 
3257 #ifdef FEAT_VISUAL
3258     if (VIsual_active)
3259     {
3260 	/* avoid getting stuck with "is" on a single space before a sent. */
3261 	if (equalpos(start_pos, curwin->w_cursor))
3262 	    goto extend;
3263 	if (*p_sel == 'e')
3264 	    ++curwin->w_cursor.col;
3265 	VIsual = start_pos;
3266 	VIsual_mode = 'v';
3267 	redraw_curbuf_later(INVERTED);	/* update the inversion */
3268     }
3269     else
3270 #endif
3271     {
3272 	/* include a newline after the sentence, if there is one */
3273 	if (incl(&curwin->w_cursor) == -1)
3274 	    oap->inclusive = TRUE;
3275 	else
3276 	    oap->inclusive = FALSE;
3277 	oap->start = start_pos;
3278 	oap->motion_type = MCHAR;
3279     }
3280     return OK;
3281 }
3282 
3283     int
3284 current_block(oap, count, include, what, other)
3285     oparg_T	*oap;
3286     long	count;
3287     int		include;	/* TRUE == include white space */
3288     int		what;		/* '(', '{', etc. */
3289     int		other;		/* ')', '}', etc. */
3290 {
3291     pos_T	old_pos;
3292     pos_T	*pos = NULL;
3293     pos_T	start_pos;
3294     pos_T	*end_pos;
3295     pos_T	old_start, old_end;
3296     char_u	*save_cpo;
3297     int		sol = FALSE;	/* { at start of line */
3298 
3299     old_pos = curwin->w_cursor;
3300     old_end = curwin->w_cursor;		    /* remember where we started */
3301     old_start = old_end;
3302 
3303     /*
3304      * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
3305      */
3306 #ifdef FEAT_VISUAL
3307     if (!VIsual_active || equalpos(VIsual, curwin->w_cursor))
3308 #endif
3309     {
3310 	setpcmark();
3311 	if (what == '{')		    /* ignore indent */
3312 	    while (inindent(1))
3313 		if (inc_cursor() != 0)
3314 		    break;
3315 	if (gchar_cursor() == what)	    /* cursor on '(' or '{' */
3316 	    ++curwin->w_cursor.col;
3317     }
3318 #ifdef FEAT_VISUAL
3319     else if (lt(VIsual, curwin->w_cursor))
3320     {
3321 	old_start = VIsual;
3322 	curwin->w_cursor = VIsual;	    /* cursor at low end of Visual */
3323     }
3324     else
3325 	old_end = VIsual;
3326 #endif
3327 
3328     /*
3329      * Search backwards for unclosed '(', '{', etc..
3330      * Put this position in start_pos.
3331      * Ignory quotes here.
3332      */
3333     save_cpo = p_cpo;
3334     p_cpo = (char_u *)"%";
3335     while (count-- > 0)
3336     {
3337 	if ((pos = findmatch(NULL, what)) == NULL)
3338 	    break;
3339 	curwin->w_cursor = *pos;
3340 	start_pos = *pos;   /* the findmatch for end_pos will overwrite *pos */
3341     }
3342     p_cpo = save_cpo;
3343 
3344     /*
3345      * Search for matching ')', '}', etc.
3346      * Put this position in curwin->w_cursor.
3347      */
3348     if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
3349     {
3350 	curwin->w_cursor = old_pos;
3351 	return FAIL;
3352     }
3353     curwin->w_cursor = *end_pos;
3354 
3355     /*
3356      * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
3357      * If the ending '}' is only preceded by indent, skip that indent.
3358      * But only if the resulting area is not smaller than what we started with.
3359      */
3360     while (!include)
3361     {
3362 	incl(&start_pos);
3363 	sol = (curwin->w_cursor.col == 0);
3364 	decl(&curwin->w_cursor);
3365 	if (what == '{')
3366 	    while (inindent(1))
3367 	    {
3368 		sol = TRUE;
3369 		if (decl(&curwin->w_cursor) != 0)
3370 		    break;
3371 	    }
3372 #ifdef FEAT_VISUAL
3373 	/*
3374 	 * In Visual mode, when the resulting area is not bigger than what we
3375 	 * started with, extend it to the next block, and then exclude again.
3376 	 */
3377 	if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
3378 		&& VIsual_active)
3379 	{
3380 	    curwin->w_cursor = old_start;
3381 	    decl(&curwin->w_cursor);
3382 	    if ((pos = findmatch(NULL, what)) == NULL)
3383 	    {
3384 		curwin->w_cursor = old_pos;
3385 		return FAIL;
3386 	    }
3387 	    start_pos = *pos;
3388 	    curwin->w_cursor = *pos;
3389 	    if ((end_pos = findmatch(NULL, other)) == NULL)
3390 	    {
3391 		curwin->w_cursor = old_pos;
3392 		return FAIL;
3393 	    }
3394 	    curwin->w_cursor = *end_pos;
3395 	}
3396 	else
3397 #endif
3398 	    break;
3399     }
3400 
3401 #ifdef FEAT_VISUAL
3402     if (VIsual_active)
3403     {
3404 	if (*p_sel == 'e')
3405 	    ++curwin->w_cursor.col;
3406 	if (sol)
3407 	    inc(&curwin->w_cursor);	/* include the line break */
3408 	VIsual = start_pos;
3409 	VIsual_mode = 'v';
3410 	redraw_curbuf_later(INVERTED);	/* update the inversion */
3411 	showmode();
3412     }
3413     else
3414 #endif
3415     {
3416 	oap->start = start_pos;
3417 	oap->motion_type = MCHAR;
3418 	if (sol)
3419 	{
3420 	    incl(&curwin->w_cursor);
3421 	    oap->inclusive = FALSE;
3422 	}
3423 	else
3424 	    oap->inclusive = TRUE;
3425     }
3426 
3427     return OK;
3428 }
3429 
3430     int
3431 current_par(oap, count, include, type)
3432     oparg_T	*oap;
3433     long	count;
3434     int		include;	/* TRUE == include white space */
3435     int		type;		/* 'p' for paragraph, 'S' for section */
3436 {
3437     linenr_T	start_lnum;
3438     linenr_T	end_lnum;
3439     int		white_in_front;
3440     int		dir;
3441     int		start_is_white;
3442     int		prev_start_is_white;
3443     int		retval = OK;
3444     int		do_white = FALSE;
3445     int		t;
3446     int		i;
3447 
3448     if (type == 'S')	    /* not implemented yet */
3449 	return FAIL;
3450 
3451     start_lnum = curwin->w_cursor.lnum;
3452 
3453 #ifdef FEAT_VISUAL
3454     /*
3455      * When visual area is more than one line: extend it.
3456      */
3457     if (VIsual_active && start_lnum != VIsual.lnum)
3458     {
3459 extend:
3460 	if (start_lnum < VIsual.lnum)
3461 	    dir = BACKWARD;
3462 	else
3463 	    dir = FORWARD;
3464 	for (i = count; --i >= 0; )
3465 	{
3466 	    if (start_lnum ==
3467 			   (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
3468 	    {
3469 		retval = FAIL;
3470 		break;
3471 	    }
3472 
3473 	    prev_start_is_white = -1;
3474 	    for (t = 0; t < 2; ++t)
3475 	    {
3476 		start_lnum += dir;
3477 		start_is_white = linewhite(start_lnum);
3478 		if (prev_start_is_white == start_is_white)
3479 		{
3480 		    start_lnum -= dir;
3481 		    break;
3482 		}
3483 		for (;;)
3484 		{
3485 		    if (start_lnum == (dir == BACKWARD
3486 					    ? 1 : curbuf->b_ml.ml_line_count))
3487 			break;
3488 		    if (start_is_white != linewhite(start_lnum + dir)
3489 			    || (!start_is_white
3490 				    && startPS(start_lnum + (dir > 0
3491 							     ? 1 : 0), 0, 0)))
3492 			break;
3493 		    start_lnum += dir;
3494 		}
3495 		if (!include)
3496 		    break;
3497 		if (start_lnum == (dir == BACKWARD
3498 					    ? 1 : curbuf->b_ml.ml_line_count))
3499 		    break;
3500 		prev_start_is_white = start_is_white;
3501 	    }
3502 	}
3503 	curwin->w_cursor.lnum = start_lnum;
3504 	curwin->w_cursor.col = 0;
3505 	return retval;
3506     }
3507 #endif
3508 
3509     /*
3510      * First move back to the start_lnum of the paragraph or white lines
3511      */
3512     white_in_front = linewhite(start_lnum);
3513     while (start_lnum > 1)
3514     {
3515 	if (white_in_front)	    /* stop at first white line */
3516 	{
3517 	    if (!linewhite(start_lnum - 1))
3518 		break;
3519 	}
3520 	else		/* stop at first non-white line of start of paragraph */
3521 	{
3522 	    if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
3523 		break;
3524 	}
3525 	--start_lnum;
3526     }
3527 
3528     /*
3529      * Move past the end of any white lines.
3530      */
3531     end_lnum = start_lnum;
3532     while (linewhite(end_lnum) && end_lnum < curbuf->b_ml.ml_line_count)
3533 	    ++end_lnum;
3534 
3535     --end_lnum;
3536     i = count;
3537     if (!include && white_in_front)
3538 	--i;
3539     while (i--)
3540     {
3541 	if (end_lnum == curbuf->b_ml.ml_line_count)
3542 	    return FAIL;
3543 
3544 	if (!include)
3545 	    do_white = linewhite(end_lnum + 1);
3546 
3547 	if (include || !do_white)
3548 	{
3549 	    ++end_lnum;
3550 	    /*
3551 	     * skip to end of paragraph
3552 	     */
3553 	    while (end_lnum < curbuf->b_ml.ml_line_count
3554 		    && !linewhite(end_lnum + 1)
3555 		    && !startPS(end_lnum + 1, 0, 0))
3556 		++end_lnum;
3557 	}
3558 
3559 	if (i == 0 && white_in_front && include)
3560 	    break;
3561 
3562 	/*
3563 	 * skip to end of white lines after paragraph
3564 	 */
3565 	if (include || do_white)
3566 	    while (end_lnum < curbuf->b_ml.ml_line_count
3567 						   && linewhite(end_lnum + 1))
3568 		++end_lnum;
3569     }
3570 
3571     /*
3572      * If there are no empty lines at the end, try to find some empty lines at
3573      * the start (unless that has been done already).
3574      */
3575     if (!white_in_front && !linewhite(end_lnum) && include)
3576 	while (start_lnum > 1 && linewhite(start_lnum - 1))
3577 	    --start_lnum;
3578 
3579 #ifdef FEAT_VISUAL
3580     if (VIsual_active)
3581     {
3582 	/* Problem: when doing "Vipipip" nothing happens in a single white
3583 	 * line, we get stuck there.  Trap this here. */
3584 	if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
3585 	    goto extend;
3586 	VIsual.lnum = start_lnum;
3587 	VIsual_mode = 'V';
3588 	redraw_curbuf_later(INVERTED);	/* update the inversion */
3589 	showmode();
3590     }
3591     else
3592 #endif
3593     {
3594 	oap->start.lnum = start_lnum;
3595 	oap->start.col = 0;
3596 	oap->motion_type = MLINE;
3597     }
3598     curwin->w_cursor.lnum = end_lnum;
3599     curwin->w_cursor.col = 0;
3600 
3601     return OK;
3602 }
3603 #endif
3604 
3605 #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \
3606 	|| defined(PROTO)
3607 /*
3608  * return TRUE if line 'lnum' is empty or has white chars only.
3609  */
3610     int
3611 linewhite(lnum)
3612     linenr_T	lnum;
3613 {
3614     char_u  *p;
3615 
3616     p = skipwhite(ml_get(lnum));
3617     return (*p == NUL);
3618 }
3619 #endif
3620 
3621 #if defined(FEAT_FIND_ID) || defined(PROTO)
3622 /*
3623  * Find identifiers or defines in included files.
3624  * if p_ic && (continue_status & CONT_SOL) then ptr must be in lowercase.
3625  */
3626 /*ARGSUSED*/
3627     void
3628 find_pattern_in_path(ptr, dir, len, whole, skip_comments,
3629 				    type, count, action, start_lnum, end_lnum)
3630     char_u	*ptr;		/* pointer to search pattern */
3631     int		dir;		/* direction of expansion */
3632     int		len;		/* length of search pattern */
3633     int		whole;		/* match whole words only */
3634     int		skip_comments;	/* don't match inside comments */
3635     int		type;		/* Type of search; are we looking for a type?
3636 				   a macro? */
3637     long	count;
3638     int		action;		/* What to do when we find it */
3639     linenr_T	start_lnum;	/* first line to start searching */
3640     linenr_T	end_lnum;	/* last line for searching */
3641 {
3642     SearchedFile *files;		/* Stack of included files */
3643     SearchedFile *bigger;		/* When we need more space */
3644     int		max_path_depth = 50;
3645     long	match_count = 1;
3646 
3647     char_u	*pat;
3648     char_u	*new_fname;
3649     char_u	*curr_fname = curbuf->b_fname;
3650     char_u	*prev_fname = NULL;
3651     linenr_T	lnum;
3652     int		depth;
3653     int		depth_displayed;	/* For type==CHECK_PATH */
3654     int		old_files;
3655     int		already_searched;
3656     char_u	*file_line;
3657     char_u	*line;
3658     char_u	*p;
3659     char_u	save_char;
3660     int		define_matched;
3661     regmatch_T	regmatch;
3662     regmatch_T	incl_regmatch;
3663     regmatch_T	def_regmatch;
3664     int		matched = FALSE;
3665     int		did_show = FALSE;
3666     int		found = FALSE;
3667     int		i;
3668     char_u	*already = NULL;
3669     char_u	*startp = NULL;
3670 #ifdef RISCOS
3671     int		previous_munging = __riscosify_control;
3672 #endif
3673 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
3674     win_T	*curwin_save = NULL;
3675 #endif
3676 
3677     regmatch.regprog = NULL;
3678     incl_regmatch.regprog = NULL;
3679     def_regmatch.regprog = NULL;
3680 
3681     file_line = alloc(LSIZE);
3682     if (file_line == NULL)
3683 	return;
3684 
3685 #ifdef RISCOS
3686     /* UnixLib knows best how to munge c file names - turn munging back on. */
3687     __riscosify_control = __RISCOSIFY_LONG_TRUNCATE;
3688 #endif
3689 
3690     if (type != CHECK_PATH && type != FIND_DEFINE
3691 #ifdef FEAT_INS_EXPAND
3692 	/* when CONT_SOL is set compare "ptr" with the beginning of the line
3693 	 * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */
3694 	    && !(continue_status & CONT_SOL)
3695 #endif
3696        )
3697     {
3698 	pat = alloc(len + 5);
3699 	if (pat == NULL)
3700 	    goto fpip_end;
3701 	sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
3702 	/* ignore case according to p_ic, p_scs and pat */
3703 	regmatch.rm_ic = ignorecase(pat);
3704 	regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
3705 	vim_free(pat);
3706 	if (regmatch.regprog == NULL)
3707 	    goto fpip_end;
3708     }
3709     if (*curbuf->b_p_inc != NUL || *p_inc != NUL)
3710     {
3711 	incl_regmatch.regprog = vim_regcomp(*curbuf->b_p_inc == NUL
3712 			   ? p_inc : curbuf->b_p_inc, p_magic ? RE_MAGIC : 0);
3713 	if (incl_regmatch.regprog == NULL)
3714 	    goto fpip_end;
3715 	incl_regmatch.rm_ic = FALSE;	/* don't ignore case in incl. pat. */
3716     }
3717     if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL))
3718     {
3719 	def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
3720 			   ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0);
3721 	if (def_regmatch.regprog == NULL)
3722 	    goto fpip_end;
3723 	def_regmatch.rm_ic = FALSE;	/* don't ignore case in define pat. */
3724     }
3725     files = (SearchedFile *)lalloc_clear((long_u)
3726 			       (max_path_depth * sizeof(SearchedFile)), TRUE);
3727     if (files == NULL)
3728 	goto fpip_end;
3729     old_files = max_path_depth;
3730     depth = depth_displayed = -1;
3731 
3732     lnum = start_lnum;
3733     if (end_lnum > curbuf->b_ml.ml_line_count)
3734 	end_lnum = curbuf->b_ml.ml_line_count;
3735     if (lnum > end_lnum)		/* do at least one line */
3736 	lnum = end_lnum;
3737     line = ml_get(lnum);
3738 
3739     for (;;)
3740     {
3741 	if (incl_regmatch.regprog != NULL
3742 		&& vim_regexec(&incl_regmatch, line, (colnr_T)0))
3743 	{
3744 	    new_fname = file_name_in_line(incl_regmatch.endp[0],
3745 		    0, FNAME_EXP|FNAME_INCL|FNAME_REL, 1L,
3746 		    curr_fname == curbuf->b_fname
3747 					     ? curbuf->b_ffname : curr_fname);
3748 	    already_searched = FALSE;
3749 	    if (new_fname != NULL)
3750 	    {
3751 		/* Check whether we have already searched in this file */
3752 		for (i = 0;; i++)
3753 		{
3754 		    if (i == depth + 1)
3755 			i = old_files;
3756 		    if (i == max_path_depth)
3757 			break;
3758 		    if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME)
3759 		    {
3760 			if (type != CHECK_PATH &&
3761 				action == ACTION_SHOW_ALL && files[i].matched)
3762 			{
3763 			    msg_putchar('\n');	    /* cursor below last one */
3764 			    if (!got_int)	    /* don't display if 'q'
3765 						       typed at "--more--"
3766 						       mesage */
3767 			    {
3768 				msg_home_replace_hl(new_fname);
3769 				MSG_PUTS(_(" (includes previously listed match)"));
3770 				prev_fname = NULL;
3771 			    }
3772 			}
3773 			vim_free(new_fname);
3774 			new_fname = NULL;
3775 			already_searched = TRUE;
3776 			break;
3777 		    }
3778 		}
3779 	    }
3780 
3781 	    if (type == CHECK_PATH && (action == ACTION_SHOW_ALL
3782 				 || (new_fname == NULL && !already_searched)))
3783 	    {
3784 		if (did_show)
3785 		    msg_putchar('\n');	    /* cursor below last one */
3786 		else
3787 		{
3788 		    gotocmdline(TRUE);	    /* cursor at status line */
3789 		    MSG_PUTS_TITLE(_("--- Included files "));
3790 		    if (action != ACTION_SHOW_ALL)
3791 			MSG_PUTS_TITLE(_("not found "));
3792 		    MSG_PUTS_TITLE(_("in path ---\n"));
3793 		}
3794 		did_show = TRUE;
3795 		while (depth_displayed < depth && !got_int)
3796 		{
3797 		    ++depth_displayed;
3798 		    for (i = 0; i < depth_displayed; i++)
3799 			MSG_PUTS("  ");
3800 		    msg_home_replace(files[depth_displayed].name);
3801 		    MSG_PUTS(" -->\n");
3802 		}
3803 		if (!got_int)		    /* don't display if 'q' typed
3804 					       for "--more--" message */
3805 		{
3806 		    for (i = 0; i <= depth_displayed; i++)
3807 			MSG_PUTS("  ");
3808 		    if (new_fname != NULL)
3809 		    {
3810 			/* using "new_fname" is more reliable, e.g., when
3811 			 * 'includeexpr' is set. */
3812 			msg_outtrans_attr(new_fname, hl_attr(HLF_D));
3813 		    }
3814 		    else
3815 		    {
3816 			/*
3817 			 * Isolate the file name.
3818 			 * Include the surrounding "" or <> if present.
3819 			 */
3820 			for (p = incl_regmatch.endp[0]; !vim_isfilec(*p); p++)
3821 			    ;
3822 			for (i = 0; vim_isfilec(p[i]); i++)
3823 			    ;
3824 			if (i == 0)
3825 			{
3826 			    /* Nothing found, use the rest of the line. */
3827 			    p = incl_regmatch.endp[0];
3828 			    i = STRLEN(p);
3829 			}
3830 			else
3831 			{
3832 			    if (p[-1] == '"' || p[-1] == '<')
3833 			    {
3834 				--p;
3835 				++i;
3836 			    }
3837 			    if (p[i] == '"' || p[i] == '>')
3838 				++i;
3839 			}
3840 			save_char = p[i];
3841 			p[i] = NUL;
3842 			msg_outtrans_attr(p, hl_attr(HLF_D));
3843 			p[i] = save_char;
3844 		    }
3845 
3846 		    if (new_fname == NULL && action == ACTION_SHOW_ALL)
3847 		    {
3848 			if (already_searched)
3849 			    MSG_PUTS(_("  (Already listed)"));
3850 			else
3851 			    MSG_PUTS(_("  NOT FOUND"));
3852 		    }
3853 		}
3854 		out_flush();	    /* output each line directly */
3855 	    }
3856 
3857 	    if (new_fname != NULL)
3858 	    {
3859 		/* Push the new file onto the file stack */
3860 		if (depth + 1 == old_files)
3861 		{
3862 		    bigger = (SearchedFile *)lalloc((long_u)(
3863 			    max_path_depth * 2 * sizeof(SearchedFile)), TRUE);
3864 		    if (bigger != NULL)
3865 		    {
3866 			for (i = 0; i <= depth; i++)
3867 			    bigger[i] = files[i];
3868 			for (i = depth + 1; i < old_files + max_path_depth; i++)
3869 			{
3870 			    bigger[i].fp = NULL;
3871 			    bigger[i].name = NULL;
3872 			    bigger[i].lnum = 0;
3873 			    bigger[i].matched = FALSE;
3874 			}
3875 			for (i = old_files; i < max_path_depth; i++)
3876 			    bigger[i + max_path_depth] = files[i];
3877 			old_files += max_path_depth;
3878 			max_path_depth *= 2;
3879 			vim_free(files);
3880 			files = bigger;
3881 		    }
3882 		}
3883 		if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r"))
3884 								    == NULL)
3885 		    vim_free(new_fname);
3886 		else
3887 		{
3888 		    if (++depth == old_files)
3889 		    {
3890 			/*
3891 			 * lalloc() for 'bigger' must have failed above.  We
3892 			 * will forget one of our already visited files now.
3893 			 */
3894 			vim_free(files[old_files].name);
3895 			++old_files;
3896 		    }
3897 		    files[depth].name = curr_fname = new_fname;
3898 		    files[depth].lnum = 0;
3899 		    files[depth].matched = FALSE;
3900 #ifdef FEAT_INS_EXPAND
3901 		    if (action == ACTION_EXPAND)
3902 		    {
3903 			sprintf((char*)IObuff, _("Scanning included file: %s"),
3904 			    (char *)new_fname);
3905 			msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R));
3906 		    }
3907 #endif
3908 		}
3909 	    }
3910 	}
3911 	else
3912 	{
3913 	    /*
3914 	     * Check if the line is a define (type == FIND_DEFINE)
3915 	     */
3916 	    p = line;
3917 search_line:
3918 	    define_matched = FALSE;
3919 	    if (def_regmatch.regprog != NULL
3920 			      && vim_regexec(&def_regmatch, line, (colnr_T)0))
3921 	    {
3922 		/*
3923 		 * Pattern must be first identifier after 'define', so skip
3924 		 * to that position before checking for match of pattern.  Also
3925 		 * don't let it match beyond the end of this identifier.
3926 		 */
3927 		p = def_regmatch.endp[0];
3928 		while (*p && !vim_iswordc(*p))
3929 		    p++;
3930 		define_matched = TRUE;
3931 	    }
3932 
3933 	    /*
3934 	     * Look for a match.  Don't do this if we are looking for a
3935 	     * define and this line didn't match define_prog above.
3936 	     */
3937 	    if (def_regmatch.regprog == NULL || define_matched)
3938 	    {
3939 		if (define_matched
3940 #ifdef FEAT_INS_EXPAND
3941 			|| (continue_status & CONT_SOL)
3942 #endif
3943 		    )
3944 		{
3945 		    /* compare the first "len" chars from "ptr" */
3946 		    startp = skipwhite(p);
3947 		    if (p_ic)
3948 			matched = !MB_STRNICMP(startp, ptr, len);
3949 		    else
3950 			matched = !STRNCMP(startp, ptr, len);
3951 		    if (matched && define_matched && whole
3952 						  && vim_iswordc(startp[len]))
3953 			matched = FALSE;
3954 		}
3955 		else if (regmatch.regprog != NULL
3956 			 && vim_regexec(&regmatch, line, (colnr_T)(p - line)))
3957 		{
3958 		    matched = TRUE;
3959 		    startp = regmatch.startp[0];
3960 		    /*
3961 		     * Check if the line is not a comment line (unless we are
3962 		     * looking for a define).  A line starting with "# define"
3963 		     * is not considered to be a comment line.
3964 		     */
3965 		    if (!define_matched && skip_comments)
3966 		    {
3967 #ifdef FEAT_COMMENTS
3968 			if ((*line != '#' ||
3969 				STRNCMP(skipwhite(line + 1), "define", 6) != 0)
3970 				&& get_leader_len(line, NULL, FALSE))
3971 			    matched = FALSE;
3972 
3973 			/*
3974 			 * Also check for a "/ *" or "/ /" before the match.
3975 			 * Skips lines like "int backwards;  / * normal index
3976 			 * * /" when looking for "normal".
3977 			 * Note: Doesn't skip "/ *" in comments.
3978 			 */
3979 			p = skipwhite(line);
3980 			if (matched
3981 				|| (p[0] == '/' && p[1] == '*') || p[0] == '*')
3982 #endif
3983 			    for (p = line; *p && p < startp; ++p)
3984 			    {
3985 				if (matched
3986 					&& p[0] == '/'
3987 					&& (p[1] == '*' || p[1] == '/'))
3988 				{
3989 				    matched = FALSE;
3990 				    /* After "//" all text is comment */
3991 				    if (p[1] == '/')
3992 					break;
3993 				    ++p;
3994 				}
3995 				else if (!matched && p[0] == '*' && p[1] == '/')
3996 				{
3997 				    /* Can find match after "* /". */
3998 				    matched = TRUE;
3999 				    ++p;
4000 				}
4001 			    }
4002 		    }
4003 		}
4004 	    }
4005 	}
4006 	if (matched)
4007 	{
4008 #ifdef FEAT_INS_EXPAND
4009 	    if (action == ACTION_EXPAND)
4010 	    {
4011 		int	reuse = 0;
4012 		int	add_r;
4013 		char_u	*aux;
4014 
4015 		if (depth == -1 && lnum == curwin->w_cursor.lnum)
4016 		    break;
4017 		found = TRUE;
4018 		aux = p = startp;
4019 		if (continue_status & CONT_ADDING)
4020 		{
4021 		    p += completion_length;
4022 		    if (vim_iswordp(p))
4023 			goto exit_matched;
4024 		    p = find_word_start(p);
4025 		}
4026 		p = find_word_end(p);
4027 		i = (int)(p - aux);
4028 
4029 		if ((continue_status & CONT_ADDING) && i == completion_length)
4030 		{
4031 		    /* get the next line */
4032 		    /* IOSIZE > completion_length, so the STRNCPY works */
4033 		    STRNCPY(IObuff, aux, i);
4034 		    if (!(     depth < 0
4035 			    && lnum < end_lnum
4036 			    && (line = ml_get(++lnum)) != NULL)
4037 			&& !(	depth >= 0
4038 			    && !vim_fgets(line = file_line,
4039 						     LSIZE, files[depth].fp)))
4040 			goto exit_matched;
4041 
4042 		    /* we read a line, set "already" to check this "line" later
4043 		     * if depth >= 0 we'll increase files[depth].lnum far
4044 		     * bellow  -- Acevedo */
4045 		    already = aux = p = skipwhite(line);
4046 		    p = find_word_start(p);
4047 		    p = find_word_end(p);
4048 		    if (p > aux)
4049 		    {
4050 			if (*aux != ')' && IObuff[i-1] != TAB)
4051 			{
4052 			    if (IObuff[i-1] != ' ')
4053 				IObuff[i++] = ' ';
4054 			    /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/
4055 			    if (p_js
4056 				&& (IObuff[i-2] == '.'
4057 				    || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
4058 					&& (IObuff[i-2] == '?'
4059 					    || IObuff[i-2] == '!'))))
4060 				IObuff[i++] = ' ';
4061 			}
4062 			/* copy as much as posible of the new word */
4063 			if (p - aux >= IOSIZE - i)
4064 			    p = aux + IOSIZE - i - 1;
4065 			STRNCPY(IObuff + i, aux, p - aux);
4066 			i += (int)(p - aux);
4067 			reuse |= CONT_S_IPOS;
4068 		    }
4069 		    IObuff[i] = NUL;
4070 		    aux = IObuff;
4071 
4072 		    if (i == completion_length)
4073 			goto exit_matched;
4074 		}
4075 
4076 		add_r = ins_compl_add_infercase(aux, i,
4077 			curr_fname == curbuf->b_fname ? NULL : curr_fname,
4078 			dir, reuse);
4079 		if (add_r == OK)
4080 		    /* if dir was BACKWARD then honor it just once */
4081 		    dir = FORWARD;
4082 		else if (add_r == RET_ERROR)
4083 		    break;
4084 	    }
4085 	    else
4086 #endif
4087 		 if (action == ACTION_SHOW_ALL)
4088 	    {
4089 		found = TRUE;
4090 		if (!did_show)
4091 		    gotocmdline(TRUE);		/* cursor at status line */
4092 		if (curr_fname != prev_fname)
4093 		{
4094 		    if (did_show)
4095 			msg_putchar('\n');	/* cursor below last one */
4096 		    if (!got_int)		/* don't display if 'q' typed
4097 						    at "--more--" mesage */
4098 			msg_home_replace_hl(curr_fname);
4099 		    prev_fname = curr_fname;
4100 		}
4101 		did_show = TRUE;
4102 		if (!got_int)
4103 		    show_pat_in_path(line, type, TRUE, action,
4104 			    (depth == -1) ? NULL : files[depth].fp,
4105 			    (depth == -1) ? &lnum : &files[depth].lnum,
4106 			    match_count++);
4107 
4108 		/* Set matched flag for this file and all the ones that
4109 		 * include it */
4110 		for (i = 0; i <= depth; ++i)
4111 		    files[i].matched = TRUE;
4112 	    }
4113 	    else if (--count <= 0)
4114 	    {
4115 		found = TRUE;
4116 		if (depth == -1 && lnum == curwin->w_cursor.lnum
4117 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4118 						      && g_do_tagpreview == 0
4119 #endif
4120 						      )
4121 		    EMSG(_("E387: Match is on current line"));
4122 		else if (action == ACTION_SHOW)
4123 		{
4124 		    show_pat_in_path(line, type, did_show, action,
4125 			(depth == -1) ? NULL : files[depth].fp,
4126 			(depth == -1) ? &lnum : &files[depth].lnum, 1L);
4127 		    did_show = TRUE;
4128 		}
4129 		else
4130 		{
4131 #ifdef FEAT_GUI
4132 		    need_mouse_correct = TRUE;
4133 #endif
4134 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4135 		    /* ":psearch" uses the preview window */
4136 		    if (g_do_tagpreview != 0)
4137 		    {
4138 			curwin_save = curwin;
4139 			prepare_tagpreview();
4140 		    }
4141 #endif
4142 		    if (action == ACTION_SPLIT)
4143 		    {
4144 #ifdef FEAT_WINDOWS
4145 			if (win_split(0, 0) == FAIL)
4146 #endif
4147 			    break;
4148 #ifdef FEAT_SCROLLBIND
4149 			curwin->w_p_scb = FALSE;
4150 #endif
4151 		    }
4152 		    if (depth == -1)
4153 		    {
4154 			/* match in current file */
4155 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4156 			if (g_do_tagpreview != 0)
4157 			{
4158 			    if (getfile(0, curwin_save->w_buffer->b_fname,
4159 						 NULL, TRUE, lnum, FALSE) > 0)
4160 				break;	/* failed to jump to file */
4161 			}
4162 			else
4163 #endif
4164 			    setpcmark();
4165 			curwin->w_cursor.lnum = lnum;
4166 		    }
4167 		    else
4168 		    {
4169 			if (getfile(0, files[depth].name, NULL, TRUE,
4170 						files[depth].lnum, FALSE) > 0)
4171 			    break;	/* failed to jump to file */
4172 			/* autocommands may have changed the lnum, we don't
4173 			 * want that here */
4174 			curwin->w_cursor.lnum = files[depth].lnum;
4175 		    }
4176 		}
4177 		if (action != ACTION_SHOW)
4178 		{
4179 		    curwin->w_cursor.col = (colnr_T) (startp - line);
4180 		    curwin->w_set_curswant = TRUE;
4181 		}
4182 
4183 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
4184 		if (g_do_tagpreview != 0
4185 			&& curwin != curwin_save && win_valid(curwin_save))
4186 		{
4187 		    /* Return cursor to where we were */
4188 		    validate_cursor();
4189 		    redraw_later(VALID);
4190 		    win_enter(curwin_save, TRUE);
4191 		}
4192 #endif
4193 		break;
4194 	    }
4195 #ifdef FEAT_INS_EXPAND
4196 exit_matched:
4197 #endif
4198 	    matched = FALSE;
4199 	    /* look for other matches in the rest of the line if we
4200 	     * are not at the end of it already */
4201 	    if (def_regmatch.regprog == NULL
4202 #ifdef FEAT_INS_EXPAND
4203 		    && action == ACTION_EXPAND
4204 		    && !(continue_status & CONT_SOL)
4205 #endif
4206 		    && *(p = startp + 1))
4207 		goto search_line;
4208 	}
4209 	line_breakcheck();
4210 #ifdef FEAT_INS_EXPAND
4211 	if (action == ACTION_EXPAND)
4212 	    ins_compl_check_keys();
4213 	if (got_int || completion_interrupted)
4214 #else
4215 	if (got_int)
4216 #endif
4217 	    break;
4218 
4219 	/*
4220 	 * Read the next line.  When reading an included file and encountering
4221 	 * end-of-file, close the file and continue in the file that included
4222 	 * it.
4223 	 */
4224 	while (depth >= 0 && !already
4225 		&& vim_fgets(line = file_line, LSIZE, files[depth].fp))
4226 	{
4227 	    fclose(files[depth].fp);
4228 	    --old_files;
4229 	    files[old_files].name = files[depth].name;
4230 	    files[old_files].matched = files[depth].matched;
4231 	    --depth;
4232 	    curr_fname = (depth == -1) ? curbuf->b_fname
4233 				       : files[depth].name;
4234 	    if (depth < depth_displayed)
4235 		depth_displayed = depth;
4236 	}
4237 	if (depth >= 0)		/* we could read the line */
4238 	    files[depth].lnum++;
4239 	else if (!already)
4240 	{
4241 	    if (++lnum > end_lnum)
4242 		break;
4243 	    line = ml_get(lnum);
4244 	}
4245 	already = NULL;
4246     }
4247     /* End of big for (;;) loop. */
4248 
4249     /* Close any files that are still open. */
4250     for (i = 0; i <= depth; i++)
4251     {
4252 	fclose(files[i].fp);
4253 	vim_free(files[i].name);
4254     }
4255     for (i = old_files; i < max_path_depth; i++)
4256 	vim_free(files[i].name);
4257     vim_free(files);
4258 
4259     if (type == CHECK_PATH)
4260     {
4261 	if (!did_show)
4262 	{
4263 	    if (action != ACTION_SHOW_ALL)
4264 		MSG(_("All included files were found"));
4265 	    else
4266 		MSG(_("No included files"));
4267 	}
4268     }
4269     else if (!found
4270 #ifdef FEAT_INS_EXPAND
4271 		    && action != ACTION_EXPAND
4272 #endif
4273 						)
4274     {
4275 #ifdef FEAT_INS_EXPAND
4276 	if (got_int || completion_interrupted)
4277 #else
4278 	if (got_int)
4279 #endif
4280 	    EMSG(_(e_interr));
4281 	else if (type == FIND_DEFINE)
4282 	    EMSG(_("E388: Couldn't find definition"));
4283 	else
4284 	    EMSG(_("E389: Couldn't find pattern"));
4285     }
4286     if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
4287 	msg_end();
4288 
4289 fpip_end:
4290     vim_free(file_line);
4291     vim_free(regmatch.regprog);
4292     vim_free(incl_regmatch.regprog);
4293     vim_free(def_regmatch.regprog);
4294 
4295 #ifdef RISCOS
4296    /* Restore previous file munging state. */
4297     __riscosify_control = previous_munging;
4298 #endif
4299 }
4300 
4301     static void
4302 show_pat_in_path(line, type, did_show, action, fp, lnum, count)
4303     char_u  *line;
4304     int	    type;
4305     int	    did_show;
4306     int	    action;
4307     FILE    *fp;
4308     linenr_T *lnum;
4309     long    count;
4310 {
4311     char_u  *p;
4312 
4313     if (did_show)
4314 	msg_putchar('\n');	/* cursor below last one */
4315     else
4316 	gotocmdline(TRUE);	/* cursor at status line */
4317     if (got_int)		/* 'q' typed at "--more--" message */
4318 	return;
4319     for (;;)
4320     {
4321 	p = line + STRLEN(line) - 1;
4322 	if (fp != NULL)
4323 	{
4324 	    /* We used fgets(), so get rid of newline at end */
4325 	    if (p >= line && *p == '\n')
4326 		--p;
4327 	    if (p >= line && *p == '\r')
4328 		--p;
4329 	    *(p + 1) = NUL;
4330 	}
4331 	if (action == ACTION_SHOW_ALL)
4332 	{
4333 	    sprintf((char *)IObuff, "%3ld: ", count);	/* show match nr */
4334 	    msg_puts(IObuff);
4335 	    sprintf((char *)IObuff, "%4ld", *lnum);	/* show line nr */
4336 						/* Highlight line numbers */
4337 	    msg_puts_attr(IObuff, hl_attr(HLF_N));
4338 	    MSG_PUTS(" ");
4339 	}
4340 	msg_prt_line(line);
4341 	out_flush();			/* show one line at a time */
4342 
4343 	/* Definition continues until line that doesn't end with '\' */
4344 	if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
4345 	    break;
4346 
4347 	if (fp != NULL)
4348 	{
4349 	    if (vim_fgets(line, LSIZE, fp)) /* end of file */
4350 		break;
4351 	    ++*lnum;
4352 	}
4353 	else
4354 	{
4355 	    if (++*lnum > curbuf->b_ml.ml_line_count)
4356 		break;
4357 	    line = ml_get(*lnum);
4358 	}
4359 	msg_putchar('\n');
4360     }
4361 }
4362 #endif
4363 
4364 #ifdef FEAT_VIMINFO
4365     int
4366 read_viminfo_search_pattern(virp, force)
4367     vir_T	*virp;
4368     int		force;
4369 {
4370     char_u	*lp;
4371     int		idx = -1;
4372     int		magic = FALSE;
4373     int		no_scs = FALSE;
4374     int		off_line = FALSE;
4375     int		off_end = FALSE;
4376     long	off = 0;
4377     int		setlast = FALSE;
4378 #ifdef FEAT_SEARCH_EXTRA
4379     static int	hlsearch_on = FALSE;
4380 #endif
4381     char_u	*val;
4382 
4383     /*
4384      * Old line types:
4385      * "/pat", "&pat": search/subst. pat
4386      * "~/pat", "~&pat": last used search/subst. pat
4387      * New line types:
4388      * "~h", "~H": hlsearch highlighting off/on
4389      * "~<magic><smartcase><line><end><off><last><which>pat"
4390      * <magic>: 'm' off, 'M' on
4391      * <smartcase>: 's' off, 'S' on
4392      * <line>: 'L' line offset, 'l' char offset
4393      * <end>: 'E' from end, 'e' from start
4394      * <off>: decimal, offset
4395      * <last>: '~' last used pattern
4396      * <which>: '/' search pat, '&' subst. pat
4397      */
4398     lp = virp->vir_line;
4399     if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M'))	/* new line type */
4400     {
4401 	if (lp[1] == 'M')		/* magic on */
4402 	    magic = TRUE;
4403 	if (lp[2] == 's')
4404 	    no_scs = TRUE;
4405 	if (lp[3] == 'L')
4406 	    off_line = TRUE;
4407 	if (lp[4] == 'E')
4408 	    off_end = SEARCH_END;
4409 	lp += 5;
4410 	off = getdigits(&lp);
4411     }
4412     if (lp[0] == '~')		/* use this pattern for last-used pattern */
4413     {
4414 	setlast = TRUE;
4415 	lp++;
4416     }
4417     if (lp[0] == '/')
4418 	idx = RE_SEARCH;
4419     else if (lp[0] == '&')
4420 	idx = RE_SUBST;
4421 #ifdef FEAT_SEARCH_EXTRA
4422     else if (lp[0] == 'h')	/* ~h: 'hlsearch' highlighting off */
4423 	hlsearch_on = FALSE;
4424     else if (lp[0] == 'H')	/* ~H: 'hlsearch' highlighting on */
4425 	hlsearch_on = TRUE;
4426 #endif
4427     if (idx >= 0)
4428     {
4429 	if (force || spats[idx].pat == NULL)
4430 	{
4431 	    val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1),
4432 									TRUE);
4433 	    if (val != NULL)
4434 	    {
4435 		set_last_search_pat(val, idx, magic, setlast);
4436 		vim_free(val);
4437 		spats[idx].no_scs = no_scs;
4438 		spats[idx].off.line = off_line;
4439 		spats[idx].off.end = off_end;
4440 		spats[idx].off.off = off;
4441 #ifdef FEAT_SEARCH_EXTRA
4442 		if (setlast)
4443 		    no_hlsearch = !hlsearch_on;
4444 #endif
4445 	    }
4446 	}
4447     }
4448     return viminfo_readline(virp);
4449 }
4450 
4451     void
4452 write_viminfo_search_pattern(fp)
4453     FILE	*fp;
4454 {
4455     if (get_viminfo_parameter('/') != 0)
4456     {
4457 #ifdef FEAT_SEARCH_EXTRA
4458 	fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c",
4459 	    (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H');
4460 #endif
4461 	wvsp_one(fp, RE_SEARCH, "", '/');
4462 	wvsp_one(fp, RE_SUBST, "Substitute ", '&');
4463     }
4464 }
4465 
4466     static void
4467 wvsp_one(fp, idx, s, sc)
4468     FILE	*fp;	/* file to write to */
4469     int		idx;	/* spats[] index */
4470     char	*s;	/* search pat */
4471     int		sc;	/* dir char */
4472 {
4473     if (spats[idx].pat != NULL)
4474     {
4475 	fprintf(fp, "\n# Last %sSearch Pattern:\n~", s);
4476 	/* off.dir is not stored, it's reset to forward */
4477 	fprintf(fp, "%c%c%c%c%ld%s%c",
4478 		spats[idx].magic    ? 'M' : 'm',	/* magic */
4479 		spats[idx].no_scs   ? 's' : 'S',	/* smartcase */
4480 		spats[idx].off.line ? 'L' : 'l',	/* line offset */
4481 		spats[idx].off.end  ? 'E' : 'e',	/* offset from end */
4482 		spats[idx].off.off,			/* offset */
4483 		last_idx == idx	    ? "~" : "",		/* last used pat */
4484 		sc);
4485 	viminfo_writestring(fp, spats[idx].pat);
4486     }
4487 }
4488 #endif /* FEAT_VIMINFO */
4489