mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-12-23 05:30:18 +00:00
361 lines
7.0 KiB
C
361 lines
7.0 KiB
C
|
/*
|
||
|
* Routines to search a file for a pattern.
|
||
|
*/
|
||
|
#pragma noroot
|
||
|
#include <string.h>
|
||
|
#include "less.h"
|
||
|
#include "position.h"
|
||
|
|
||
|
#ifdef _ORCAC_
|
||
|
segment "LoadSegONE";
|
||
|
#endif
|
||
|
|
||
|
#if REGCOMP
|
||
|
#include <regexp.h>
|
||
|
#endif
|
||
|
|
||
|
extern int sigs;
|
||
|
extern int how_search;
|
||
|
extern int caseless;
|
||
|
extern int linenums;
|
||
|
extern int jump_sline;
|
||
|
|
||
|
/*
|
||
|
* Search for the n-th occurrence of a specified pattern,
|
||
|
* either forward or backward.
|
||
|
* Return the number of matches not yet found in this file
|
||
|
* (that is, n minus the number of matches found).
|
||
|
* Return -1 if the search should be aborted.
|
||
|
* Caller may continue the search in another file
|
||
|
* if less than n matches are found in this file.
|
||
|
*/
|
||
|
public int
|
||
|
search(search_type, pattern, n)
|
||
|
int search_type;
|
||
|
char *pattern;
|
||
|
int n;
|
||
|
{
|
||
|
POSITION pos, linepos, oldpos;
|
||
|
register char *p;
|
||
|
register char *q;
|
||
|
register int goforw;
|
||
|
register int want_match;
|
||
|
char *line;
|
||
|
int linenum;
|
||
|
int line_match;
|
||
|
static int is_caseless;
|
||
|
#if RECOMP
|
||
|
char *re_comp();
|
||
|
PARG parg;
|
||
|
#else
|
||
|
#if REGCMP
|
||
|
char *regcmp();
|
||
|
static char *cpattern = NULL;
|
||
|
#else
|
||
|
#if REGCOMP
|
||
|
static struct regexp *regpattern = NULL;
|
||
|
#else
|
||
|
static char lpbuf[100];
|
||
|
static char *last_pattern = NULL;
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Extract flags and type of search.
|
||
|
*/
|
||
|
goforw = (SRCH_DIR(search_type) == SRCH_FORW);
|
||
|
want_match = !(search_type & SRCH_NOMATCH);
|
||
|
|
||
|
if (pattern != NULL && *pattern != '\0' && (is_caseless = caseless))
|
||
|
{
|
||
|
/*
|
||
|
* Search will ignore case, unless
|
||
|
* there are any uppercase letters in the pattern.
|
||
|
*/
|
||
|
for (p = pattern; *p != '\0'; p++)
|
||
|
if (*p >= 'A' && *p <= 'Z')
|
||
|
{
|
||
|
is_caseless = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#if RECOMP
|
||
|
|
||
|
/*
|
||
|
* (re_comp handles a null pattern internally,
|
||
|
* so there is no need to check for a null pattern here.)
|
||
|
*/
|
||
|
if ((parg.p_string = re_comp(pattern)) != NULL)
|
||
|
{
|
||
|
error("%s", &parg);
|
||
|
return (-1);
|
||
|
}
|
||
|
#else
|
||
|
#if REGCMP
|
||
|
if (pattern == NULL || *pattern == '\0')
|
||
|
{
|
||
|
/*
|
||
|
* A null pattern means use the previous pattern.
|
||
|
* The compiled previous pattern is in cpattern, so just use it.
|
||
|
*/
|
||
|
if (cpattern == NULL)
|
||
|
{
|
||
|
error("No previous regular expression", NULL_PARG);
|
||
|
return (-1);
|
||
|
}
|
||
|
} else
|
||
|
{
|
||
|
/*
|
||
|
* Otherwise compile the given pattern.
|
||
|
*/
|
||
|
char *s;
|
||
|
if ((s = regcmp(pattern, 0)) == NULL)
|
||
|
{
|
||
|
error("Invalid pattern", NULL_PARG);
|
||
|
return (-1);
|
||
|
}
|
||
|
if (cpattern != NULL)
|
||
|
free(cpattern);
|
||
|
cpattern = s;
|
||
|
}
|
||
|
#else
|
||
|
#if REGCOMP
|
||
|
if (pattern == NULL || *pattern == '\0')
|
||
|
{
|
||
|
/*
|
||
|
* A null pattern means use the previous pattern.
|
||
|
* The compiled previous pattern is in regpattern,
|
||
|
* so just use it.
|
||
|
*/
|
||
|
if (regpattern == NULL)
|
||
|
{
|
||
|
error("No previous regular expression", NULL_PARG);
|
||
|
return (-1);
|
||
|
}
|
||
|
} else
|
||
|
{
|
||
|
/*
|
||
|
* Otherwise compile the given pattern.
|
||
|
*/
|
||
|
struct regexp *s;
|
||
|
if ((s = regcomp(pattern)) == NULL)
|
||
|
{
|
||
|
error("Invalid pattern", NULL_PARG);
|
||
|
return (-1);
|
||
|
}
|
||
|
if (regpattern != NULL)
|
||
|
free(regpattern);
|
||
|
regpattern = s;
|
||
|
}
|
||
|
#else
|
||
|
if (pattern == NULL || *pattern == '\0')
|
||
|
{
|
||
|
/*
|
||
|
* Null pattern means use the previous pattern.
|
||
|
*/
|
||
|
if (last_pattern == NULL)
|
||
|
{
|
||
|
error("No previous regular expression", NULL_PARG);
|
||
|
return (-1);
|
||
|
}
|
||
|
pattern = last_pattern;
|
||
|
} else
|
||
|
{
|
||
|
strcpy(lpbuf, pattern);
|
||
|
last_pattern = lpbuf;
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Figure out where to start the search.
|
||
|
*/
|
||
|
if (empty_screen())
|
||
|
{
|
||
|
/*
|
||
|
* Start at the beginning (or end) of the file.
|
||
|
* (The empty_screen() case is mainly for
|
||
|
* command line initiated searches;
|
||
|
* for example, "+/xyz" on the command line.)
|
||
|
*/
|
||
|
if (goforw)
|
||
|
pos = ch_zero();
|
||
|
else
|
||
|
{
|
||
|
pos = ch_length();
|
||
|
if (pos == NULL_POSITION)
|
||
|
pos = ch_zero();
|
||
|
}
|
||
|
} else
|
||
|
{
|
||
|
if (how_search)
|
||
|
{
|
||
|
if (goforw)
|
||
|
linenum = BOTTOM_PLUS_ONE;
|
||
|
else
|
||
|
linenum = TOP;
|
||
|
pos = position(linenum);
|
||
|
} else
|
||
|
{
|
||
|
linenum = adjsline(jump_sline);
|
||
|
pos = position(linenum);
|
||
|
if (goforw)
|
||
|
pos = forw_raw_line(pos, (char **)NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pos == NULL_POSITION)
|
||
|
{
|
||
|
/*
|
||
|
* Can't find anyplace to start searching from.
|
||
|
*/
|
||
|
error("Nothing to search", NULL_PARG);
|
||
|
return (-1);
|
||
|
}
|
||
|
|
||
|
linenum = find_linenum(pos);
|
||
|
oldpos = pos;
|
||
|
for (;;)
|
||
|
{
|
||
|
/*
|
||
|
* Get lines until we find a matching one or
|
||
|
* until we hit end-of-file (or beginning-of-file
|
||
|
* if we're going backwards).
|
||
|
*/
|
||
|
if (sigs)
|
||
|
/*
|
||
|
* A signal aborts the search.
|
||
|
*/
|
||
|
return (-1);
|
||
|
|
||
|
if (goforw)
|
||
|
{
|
||
|
/*
|
||
|
* Read the next line, and save the
|
||
|
* starting position of that line in linepos.
|
||
|
*/
|
||
|
linepos = pos;
|
||
|
pos = forw_raw_line(pos, &line);
|
||
|
if (linenum != 0)
|
||
|
linenum++;
|
||
|
} else
|
||
|
{
|
||
|
/*
|
||
|
* Read the previous line and save the
|
||
|
* starting position of that line in linepos.
|
||
|
*/
|
||
|
pos = back_raw_line(pos, &line);
|
||
|
linepos = pos;
|
||
|
if (linenum != 0)
|
||
|
linenum--;
|
||
|
}
|
||
|
|
||
|
if (pos == NULL_POSITION)
|
||
|
{
|
||
|
/*
|
||
|
* We hit EOF/BOF without a match.
|
||
|
*/
|
||
|
return (n);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we're using line numbers, we might as well
|
||
|
* remember the information we have now (the position
|
||
|
* and line number of the current line).
|
||
|
* Don't do it for every line because it slows down
|
||
|
* the search. Remember the line number only if
|
||
|
* we're "far" from the last place we remembered it.
|
||
|
*/
|
||
|
if (linenums && abs(pos - oldpos) > 1024)
|
||
|
{
|
||
|
add_lnum(linenum, pos);
|
||
|
oldpos = pos;
|
||
|
}
|
||
|
|
||
|
if (is_caseless)
|
||
|
{
|
||
|
/*
|
||
|
* If this is a caseless search, convert
|
||
|
* uppercase in the input line to lowercase.
|
||
|
* While we're at it, remove any backspaces
|
||
|
* along with the preceding char.
|
||
|
* This allows us to match text which is
|
||
|
* underlined or overstruck.
|
||
|
*/
|
||
|
for (p = q = line; *p != '\0'; p++, q++)
|
||
|
{
|
||
|
if (*p >= 'A' && *p <= 'Z')
|
||
|
/* Convert uppercase to lowercase. */
|
||
|
*q = *p + 'a' - 'A';
|
||
|
else if (q > line && *p == '\b')
|
||
|
/* Delete BS and preceding char. */
|
||
|
q -= 2;
|
||
|
else
|
||
|
/* Otherwise, just copy. */
|
||
|
*q = *p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Test the next line to see if we have a match.
|
||
|
* This is done in a variety of ways, depending
|
||
|
* on what pattern matching functions are available.
|
||
|
*/
|
||
|
#if REGCMP
|
||
|
line_match = (regex(cpattern, line) != NULL);
|
||
|
#else
|
||
|
#if RECOMP
|
||
|
line_match = (re_exec(line) == 1);
|
||
|
#else
|
||
|
#if REGCOMP
|
||
|
line_match = regexec(regpattern, line);
|
||
|
#else
|
||
|
line_match = match(pattern, line);
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
/*
|
||
|
* We are successful if want_match and line_match are
|
||
|
* both true (want a match and got it),
|
||
|
* or both false (want a non-match and got it).
|
||
|
*/
|
||
|
if (((want_match && line_match) || (!want_match && !line_match)) &&
|
||
|
--n <= 0)
|
||
|
/*
|
||
|
* Found the line.
|
||
|
*/
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
jump_loc(linepos, jump_sline);
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
#if (!REGCMP) && (!RECOMP) && (!REGCOMP)
|
||
|
/*
|
||
|
* We have neither regcmp() nor re_comp().
|
||
|
* We use this function to do simple pattern matching.
|
||
|
* It supports no metacharacters like *, etc.
|
||
|
*/
|
||
|
static int match(char *pattern, char *buf);
|
||
|
|
||
|
static int
|
||
|
match(pattern, buf)
|
||
|
char *pattern, *buf;
|
||
|
{
|
||
|
register char *pp, *lp;
|
||
|
|
||
|
for ( ; *buf != '\0'; buf++)
|
||
|
{
|
||
|
for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
|
||
|
if (*pp == '\0' || *lp == '\0')
|
||
|
break;
|
||
|
if (*pp == '\0')
|
||
|
return (1);
|
||
|
}
|
||
|
return (0);
|
||
|
}
|
||
|
#endif
|