home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d3xx / d352 / mg.lha / MG / src.LZH / mg / search.c < prev    next >
C/C++ Source or Header  |  1990-05-23  |  15KB  |  685 lines

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