home *** CD-ROM | disk | FTP | other *** search
- /*
- * The functions in this file
- * implement commands that search in the
- * forward and backward directions. There are
- * no special characters in the search strings.
- * Probably should have a regular expression
- * search, or something like that.
- */
- #include <stdio.h>
- #include <ctype.h>
- #include "ed.h"
-
-
- #define CCHR(x) ((x)-'@')
-
- #define SRCH_BEGIN (0) /* Search sub-codes. */
- #define SRCH_FORW (-1)
- #define SRCH_BACK (-2)
- #define SRCH_PREV (-3)
- #define SRCH_NEXT (-4)
- #define SRCH_NOPR (-5)
- #define SRCH_ACCM (-6)
- #define NSRCH 128 /* Undoable search commands. */
-
- typedef struct {
- int s_code;
- LINE *s_dotp;
- int s_doto;
- } SRCHCOM;
-
- static 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.
- */
- forwsearch(f, n)
- register int f, n;
- {
-
- if ((f=readpattern("Search", &pat)) != TRUE)
- return (f);
- if (forwsrch() == FALSE) {
- mlwrite("Not found");
- return (FALSE);
- }
- 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].
- */
- backsearch(f, n)
- register int f, n;
- {
-
- if ((f=readpattern("Reverse search", &pat)) != TRUE)
- return (f);
- if (backsrch() == FALSE) {
- mlwrite("Not found");
- return (FALSE);
- }
- 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 LINE *clp;
- register int cbo;
- register LINE *tlp;
- register int tbo;
- register char *pp;
- register int c;
-
- clp = curwp->w_dotp;
- cbo = curwp->w_doto;
- while (clp != curbp->b_linep) {
- if (cbo == llength(clp)) {
- clp = lforw(clp);
- cbo = 0;
- c = '\n';
- } else
- c = lgetc(clp, cbo++);
- if (eq(c, pat[0]) != FALSE) {
- tlp = clp;
- tbo = cbo;
- pp = &pat[1];
- while (*pp != 0) {
- if (tlp == curbp->b_linep)
- goto fail;
- if (tbo == llength(tlp)) {
- tlp = lforw(tlp);
- if (tlp == curbp->b_linep)
- goto fail;
- tbo = 0;
- c = '\n';
- } 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 LINE *clp;
- register int cbo;
- register 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 = '\n';
- 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 = '\n';
- 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: ;
- }
- }
-
- /*
- * Compare two characters.
- * The "bc" comes from the buffer.
- * It has it's case folded out. The
- * "pc" is from the pattern.
- */
- eq(bc, pc)
- register int bc;
- register int pc;
- {
- if (bc>='a' && bc<='z')
- bc -= 0x20;
- if (pc>='a' && pc<='z')
- pc -= 0x20;
- if (bc == pc)
- return (TRUE);
- return (FALSE);
- }
-
- /*
- * Read a pattern.
- * Stash it in an external
- * variable. The calling function
- * passes the address of inpat. 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, inpat)
- char *prompt;
- char *inpat;
- {
- register char *cp1;
- register char *cp2;
- register int c;
- register int s;
- char tpat[NPAT+20];
-
- cp1 = &tpat[0]; /* Copy prompt */
- cp2 = prompt;
- while ((c = *cp2++) != '\0')
- *cp1++ = c;
- if (inpat[0] != '\0') { /* Old pattern */
- *cp1++ = ' ';
- *cp1++ = '[';
- cp2 = &inpat[0];
- while ((c = *cp2++) != 0) {
- if (cp1 < &tpat[NPAT+20-6]) { /* "??]: \0" */
- if (c<0x20 || c==0x7F) {
- *cp1++ = '^';
- c ^= 0x40;
- } else if (c == '%') /* Map "%" to */
- *cp1++ = c; /* "%%". */
- *cp1++ = c;
- }
- }
- *cp1++ = ']';
- }
- *cp1++ = ':'; /* Finish prompt */
- *cp1++ = ' ';
- *cp1++ = '\0';
- s = mlreply(tpat, tpat, NPAT); /* Read pattern */
- if (s == TRUE) /* Specified */
- strcpy(inpat, tpat);
- else if (s==FALSE && inpat[0]!=0) /* CR, but old one */
- s = TRUE;
- return (s);
- }
-
- /*
- * Use incremental searching, initially in the forward direction.
- * isearch ignores any explicit arguments.
- */
- forwisearch(f, n)
- register int f, n;
- {
- return (isearch(SRCH_FORW));
- }
-
- /*
- * Use incremental searching, initially in the reverse direction.
- * isearch ignores any explicit arguments.
- */
- backisearch(f, n)
- register int f, n;
- {
- return (isearch(SRCH_BACK));
- }
-
- /*
- * Incremental Search.
- * dir is used as the initial direction to search.
- * ^N find next occurance (if first thing typed reuse old string).
- * ^P find prev occurance (if first thing typed reuse old string).
- * ^S switch direction to forward, find next
- * ^R switch direction to reverse, find prev
- * ^Q quote next character (allows searching for ^N etc.)
- * <ESC> exit from Isearch.
- * <DEL> undoes last character typed. (tricky job to do this correctly).
- * else accumulate into search string
- */
- isearch(dir)
- int dir;
- {
- register int c;
- register LINE *clp;
- register int cbo;
- register int success;
- int pptr;
-
- for (cip=0; cip<NSRCH; cip++)
- cmds[cip].s_code = SRCH_NOPR;
- 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 = ttgetc()) {
- case CCHR('M'):
- case METACH:
- srch_lastdir = dir;
- mlwrite("[Done]");
- return (TRUE);
-
- case CCHR('G'):
- curwp->w_dotp = clp;
- curwp->w_doto = cbo;
- curwp->w_flag |= WFMOVE;
- srch_lastdir = dir;
- ctrlg(FALSE, 0);
- return (FALSE);
-
- case CCHR('S'):
- case CCHR('F'):
- if (dir == SRCH_BACK) {
- dir = SRCH_FORW;
- is_lpush();
- is_cpush(SRCH_FORW);
- success = TRUE;
- }
- /* Drop through to find next. */
- case CCHR('N'):
- if (success==FALSE && dir==SRCH_FORW)
- break;
- is_lpush();
- forwchar(FALSE, 1);
- if (is_find(SRCH_NEXT) != FALSE) {
- is_cpush(SRCH_NEXT);
- pptr = strlen(pat);
- } else {
- backchar(FALSE, 1);
- (*term.t_beep)();
- success = FALSE;
- }
- is_prompt(dir, FALSE, success);
- break;
-
- case CCHR('R'):
- case CCHR('B'):
- if (dir == SRCH_FORW) {
- dir = SRCH_BACK;
- is_lpush();
- is_cpush(SRCH_BACK);
- success = TRUE;
- }
- /* Drop through to find previous. */
- case CCHR('P'):
- if (success==FALSE && dir==SRCH_BACK)
- break;
- is_lpush();
- backchar(FALSE, 1);
- if (is_find(SRCH_PREV) != FALSE) {
- is_cpush(SRCH_PREV);
- pptr = strlen(pat);
- } else {
- forwchar(FALSE, 1);
- (*term.t_beep)();
- success = FALSE;
- }
- is_prompt(dir,FALSE,success);
- break;
-
- case 0x7F:
- if (is_undo(&pptr, &dir) != TRUE)
- return (ABORT);
- if (is_peek() != SRCH_ACCM)
- success = TRUE;
- is_prompt(dir, FALSE, success);
- break;
-
- case CCHR('^'):
- case CCHR('Q'):
- c = ttgetc();
- case CCHR('U'):
- case CCHR('X'):
- case CCHR('J'):
- goto addchar;
-
- default:
- if (iscntrl(c) != FALSE) {
- c += '@';
- c |= CTRL;
- success = execute(c, FALSE, 1);
- curwp->w_flag |= WFMOVE;
- return (success);
- }
- addchar:
- if (pptr == -1)
- pptr = 0;
- if (pptr == 0)
- success = TRUE;
- pat[pptr++] = c;
- if (pptr == NPAT) {
- mlwrite("Pattern too long");
- ctrlg(FALSE, 0);
- return (ABORT);
- }
- pat[pptr] = '\0';
- is_lpush();
- if (success != FALSE) {
- if (is_find(dir) != FALSE)
- is_cpush(c);
- else {
- success = FALSE;
- (*term.t_beep)();
- is_cpush(SRCH_ACCM);
- }
- } else
- is_cpush(SRCH_ACCM);
- is_prompt(dir, FALSE, success);
- }
- }
- }
-
- is_cpush(cmd)
- register int cmd;
- {
- if (++cip >= NSRCH)
- cip = 0;
- cmds[cip].s_code = cmd;
- }
-
- 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;
- }
-
- 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;
- }
-
- is_peek()
- {
- if (cip == 0)
- return (cmds[NSRCH-1].s_code);
- else
- return (cmds[cip-1].s_code);
- }
-
- is_undo(pptr, dir)
- register int *pptr;
- register int *dir;
- {
- switch (cmds[cip].s_code) {
- case SRCH_NOPR:
- case SRCH_BEGIN:
- case SRCH_NEXT:
- case SRCH_PREV:
- break;
-
- case SRCH_FORW:
- *dir = SRCH_BACK;
- break;
-
- case SRCH_BACK:
- *dir = SRCH_FORW;
- break;
-
- case SRCH_ACCM:
- default:
- *pptr -= 1;
- if (*pptr < 0)
- *pptr = 0;
- pat[*pptr] = '\0';
- break;
- }
- is_pop();
- return (TRUE);
- }
-
- is_find(dir)
- register int dir;
- {
- register int plen;
-
- plen = strlen(pat);
- if (plen != 0) {
- if (dir==SRCH_FORW || dir==SRCH_NEXT) {
- backchar(FALSE, plen);
- if (forwsrch() == FALSE) {
- forwchar(FALSE, plen);
- return (FALSE);
- }
- return (TRUE);
- }
- if (dir==SRCH_BACK || dir==SRCH_PREV) {
- forwchar(FALSE, plen);
- if (backsrch() == FALSE) {
- backchar(FALSE, plen);
- return (FALSE);
- }
- return (TRUE);
- }
- mlwrite("bad call to is_find");
- ctrlg(FALSE, 0);
- 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.
- */
- is_prompt(dir, flag, success)
- {
- if (dir == SRCH_FORW) {
- if (success != FALSE)
- is_dspl("i-search forward", flag);
- else
- is_dspl("failing i-search forward", flag);
- } else if (dir == SRCH_BACK) {
- if (success != FALSE)
- is_dspl("i-search backward", flag);
- else
- is_dspl("failing i-search backward", flag);
- }
- }
-
- /*
- * Prompt writing routine for the incremental search.
- * The "prompt" is just a string. The "flag" determines
- * if a "[ ]" or ":" embelishment is used.
- */
- is_dspl(prompt, flag)
- char *prompt;
- {
- if (flag != FALSE)
- mlwrite("%s [%s]", prompt, pat);
- else
- mlwrite("%s: %s", prompt, pat);
- }
-
- /* META Command Search and replace in forward direction only. Prompts
- * before replacement allows user to ABORT or continue. Calling with an
- * argument prevents case distinctions. Bound to M-R.
- */
-
- replace(f, n)
- register int f, n;
- {
- register int s;
- register int kludge; /* Watch for saved line move */
- register LINE *clp; /* saved line pointer */
- char toprompt[81]; /* temporary string */
- char prompt[81]; /* final prompt */
- char *perptr, *index(); /* remap '%' to '%%' in prompt */
- int cbo; /* offset into the saved line */
- int rcnt = 0; /* Replacements made so far */
- int plen; /* length of found string */
-
- strcpy(prompt,"Query Replace ");
- if ((s=readpattern(prompt, &pat)) != TRUE)
- return (s);
- strcat(prompt,pat);
- strcat(prompt," with");
- if ((perptr=index(prompt,'%'))!=NULL)
- {
- *perptr = '\0'; /* terminate prompt */
- strcpy(toprompt,"%%"); /* setup mapping chars */
- strcat(toprompt,++perptr); /* append end of prompt */
- strcat(prompt,toprompt); /* append end to prompt */
- }
- if ((s=readpattern(prompt, &rpat)) == ABORT)
- return (s);
- if (s == FALSE)
- rpat[0] = '\0';
- 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.
- *
- * If we change the line that is the remembered value of dot, then
- * it is possible for the remembered value to move. This causes great
- * pain when trying to return to the non-existant line.
- *
- * possible fixes:
- * 1) put a single, relocated marker in the WINDOW structure, handled
- * like mark. The problem now becomes a what if two are needed...
- * 2) link markers into a list that gets updated (auto structures for
- * the nodes)
- * 3) Expand the mark into a stack of marks and add pushmark, popmark.
- */
-
- clp = curwp->w_dotp; /* save the return location */
- cbo = curwp->w_doto;
- while (forwsrch() == TRUE) {
- retry:
- update();
- switch (ttgetc()) {
- case ' ':
- case ',':
- kludge = (curwp->w_dotp == clp);
- if (lreplace(plen, rpat, f) == FALSE)
- return (FALSE);
- rcnt++;
- if (kludge != FALSE)
- clp = curwp->w_dotp;
- break;
-
- case '.':
- kludge = (curwp->w_dotp == clp);
- if (lreplace(plen, rpat, f) == FALSE)
- return (FALSE);
- rcnt++;
- if (kludge != FALSE)
- clp = curwp->w_dotp;
- goto stopsearch;
-
- case 0x07:
- ctrlg(FALSE, 0);
- goto stopsearch;
-
- case '!':
- do {
- kludge = (curwp->w_dotp == clp);
- if (lreplace(plen, rpat, f) == FALSE)
- return (FALSE);
- rcnt++;
- if (kludge != FALSE)
- clp = curwp->w_dotp;
- } while (forwsrch() == TRUE);
- goto stopsearch;
-
- case 'n':
- break;
-
- default:
- mlwrite("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
- goto retry;
- }
- }
- stopsearch:
- curwp->w_dotp = clp;
- curwp->w_doto = cbo;
- curwp->w_flag |= WFHARD;
- update();
- if (rcnt == 0)
- mlwrite("[No replacements done]");
- else if (rcnt == 1)
- mlwrite("[1 replacement done]");
- else
- mlwrite("[%d replacements done]", rcnt);
- return (TRUE);
- }
-
- /*
- * Replace plen characters before dot with argument string.
- * Control-J characters in st are interpreted as newlines.
- * There is a casehack disable flag (normally it likes to match
- * case of replacement to what was there).
- */
- lreplace(plen, st, f)
- register int plen; /* length to remove */
- char *st; /* replacement string */
- int f; /* case hack disable */
- {
- register int rlen; /* replacement length */
- register int rtype; /* capitalization */
- register int c; /* used for random characters */
- register int doto; /* offset into line */
-
- /*
- * Find the capitalization of the word that was found.
- * f says use exact case of replacement string (same thing that
- * happens with lowercase found), so bypass check.
- */
- backchar(TRUE, plen);
- rtype = __l;
- c = lgetc(curwp->w_dotp, curwp->w_doto);
- if (isupper(c)!=FALSE && f==FALSE) {
- rtype = __u|__l;
- if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
- c = lgetc(curwp->w_dotp, curwp->w_doto+1);
- if (isupper(c) != FALSE) {
- rtype = __u;
- }
- }
- }
-
- /*
- * make the string lengths match (either pad the line
- * so that it will fit, or scrunch out the excess).
- * be careful with dot's offset.
- */
- rlen = strlen(st);
- doto = curwp->w_doto;
- if (plen > rlen)
- ldelete(plen-rlen, FALSE);
- else if (plen < rlen) {
- if (linsert(rlen-plen, ' ') == FALSE)
- return (FALSE);
- }
- curwp->w_doto = doto;
-
- /*
- * do the replacement: If was capital, then place first
- * char as if upper, and subsequent chars as if lower.
- * If inserting upper, check replacement for case.
- */
- while ((c = *st++&0xff) != '\0') {
- if ((rtype&__u)!=0 && islower(c)!=0)
- c = toupper(c);
- if (rtype == (__u|__l))
- rtype = __l;
- if (c == '\n') {
- if (curwp->w_doto == llength(curwp->w_dotp))
- forwchar(FALSE, 1);
- else {
- ldelete(1, FALSE);
- lnewline();
- }
- } else if (curwp->w_dotp == curbp->b_linep) {
- linsert(1, c);
- } else if (curwp->w_doto == llength(curwp->w_dotp)) {
- ldelete(1, FALSE);
- linsert(1, c);
- } else
- lputc(curwp->w_dotp, curwp->w_doto++, c);
- }
- lchange(WFHARD);
- return (TRUE);
- }
-
-