#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)
#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;
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);
}
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);
}
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);
}
int
forwisearch(int f, int n)
{
if (macrodef || inmacro)
return (forwsearch(f,n));
else
return (isearch(SRCH_FORW));
}
int
backisearch(int f, int n)
{
if (macrodef || inmacro)
return (backsearch(f,n));
else
return (isearch(SRCH_BACK));
}
static int
isearch(int dir)
{
struct line *clp;
int c;
int cbo;
int success;
int pptr;
int firstc;
int xcase;
int i;
char opat[NPAT];
int cdotline;
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 (ttwait(300) == FALSE)
ungetkey(c);
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) {
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) {
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'):
clp = curwp->w_dotp;
cbo = curwp->w_doto;
firstc = 1;
if (pptr == -1)
pptr = 0;
if (dir == SRCH_BACK) {
cbo += pptr;
}
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';
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);
}
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);
}
}
}
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);
}
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);
}
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");
}
static void
is_dspl(char *i_prompt, int flag)
{
if (flag != FALSE)
ewprintf("%s: ", i_prompt);
else
ewprintf("%s: %s", i_prompt, pat);
}
int
queryrepl(int f, int n)
{
int s;
int rcnt = 0;
int plen;
char news[NPAT], *rep;
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);
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;
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'):
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);
}
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);
}
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);
}
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: ;
}
}
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);
}
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);
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);
}
int
zaptochar(int f, int n)
{
return (zap(TRUE, n));
}
int
zapuptochar(int f, int n)
{
return (zap(FALSE, n));
}
int
zap(int including, int n)
{
int s, backward;
backward = n < 0;
if (backward)
n = -n;
if (including)
ewprintf("Zap to char: ");
else
ewprintf("Zap up to char: ");
s = getkey(FALSE);
eerase();
if (s == ABORT || s == CCHR('G'))
return (FALSE);
if (n == 0)
return (TRUE);
pat[0] = (char)s;
pat[1] = '\0';
isetmark();
while (n--) {
s = backward ? backsrch() : forwsrch();
if (s != TRUE) {
dobeep();
ewprintf("Search failed: \"%s\"", pat);
swapmark(FFARG, 0);
clearmark(FFARG, 0);
return (s);
}
}
if (!including) {
if (backward)
forwchar(FFARG, 1);
else
backchar(FFARG, 1);
}
killregion(FFARG, 0);
clearmark(FFARG, 0);
return (TRUE);
}