home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
programs
/
text
/
vim
/
src
/
search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-27
|
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 offset */
static int lastend; /* previous/current search set cursor at end */
static long lastoff; /* previous/current line or char offset */
int old_lastsdir;
int old_lastoffline;
int old_lastend;
long old_lastoff;
int ret; /* Return value */
register char_u *p;
register long c;
char_u *dircp = NULL;
/*
* save the values for when keep_old_search_pattern is set
* (no if around this because gcc wants them initialized)
*/
old_lastsdir = lastsdir;
old_lastoffline = lastoffline;
old_lastend = lastend;
old_lastoff = lastoff;
if (dirc == 0)
dirc = lastsdir;
else
lastsdir = dirc;
if (reverse)
{
if (dirc == '/')
dirc = '?';
else
dirc = '/';
}
searchstr = str;
/* use previous string */
if (str == NULL || *str == NUL || *str == dirc)
{
if (search_pattern == NULL)
{
emsg(e_noprevre);
ret = 0;
goto end_dosearch;
}
searchstr = (char_u *)""; /* will use search_pattern in myregcomp() */
}
if (str != NULL && *str != NUL) /* look for (new) offset */
{
/*
* Find end of regular expression.
* If there is a matching '/' or '?', toss it.
*/
p = skip_regexp(str, dirc);
if (*p == dirc)
{
dircp = p; /* remember where we put the NUL */
*p++ = NUL;
}
lastoffline = FALSE;
lastend = FALSE;
lastoff = 0;
/*
* Check for a line offset or a character offset.
* for get_address (echo off) we don't check for a character offset,
* because it is meaningless and the 's' could be a substitute command.
*/
if (*p == '+' || *p == '-' || isdigit(*p))
lastoffline = TRUE;
else if (echo && (*p == 'e' || *p == 's' || *p == 'b'))
{
if (*p == 'e') /* end */
lastend = TRUE;
++p;
}
if (isdigit(*p) || *p == '+' || *p == '-') /* got an offset */
{
if (isdigit(*p) || isdigit(*(p + 1)))
lastoff = atol((char *)p); /* 'nr' or '+nr' or '-nr' */
else if (*p == '-') /* single '-' */
lastoff = -1;
else /* single '+' */
lastoff = 1;
++p;
while (isdigit(*p)) /* skip number */
++p;
}
searchcmdlen = p - str; /* compute lenght of search command
for get_address() */
}
if (echo)
{
msg_start();
msg_outchar(dirc);
msg_outtrans(*searchstr == NUL ? search_pattern : searchstr, -1);
if (lastoffline || lastend || lastoff)
{
msg_outchar(dirc);
if (lastend)
msg_outchar('e');
else if (!lastoffline)
msg_outchar('s');
if (lastoff < 0)
{
msg_outchar('-');
msg_outnum((long)-lastoff);
}
else if (lastoff > 0 || lastoffline)
{
msg_outchar('+');
msg_outnum((long)lastoff);
}
}
msg_ceol();
(void)msg_check();
gotocmdline(FALSE, NUL);
flushbuf();
}
pos = curwin->w_cursor;
c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count, lastend, message);
if (dircp)
*dircp = dirc; /* put second '/' or '?' back for normal() */
if (c == FAIL)
{
ret = 0;
goto end_dosearch;
}
if (lastend)
mincl = TRUE; /* 'e' includes last character */
if (!lastoffline) /* add the character offset to the column */
{
if (lastoff > 0) /* offset to the right, check for end of line */
{
p = ml_get_pos(&pos) + 1;
c = lastoff;
while (c-- && *p++ != NUL)
++pos.col;
}
else /* offset to the left, check for start of line */
{
if ((c = pos.col + lastoff) < 0)
c = 0;
pos.col = c;
}
}
if (!tag_busy)
setpcmark();
curwin->w_cursor = pos;
curwin->w_set_curswant = TRUE;
if (!lastoffline)
{
ret = 1;
goto end_dosearch;
}
/*
* add the offset to the line number.
*/
c = curwin->w_cursor.lnum + lastoff;
if (c < 1)
curwin->w_cursor.lnum = 1;
else if (c > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = c;
curwin->w_cursor.col = 0;
ret = 2;
end_dosearch:
if (keep_old_search_pattern)
{
lastsdir = old_lastsdir;
lastoffline = old_lastoffline;
lastend = old_lastend;
lastoff = old_lastoff;
}
return ret;
}
/*
* Character Searches
*/
/*
* searchc(c, dir, type, count)
*
* Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
* position of the character, otherwise move to just before the char.
* Repeat this 'count' times.
*/
int
searchc(c, dir, type, count)
int c;
register int dir;
int type;
long count;
{
static int lastc = NUL; /* last character searched for */
static int lastcdir; /* last direction of character search */
static int lastctype; /* last type of search ("find" or "to") */
register int col;
char_u *p;
int len;
if (c != NUL) /* normal search: remember args for repeat */
{
lastc = c;
lastcdir = dir;
lastctype = type;
}
else /* repeat previous search */
{
if (lastc == NUL)
return FALSE;
if (dir) /* repeat in opposite direction */
dir = -lastcdir;
else
dir = lastcdir;
}
p = ml_get(curwin->w_cursor.lnum);
col = curwin->w_cursor.col;
len = STRLEN(p);
/*
* On 'to' searches, skip one to start with so we can repeat searches in
* the same direction and have it work right.
* REMOVED to get vi compatibility
* if (lastctype)
* col += dir;
*/
while (count--)
{
for (;;)
{
if ((col += dir) < 0 || col >= len)
return FALSE;
if (p[col] == lastc)
break;
}
}
if (lastctype)
col -= dir;
curwin->w_cursor.col = col;
return TRUE;
}
/*
* "Other" Searches
*/
/*
* showmatch - move the cursor to the matching paren or brace
*
* Improvement over vi: Braces inside quotes are ignored.
*/
FPOS *
showmatch(initc)
int initc;
{
static FPOS pos; /* current search position */
int findc; /* matching brace */
int c;
int count = 0; /* cumulative number of braces */
int idx = 0; /* init for gcc */
static char_u table[6] = {'(', ')', '[', ']', '{', '}'};
int inquote = 0; /* non-zero when inside quotes */
register char_u *linep; /* pointer to current line */
register char_u *ptr;
int do_quotes; /* check for quotes in current line */
int hash_dir = 0; /* Direction searched for # things */
int comment_dir = 0; /* Direction searched for comments */
pos = curwin->w_cursor;
linep = ml_get(pos.lnum);
/*
* if initc given, look in the table for the matching character
*/
if (initc != NUL)
{
for (idx = 0; idx < 6; ++idx)
if (table[idx] == initc)
{
initc = table[idx = idx ^ 1];
break;
}
if (idx == 6) /* invalid initc! */
return NULL;
}
/*
* no initc given, look under the cursor
*/
else
{
if (linep[0] == '#' && pos.col == 0)
hash_dir = 1;
/*
* Are we on a comment?
*/
if (linep[pos.col] == '/')
{
if (linep[pos.col + 1] == '*')
{
comment_dir = 1;
idx = 0;
}
else if (pos.col > 0 && linep[pos.col - 1] == '*')
{
comment_dir = -1;
idx = 1;
}
}
if (linep[pos.col] == '*')
{
if (linep[pos.col + 1] == '/')
{
comment_dir = -1;
idx = 1;
}
else if (pos.col > 0 && linep[pos.col - 1] == '/')
{
comment_dir = 1;
idx = 0;
}
}
/*
* If we are not on a comment or the # at the start of a line, then
* look for brace anywhere on this line after the cursor.
*/
if (!hash_dir && !comment_dir)
{
/*
* find the brace under or after the cursor
*/
linep = ml_get(pos.lnum);
for (;;)
{
initc = linep[pos.col];
if (initc == NUL)
break;
for (idx = 0; idx < 6; ++idx)
if (table[idx] == initc)
break;
if (idx != 6)
break;
++pos.col;
}
if (idx == 6)
{
if (linep[0] == '#')
hash_dir = 1;
else
return NULL;
}
}
if (hash_dir)
{
/*
* Look for matching #if, #else, #elif, or #endif
*/
mtype = MLINE; /* Linewise for this case only */
ptr = linep + 1;
while (*ptr == ' ' || *ptr == TAB)
ptr++;
if (STRNCMP(ptr, "if", (size_t)2) == 0 || STRNCMP(ptr, "el", (size_t)2) == 0)
hash_dir = 1;
else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
hash_dir = -1;
else
return NULL;
pos.col = 0;
while (!got_int)
{
if (hash_dir > 0)
{
if (pos.lnum == curbuf->b_ml.ml_line_count)
break;
}
else if (pos.lnum == 1)
break;
pos.lnum += hash_dir;
linep = ml_get(pos.lnum);
if ((pos.lnum & 15) == 0)
breakcheck();
if (linep[0] != '#')
continue;
ptr = linep + 1;
while (*ptr == ' ' || *ptr == TAB)
ptr++;
if (hash_dir > 0)
{
if (STRNCMP(ptr, "if", (size_t)2) == 0)
count++;
else if (STRNCMP(ptr, "el", (size_t)2) == 0)
{
if (count == 0)
return &pos;
}
else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
{
if (count == 0)
return &pos;
count--;
}
}
else
{
if (STRNCMP(ptr, "if", (size_t)2) == 0)
{
if (count == 0)
return &pos;
count--;
}
else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
count++;
}
}
return NULL;
}
}
findc = table[idx ^ 1]; /* get matching brace */
idx &= 1;
do_quotes = -1;
while (!got_int)
{
/*
* Go to the next position, forward or backward. We could use
* inc() and dec() here, but that is much slower
*/
if (idx) /* backward search */
{
if (pos.col == 0) /* at start of line, go to previous one */
{
if (pos.lnum == 1) /* start of file */
break;
--pos.lnum;
linep = ml_get(pos.lnum);
pos.col = STRLEN(linep); /* put pos.col on trailing NUL */
do_quotes = -1;
/* we only do a breakcheck() once for every 16 lines */
if ((pos.lnum & 15) == 0)
breakcheck();
}
else
--pos.col;
}
else /* forward search */
{
if (linep[pos.col] == NUL) /* at end of line, go to next one */
{
if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
break;
++pos.lnum;
linep = ml_get(pos.lnum);
pos.col = 0;
do_quotes = -1;
/* we only do a breakcheck() once for every 16 lines */
if ((pos.lnum & 15) == 0)
breakcheck();
}
else
++pos.col;
}
if (comment_dir)
{
/* Note: comments do not nest, and we ignore quotes in them */
if (linep[pos.col] != '/' ||
(comment_dir == 1 && pos.col == 0) ||
linep[pos.col - comment_dir] != '*')
continue;
return &pos;
}
if (do_quotes == -1) /* count number of quotes in this line */
{
/*
* count the number of quotes in the line, skipping \" and '"'
*/
for (ptr = linep; *ptr; ++ptr)
if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
(ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
++do_quotes;
do_quotes &= 1; /* result is 1 with even number of quotes */
/*
* If we find an uneven count, check current line and previous
* one for a '\' at the end.
*/
if (!do_quotes)
{
inquote = FALSE;
if (ptr[-1] == '\\')
{
do_quotes = 1;
if (idx) /* backward search */
inquote = TRUE;
}
if (pos.lnum > 1)
{
ptr = ml_get(pos.lnum - 1);
if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
{
do_quotes = 1;
if (!idx) /* forward search */
inquote = TRUE;
}
}
}
}
/*
* Things inside quotes are ignored by setting 'inquote'.
* If we find a quote without a preceding '\' invert 'inquote'.
* At the end of a line not ending in '\' we reset 'inquote'.
*
* In lines with an uneven number of quotes (without preceding '\')
* we do not know which part to ignore. Therefore we only set
* inquote if the number of quotes in a line is even,
* unless this line or the previous one ends in a '\'.
* Complicated, isn't it?
*/
switch (c = linep[pos.col])
{
case NUL:
inquote = FALSE;
break;
case '"':
/* a quote that is preceded with a backslash is ignored */
if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
inquote = !inquote;
break;
/*
* Skip things in single quotes: 'x' or '\x'.
* Be careful for single single quotes, eg jon's.
* Things like '\233' or '\x3f' are not skipped, there is never a
* brace in them.
*/
case '\'':
if (idx) /* backward search */
{
if (pos.col > 1)
{
if (linep[pos.col - 2] == '\'')
pos.col -= 2;
else if (linep[pos.col - 2] == '\\' && pos.col > 2 && linep[pos.col - 3] == '\'')
pos.col -= 3;
}
}
else if (linep[pos.col + 1]) /* forward search */
{
if (linep[pos.col + 1] == '\\' && linep[pos.col + 2] && linep[pos.col + 3] == '\'')
pos.col += 3;
else if (linep[pos.col + 2] == '\'')
pos.col += 2;
}
break;
default:
if (!inquote) /* only check for match outside of quotes */
{
if (c == initc)
count++;
else if (c == findc)
{
if (count == 0)
return &pos;
count--;
}
}
}
}
return (FPOS *) NULL; /* never found it */
}
/*
* findfunc(dir, what) - Find the next line starting with 'what' in direction 'dir'
*
* Return TRUE if a line was found.
*/
int
findfunc(dir, what, count)
int dir;
int what;
long count;
{
linenr_t curr;
curr = curwin->w_cursor.lnum;
for (;;)
{
if (dir == FORWARD)
{
if (curr++ == curbuf->b_ml.ml_line_count)
break;
}
else
{
if (curr-- == 1)
break;
}
if (*ml_get(curr) == what)
{
if (--count > 0)
continue;
setpcmark();
curwin->w_cursor.lnum = curr;
curwin->w_cursor.col = 0;
return TRUE;
}
}
return FALSE;
}
/*
* findsent(dir, count) - Find the start of the next sentence in direction 'dir'
* Sentences are supposed to end in ".", "!" or "?" followed by white space or
* a line break. Also stop at an empty line.
* Return TRUE if the next sentence was found.
*/
int
findsent(dir, count)
int dir;
long count;
{
FPOS pos, tpos;
register int c;
int (*func) __PARMS((FPOS *));
int startlnum;
int noskip = FALSE; /* do not skip blanks */
pos = curwin->w_cursor;
if (dir == FORWARD)
func = incl;
else
func = decl;
while (count--)
{
/* if on an empty line, skip upto a non-empty line */
if (gchar(&pos) == NUL)
{
do
if ((*func)(&pos) == -1)
break;
while (gchar(&pos) == NUL);
if (dir == FORWARD)
goto found;
}
/* if on the start of a paragraph or a section and searching
* forward, go to the next line */
else if (dir == FORWARD && pos.col == 0 && startPS(pos.lnum, NUL, FALSE))
{
if (pos.lnum == curbuf->b_ml.ml_line_count)
return FALSE;
++pos.lnum;
goto found;
}
else if (dir == BACKWARD)
decl(&pos);
/* go back to the previous non-blank char */
while ((c = gchar(&pos)) == ' ' || c == '\t' ||
(dir == BACKWARD && strchr(".!?)]\"'", c) != NULL && c != NUL))
if (decl(&pos) == -1)
break;
/* remember the line where the search started */
startlnum = pos.lnum;
for (;;) /* find end of sentence */
{
if ((c = gchar(&pos)) == NUL ||
(pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
{
if (dir == BACKWARD && pos.lnum != startlnum)
++pos.lnum;
break;
}
if (c == '.' || c == '!' || c == '?')
{
tpos = pos;
do
if ((c = inc(&tpos)) == -1)
break;
while (strchr(")}\"'", c = gchar(&tpos)) != NULL && c != NUL);
if (c == -1 || c == ' ' || c == '\t' || c == NUL)
{
pos = tpos;
if (gchar(&pos) == NUL) /* skip NUL at EOL */
inc(&pos);
break;
}
}
if ((*func)(&pos) == -1)
{
if (count)
return FALSE;
noskip = TRUE;
break;
}
}
found:
/* skip white space */
while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
if (incl(&pos) == -1)
break;
}
setpcmark();
curwin->w_cursor = pos;
return TRUE;
}
/*
* findpar(dir, count, what) - Find the next paragraph in direction 'dir'
* Paragraphs are currently supposed to be separated by empty lines.
* Return TRUE if the next paragraph was found.
* If 'what' is '{' or '}' we go to the next section.
* If 'both' is TRUE also stop at '}'.
*/
int
findpar(dir, count, what, both)
register int dir;
long count;
int what;
int both;
{
register linenr_t curr;
int did_skip; /* TRUE after separating lines have
been skipped */
int first; /* TRUE on first line */
curr = curwin->w_cursor.lnum;
while (count--)
{
did_skip = FALSE;
for (first = TRUE; ; first = FALSE)
{
if (*ml_get(curr) != NUL)
did_skip = TRUE;
if (!first && did_skip && startPS(curr, what, both))
break;
if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
{
if (count)
return FALSE;
curr -= dir;
break;
}
}
}
setpcmark();
if (both && *ml_get(curr) == '}') /* include line with '}' */
++curr;
curwin->w_cursor.lnum = curr;
if (curr == curbuf->b_ml.ml_line_count)
{
if ((curwin->w_cursor.col = STRLEN(ml_get(curr))) != 0)
--curwin->w_cursor.col;
mincl = TRUE;
}
else
curwin->w_cursor.col = 0;
return TRUE;
}
/*
* check if the string 's' is a nroff macro that is in option 'opt'
*/
static int
inmacro(opt, s)
char_u *opt;
register char_u *s;
{
register char_u *macro;
for (macro = opt; macro[0]; ++macro)
{
if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ')
&& (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
break;
++macro;
if (macro[0] == NUL)
break;
}
return (macro[0] != NUL);
}
/*
* startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
* If 'para' is '{' or '}' only check for sections.
* If 'both' is TRUE also stop at '}'
*/
int
startPS(lnum, para, both)
linenr_t lnum;
int para;
int both;
{
register char_u *s;
s = ml_get(lnum);
if (*s == para || *s == '\f' || (both && *s == '}'))
return TRUE;
if (*s == '.' && (inmacro(p_sections, s + 1) || (!para && inmacro(p_para, s + 1))))
return TRUE;
return FALSE;
}
/*
* The following routines do the word searches performed by the 'w', 'W',
* 'b', 'B', 'e', and 'E' commands.
*/
/*
* To perform these searches, characters are placed into one of three
* classes, and transitions between classes determine word boundaries.
*
* The classes are:
*
* 0 - white space
* 1 - letters, digits and underscore
* 2 - everything else
*/
static int stype; /* type of the word motion being performed */
/*
* cls() - returns the class of character at curwin->w_cursor
*
* The 'type' of the current search modifies the classes of characters if a 'W',
* 'B', or 'E' motion is being done. In this case, chars. from class 2 are
* reported as class 1 since only white space boundaries are of interest.
*/
static int
cls()
{
register int c;
c = gchar_cursor();
if (c == ' ' || c == '\t' || c == NUL)
return 0;
if (isidchar(c))
return 1;
/*
* If stype is non-zero, report these as class 1.
*/
return (stype == 0) ? 2 : 1;
}
/*
* fwd_word(count, type, eol) - move forward one word
*
* Returns TRUE if the cursor was already at the end of the file.
* If eol is TRUE, last word stops at end of line (for operators).
*/
int
fwd_word(count, type, eol)
long count;
int type;
int eol;
{
int sclass; /* starting class */
int i;
stype = type;
while (--count >= 0)
{
sclass = cls();
/*
* We always move at least one character.
*/
i = inc_cursor();
if (i == -1)
return TRUE;
if (i == 1 && eol && count == 0) /* started at last char in line */
return FALSE;
if (sclass != 0)
while (cls() == sclass)
{
i = inc_cursor();
if (i == -1 || (i == 1 && eol && count == 0))
return FALSE;
}
/*
* go to next non-white
*/
while (cls() == 0)
{
/*
* We'll stop if we land on a blank line
*/
if (curwin->w_cursor.col == 0 && *ml_get(curwin->w_cursor.lnum) == NUL)
break;
i = inc_cursor();
if (i == -1 || (i == 1 && eol && count == 0))
return FALSE;
}
}
return FALSE;
}
/*
* bck_word(count, type) - move backward 'count' words
*
* Returns TRUE if top of the file was reached.
*/
int
bck_word(count, type)
long count;
int type;
{
int sclass; /* starting class */
stype = type;
while (--count >= 0)
{
sclass = cls();
if (dec_cursor() == -1) /* started at start of file */
return TRUE;
if (cls() != sclass || sclass == 0)
{
/*
* We were at the start of a word. Go back to the end of the prior
* word.
*/
while (cls() == 0) /* skip any white space */
{
/*
* We'll stop if we land on a blank line
*/
if (curwin->w_cursor.col == 0 && *ml_get(curwin->w_cursor.lnum) == NUL)
goto finished;
if (dec_cursor() == -1) /* hit start of file, stop here */
return FALSE;
}
sclass = cls();
}
/*
* Move backward to start of this word.
*/
if (skip_chars(sclass, BACKWARD))
return FALSE;
inc_cursor(); /* overshot - forward one */
finished:
;
}
return FALSE;
}
/*
* end_word(count, type, stop) - move to the end of the word
*
* There is an apparent bug in the 'e' motion of the real vi. At least on the
* System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
* motion crosses blank lines. When the real vi crosses a blank line in an
* 'e' motion, the cursor is placed on the FIRST character of the next
* non-blank line. The 'E' command, however, works correctly. Since this
* appears to be a bug, I have not duplicated it here.
*
* Returns TRUE if end of the file was reached.
*
* If stop is TRUE and we are already on the end of a word, move one less.
*/
int
end_word(count, type, stop)
long count;
int type;
int stop;
{
int sclass; /* starting class */
stype = type;
while (--count >= 0)
{
sclass = cls();
if (inc_cursor() == -1)
return TRUE;
/*
* If we're in the middle of a word, we just have to move to the end of it.
*/
if (cls() == sclass && sclass != 0)
{
/*
* Move forward to end of the current word
*/
if (skip_chars(sclass, FORWARD))
return TRUE;
}
else if (!stop || sclass == 0)
{
/*
* We were at the end of a word. Go to the end of the next word.
*/
if (skip_chars(0, FORWARD)) /* skip any white space */
return TRUE;
/*
* Move forward to the end of this word.
*/
if (skip_chars(cls(), FORWARD))
return TRUE;
}
dec_cursor(); /* overshot - backward one */
stop = FALSE; /* we move only one word less */
}
return FALSE;
}
int
skip_chars(class, dir)
int class;
int dir;
{
while (cls() == class)
if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
return TRUE;
return FALSE;
}