home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
CMDS
/
pvic_10a.lzh
/
SRCE
/
search.c
< prev
next >
Wrap
Text File
|
1998-04-23
|
20KB
|
1,016 lines
/*
* This file contains various searching-related routines. These fall into
* three groups: string searches (for /, ?, n, and N), character searches
* within a single line (for f, F, t, T, etc), and "other" kinds of searches
* like the '%' command, and 'word' searches.
*
* v1.1 Toad Hall Tweak, 20 Apr 90
*/
#include <stdio.h>
#include "pvic.h"
#include "locdefs.h"
/*
* String searches
*
* The actual searches are done using Henry Spencer's regular expression
* library.
*/
#define BEGWORD "([^a-zA-Z0-9_]|^)" /* replaces "\<" in search strings */
#define ENDWORD "([^a-zA-Z0-9_]|$)" /* likewise replaces "\>" */
#define BEGCHAR(c) (is_lower(c) || is_upper(c) || is_digit(c) || ((c) == '_'))
int begin_word; /* does the search include a 'begin word' match */
/*
* mapstring(s) - map special backslash sequences
*/
static char *
mapstring(s)
register char *s;
{
static char ns[80];
register char *p;
begin_word = (0);
for (p = ns; *s ;s++) {
if (*s != '\\') { /* not an escape */
*p++ = *s;
continue;
}
switch (*++s) {
case '/':
*p++ = '/';
break;
case '<':
strcpy(p, BEGWORD);
p += strlen(BEGWORD);
begin_word = (1);
break;
case '>':
strcpy(p, ENDWORD);
p += strlen(ENDWORD);
break;
default:
*p++ = '\\';
*p++ = *s;
break;
}
}
*p++ = '\0';
return ns;
}
static char *laststr = NULL;
static int lastsdir;
static LPTR * ssearch(dir,str)
int dir; /* SEARCH_FORWARD or SEARCH_BACKWARD */
char *str;
{
LPTR *bcksearch(), *fwdsearch();
LPTR *pos;
char *old_ls = laststr;
reg_ic = PARAMETER_VALUE(PARAMETER_IGNORECASE); /* tell the regexp routines how to search */
laststr = strsave(str);
lastsdir = dir;
if (old_ls != NULL)
free(old_ls);
if (dir == SEARCH_BACKWARD) {
show_message("?%s", laststr);
pos = bcksearch(mapstring(laststr));
} else {
show_message("/%s", laststr);
pos = fwdsearch(mapstring(laststr));
}
/*
* This is kind of a kludge, but its needed to make
* 'beginning of word' searches land on the right place.
*/
if (pos != NULL && begin_word) {
if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
pos->index += 1;
}
return pos;
}
int do_search(dir,str)
int dir;
char *str;
{
LPTR *p;
if (str == NULL)
str = laststr;
if (str == NULL)
{
error_message("No remembered regular expression");
return 0;
}
local_reset_control_c_pressed();
if ((p = ssearch(dir,str)) == NULL) {
if (local_control_c_pressed())
{
clear_screen();
update_screen(0);
msg("Interrupt");
}
else
msg("Pattern not found");
local_reset_control_c_pressed();
return (0);
} else {
update_cursor(0);
/*
* if we're backing up, we make sure the line we're on
* is on the screen.
*/
set_pc_mark();
*cursor_char = *p; /* v1.1 */
set_wanted_cursor_column = (1);
update_cursor(0);
return (1);
}
}
#define OTHERDIR(x) (((x) == SEARCH_FORWARD) ? SEARCH_BACKWARD : SEARCH_FORWARD)
int rep_search(flag)
int flag;
{
int dir = lastsdir;
int found;
if ( laststr == NULL )
{
error_message("No remembered regular expression");
return 0;
}
found = do_search(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
/*
* We have to save and restore 'lastsdir' because it gets munged
* by ssearch() and winds up saving the wrong direction from here
* if 'flag' is true.
*/
lastsdir = dir;
return found;
}
/*
* reg_error - called by regexp routines when errors are detected.
*/
void reg_error(s)
char *s;
{
error_message(s);
}
static LPTR * fwdsearch(str)
register char *str;
{
static LPTR infile;
register LPTR *p;
regexp *prog;
register char *s;
register int i;
if ((prog = reg_comp(str)) == NULL) {
error_message("Invalid search string");
return NULL;
}
p = cursor_char;
i = cursor_char->index + 1;
do {
s = p->linep->s + i;
if (reg_exec(prog, s, i == 0)) { /* got a match */
infile.linep = p->linep;
infile.index = (int) (prog->startp[0] - p->linep->s);
free((char *)prog);
return (&infile);
}
i = 0;
if (local_control_c_pressed())
{
clear_screen();
update_screen(0);
goto fwdfail;
}
} while ((p = next_line(p)) != NULL);
/*
* If wrapscan isn't set, then don't scan from the beginning
* of the file. Just return failure here.
*/
if (!PARAMETER_VALUE(PARAMETER_WRAPSCAN))
goto fwdfail;
/* search from the beginning of the file to cursor_char */
for (p = file_memory; p != NULL ;p = next_line(p)) {
s = p->linep->s;
if (reg_exec(prog, s, (1))) { /* got a match */
infile.linep = p->linep;
infile.index = (int) (prog->startp[0] - s);
free((char *)prog);
return (&infile);
}
if (p->linep == cursor_char->linep)
break;
if (local_control_c_pressed())
{
clear_screen();
update_screen(0);
goto fwdfail;
}
}
fwdfail:
free((char *)prog);
return NULL;
}
static LPTR * bcksearch(str)
char *str;
{
static LPTR infile;
register LPTR *p = &infile;
register char *s;
register int i;
register char *match;
regexp *prog;
/* make sure str isn't empty */
if (str == NULL || *str == '\0')
return NULL;
if ((prog = reg_comp(str)) == NULL) {
error_message("Invalid search string");
return NULL;
}
*p = *cursor_char;
if (dec(p) == -1) { /* already at start of file? */
*p = *end_of_file;
p->index = strlen(p->linep->s) - 1;
}
if (begin_word) /* so we don't get stuck on one match */
dec(p);
i = p->index;
do {
s = p->linep->s;
if (reg_exec(prog, s, (1))) { /* match somewhere on line */
/*
* 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.
*/
match = prog->startp[0];
while (reg_exec(prog, prog->endp[0], (0))) {
if ((i >= 0) && ((prog->startp[0] - s) > i))
break;
match = prog->startp[0];
}
if ((i >= 0) && ((match - s) > i)) {
i = -1;
continue;
}
infile.linep = p->linep;
infile.index = (int) (match - s);
free((char *)prog);
return (&infile);
}
i = -1;
if (local_control_c_pressed())
{
clear_screen();
update_screen(0);
goto bckfail;
}
} while ((p = previous_line(p)) != NULL);
/*
* If wrapscan isn't set, bag the search now
*/
if (!PARAMETER_VALUE(PARAMETER_WRAPSCAN))
goto bckfail;
/* search backward from the end of the file */
p = previous_line(end_of_file);
do {
s = p->linep->s;
if (reg_exec(prog, s, (1))) { /* match somewhere on line */
/*
* Now, if there are multiple matches on this line,
* we have to get the last one.
*/
match = prog->startp[0];
while (reg_exec(prog, prog->endp[0], (0)))
match = prog->startp[0];
infile.linep = p->linep;
infile.index = (int) (match - s);
free((char *)prog);
return (&infile);
}
if (p->linep == cursor_char->linep)
break;
if (local_control_c_pressed())
{
clear_screen();
update_screen(0);
goto bckfail;
}
} while ((p = previous_line(p)) != NULL);
bckfail:
free((char *)prog);
return NULL;
}
/*
* do_sub(lp, up, cmd)
*
* Perform a substitution from line 'lp' to line 'up' using the
* command pointed to by 'cmd' which should be of the form:
*
* /pattern/substitution/g
*
* The trailing 'g' is optional and, if present, indicates that multiple
* substitutions should be performed on each line, if applicable.
* The usual escapes are supported as described in the regexp docs.
*/
void do_sub(lp, up, cmd)
LPTR *lp, *up;
char *cmd;
{
LINE *cp;
char *pat, *sub;
regexp *prog;
int nsubs;
int do_all; /* do multiple substitutions per line */
int i;
/*
* If no range was given, do the current line. If only one line
* was given, just do that one.
*/
if (lp->linep == NULL)
*up = *lp = *cursor_char;
else {
if (up->linep == NULL)
*up = *lp;
}
pat = ++cmd; /* skip the initial '/' */
while (*cmd) {
if (*cmd == '\\') /* next char is quoted */
cmd += 2;
else if (*cmd == '/') { /* delimiter */
*cmd++ = '\0';
break;
} else
cmd++; /* regular character */
}
if (pat[0] == '\0' || pat[0]=='|')
{
error_message("NULL pattern specified");
return;
}
for(i=1;;i++)
{
if( (pat[i]=='\0' || pat[i]=='|') && pat[i-1]=='|')
{
error_message("NULL pattern specified");
return;
}
if(pat[i]=='\0')break;
}
sub = cmd;
do_all = (0);
while (*cmd) {
if (*cmd == '\\') /* next char is quoted */
cmd += 2;
else if (*cmd == '/') { /* delimiter */
do_all = (cmd[1] == 'g');
*cmd++ = '\0';
break;
} else
cmd++; /* regular character */
}
reg_ic = PARAMETER_VALUE(PARAMETER_IGNORECASE); /* set "ignore case" flag appropriately */
if ((prog = reg_comp(pat)) == NULL) {
error_message("Invalid search string");
return;
}
nsubs = 0;
for (cp = lp->linep; cp != NULL ;cp = cp->next) {
if (reg_exec(prog, cp->s, (1))) { /* a match on this line */
char *ns, *sns, *p;
/*
* Get some space for a temporary buffer
* to do the substitution into.
*/
sns = ns = alloc(2048);
if (!sns) return;
*sns = '\0';
p = cp->s;
do {
for (ns = sns; *ns ;ns++)
;
/*
* copy up to the part that matched
*/
while (p < prog->startp[0])
*ns++ = *p++;
reg_sub(prog, sub, ns);
/*
* continue searching after the match
*/
p = prog->endp[0];
} while (reg_exec(prog, p, (0)) && do_all);
for (ns = sns; *ns ;ns++)
;
/*
* copy the rest of the line, that didn't match
*/
while (*p)
*ns++ = *p++;
*ns = '\0';
free(cp->s); /* free the original line */
cp->s = strsave(sns); /* and save the modified str */
cp->size = strlen(cp->s) + 1;
free(sns); /* free the temp buffer */
nsubs++;
CHANGED;
}
if (cp == up->linep)
break;
}
if (nsubs) {
update_screen(0);
if (nsubs >= PARAMETER_VALUE(PARAMETER_REPORT))
show_message("%d substitution%c", nsubs, (nsubs>1) ? 's' : ' ');
} else
msg("No match");
free((char *)prog);
}
/*
* do_glob(cmd)
*
* Execute a global command of the form:
*
* g/pattern/X
*
* where 'x' is a command character, currently one of the following:
*
* d Delete all matching lines
* p Print all matching lines
*
* The command character (as well as the trailing slash) is optional, and
* is assumed to be 'p' if missing.
*/
void
do_glob(lp, up, cmd)
LPTR *lp, *up;
char *cmd;
{
LINE *cp;
char *pat;
regexp *prog;
int ndone;
char cmdchar = '\0'; /* what to do with matching lines */
/*
* If no range was given, do every line. If only one line
* was given, just do that one.
*/
if (lp->linep == NULL) {
*lp = *file_memory;
*up = *end_of_file;
} else {
if (up->linep == NULL)
*up = *lp;
}
pat = ++cmd; /* skip the initial '/' */
while (*cmd) {
if (*cmd == '\\') /* next char is quoted */
cmd += 2;
else if (*cmd == '/') { /* delimiter */
cmdchar = cmd[1];
*cmd++ = '\0';
break;
} else
cmd++; /* regular character */
}
if (cmdchar == '\0')
cmdchar = 'p';
reg_ic = PARAMETER_VALUE(PARAMETER_IGNORECASE); /* set "ignore case" flag appropriately */
if (cmdchar != 'd' && cmdchar != 'p') {
error_message("Invalid command character");
return;
}
if ((prog = reg_comp(pat)) == NULL) {
error_message("Invalid search string");
return;
}
msg("");
ndone = 0;
local_reset_control_c_pressed();
for (cp = lp->linep; cp != NULL && !local_control_c_pressed() ;cp = cp->next) {
if (reg_exec(prog, cp->s, (1))) { /* a match on this line */
switch (cmdchar) {
case 'd': /* delete the line */
if (cursor_char->linep != cp) {
LPTR savep;
savep = *cursor_char;
cursor_char->linep = cp;
cursor_char->index = 0;
delete_line(1, (0));
*cursor_char = savep;
} else
delete_line(1, (0));
break;
case 'p': /* print the line */
prt_line(cp->s);
fprintf(stdout,"%s","r\n");
break;
}
ndone++;
}
if (cp == up->linep)
break;
}
if (ndone) {
switch (cmdchar) {
case 'd':
update_screen(0);
if(ndone >= PARAMETER_VALUE(PARAMETER_REPORT))
{
show_message("%d fewer line%c",
ndone,
(ndone > 1) ? 's' : ' ');
}
if(local_control_c_pressed())
{
clear_screen();
update_screen(0);
show_message("Interrupt %d fewer line%c",
ndone,
(ndone > 1) ? 's' : ' ');
}
break;
case 'p':
wait_return();
break;
}
} else {
if (local_control_c_pressed())
{
clear_screen();
update_screen(0);
msg("Interrupt");
}
else msg("No match");
}
local_reset_control_c_pressed();
free((char *)prog);
}
/*
* Character Searches
*/
static char lastc = '\0'; /* last character searched for */
static int lastcdir; /* last direction of character search */
static int lastctype; /* last type of search ("find" or "to") */
/*
* search_char(c, dir, type)
*
* 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.
*/
int search_char(c, dir, type)
char c;
int dir;
int type;
{
LPTR save;
save = *cursor_char; /* save position in case we fail */
lastc = c;
lastcdir = dir;
lastctype = type;
/*
* On 'to' searches, skip one to start with so we can repeat
* searches in the same direction and have it work right.
*/
if (type)
(dir == SEARCH_FORWARD) ? one_right() : one_left();
while ( (dir == SEARCH_FORWARD) ? one_right() : one_left() ) {
if (gchar(cursor_char) == c) {
if (type)
(dir == SEARCH_FORWARD) ? one_left() : one_right();
return (1);
}
}
*cursor_char = save;
return (0);
}
int crep_search(flag)
int flag;
{
int dir = lastcdir;
int rval;
if (lastc == '\0')
return (0);
rval = search_char(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
lastcdir = dir; /* restore dir., since it may have changed */
return rval;
}
/*
* "Other" Searches
*/
/*
* show_match - move the cursor to the matching paren or brace
*/
LPTR * show_match()
{
static LPTR pos;
int (*move)(), inc(), dec();
char initc = gchar(cursor_char); /* initial char */
char findc; /* terminating char */
char c;
int count = 0;
pos = *cursor_char; /* set starting point */
switch (initc) {
case '(':
findc = ')';
move = inc;
break;
case ')':
findc = '(';
move = dec;
break;
case '{':
findc = '}';
move = inc;
break;
case '}':
findc = '{';
move = dec;
break;
case '[':
findc = ']';
move = inc;
break;
case ']':
findc = '[';
move = dec;
break;
default:
return (LPTR *) NULL;
}
while ((*move)(&pos) != -1) { /* until end of file */
c = gchar(&pos);
if (c == initc)
count++;
else if (c == findc) {
if (count == 0)
return &pos;
count--;
}
}
return (LPTR *) NULL; /* never found it */
}
/*
* 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 */
#define C0(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\0'))
#define C1(c) (is_alpha(c) || is_digit(c) || ((c) == '_'))
/*
* cls(c) - returns the class of character 'c'
*
* 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(c)
char c;
{
if (C0(c))
return 0;
if (C1(c))
return 1;
/*
* If stype is non-zero, report these as class 1.
*/
return (stype == 0) ? 2 : 1;
}
/*
* forward_word(pos, type) - move forward one word
*
* Returns the resulting position, or NULL if EOF was reached.
*/
LPTR * forward_word(p, type)
LPTR *p;
int type;
{
static LPTR pos;
int sclass = cls(gchar(p)); /* starting class */
pos = *p;
stype = type;
/*
* We always move at least one character.
*/
if (inc(&pos) == -1)
return NULL;
if (sclass != 0) {
while (cls(gchar(&pos)) == sclass) {
if (inc(&pos) == -1)
return NULL;
}
/*
* If we went from 1 -> 2 or 2 -> 1, return here.
*/
if (cls(gchar(&pos)) != 0)
return &pos;
}
/* We're in white space; go to next non-white */
while (cls(gchar(&pos)) == 0) {
/*
* We'll stop if we land on a blank line
*/
if (pos.index == 0 && pos.linep->s[0] == '\0')
break;
if (inc(&pos) == -1)
return NULL;
}
return &pos;
}
/*
* back_word(pos, type) - move backward one word
*
* Returns the resulting position, or NULL if EOF was reached.
*/
LPTR * back_word(p, type)
LPTR *p;
int type;
{
static LPTR pos;
int sclass = cls(gchar(p)); /* starting class */
pos = *p;
stype = type;
if (dec(&pos) == -1)
return NULL;
/*
* If we're in the middle of a word, we just have to
* back up to the start of it.
*/
if (cls(gchar(&pos)) == sclass && sclass != 0) {
/*
* Move backward to start of the current word
*/
while (cls(gchar(&pos)) == sclass) {
if (dec(&pos) == -1)
return NULL;
}
inc(&pos); /* overshot - forward one */
return &pos;
}
/*
* We were at the start of a word. Go back to the start
* of the prior word.
*/
while (cls(gchar(&pos)) == 0) { /* skip any white space */
/*
* We'll stop if we land on a blank line
*/
if (pos.index == 0 && pos.linep->s[0] == '\0')
return &pos;
if (dec(&pos) == -1)
return NULL;
}
sclass = cls(gchar(&pos));
/*
* Move backward to start of this word.
*/
while (cls(gchar(&pos)) == sclass) {
if (dec(&pos) == -1)
return NULL;
}
inc(&pos); /* overshot - forward one */
return &pos;
}
/*
* end_word(pos, type, in_change) - 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.
*
* There's a strange special case here that the 'in_change' parameter
* helps us deal with. Vi effectively turns 'cw' into 'ce'. If we're on
* a word with only one character, we need to stick at the current
* position so we don't change two words.
*
* Returns the resulting position, or NULL if EOF was reached.
*/
LPTR * end_word(p, type, in_change)
LPTR *p;
int type;
int in_change;
{
static LPTR pos;
int sclass = cls(gchar(p)); /* starting class */
pos = *p;
stype = type;
if (inc(&pos) == -1)
return NULL;
/*
* If we're in the middle of a word, we just have to
* move to the end of it.
*/
if (cls(gchar(&pos)) == sclass && sclass != 0) {
/*
* Move forward to end of the current word
*/
while (cls(gchar(&pos)) == sclass) {
if (inc(&pos) == -1)
return NULL;
}
dec(&pos); /* overshot - forward one */
return &pos;
}
/*
* We were at the end of a word. Go to the end of the next
* word, unless we're doing a change. In that case we stick
* at the end of the current word.
*/
if (in_change)
return p;
while (cls(gchar(&pos)) == 0) { /* skip any white space */
if (inc(&pos) == -1)
return NULL;
}
sclass = cls(gchar(&pos));
/*
* Move forward to end of this word.
*/
while (cls(gchar(&pos)) == sclass) {
if (inc(&pos) == -1)
return NULL;
}
dec(&pos); /* overshot - forward one */
return &pos;
}