home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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;
- }
-