Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/search.c
- This file was added.
/* $OpenBSD: search.c,v 1.47 2018/07/11 12:21:37 krw Exp $ */ | |||||
/* This file is in the public domain. */ | |||||
/* | |||||
* 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 <sys/queue.h> | |||||
#include <ctype.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "def.h" | |||||
#include "macro.h" | |||||
#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; | |||||
int s_dotline; | |||||
}; | |||||
static int isearch(int); | |||||
static void is_cpush(int); | |||||
static void is_lpush(void); | |||||
static void is_pop(void); | |||||
static int is_peek(void); | |||||
static void is_undo(int *, int *); | |||||
static int is_find(int); | |||||
static void is_prompt(int, int, int); | |||||
static void is_dspl(char *, int); | |||||
static int eq(int, int, int); | |||||
static struct srchcom cmds[NSRCH]; | |||||
static int cip; | |||||
int srch_lastdir = SRCH_NOPR; /* Last search flags. */ | |||||
/* | |||||
* 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 */ | |||||
int | |||||
forwsearch(int f, int n) | |||||
{ | |||||
int s; | |||||
if ((s = readpattern("Search")) != TRUE) | |||||
return (s); | |||||
if (forwsrch() == FALSE) { | |||||
dobeep(); | |||||
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 */ | |||||
int | |||||
backsearch(int f, int n) | |||||
{ | |||||
int s; | |||||
if ((s = readpattern("Search backward")) != TRUE) | |||||
return (s); | |||||
if (backsrch() == FALSE) { | |||||
dobeep(); | |||||
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 */ | |||||
int | |||||
searchagain(int f, int n) | |||||
{ | |||||
if (srch_lastdir == SRCH_FORW) { | |||||
if (forwsrch() == FALSE) { | |||||
dobeep(); | |||||
ewprintf("Search failed: \"%s\"", pat); | |||||
return (FALSE); | |||||
} | |||||
return (TRUE); | |||||
} | |||||
if (srch_lastdir == SRCH_BACK) { | |||||
if (backsrch() == FALSE) { | |||||
dobeep(); | |||||
ewprintf("Search failed: \"%s\"", pat); | |||||
return (FALSE); | |||||
} | |||||
return (TRUE); | |||||
} | |||||
dobeep(); | |||||
ewprintf("No last search"); | |||||
return (FALSE); | |||||
} | |||||
/* | |||||
* Use incremental searching, initially in the forward direction. | |||||
* isearch ignores any explicit arguments. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
forwisearch(int f, int n) | |||||
{ | |||||
if (macrodef || inmacro) | |||||
/* We can't isearch in macro. Use search instead */ | |||||
return (forwsearch(f,n)); | |||||
else | |||||
return (isearch(SRCH_FORW)); | |||||
} | |||||
/* | |||||
* Use incremental searching, initially in the reverse direction. | |||||
* isearch ignores any explicit arguments. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
backisearch(int f, int n) | |||||
{ | |||||
if (macrodef || inmacro) | |||||
/* We can't isearch in macro. Use search instead */ | |||||
return (backsearch(f,n)); | |||||
else | |||||
return (isearch(SRCH_BACK)); | |||||
} | |||||
/* | |||||
* Incremental Search. | |||||
* dir is used as the initial direction to search. | |||||
* ^M exit from Isearch, set mark | |||||
* ^S switch direction to forward | |||||
* ^R switch direction to reverse | |||||
* ^Q quote next character (allows searching for ^N etc.) | |||||
* <ESC> exit from Isearch, set mark | |||||
* <DEL> undoes last character typed. (tricky job to do this correctly). | |||||
* other ^ exit search, don't set mark | |||||
* else accumulate into search string | |||||
*/ | |||||
static int | |||||
isearch(int dir) | |||||
{ | |||||
struct line *clp; /* Saved line pointer */ | |||||
int c; | |||||
int cbo; /* Saved offset */ | |||||
int success; | |||||
int pptr; | |||||
int firstc; | |||||
int xcase; | |||||
int i; | |||||
char opat[NPAT]; | |||||
int cdotline; /* Saved line number */ | |||||
if (macrodef) { | |||||
dobeep(); | |||||
ewprintf("Can't isearch in macro"); | |||||
return (FALSE); | |||||
} | |||||
for (cip = 0; cip < NSRCH; cip++) | |||||
cmds[cip].s_code = SRCH_NOPR; | |||||
(void)strlcpy(opat, pat, sizeof(opat)); | |||||
cip = 0; | |||||
pptr = -1; | |||||
clp = curwp->w_dotp; | |||||
cbo = curwp->w_doto; | |||||
cdotline = curwp->w_dotline; | |||||
is_lpush(); | |||||
is_cpush(SRCH_BEGIN); | |||||
success = TRUE; | |||||
is_prompt(dir, TRUE, success); | |||||
for (;;) { | |||||
update(CMODE); | |||||
switch (c = getkey(FALSE)) { | |||||
case CCHR('['): | |||||
/* | |||||
* If new characters come in the next 300 msec, | |||||
* we can assume that they belong to a longer | |||||
* escaped sequence so we should ungetkey the | |||||
* ESC to avoid writing out garbage. | |||||
*/ | |||||
if (ttwait(300) == FALSE) | |||||
ungetkey(c); | |||||
/* FALLTHRU */ | |||||
case CCHR('M'): | |||||
srch_lastdir = dir; | |||||
curwp->w_markp = clp; | |||||
curwp->w_marko = cbo; | |||||
curwp->w_markline = cdotline; | |||||
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_dotline = cdotline; | |||||
curwp->w_rflag |= WFMOVE; | |||||
srch_lastdir = dir; | |||||
(void)ctrlg(FFRAND, 0); | |||||
(void)strlcpy(pat, opat, sizeof(pat)); | |||||
return (ABORT); | |||||
case CCHR('S'): | |||||
if (dir == SRCH_BACK) { | |||||
dir = SRCH_FORW; | |||||
is_lpush(); | |||||
is_cpush(SRCH_FORW); | |||||
success = TRUE; | |||||
} | |||||
if (success == FALSE && dir == SRCH_FORW) { | |||||
/* wrap the search to beginning */ | |||||
curwp->w_dotp = bfirstlp(curbp); | |||||
curwp->w_doto = 0; | |||||
curwp->w_dotline = 1; | |||||
if (is_find(dir) != FALSE) { | |||||
is_cpush(SRCH_MARK); | |||||
success = TRUE; | |||||
} | |||||
ewprintf("Overwrapped I-search: %s", pat); | |||||
break; | |||||
} | |||||
is_lpush(); | |||||
pptr = strlen(pat); | |||||
if (forwchar(FFRAND, 1) == FALSE) { | |||||
dobeep(); | |||||
success = FALSE; | |||||
ewprintf("Failed I-search: %s", pat); | |||||
} else { | |||||
if (is_find(SRCH_FORW) != FALSE) | |||||
is_cpush(SRCH_MARK); | |||||
else { | |||||
(void)backchar(FFRAND, 1); | |||||
dobeep(); | |||||
success = FALSE; | |||||
ewprintf("Failed I-search: %s", pat); | |||||
} | |||||
} | |||||
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) { | |||||
/* wrap the search to end */ | |||||
curwp->w_dotp = blastlp(curbp); | |||||
curwp->w_doto = llength(curwp->w_dotp); | |||||
curwp->w_dotline = curwp->w_bufp->b_lines; | |||||
if (is_find(dir) != FALSE) { | |||||
is_cpush(SRCH_MARK); | |||||
success = TRUE; | |||||
} | |||||
ewprintf("Overwrapped I-search: %s", pat); | |||||
break; | |||||
} | |||||
is_lpush(); | |||||
pptr = strlen(pat); | |||||
if (backchar(FFRAND, 1) == FALSE) { | |||||
dobeep(); | |||||
success = FALSE; | |||||
} else { | |||||
if (is_find(SRCH_BACK) != FALSE) | |||||
is_cpush(SRCH_MARK); | |||||
else { | |||||
(void)forwchar(FFRAND, 1); | |||||
dobeep(); | |||||
success = FALSE; | |||||
} | |||||
} | |||||
is_prompt(dir, pptr < 0, success); | |||||
break; | |||||
case CCHR('W'): | |||||
/* add the rest of the current word to the pattern */ | |||||
clp = curwp->w_dotp; | |||||
cbo = curwp->w_doto; | |||||
firstc = 1; | |||||
if (pptr == -1) | |||||
pptr = 0; | |||||
if (dir == SRCH_BACK) { | |||||
/* when isearching backwards, cbo is the start of the pattern */ | |||||
cbo += pptr; | |||||
} | |||||
/* if the search is case insensitive, add to pattern using lowercase */ | |||||
xcase = 0; | |||||
for (i = 0; pat[i]; i++) | |||||
if (ISUPPER(CHARMASK(pat[i]))) | |||||
xcase = 1; | |||||
while (cbo < llength(clp)) { | |||||
c = lgetc(clp, cbo++); | |||||
if ((!firstc && !isalnum(c))) | |||||
break; | |||||
if (pptr == NPAT - 1) { | |||||
dobeep(); | |||||
break; | |||||
} | |||||
firstc = 0; | |||||
if (!xcase && ISUPPER(c)) | |||||
c = TOLOWER(c); | |||||
pat[pptr++] = c; | |||||
pat[pptr] = '\0'; | |||||
/* cursor only moves when isearching forwards */ | |||||
if (dir == SRCH_FORW) { | |||||
curwp->w_doto = cbo; | |||||
curwp->w_rflag |= WFMOVE; | |||||
update(CMODE); | |||||
} | |||||
} | |||||
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(FALSE); | |||||
goto addchar; | |||||
default: | |||||
if (ISCTRL(c)) { | |||||
ungetkey(c); | |||||
curwp->w_markp = clp; | |||||
curwp->w_marko = cbo; | |||||
curwp->w_markline = cdotline; | |||||
ewprintf("Mark set"); | |||||
curwp->w_rflag |= WFMOVE; | |||||
return (TRUE); | |||||
} | |||||
/* FALLTHRU */ | |||||
case CCHR('I'): | |||||
case CCHR('J'): | |||||
addchar: | |||||
if (pptr == -1) | |||||
pptr = 0; | |||||
if (pptr == 0) | |||||
success = TRUE; | |||||
if (pptr == NPAT - 1) | |||||
dobeep(); | |||||
else { | |||||
pat[pptr++] = c; | |||||
pat[pptr] = '\0'; | |||||
} | |||||
is_lpush(); | |||||
if (success != FALSE) { | |||||
if (is_find(dir) != FALSE) | |||||
is_cpush(c); | |||||
else { | |||||
success = FALSE; | |||||
dobeep(); | |||||
is_cpush(SRCH_ACCM); | |||||
} | |||||
} else | |||||
is_cpush(SRCH_ACCM); | |||||
is_prompt(dir, FALSE, success); | |||||
} | |||||
} | |||||
/* NOTREACHED */ | |||||
} | |||||
static void | |||||
is_cpush(int cmd) | |||||
{ | |||||
if (++cip >= NSRCH) | |||||
cip = 0; | |||||
cmds[cip].s_code = cmd; | |||||
} | |||||
static void | |||||
is_lpush(void) | |||||
{ | |||||
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; | |||||
cmds[ctp].s_dotline = curwp->w_dotline; | |||||
} | |||||
static void | |||||
is_pop(void) | |||||
{ | |||||
if (cmds[cip].s_code != SRCH_NOPR) { | |||||
curwp->w_doto = cmds[cip].s_doto; | |||||
curwp->w_dotp = cmds[cip].s_dotp; | |||||
curwp->w_dotline = cmds[cip].s_dotline; | |||||
curwp->w_rflag |= WFMOVE; | |||||
cmds[cip].s_code = SRCH_NOPR; | |||||
} | |||||
if (--cip <= 0) | |||||
cip = NSRCH - 1; | |||||
} | |||||
static int | |||||
is_peek(void) | |||||
{ | |||||
return (cmds[cip].s_code); | |||||
} | |||||
/* this used to always return TRUE (the return value was checked) */ | |||||
static void | |||||
is_undo(int *pptr, int *dir) | |||||
{ | |||||
int redo = FALSE; | |||||
switch (cmds[cip].s_code) { | |||||
case SRCH_BEGIN: | |||||
case SRCH_NOPR: | |||||
*pptr = -1; | |||||
break; | |||||
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(int dir) | |||||
{ | |||||
int plen, odoto, odotline; | |||||
struct line *odotp; | |||||
odoto = curwp->w_doto; | |||||
odotp = curwp->w_dotp; | |||||
odotline = curwp->w_dotline; | |||||
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; | |||||
curwp->w_dotline = odotline; | |||||
return (FALSE); | |||||
} | |||||
return (TRUE); | |||||
} | |||||
if (dir == SRCH_BACK) { | |||||
(void)forwchar(FFARG | FFRAND, plen); | |||||
if (backsrch() == FALSE) { | |||||
curwp->w_doto = odoto; | |||||
curwp->w_dotp = odotp; | |||||
curwp->w_dotline = odotline; | |||||
return (FALSE); | |||||
} | |||||
return (TRUE); | |||||
} | |||||
dobeep(); | |||||
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(int dir, int flag, int 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 "i_prompt" is just | |||||
* a string. The "flag" determines whether pat should be printed. | |||||
*/ | |||||
static void | |||||
is_dspl(char *i_prompt, int flag) | |||||
{ | |||||
if (flag != FALSE) | |||||
ewprintf("%s: ", i_prompt); | |||||
else | |||||
ewprintf("%s: %s", i_prompt, pat); | |||||
} | |||||
/* | |||||
* Query Replace. | |||||
* Replace strings selectively. Does a search and replace operation. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
queryrepl(int f, int n) | |||||
{ | |||||
int s; | |||||
int rcnt = 0; /* replacements made so far */ | |||||
int plen; /* length of found string */ | |||||
char news[NPAT], *rep; /* replacement string */ | |||||
if (macrodef) { | |||||
dobeep(); | |||||
ewprintf("Can't query replace in macro"); | |||||
return (FALSE); | |||||
} | |||||
if ((s = readpattern("Query replace")) != TRUE) | |||||
return (s); | |||||
if ((rep = eread("Query replace %s with: ", news, NPAT, | |||||
EFNUL | EFNEW | EFCR, pat)) == NULL) | |||||
return (ABORT); | |||||
else if (rep[0] == '\0') | |||||
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(CMODE); | |||||
switch (getkey(FALSE)) { | |||||
case 'y': | |||||
case ' ': | |||||
if (lreplace((RSIZE)plen, news) == FALSE) | |||||
return (FALSE); | |||||
rcnt++; | |||||
break; | |||||
case '.': | |||||
if (lreplace((RSIZE)plen, news) == FALSE) | |||||
return (FALSE); | |||||
rcnt++; | |||||
goto stopsearch; | |||||
/* ^G, CR or ESC */ | |||||
case CCHR('G'): | |||||
(void)ctrlg(FFRAND, 0); | |||||
goto stopsearch; | |||||
case CCHR('['): | |||||
case CCHR('M'): | |||||
goto stopsearch; | |||||
case '!': | |||||
do { | |||||
if (lreplace((RSIZE)plen, news) == FALSE) | |||||
return (FALSE); | |||||
rcnt++; | |||||
} while (forwsrch() == TRUE); | |||||
goto stopsearch; | |||||
case 'n': | |||||
case CCHR('H'): | |||||
/* To not replace */ | |||||
case CCHR('?'): | |||||
break; | |||||
default: | |||||
ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit"); | |||||
goto retry; | |||||
} | |||||
} | |||||
stopsearch: | |||||
curwp->w_rflag |= WFFULL; | |||||
update(CMODE); | |||||
if (rcnt == 1) | |||||
ewprintf("Replaced 1 occurrence"); | |||||
else | |||||
ewprintf("Replaced %d occurrences", rcnt); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Replace string globally without individual prompting. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
replstr(int f, int n) | |||||
{ | |||||
char news[NPAT]; | |||||
int s, plen, rcnt = 0; | |||||
char *r; | |||||
if ((s = readpattern("Replace string")) != TRUE) | |||||
return s; | |||||
r = eread("Replace string %s with: ", news, NPAT, | |||||
EFNUL | EFNEW | EFCR, pat); | |||||
if (r == NULL) | |||||
return (ABORT); | |||||
plen = strlen(pat); | |||||
while (forwsrch() == TRUE) { | |||||
update(CMODE); | |||||
if (lreplace((RSIZE)plen, news) == FALSE) | |||||
return (FALSE); | |||||
rcnt++; | |||||
} | |||||
curwp->w_rflag |= WFFULL; | |||||
update(CMODE); | |||||
if (rcnt == 1) | |||||
ewprintf("Replaced 1 occurrence"); | |||||
else | |||||
ewprintf("Replaced %d occurrences", 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. | |||||
*/ | |||||
int | |||||
forwsrch(void) | |||||
{ | |||||
struct line *clp, *tlp; | |||||
int cbo, tbo, c, i, xcase = 0; | |||||
char *pp; | |||||
int nline; | |||||
clp = curwp->w_dotp; | |||||
cbo = curwp->w_doto; | |||||
nline = curwp->w_dotline; | |||||
for (i = 0; pat[i]; i++) | |||||
if (ISUPPER(CHARMASK(pat[i]))) | |||||
xcase = 1; | |||||
for (;;) { | |||||
if (cbo == llength(clp)) { | |||||
if ((clp = lforw(clp)) == curbp->b_headp) | |||||
break; | |||||
nline++; | |||||
cbo = 0; | |||||
c = CCHR('J'); | |||||
} else | |||||
c = lgetc(clp, cbo++); | |||||
if (eq(c, pat[0], xcase) != FALSE) { | |||||
tlp = clp; | |||||
tbo = cbo; | |||||
pp = &pat[1]; | |||||
while (*pp != 0) { | |||||
if (tbo == llength(tlp)) { | |||||
tlp = lforw(tlp); | |||||
if (tlp == curbp->b_headp) | |||||
goto fail; | |||||
tbo = 0; | |||||
c = CCHR('J'); | |||||
if (eq(c, *pp++, xcase) == FALSE) | |||||
goto fail; | |||||
nline++; | |||||
} else { | |||||
c = lgetc(tlp, tbo++); | |||||
if (eq(c, *pp++, xcase) == FALSE) | |||||
goto fail; | |||||
} | |||||
} | |||||
curwp->w_dotp = tlp; | |||||
curwp->w_doto = tbo; | |||||
curwp->w_dotline = nline; | |||||
curwp->w_rflag |= 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. | |||||
*/ | |||||
int | |||||
backsrch(void) | |||||
{ | |||||
struct line *clp, *tlp; | |||||
int cbo, tbo, c, i, xcase = 0; | |||||
char *epp, *pp; | |||||
int nline, pline; | |||||
for (epp = &pat[0]; epp[1] != 0; ++epp); | |||||
clp = curwp->w_dotp; | |||||
cbo = curwp->w_doto; | |||||
nline = curwp->w_dotline; | |||||
for (i = 0; pat[i]; i++) | |||||
if (ISUPPER(CHARMASK(pat[i]))) | |||||
xcase = 1; | |||||
for (;;) { | |||||
if (cbo == 0) { | |||||
clp = lback(clp); | |||||
if (clp == curbp->b_headp) | |||||
return (FALSE); | |||||
nline--; | |||||
cbo = llength(clp) + 1; | |||||
} | |||||
if (--cbo == llength(clp)) | |||||
c = CCHR('J'); | |||||
else | |||||
c = lgetc(clp, cbo); | |||||
if (eq(c, *epp, xcase) != FALSE) { | |||||
tlp = clp; | |||||
tbo = cbo; | |||||
pp = epp; | |||||
pline = nline; | |||||
while (pp != &pat[0]) { | |||||
if (tbo == 0) { | |||||
tlp = lback(tlp); | |||||
if (tlp == curbp->b_headp) | |||||
goto fail; | |||||
nline--; | |||||
tbo = llength(tlp) + 1; | |||||
} | |||||
if (--tbo == llength(tlp)) | |||||
c = CCHR('J'); | |||||
else | |||||
c = lgetc(tlp, tbo); | |||||
if (eq(c, *--pp, xcase) == FALSE) { | |||||
nline = pline; | |||||
goto fail; | |||||
} | |||||
} | |||||
curwp->w_dotp = tlp; | |||||
curwp->w_doto = tbo; | |||||
curwp->w_dotline = nline; | |||||
curwp->w_rflag |= 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(int bc, int pc, int xcase) | |||||
{ | |||||
bc = CHARMASK(bc); | |||||
pc = CHARMASK(pc); | |||||
if (bc == pc) | |||||
return (TRUE); | |||||
if (xcase) | |||||
return (FALSE); | |||||
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. | |||||
*/ | |||||
int | |||||
readpattern(char *r_prompt) | |||||
{ | |||||
char tpat[NPAT], *rep; | |||||
int retval; | |||||
if (pat[0] == '\0') | |||||
rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, r_prompt); | |||||
else | |||||
rep = eread("%s (default %s): ", tpat, NPAT, | |||||
EFNUL | EFNEW | EFCR, r_prompt, pat); | |||||
/* specified */ | |||||
if (rep == NULL) { | |||||
retval = ABORT; | |||||
} else if (rep[0] != '\0') { | |||||
(void)strlcpy(pat, tpat, sizeof(pat)); | |||||
retval = TRUE; | |||||
} else if (pat[0] != '\0') { | |||||
retval = TRUE; | |||||
} else | |||||
retval = FALSE; | |||||
return (retval); | |||||
} |