home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 100-199 / ff147.lzh / src / search.c < prev    next >
C/C++ Source or Header  |  1988-08-15  |  14KB  |  665 lines

  1. /*
  2.  *        Search commands.
  3.  * The functions in this file implement the
  4.  * search commands (both plain and incremental searches
  5.  * are supported) and the query-replace command.
  6.  *
  7.  * The plain old search code is part of the original
  8.  * MicroEMACS "distribution". The incremental search code,
  9.  * and the query-replace code, is by Rich Ellison.
  10.  */
  11. #include    "def.h"
  12. #ifndef NO_MACRO
  13. #include    "macro.h"
  14. #endif
  15.  
  16. #define SRCH_BEGIN    (0)            /* Search sub-codes.    */
  17. #define SRCH_FORW    (-1)
  18. #define SRCH_BACK    (-2)
  19. #define SRCH_NOPR    (-3)
  20. #define SRCH_ACCM    (-4)
  21. #define SRCH_MARK    (-5)
  22.  
  23. typedef struct    {
  24.     int    s_code;
  25.     LINE    *s_dotp;
  26.     int    s_doto;
  27. }    SRCHCOM;
  28.  
  29. static    SRCHCOM cmds[NSRCH];
  30. static    int    cip;
  31.  
  32. int    srch_lastdir = SRCH_NOPR;        /* Last search flags.    */
  33.  
  34. static VOID    is_cpush();
  35. static VOID    is_lpush();
  36. static VOID    is_pop();
  37. static int    is_peek();
  38. static VOID    is_undo();
  39. static int    is_find();
  40. static VOID    is_prompt();
  41. static VOID    is_dspl();
  42. static int    eq();
  43.  
  44. /*
  45.  * Search forward.
  46.  * Get a search string from the user, and search for it,
  47.  * starting at ".". If found, "." gets moved to just after the
  48.  * matched characters, and display does all the hard stuff.
  49.  * If not found, it just prints a message.
  50.  */
  51. /*ARGSUSED*/
  52. forwsearch(f, n)
  53. {
  54.     register int    s;
  55.  
  56.     if ((s=readpattern("Search")) != TRUE)
  57.         return s;
  58.     if (forwsrch() == FALSE) {
  59.         ewprintf("Search failed: \"%s\"", pat);
  60.         return FALSE;
  61.     }
  62.     srch_lastdir = SRCH_FORW;
  63.     return TRUE;
  64. }
  65.  
  66. /*
  67.  * Reverse search.
  68.  * Get a search string from the     user, and search, starting at "."
  69.  * and proceeding toward the front of the buffer. If found "." is left
  70.  * pointing at the first character of the pattern [the last character that
  71.  * was matched].
  72.  */
  73. /*ARGSUSED*/
  74. backsearch(f, n)
  75. {
  76.     register int    s;
  77.  
  78.     if ((s=readpattern("Search backward")) != TRUE)
  79.         return (s);
  80.     if (backsrch() == FALSE) {
  81.         ewprintf("Search failed: \"%s\"", pat);
  82.         return FALSE;
  83.     }
  84.     srch_lastdir = SRCH_BACK;
  85.     return TRUE;
  86. }
  87.  
  88. /*
  89.  * Search again, using the same search string
  90.  * and direction as the last search command. The direction
  91.  * has been saved in "srch_lastdir", so you know which way
  92.  * to go.
  93.  */
  94. /*ARGSUSED*/
  95. searchagain(f, n)
  96. {
  97.     if (srch_lastdir == SRCH_FORW) {
  98.         if (forwsrch() == FALSE) {
  99.             ewprintf("Search failed: \"%s\"", pat);
  100.             return FALSE;
  101.         }
  102.         return TRUE;
  103.     }
  104.     if (srch_lastdir == SRCH_BACK) {
  105.         if (backsrch() == FALSE) {
  106.             ewprintf("Search failed: \"%s\"", pat);
  107.             return FALSE;
  108.         }
  109.         return TRUE;
  110.     }
  111.     ewprintf("No last search");
  112.     return FALSE;
  113. }
  114.  
  115. /*
  116.  * Use incremental searching, initially in the forward direction.
  117.  * isearch ignores any explicit arguments.
  118.  */
  119. /*ARGSUSED*/
  120. forwisearch(f, n)
  121. {
  122.     return isearch(SRCH_FORW);
  123. }
  124.  
  125. /*
  126.  * Use incremental searching, initially in the reverse direction.
  127.  * isearch ignores any explicit arguments.
  128.  */
  129. /*ARGSUSED*/
  130. backisearch(f, n)
  131. {
  132.     return isearch(SRCH_BACK);
  133. }
  134.  
  135. /*
  136.  * Incremental Search.
  137.  *    dir is used as the initial direction to search.
  138.  *    ^S    switch direction to forward
  139.  *    ^R    switch direction to reverse
  140.  *    ^Q    quote next character (allows searching for ^N etc.)
  141.  *    <ESC>    exit from Isearch
  142.  *    <DEL>    undoes last character typed. (tricky job to do this correctly).
  143.  *    other ^ exit search, don't set mark
  144.  *    else    accumulate into search string
  145.  */
  146. isearch(dir) {
  147.     register int    c;
  148.     register LINE    *clp;
  149.     register int    cbo;
  150.     register int    success;
  151.     int        pptr;
  152.     char        opat[NPAT];
  153.     VOID        ungetkey();
  154.  
  155. #ifndef NO_MACRO
  156.     if(macrodef) {
  157.         ewprintf("Can't isearch in macro");
  158.         return FALSE;
  159.     }
  160. #endif
  161.     for (cip=0; cip<NSRCH; cip++)
  162.         cmds[cip].s_code = SRCH_NOPR;
  163.     (VOID) strcpy(opat, pat);
  164.     cip = 0;
  165.     pptr = -1;
  166.     clp = curwp->w_dotp;
  167.     cbo = curwp->w_doto;
  168.     is_lpush();
  169.     is_cpush(SRCH_BEGIN);
  170.     success = TRUE;
  171.     is_prompt(dir, TRUE, success);
  172.     for (;;) {
  173.         update();
  174.         switch (c = getkey(FALSE)) {
  175.         case CCHR('['):
  176.             srch_lastdir = dir;
  177.             curwp->w_markp = clp;
  178.             curwp->w_marko = cbo;
  179.             ewprintf("Mark set");
  180.             return (TRUE);
  181.  
  182.         case CCHR('G'):
  183.             if (success != TRUE) {
  184.                 while (is_peek() == SRCH_ACCM)
  185.                     is_undo(&pptr, &dir);
  186.                 success = TRUE;
  187.                 is_prompt(dir, pptr < 0, success);
  188.                 break;
  189.             }
  190.             curwp->w_dotp = clp;
  191.             curwp->w_doto = cbo;
  192.             curwp->w_flag |= WFMOVE;
  193.             srch_lastdir = dir;
  194.             (VOID) ctrlg(FFRAND, 0);
  195.             (VOID) strcpy(pat, opat);
  196.             return ABORT;
  197.  
  198.         case CCHR(']'):
  199.         case CCHR('S'):
  200.             if (dir == SRCH_BACK) {
  201.                 dir = SRCH_FORW;
  202.                 is_lpush();
  203.                 is_cpush(SRCH_FORW);
  204.                 success = TRUE;
  205.             }
  206.             if (success==FALSE && dir==SRCH_FORW)
  207.                 break;
  208.             is_lpush();
  209.             pptr = strlen(pat);
  210.             (VOID) forwchar(FFRAND, 1);
  211.             if (is_find(SRCH_FORW) != FALSE) is_cpush(SRCH_MARK);
  212.             else {
  213.                 (VOID) backchar(FFRAND, 1);
  214.                 ttbeep();
  215.                 success = FALSE;
  216.             }
  217.             is_prompt(dir, pptr < 0, success);
  218.             break;
  219.  
  220.         case CCHR('R'):
  221.             if (dir == SRCH_FORW) {
  222.                 dir = SRCH_BACK;
  223.                 is_lpush();
  224.                 is_cpush(SRCH_BACK);
  225.                 success = TRUE;
  226.             }
  227.             if (success==FALSE && dir==SRCH_BACK)
  228.                 break;
  229.             is_lpush();
  230.             pptr = strlen(pat);
  231.             (VOID) backchar(FFRAND, 1);
  232.             if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
  233.             else {
  234.                 (VOID) forwchar(FFRAND, 1);
  235.                 ttbeep();
  236.                 success = FALSE;
  237.             }
  238.             is_prompt(dir, pptr < 0, success);
  239.             break;
  240.  
  241.         case CCHR('H'):
  242.         case CCHR('?'):
  243.             is_undo(&pptr, &dir);
  244.             if (is_peek() != SRCH_ACCM) success = TRUE;
  245.             is_prompt(dir, pptr < 0, success);
  246.             break;
  247.  
  248.         case CCHR('\\'):
  249.         case CCHR('Q'):
  250.             c = (char) getkey(FALSE);
  251.             goto  addchar;
  252.         case CCHR('M'):
  253.             c = CCHR('J');
  254.             goto  addchar;
  255.  
  256.         default:
  257.             if (ISCTRL(c)) {
  258.                 ungetkey(c);
  259.                 curwp->w_markp = clp;
  260.                 curwp->w_marko = cbo;
  261.                 ewprintf("Mark set");
  262.                 curwp->w_flag |= WFMOVE;
  263.                 return    TRUE;
  264.             }    /* and continue */
  265.         case CCHR('I'):
  266.         case CCHR('J'):
  267.         addchar:
  268.             if (pptr == -1)
  269.                 pptr = 0;
  270.             if (pptr == 0)
  271.                 success = TRUE;
  272.             pat[pptr++] = c;
  273.             if (pptr == NPAT) {
  274.                 ewprintf("Pattern too long");
  275.                 return FALSE;
  276.             }
  277.             pat[pptr] = '\0';
  278.             is_lpush();
  279.             if (success != FALSE) {
  280.                 if (is_find(dir) != FALSE)
  281.                     is_cpush(c);
  282.                 else {
  283.                     success = FALSE;
  284.                     ttbeep();
  285.                     is_cpush(SRCH_ACCM);
  286.                 }
  287.             } else
  288.                 is_cpush(SRCH_ACCM);
  289.             is_prompt(dir, FALSE, success);
  290.         }
  291.     }
  292.     /*NOTREACHED*/
  293. }
  294.  
  295. static VOID
  296. is_cpush(cmd) register int cmd; {
  297.     if (++cip >= NSRCH)
  298.         cip = 0;
  299.     cmds[cip].s_code = cmd;
  300. }
  301.  
  302. static VOID
  303. is_lpush() {
  304.     register int    ctp;
  305.  
  306.     ctp = cip+1;
  307.     if (ctp >= NSRCH)
  308.         ctp = 0;
  309.     cmds[ctp].s_code = SRCH_NOPR;
  310.     cmds[ctp].s_doto = curwp->w_doto;
  311.     cmds[ctp].s_dotp = curwp->w_dotp;
  312. }
  313.  
  314. static VOID
  315. is_pop() {
  316.     if (cmds[cip].s_code != SRCH_NOPR) {
  317.         curwp->w_doto  = cmds[cip].s_doto;
  318.         curwp->w_dotp  = cmds[cip].s_dotp;
  319.         curwp->w_flag |= WFMOVE;
  320.         cmds[cip].s_code = SRCH_NOPR;
  321.     }
  322.     if (--cip <= 0)
  323.         cip = NSRCH-1;
  324. }
  325.  
  326. static int
  327. is_peek() {
  328.     return cmds[cip].s_code;
  329. }
  330.  
  331. /* this used to always return TRUE (the return value was checked) */
  332. static VOID
  333. is_undo(pptr, dir) register int *pptr; register int *dir; {
  334.     register int    redo = FALSE ;
  335.     switch (cmds[cip].s_code) {
  336.     case SRCH_BEGIN:
  337.     case SRCH_NOPR:
  338.         *pptr = -1;
  339.     case SRCH_MARK:
  340.         break;
  341.  
  342.     case SRCH_FORW:
  343.         *dir = SRCH_BACK;
  344.         redo = TRUE;
  345.         break;
  346.  
  347.     case SRCH_BACK:
  348.         *dir = SRCH_FORW;
  349.         redo = TRUE;
  350.         break;
  351.  
  352.     case SRCH_ACCM:
  353.     default:
  354.         *pptr -= 1;
  355.         if (*pptr < 0)
  356.             *pptr = 0;
  357.         pat[*pptr] = '\0';
  358.         break;
  359.     }
  360.     is_pop();
  361.     if (redo) is_undo(pptr, dir);
  362. }
  363.  
  364. static int
  365. is_find(dir) register int dir; {
  366.     register int    plen, odoto;
  367.     register LINE    *odotp ;
  368.  
  369.     odoto = curwp->w_doto;
  370.     odotp = curwp->w_dotp;
  371.     plen = strlen(pat);
  372.     if (plen != 0) {
  373.         if (dir==SRCH_FORW) {
  374.             (VOID) backchar(FFARG | FFRAND, plen);
  375.             if (forwsrch() == FALSE) {
  376.                 curwp->w_doto = odoto;
  377.                 curwp->w_dotp = odotp;
  378.                 return FALSE;
  379.             }
  380.             return TRUE;
  381.         }
  382.         if (dir==SRCH_BACK) {
  383.             (VOID) forwchar(FFARG | FFRAND, plen);
  384.             if (backsrch() == FALSE) {
  385.                 curwp->w_doto = odoto;
  386.                 curwp->w_dotp = odotp;
  387.                 return FALSE;
  388.             }
  389.             return TRUE;
  390.         }
  391.         ewprintf("bad call to is_find");
  392.         return FALSE;
  393.     }
  394.     return FALSE;
  395. }
  396.  
  397. /*
  398.  * If called with "dir" not one of SRCH_FORW
  399.  * or SRCH_BACK, this routine used to print an error
  400.  * message. It also used to return TRUE or FALSE,
  401.  * depending on if it liked the "dir". However, none
  402.  * of the callers looked at the status, so I just
  403.  * made the checking vanish.
  404.  */
  405. static VOID
  406. is_prompt(dir, flag, success) {
  407.     if (dir == SRCH_FORW) {
  408.         if (success != FALSE)
  409.             is_dspl("I-search", flag);
  410.         else
  411.             is_dspl("Failing I-search", flag);
  412.     } else if (dir == SRCH_BACK) {
  413.         if (success != FALSE)
  414.             is_dspl("I-search backward", flag);
  415.         else
  416.             is_dspl("Failing I-search backward", flag);
  417.     } else ewprintf("Broken call to is_prompt");
  418. }
  419.  
  420. /*
  421.  * Prompt writing routine for the incremental search.
  422.  * The "prompt" is just a string. The "flag" determines
  423.  * whether pat should be printed.
  424.  */
  425. static VOID
  426. is_dspl(prompt, flag) char *prompt; {
  427.  
  428.     if (flag != FALSE)
  429.         ewprintf("%s: ", prompt);
  430.     else
  431.         ewprintf("%s: %s", prompt, pat);
  432. }
  433.  
  434. /*
  435.  * Query Replace.
  436.  *    Replace strings selectively.  Does a search and replace operation.
  437.  */
  438. /*ARGSUSED*/
  439. queryrepl(f, n)
  440. {
  441.     register int    s;
  442.     register int    rcnt = 0;    /* Replacements made so far    */
  443.     register int    plen;        /* length of found string    */
  444.     char        news[NPAT];    /* replacement string        */
  445.  
  446. #ifndef NO_MACRO
  447.     if(macrodef) {
  448.         ewprintf("Can't query replace in macro");
  449.         return FALSE;
  450.     }
  451. #endif
  452.     if ((s=readpattern("Query replace")) != TRUE)
  453.         return (s);
  454.     if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
  455.         return (s);
  456.     if (s == FALSE)
  457.         news[0] = '\0';
  458.     ewprintf("Query replacing %s with %s:", pat, news);
  459.     plen = strlen(pat);
  460.  
  461.     /*
  462.      * Search forward repeatedly, checking each time whether to insert
  463.      * or not.  The "!" case makes the check always true, so it gets put
  464.      * into a tighter loop for efficiency.
  465.      */
  466.  
  467.     while (forwsrch() == TRUE) {
  468.     retry:
  469.         update();
  470.         switch (getkey(FALSE)) {
  471.         case ' ':
  472.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  473.                 return (FALSE);
  474.             rcnt++;
  475.             break;
  476.  
  477.         case '.':
  478.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  479.                 return (FALSE);
  480.             rcnt++;
  481.             goto stopsearch;
  482.  
  483.         case CCHR('G'): /* ^G or ESC */
  484.             (VOID) ctrlg(FFRAND, 0);
  485.         case CCHR('['):
  486.             goto stopsearch;
  487.  
  488.         case '!':
  489.             do {
  490.                 if (lreplace((RSIZE) plen, news, f) == FALSE)
  491.                     return (FALSE);
  492.                 rcnt++;
  493.             } while (forwsrch() == TRUE);
  494.             goto stopsearch;
  495.  
  496.         case CCHR('H'):
  497.         case CCHR('?'):        /* To not replace */
  498.             break;
  499.  
  500.         default:
  501. ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
  502.             goto retry;
  503.         }
  504.     }
  505. stopsearch:
  506.     curwp->w_flag |= WFHARD;
  507.     update();
  508.     if (rcnt == 0)
  509.         ewprintf("(No replacements done)");
  510.     else if (rcnt == 1)
  511.         ewprintf("(1 replacement done)");
  512.     else
  513.         ewprintf("(%d replacements done)", rcnt);
  514.     return TRUE;
  515. }
  516.  
  517. /*
  518.  * This routine does the real work of a
  519.  * forward search. The pattern is sitting in the external
  520.  * variable "pat". If found, dot is updated, the window system
  521.  * is notified of the change, and TRUE is returned. If the
  522.  * string isn't found, FALSE is returned.
  523.  */
  524. forwsrch() {
  525.     register LINE    *clp;
  526.     register int    cbo;
  527.     register LINE    *tlp;
  528.     register int    tbo;
  529.     char        *pp;
  530.     register int    c;
  531.  
  532.     clp = curwp->w_dotp;
  533.     cbo = curwp->w_doto;
  534.     for(;;) {
  535.         if (cbo == llength(clp)) {
  536.             if((clp = lforw(clp)) == curbp->b_linep) break;
  537.             cbo = 0;
  538.             c = CCHR('J');
  539.         } else
  540.             c = lgetc(clp, cbo++);
  541.         if (eq(c, pat[0]) != FALSE) {
  542.             tlp = clp;
  543.             tbo = cbo;
  544.             pp  = &pat[1];
  545.             while (*pp != 0) {
  546.                 if (tbo == llength(tlp)) {
  547.                     tlp = lforw(tlp);
  548.                     if (tlp == curbp->b_linep)
  549.                         goto fail;
  550.                     tbo = 0;
  551.                     c = CCHR('J');
  552.                 } else
  553.                     c = lgetc(tlp, tbo++);
  554.                 if (eq(c, *pp++) == FALSE)
  555.                     goto fail;
  556.             }
  557.             curwp->w_dotp  = tlp;
  558.             curwp->w_doto  = tbo;
  559.             curwp->w_flag |= WFMOVE;
  560.             return TRUE;
  561.         }
  562.     fail:    ;
  563.     }
  564.     return FALSE;
  565. }
  566.  
  567. /*
  568.  * This routine does the real work of a
  569.  * backward search. The pattern is sitting in the external
  570.  * variable "pat". If found, dot is updated, the window system
  571.  * is notified of the change, and TRUE is returned. If the
  572.  * string isn't found, FALSE is returned.
  573.  */
  574. backsrch() {
  575.     register LINE    *clp;
  576.     register int    cbo;
  577.     register LINE    *tlp;
  578.     register int    tbo;
  579.     register int    c;
  580.     register char    *epp;
  581.     register char    *pp;
  582.  
  583.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  584.         ;
  585.     clp = curwp->w_dotp;
  586.     cbo = curwp->w_doto;
  587.     for (;;) {
  588.         if (cbo == 0) {
  589.             clp = lback(clp);
  590.             if (clp == curbp->b_linep)
  591.                 return FALSE;
  592.             cbo = llength(clp)+1;
  593.         }
  594.         if (--cbo == llength(clp))
  595.             c = CCHR('J');
  596.         else
  597.             c = lgetc(clp,cbo);
  598.         if (eq(c, *epp) != FALSE) {
  599.             tlp = clp;
  600.             tbo = cbo;
  601.             pp  = epp;
  602.             while (pp != &pat[0]) {
  603.                 if (tbo == 0) {
  604.                     tlp = lback(tlp);
  605.                     if (tlp == curbp->b_linep)
  606.                         goto fail;
  607.                     tbo = llength(tlp)+1;
  608.                 }
  609.                 if (--tbo == llength(tlp))
  610.                     c = CCHR('J');
  611.                 else
  612.                     c = lgetc(tlp,tbo);
  613.                 if (eq(c, *--pp) == FALSE)
  614.                     goto fail;
  615.             }
  616.             curwp->w_dotp  = tlp;
  617.             curwp->w_doto  = tbo;
  618.             curwp->w_flag |= WFMOVE;
  619.             return TRUE;
  620.         }
  621.     fail:    ;
  622.     }
  623.     /*NOTREACHED*/
  624. }
  625.  
  626. /*
  627.  * Compare two characters.
  628.  * The "bc" comes from the buffer.
  629.  * It has its case folded out. The
  630.  * "pc" is from the pattern.
  631.  */
  632. static int
  633. eq(bc, pc)
  634. register int bc, pc;
  635. {
  636.     bc = CHARMASK(bc);
  637.     pc = CHARMASK(pc);
  638.     if (bc == pc) return TRUE;
  639.     if (ISUPPER(bc)) return TOLOWER(bc) == pc;
  640.     if (ISUPPER(pc)) return bc == TOLOWER(pc);
  641.     return FALSE;
  642. }
  643.  
  644. /*
  645.  * Read a pattern.
  646.  * Stash it in the external variable "pat". The "pat" is
  647.  * not updated if the user types in an empty line. If the user typed
  648.  * an empty line, and there is no old pattern, it is an error.
  649.  * Display the old pattern, in the style of Jeff Lomicka. There is
  650.  * some do-it-yourself control expansion.
  651.  */
  652. readpattern(prompt) char *prompt; {
  653.     register int    s;
  654.     char        tpat[NPAT];
  655.  
  656.     if (tpat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
  657.     else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
  658.  
  659.     if (s == TRUE)                /* Specified        */
  660.         (VOID) strcpy(pat, tpat);
  661.     else if (s==FALSE && pat[0]!=0)        /* CR, but old one    */
  662.         s = TRUE;
  663.     return s;
  664. }
  665.