home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD1.bin
/
useful
/
util
/
edit
/
vim
/
src
/
search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-09
|
31KB
|
1,376 lines
/* vi:ts=4:sw=4
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Read the file "credits.txt" for a list of people who contributed.
* Read the file "uganda.txt" for copying and usage conditions.
*/
/*
* search.c: code for normal mode searching commands
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#include "ops.h" /* for mincl */
/* modified Henry Spencer's regular expression routines */
#include "regexp.h"
static int inmacro __ARGS((char_u *, char_u *));
static int cls __ARGS((void));
static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
/*
* This file contains various searching-related routines. These fall into
* three groups:
* 1. string searches (for /, ?, n, and N)
* 2. character searches within a single line (for f, F, t, T, etc)
* 3. "other" kinds of searches like the '%' command, and 'word' searches.
*/
/*
* String searches
*
* The string search functions are divided into two levels:
* lowest: searchit(); called by dosearch() and edit().
* Highest: dosearch(); changes curwin->w_cursor, called by normal().
*
* The last search pattern is remembered for repeating the same search.
* This pattern is shared between the :g, :s, ? and / commands.
* This is in myregcomp().
*
* The actual string matching is done using a heavily modified version of
* Henry Spencer's regular expression library.
*/
/*
* Two search patterns are remembered: One for the :substitute command and
* one for other searches. last_pattern points to the one that was
* used the last time.
*/
static char_u *search_pattern = NULL;
static char_u *subst_pattern = NULL;
static char_u *last_pattern = NULL;
static int want_start; /* looking for start of line? */
/*
* translate search pattern for regcomp()
*
* sub_cmd == 0: save pat in search_pattern (normal search command)
* sub_cmd == 1: save pat in subst_pattern (:substitute command)
* sub_cmd == 2: save pat in both patterns (:global command)
* which_pat == 0: use previous search pattern if "pat" is NULL
* which_pat == 1: use previous sustitute pattern if "pat" is NULL
* which_pat == 2: use last used pattern if "pat" is NULL
*
*/
regexp *
myregcomp(pat, sub_cmd, which_pat)
char_u *pat;
int sub_cmd;
int which_pat;
{
regexp *retval;
if (pat == NULL || *pat == NUL) /* use previous search pattern */
{
if (which_pat == 0)
{
if (search_pattern == NULL)
{
emsg(e_noprevre);
return (regexp *) NULL;
}
pat = search_pattern;
}
else if (which_pat == 1)
{
if (subst_pattern == NULL)
{
emsg(e_nopresub);
return (regexp *) NULL;
}
pat = subst_pattern;
}
else /* which_pat == 2 */
{
if (last_pattern == NULL)
{
emsg(e_noprevre);
return (regexp *) NULL;
}
pat = last_pattern;
}
}
/*
* save the currently used pattern in the appropriate place,
* unless the pattern should not be remembered
*/
if (!keep_old_search_pattern)
{
if (sub_cmd == 0 || sub_cmd == 2) /* search or global command */
{
if (search_pattern != pat)
{
free(search_pattern);
search_pattern = strsave(pat);
last_pattern = search_pattern;
reg_magic = p_magic; /* Magic sticks with the r.e. */
}
}
if (sub_cmd == 1 || sub_cmd == 2) /* substitute or global command */
{
if (subst_pattern != pat)
{
free(subst_pattern);
subst_pattern = strsave(pat);
last_pattern = subst_pattern;
reg_magic = p_magic; /* Magic sticks with the r.e. */
}
}
}
want_start = (*pat == '^'); /* looking for start of line? */
reg_ic = p_ic; /* tell the regexec routine how to search */
retval = regcomp(pat);
return retval;
}
/*
* lowest level search function.
* Search for 'count'th occurrence of 'str' in direction 'dir'.
* Start at position 'pos' and return the found position in 'pos'.
* Return OK for success, FAIL for failure.
*/
int
searchit(pos, dir, str, count, end, message)
FPOS *pos;
int dir;
char_u *str;
long count;
int end;
int message;
{
int found;
linenr_t lnum = 0; /* init to shut up gcc */
linenr_t startlnum;
regexp *prog;
register char_u *s;
char_u *ptr;
register int i;
register char_u *match, *matchend;
int loop;
if ((prog = myregcomp(str, 0, 2)) == NULL)
{
if (message)
emsg(e_invstring);
return FAIL;
}
/*
* find the string
*/
found = 1;
while (count-- && found) /* stop after count matches, or no more matches */
{
startlnum = pos->lnum; /* remember start of search for detecting no match */
found = 0; /* default: not found */
i = pos->col + dir; /* search starts one postition away */
lnum = pos->lnum;
if (dir == BACKWARD && i < 0)
--lnum;
for (loop = 0; loop != 2; ++loop) /* do this twice if 'wrapscan' is set */
{
for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count; lnum += dir, i = -1)
{
s = ptr = ml_get(lnum);
if (dir == FORWARD && i > 0) /* first line for forward search */
{
if (want_start || STRLEN(s) <= (size_t)i) /* match not possible */
continue;
s += i;
}
if (regexec(prog, s, dir == BACKWARD || i <= 0))
{ /* match somewhere on line */
match = prog->startp[0];
matchend = prog->endp[0];
if (dir == BACKWARD && !want_start)
{
/*
* Now, if there are multiple matches on this line,
* we have to get the last one. Or the last one before
* the cursor, if we're on that line.
*/
while (*match != NUL && regexec(prog, match + 1, (int)FALSE))
{
if ((i >= 0) && ((prog->startp[0] - s) > i))
break;
match = prog->startp[0];
matchend = prog->endp[0];
}
if ((i >= 0) && ((match - s) > i))
continue;
}
pos->lnum = lnum;
if (end)
pos->col = (int) (matchend - ptr - 1);
else
pos->col = (int) (match - ptr);
found = 1;
break;
}
/* breakcheck is slow, do it only once in 16 lines */
if ((lnum & 15) == 0)
breakcheck(); /* stop if ctrl-C typed */
if (got_int)
break;
if (loop && lnum == startlnum) /* if second loop stop where started */
break;
}
/* stop the search if wrapscan isn't set, after an interrupt and after a match */
if (!p_ws || got_int || found)
break;
/*
* If 'wrapscan' is set we continue at the other end of the file.
* If 'terse' is not set, we give a message.
* This message is also remembered in keep_msg for when the screen
* is redrawn. The keep_msg is cleared whenever another message is
* written.
*/
if (dir == BACKWARD) /* start second loop at the other end */
{
lnum = curbuf->b_ml.ml_line_count;
if (!p_terse && message)
{
msg(top_bot_msg);
keep_msg = top_bot_msg;
}
}
else
{
lnum = 1;
if (!p_terse && message)
{
msg(bot_top_msg);
keep_msg = bot_top_msg;
}
}
}
if (got_int)
break;
}
free(prog);
if (!found) /* did not find it */
{
if (got_int)
emsg(e_interr);
else if (message)
{
if (p_ws)
emsg(e_patnotf);
else if (lnum == 0)
EMSG("search hit TOP without match");
else
EMSG("search hit BOTTOM without match");
}
return FAIL;
}
return OK;
}
/*
* Highest level string search function.
* Search for the 'count'th occurence of string 'str' in direction 'dirc'
* If 'dirc' is 0: use previous dir.
* If 'str' is 0 or 'str' is empty: use previous string.
* If 'reverse' is TRUE: go in reverse of previous dir.
* If 'echo' is TRUE: echo the search command and handle options
* If 'message' is TRUE: may give error message
*
* return 0 for failure, 1 for found, 2 for found and line offset added
*/
int
dosearch(dirc, str, reverse, count, echo, message)
int dirc;
char_u *str;
int reverse;
long count;
int echo;
int message;
{
FPOS pos; /* position of the last match */
char_u *searchstr;
static int lastsdir = '/'; /* previous search direction */
static int lastoffline;/* previous/current search has line of