home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d3xx
/
d352
/
mg.lha
/
MG
/
src.LZH
/
mg
/
search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-23
|
15KB
|
685 lines
/*
* Search commands. The functions in this file implement the search commands
* (both plain and incremental searches are supported) and the query-replace
* command.
*
* The plain old search code is part of the original MicroEMACS "distribution".
* The incremental search code, and the query-replace code, is by Rich
* Ellison.
*/
#include "def.h"
#include "line.h"
#include "buffer.h"
#include "window.h"
#ifdef ANSI
#include <string.h>
#endif
static VOID is_cpush PROTO((int cmd));
static VOID is_lpush PROTO((void));
static VOID is_pop PROTO((void));
static int is_peek PROTO((void));
static VOID is_undo PROTO((int *pptr, int *dir));
static int is_find PROTO((int dir));
static VOID is_prompt PROTO((int dir, int flag, int success));
static VOID is_dspl PROTO((char *prompt, int flag));
static int eq PROTO((int bc, int pc));
#define SRCH_BEGIN (0) /* Search sub-codes. */
#define SRCH_FORW (-1)
#define SRCH_BACK (-2)
#define SRCH_NOPR (-3)
#define SRCH_ACCM (-4)
#define SRCH_MARK (-5)
struct srchcom {
int s_code;
struct line *s_dotp;
int s_doto;
};
static struct srchcom cmds[NSRCH];
static int cip;
int srch_lastdir = SRCH_NOPR; /* Last search flags. */
static VOID is_cpush();
static VOID is_lpush();
static VOID is_pop();
static int is_peek();
static VOID is_undo();
static int is_find();
static VOID is_prompt();
static VOID is_dspl();
static int eq();
/*
* Search forward. Get a search string from the user, and search for it,
* starting at ".". If found, "." gets moved to just after the matched
* characters, and display does all the hard stuff. If not found, it just
* prints a message.
*/
/* ARGSUSED */
forwsearch(f, n)
{
register int s;
if ((s = readpattern("Search")) != TRUE)
return s;
if (forwsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", pat);
return FALSE;
}
srch_lastdir = SRCH_FORW;
return TRUE;
}
/*
* Reverse search. Get a search string from the user, and search, starting
* at "." and proceeding toward the front of the buffer. If found "." is left
* pointing at the first character of the pattern [the last character that
* was matched].
*/
/* ARGSUSED */
backsearch(f, n)
{
register int s;
if ((s = readpattern("Search backward")) != TRUE)
return (s);
if (backsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", pat);
return FALSE;
}
srch_lastdir = SRCH_BACK;
return TRUE;
}
/*
* Search again, using the same search string and direction as the last
* search command. The direction has been saved in "srch_lastdir", so you
* know which way to go.
*/
/* ARGSUSED */
searchagain(f, n)
{
if (srch_lastdir == SRCH_FORW) {
if (forwsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", pat);
return FALSE;
}
return TRUE;
}
if (srch_lastdir == SRCH_BACK) {
if (backsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", pat);
return FALSE;
}
return TRUE;
}
ewprintf("No last search");
return FALSE;
}
/*
* Use incremental searching, initially in the forward direction. isearch
* ignores any explicit arguments.
*/
/* ARGSUSED */
forwisearch(f, n)
{
return isearch(SRCH_FORW);
}
/*
* Use incremental searching, initially in the reverse direction. isearch
* ignores any explicit arguments.
*/
/* ARGSUSED */
backisearch(f, n)
{
return isearch(SRCH_BACK);
}
/*
* Incremental Search. dir is used as the initial direction to search. ^S
* switch direction to forward ^R switch direction to reverse ^Q quote
* next character (allows searching for ^N etc.) <ESC> exit from Isearch
* <DEL> undoes last character typed. (tricky job to do this
* correctly). other ^ exit search, don't set mark else accumulate into
* search string
*/
isearch(dir)
{
register int c;
register struct line *clp;
register int cbo;
register int success;
int pptr;
char opat[NPAT];
VOID ungetkey();
for (cip = 0; cip < NSRCH; cip++)
cmds[cip].s_code = SRCH_NOPR;
(VOID) strcpy(opat, pat);
cip = 0;
pptr = -1;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
is_lpush();
is_cpush(SRCH_BEGIN);
success = TRUE;
is_prompt(dir, TRUE, success);
for (;;) {
update();
switch (c = getkey(DISSCR)) {
case CCHR('['):
srch_lastdir = dir;
curwp->w_markp = clp;
curwp->w_marko = cbo;
ewprintf("Mark set");
return (TRUE);
case CCHR('G'):
if (success != TRUE) {
while (is_peek() == SRCH_ACCM)
is_undo(&pptr, &dir);
success = TRUE;
is_prompt(dir, pptr < 0, success);
break;
}
curwp->w_dotp = clp;
curwp->w_doto = cbo;
curwp->w_flag |= WFMOVE;
srch_lastdir = dir;
(VOID) ctrlg(FFRAND, 0);
(VOID) strcpy(pat, opat);
return ABORT;
case CCHR(']'):
case CCHR('S'):
if (dir == SRCH_BACK) {
dir = SRCH_FORW;
is_lpush();
is_cpush(SRCH_FORW);
success = TRUE;
}
if (success == FALSE && dir == SRCH_FORW)
break;
is_lpush();
pptr = strlen(pat);
(VOID) forwchar(FFRAND, 1);
if (is_find(SRCH_FORW) != FALSE)
is_cpush(SRCH_MARK);
else {
(VOID) backchar(FFRAND, 1);
ttbeep();
success = FALSE;
}
is_prompt(dir, pptr < 0, success);
break;
case CCHR('R'):
if (dir == SRCH_FORW) {
dir = SRCH_BACK;
is_lpush();
is_cpush(SRCH_BACK);
success = TRUE;
}
if (success == FALSE && dir == SRCH_BACK)
break;
is_lpush();
pptr = strlen(pat);
(VOID) backchar(FFRAND, 1);
if (is_find(SRCH_BACK) != FALSE)
is_cpush(SRCH_MARK);
else {
(VOID) forwchar(FFRAND, 1);
ttbeep();
success = FALSE;
}
is_prompt(dir, pptr < 0, success);
break;
case CCHR('H'):
case CCHR('?'):
is_undo(&pptr, &dir);
if (is_peek() != SRCH_ACCM)
success = TRUE;
is_prompt(dir, pptr < 0, success);
break;
case CCHR('\\'):
case CCHR('Q'):
c = (char) getkey(DISSCR);
goto addchar;
case CCHR('M'):
c = CCHR('J');
goto addchar;
default:
if (ISCTRL(c)) {
ungetkey(c);
curwp->w_markp = clp;
curwp->w_marko = cbo;
ewprintf("Mark set");
curwp->w_flag |= WFMOVE;
return TRUE;
} /* and continue */
case CCHR('I'):
case CCHR('J'):
addchar:
if (pptr == -1)
pptr = 0;
if (pptr == 0)
success = TRUE;
pat[pptr++] = c;
if (pptr == NPAT) {
ewprintf("Pattern too long");
return FALSE;
}
pat[pptr] = '\0';
is_lpush();
if (success != FALSE) {
if (is_find(dir) != FALSE)
is_cpush(c);
else {
success = FALSE;
ttbeep();
is_cpush(SRCH_ACCM);
}
} else
is_cpush(SRCH_ACCM);
is_prompt(dir, FALSE, success);
}
}
/* NOTREACHED */
}
static VOID
is_cpush(cmd)
register int cmd;
{
if (++cip >= NSRCH)
cip = 0;
cmds[cip].s_code = cmd;
}
static VOID
is_lpush()
{
register int ctp;
ctp = cip + 1;
if (ctp >= NSRCH)
ctp = 0;
cmds[ctp].s_code = SRCH_NOPR;
cmds[ctp].s_doto = curwp->w_doto;
cmds[ctp].s_dotp = curwp->w_dotp;
}
static VOID
is_pop()
{
if (cmds[cip].s_code != SRCH_NOPR) {
curwp->w_doto = cmds[cip].s_doto;
curwp->w_dotp = cmds[cip].s_dotp;
curwp->w_flag |= WFMOVE;
cmds[cip].s_code = SRCH_NOPR;
}
if (--cip <= 0)
cip = NSRCH - 1;
}
static int
is_peek()
{
return cmds[cip].s_code;
}
/* this used to always return TRUE (the return value was checked) */
static VOID
is_undo(pptr, dir)
register int *pptr;
register int *dir;
{
register int redo = FALSE;
switch (cmds[cip].s_code) {
case SRCH_BEGIN:
case SRCH_NOPR:
*pptr = -1;
case SRCH_MARK:
break;
case SRCH_FORW:
*dir = SRCH_BACK;
redo = TRUE;
break;
case SRCH_BACK:
*dir = SRCH_FORW;
redo = TRUE;
break;
case SRCH_ACCM:
default:
*pptr -= 1;
if (*pptr < 0)
*pptr = 0;
pat[*pptr] = '\0';
break;
}
is_pop();
if (redo)
is_undo(pptr, dir);
}
static int
is_find(dir)
register int dir;
{
register int plen, odoto;
register struct line *odotp;
odoto = curwp->w_doto;
odotp = curwp->w_dotp;
plen = strlen(pat);
if (plen != 0) {
if (dir == SRCH_FORW) {
(VOID) backchar(FFARG | FFRAND, plen);
if (forwsrch() == FALSE) {
curwp->w_doto = odoto;
curwp->w_dotp = odotp;
return FALSE;
}
return TRUE;
}
if (dir == SRCH_BACK) {
(VOID) forwchar(FFARG | FFRAND, plen);
if (backsrch() == FALSE) {
curwp->w_doto = odoto;
curwp->w_dotp = odotp;
return FALSE;
}
return TRUE;
}
ewprintf("bad call to is_find");
return FALSE;
}
return FALSE;
}
/*
* If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used
* to print an error message. It also used to return TRUE or FALSE, depending
* on if it liked the "dir". However, none of the callers looked at the
* status, so I just made the checking vanish.
*/
static VOID
is_prompt(dir, flag, success)
{
if (dir == SRCH_FORW) {
if (success != FALSE)
is_dspl("I-search", flag);
else
is_dspl("Failing I-search", flag);
} else if (dir == SRCH_BACK) {
if (success != FALSE)
is_dspl("I-search backward", flag);
else
is_dspl("Failing I-search backward", flag);
} else
ewprintf("Broken call to is_prompt");
}
/*
* Prompt writing routine for the incremental search. The "prompt" is just a
* string. The "flag" determines whether pat should be printed.
*/
static VOID
is_dspl(prompt, flag)
char *prompt;
{
if (flag != FALSE)
ewprintf("%s: ", prompt);
else
ewprintf("%s: %s", prompt, pat);
}
/*
* Query Replace. Replace strings selectively. Does a search and replace
* operation.
*/
/* ARGSUSED */
queryrepl(f, n)
{
register int s;
register int rcnt = 0; /* Replacements made so far */
register int plen; /* length of found string */
char news[NPAT]; /* replacement string */
if ((s = readpattern("Query replace")) != TRUE)
return (s);
if ((s = ereply("Query replace %s with: ", news, NPAT, pat)) == ABORT)
return (s);
if (s == FALSE)
news[0] = '\0';
ewprintf("Query replacing %s with %s:", pat, news);
plen = strlen(pat);
/*
* Search forward repeatedly, checking each time whether to insert or
* not. The "!" case makes the check always true, so it gets put
* into a tighter loop for efficiency.
*/
while (forwsrch() == TRUE) {
retry:
update();
switch (getkey(DISSCR)) {
case ' ':
case 'y':
if (lreplace((RSIZE) plen, news, f) == FALSE)
return (FALSE);
rcnt++;
break;
case '.':
if (lreplace((RSIZE) plen, news, f) == FALSE)
return (FALSE);
rcnt++;
goto stopsearch;
case CCHR('G'):/* ^G or ESC */
(VOID) ctrlg(FFRAND, 0);
case CCHR('['):
case 'q':
goto stopsearch;
case '!':
do {
if (lreplace((RSIZE) plen, news, f) == FALSE)
return (FALSE);
rcnt++;
} while (forwsrch() == TRUE);
goto stopsearch;
case CCHR('H'):
case CCHR('?'):
case ('n'):
break;
default:
ewprintf(
"<SP>,'y' replace, <DEL>,'n' don't, <ESC>,'q' quit, '.' rep-end, '!' repl rest");
goto retry;
}
}
stopsearch:
curwp->w_flag |= WFHARD;
update();
if (rcnt == 0)
ewprintf("(No replacements done)");
else if (rcnt == 1)
ewprintf("(1 replacement done)");
else
ewprintf("(%d replacements done)", rcnt);
return TRUE;
}
/*
* This routine does the real work of a forward search. The pattern is
* sitting in the external variable "pat". If found, dot is updated, the
* window system is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
forwsrch()
{
register struct line *clp;
register int cbo;
register struct line *tlp;
register int tbo;
char *pp;
register int c;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
for (;;) {
if (cbo == llength(clp)) {
if ((clp = lforw(clp)) == curbp->b_linep)
break;
cbo = 0;
c = CCHR('J');
} else
c = lgetc(clp, cbo++);
if (eq(c, pat[0]) != FALSE) {
tlp = clp;
tbo = cbo;
pp = &pat[1];
while (*pp != 0) {
if (tbo == llength(tlp)) {
tlp = lforw(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = 0;
c = CCHR('J');
} else
c = lgetc(tlp, tbo++);
if (eq(c, *pp++) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return TRUE;
}
fail: ;
}
return FALSE;
}
/*
* This routine does the real work of a backward search. The pattern is
* sitting in the external variable "pat". If found, dot is updated, the
* window system is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
backsrch()
{
register struct line *clp;
register int cbo;
register struct line *tlp;
register int tbo;
register int c;
register char *epp;
register char *pp;
for (epp = &pat[0]; epp[1] != 0; ++epp);
clp = curwp->w_dotp;
cbo = curwp->w_doto;
for (;;) {
if (cbo == 0) {
clp = lback(clp);
if (clp == curbp->b_linep)
return FALSE;
cbo = llength(clp) + 1;
}
if (--cbo == llength(clp))
c = CCHR('J');
else
c = lgetc(clp, cbo);
if (eq(c, *epp) != FALSE) {
tlp = clp;
tbo = cbo;
pp = epp;
while (pp != &pat[0]) {
if (tbo == 0) {
tlp = lback(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = llength(tlp) + 1;
}
if (--tbo == llength(tlp))
c = CCHR('J');
else
c = lgetc(tlp, tbo);
if (eq(c, *--pp) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return TRUE;
}
fail: ;
}
/* NOTREACHED */
}
/*
* Compare two characters. The "bc" comes from the buffer. It has its case
* folded out. The "pc" is from the pattern.
*/
static int
eq(bc, pc)
register int bc, pc;
{
bc = CHARMASK(bc);
pc = CHARMASK(pc);
if (bc == pc)
return TRUE;
if (ISUPPER(bc))
return TOLOWER(bc) == pc;
if (ISUPPER(pc))
return bc == TOLOWER(pc);
return FALSE;
}
/*
* Read a pattern. Stash it in the external variable "pat". The "pat" is not
* updated if the user types in an empty line. If the user typed an empty
* line, and there is no old pattern, it is an error. Display the old
* pattern, in the style of Jeff Lomicka. There is some do-it-yourself
* control expansion.
*/
readpattern(prompt)
char *prompt;
{
register int s;
char tpat[NPAT];
if (pat[0] == '\0')
s = ereply("%s: ", tpat, NPAT, prompt);
else
s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
if (s == TRUE) /* Specified */
(VOID) strcpy(pat, tpat);
else if (s == FALSE && pat[0] != 0) /* CR, but old one */
s = TRUE;
return s;
}