home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / UE311C.ZIP / ISEARCH.C < prev    next >
C/C++ Source or Header  |  1991-07-09  |  16KB  |  466 lines

  1. /*
  2.  * The functions in this file implement commands that perform incremental
  3.  * searches in the forward and backward directions.  This "ISearch" command
  4.  * is intended to emulate the same command from the original EMACS
  5.  * implementation (ITS).  Contains references to routines internal to
  6.  * SEARCH.C.
  7.  *
  8.  * REVISION HISTORY:
  9.  *
  10.  *    D. R. Banks 9-May-86
  11.  *    - added ITS EMACSlike ISearch
  12.  *
  13.  *    John M. Gamble 5-Oct-86
  14.  *    - Made iterative search use search.c's scanner() routine.
  15.  *      This allowed the elimination of bakscan().
  16.  *    - Put isearch constants into estruct.h
  17.  *    - Eliminated the passing of 'status' to scanmore() and
  18.  *      checknext(), since there were no circumstances where
  19.  *      it ever equalled FALSE.
  20.  *    Dan Corkill 6-Oct-87
  21.  *      - Changed character loop to terminate with extended characters
  22.  *        (thus arrow keys, and most commands behave intuitively).
  23.  *      - Changed META to be reread rather than simply aborting.
  24.  *      - Conditionalized VMS alternates for ^S and ^Q to only apply
  25.  *        to VMS ports.  (Allowing ^X as a synonym for ^S defeats some
  26.  *        of the benefits of the first change above.)
  27.  */
  28.  
  29. #include <stdio.h>
  30. #include "estruct.h"
  31. #include "eproto.h"
  32. #include "edef.h"
  33. #include "elang.h"
  34.  
  35. #if    ISRCH
  36.  
  37. #define CMDBUFLEN    256    /* Length of our command buffer */
  38.  
  39. /* A couple of "own" variables for re-eat */
  40. /* Hey, BLISS user, these were "GLOBAL", I made them "OWN". */
  41. static int    (PASCAL NEAR *saved_get_char)();/* Get character routine */
  42. static int    eaten_char = -1;        /* Re-eaten char */
  43.  
  44. /* A couple more "own" variables for the command string */
  45.  
  46. static int cmd_buff[CMDBUFLEN];        /* Save the command args here */
  47. static int cmd_offset;            /* Current offset into command buff */
  48. static int cmd_reexecute = -1;        /* > 0 if re-executing command */
  49.  
  50. PASCAL NEAR reeat();    /* A local function type definition */
  51.  
  52. /*
  53.  * Subroutine to do incremental reverse search.  It actually uses the
  54.  * same code as the normal incremental search, as both can go both ways.
  55.  */
  56. int PASCAL NEAR risearch(f, n)
  57.  
  58. int f,n;    /* prefix flag and argument */
  59.  
  60. {
  61.     register int    status;
  62.  
  63.     /* Make sure the search doesn't match where we already are:              */
  64.  
  65.     backchar(TRUE, 1);            /* Back up a character              */
  66.  
  67.     if (status = isearch(REVERSE))
  68.     mlerase();            /* If happy, just erase the cmd line  */
  69.     else
  70.     mlwrite(TEXT164);
  71. /*               "[search failed]" */
  72.     return (status);
  73. }
  74.  
  75. /* Again, but for the forward direction */
  76.  
  77. int PASCAL NEAR fisearch(f, n)
  78.  
  79. int f,n;    /* prefix flag and argument */
  80.  
  81. {
  82.     register int     status;
  83.  
  84.     if (status = isearch(FORWARD))
  85.     mlerase();            /* If happy, just erase the cmd line  */
  86.     else
  87.     mlwrite(TEXT164);
  88. /*               "[search failed]" */
  89.     return (status);
  90. }
  91.  
  92. /*
  93.  * Subroutine to do an incremental search.  In general, this works similarly
  94.  * to the older micro-emacs search function, except that the search happens
  95.  * as each character is typed, with the screen and cursor updated with each
  96.  * new search character.
  97.  *
  98.  * While searching forward, each successive character will leave the cursor
  99.  * at the end of the entire matched string.  Typing a Control-S
  100.  * will cause the next occurrence of the string to be searched for (where the
  101.  * next occurrence does NOT overlap the current occurrence).  A Control-R will
  102.  * change to a backwards search, META will terminate the search and Control-G
  103.  * will abort the search.  Rubout will back up to the previous match of the
  104.  * string, or if the starting point is reached first, it will delete the
  105.  * last character from the search string.
  106.  *
  107.  * While searching backward, each successive character will leave the cursor
  108.  * at the beginning of the matched string.  Typing a Control-R will search
  109.  * backward for the next occurrence of the string.  Control-S
  110.  * will revert the search to the forward direction.  In general, the reverse
  111.  * incremental search is just like the forward incremental search inverted.
  112.  *
  113.  * In all cases, if the search fails, the user will be feeped, and the search
  114.  * will stall until the pattern string is edited back into something that
  115.  * exists (or until the search is aborted).
  116.  */
  117. PASCAL NEAR isearch(dir)
  118.  
  119. int    dir;
  120.  
  121. {
  122.     int            status;        /* Search status */
  123.     int            col;        /* prompt column */
  124.     register int    cpos;        /* character number in search string  */
  125.     register int    c;        /* current input character */
  126.     register int    expc;        /* function expanded input char          */
  127.     char        pat_save[NPAT];    /* Saved copy of the old pattern str  */
  128.     LINE        *curline;    /* Current line on entry          */
  129.     int            curoff;        /* Current offset on entry          */
  130.     int            init_direction;    /* The initial search direction          */
  131.     KEYTAB        *ktp;        /* The command bound to the key          */
  132.     register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
  133.  
  134.     /* Initialize starting conditions */
  135.  
  136.     cmd_reexecute = -1;            /* We're not re-executing (yet?)      */
  137.     cmd_offset = 0;            /* Start at the beginning of cmd_buff */
  138.     cmd_buff[0] = '\0';            /* Reset the command buffer          */
  139.     bytecopy(pat_save, pat, NPAT);    /* Save the old pattern string          */
  140.     curline = curwp->w_dotp;        /* Save the current line pointer      */
  141.     curoff  = curwp->w_doto;        /* Save the current offset          */
  142.     init_direction = dir;        /* Save the initial search direction  */
  143.  
  144.  
  145. start_over:    /* This is a good place to start a re-execution: */
  146.  
  147.     /*
  148.      * Ask the user for the text of a pattern,
  149.      * and remember the col.
  150.      */
  151.     col = promptpattern(TEXT165);
  152. /*                      "ISearch: " */
  153.  
  154.     cpos = 0;                    /* Start afresh              */
  155.     status = TRUE;                /* Assume everything's cool   */
  156.  
  157.     for (;;)                    /* ISearch per character loop */
  158.     {
  159.     /* Check for special characters first.
  160.          * That is, a control or ^X or FN or mouse function.
  161.      * Most cases here change the search.
  162.      */
  163.     c = ectoc(expc = get_char());
  164.  
  165.     if (expc == sterm)            /* Want to quit searching?    */
  166.         return(TRUE);            /* Quit searching now          */
  167.  
  168.     if (expc == abortc)            /* If abort search request    */
  169.         break;                /* Quit searching          */
  170.  
  171.     if (expc == quotec)            /* Quote character?          */
  172.     {
  173.         c = ectoc(expc = get_char());    /* Get the next char          */
  174.     }
  175.     else if ((expc > 255 || expc == 0) && (c != '\t' && c != '\r'))
  176.     {
  177.         if (ktp = getbind(expc))
  178.             kfunc = ktp->k_ptr.fp;
  179.         else
  180.             kfunc = NULL;
  181.  
  182.         if (kfunc == forwsearch || kfunc == forwhunt || kfunc == fisearch || 
  183.         kfunc == backsearch || kfunc == backhunt || kfunc == risearch)
  184.             {
  185.                 dir = (kfunc == backsearch || kfunc == backhunt || kfunc == risearch)? REVERSE: FORWARD;
  186.  
  187.         /*
  188.          * if cpos == 0 then we are either just starting
  189.          * or starting over.  Use the original pattern
  190.          * in pat, which has either not been changed or
  191.          * has just been restored.  Find the length and
  192.          * re-echo the string.
  193.          */
  194.             if (cpos == 0)
  195.                 while (pat[cpos] != 0)
  196.                     col = echochar((int)pat[cpos++],col);
  197.  
  198.             status = scanmore(dir);
  199.             continue;
  200.             }
  201.             else if (kfunc == backdel)
  202.             {
  203.             if (cmd_offset <= 1)        /* Anything to delete?          */
  204.                 return(TRUE);        /* No, just exit          */
  205.  
  206.             cmd_offset -= 2;        /* Back up over the Rubout    */
  207.             cmd_buff[cmd_offset] = '\0';    /* Yes, delete last char   */
  208.             curwp->w_dotp = curline;    /* Reset the line pointer     */
  209.             curwp->w_doto = curoff;        /*  and the offset          */
  210.         dir = init_direction;        /* Reset the search direction */
  211.         bytecopy(pat, pat_save, NPAT);    /* Restore the old search str */
  212.         setjtable();            /* and its jump tables.       */
  213.         cmd_reexecute = 0;        /* Start the whole mess over  */
  214.         goto start_over;        /* Let it take care of itself */
  215.             }
  216.  
  217.             /* Presumably the key was uninteresting...*/
  218.  
  219.             reeat(expc);        /* Re-eat the char          */
  220.             return(TRUE);        /* And return the last status */
  221.     }
  222.  
  223.     /* I guess we got something to search for, so search for it          */
  224.  
  225.     pat[cpos++] = c;        /* put the char in the buffer */
  226.  
  227.     if (cpos >= NPAT)            /* too many chars in string?  */
  228.     {                    /* Yup.  Complain about it    */
  229.         mlwrite(TEXT166);
  230. /*                  "? Search string too long" */
  231.         bytecopy(pat, pat_save, NPAT);    /* Restore the old search str */
  232.         setjtable();            /* and its jump tables.       */
  233.         return(FALSE);            /* Return an error, but stay. */
  234.     }
  235.  
  236.     pat[cpos] = 0;                /* null terminate the buffer  */
  237.     col = echochar(c,col);            /* Echo the character          */
  238.     if (!status)                /* If we lost last time          */
  239.         TTbeep();                /* Feep again        */
  240.     else                    /* Otherwise, we must have won*/
  241.         status = checknext(c, dir);    /* See if still matches or find next */
  242.  
  243.     }    /* for {;;} */
  244.  
  245.     curwp->w_dotp = curline;        /* Reset the line pointer          */
  246.     curwp->w_doto = curoff;        /*  and the offset to original value  */
  247.     curwp->w_flag |= WFMOVE;        /* Say we've moved              */
  248.     update(FALSE);            /* And force an update              */
  249.     return (FALSE);
  250. }
  251.  
  252. /*
  253.  * This hack will search for the next occurrence of <pat> in the buffer,
  254.  * either forward or backward.  If we can't find any more matches, "point"
  255.  * is left where it was before.  If we do find a match, "point" will be at
  256.  * the end of the matched string for forward searches and at the beginning
  257.  * of the matched string for reverse searches.
  258.  */
  259.  
  260. int PASCAL NEAR scanmore(dir)
  261. int    dir;            /* direction to search            */
  262. {
  263.     register int    sts;        /* search status        */
  264.  
  265.     setjtable();            /* Set up fast search arrays    */
  266.  
  267.     sts = scanner(dir, (dir == REVERSE)? PTBEG: PTEND, 1);
  268.  
  269.     if (!sts)
  270.         TTbeep();    /* Feep if search fails       */
  271.  
  272.     return(sts);
  273. }
  274.  
  275. /*
  276.  * Trivial routine to insure that the next character in the search
  277.  * string is still true to whatever we're pointing to in the buffer.
  278.  * This routine will not attempt to move the "point" if the match
  279.  * fails, although it will implicitly move the "point" if we're
  280.  * forward searching, and find a match, since that's the way forward
  281.  * isearch works.  If we are reverse searching we compare all
  282.  * characters in the pattern string from "point" to the new end.
  283.  *
  284.  * If the compare fails, we return FALSE and call scanmore or something.
  285.  */
  286. int PASCAL NEAR checknext(chr, dir)
  287. int    chr;            /* Next char to look for         */
  288. int    dir;            /* Search direction             */
  289. {
  290.     LINE *curline;            /* current line during scan          */
  291.     int curoff;                /* position within current line          */
  292.     register char *patrn;        /* The entire search string (incl chr)   */
  293.     register int sts;            /* how well things go              */
  294.  
  295.     /* setup the local scan pointer to current "." */
  296.  
  297.     curline = curwp->w_dotp;        /* Get the current line structure     */
  298.     curoff  = curwp->w_doto;        /* Get the offset within that line    */
  299.  
  300.     if (dir == FORWARD)            /* If searching forward         */
  301.     {
  302.     if (sts = !boundry(curline, curoff, FORWARD))
  303.     {
  304.         /* Is it what we're looking for?      */
  305.         if (sts = eq(nextch(&curline, &curoff, FORWARD), chr))
  306.         {
  307.         curwp->w_dotp = curline;    /* Yes, set the buffer's point */
  308.         curwp->w_doto = curoff;        /*  to the matched character */
  309.         curwp->w_flag |= WFMOVE;    /* Say that we've moved */
  310.         }
  311.     }
  312.     }
  313.     else        /* Else, reverse search check. */
  314.     {
  315.         patrn = pat;
  316.         while (*patrn)    /* Loop for all characters in patrn   */
  317.         {
  318.         if ((sts = !boundry(curline, curoff, FORWARD)) == FALSE ||
  319.             (sts = eq(nextch(&curline, &curoff, FORWARD), *patrn)) == FALSE)
  320.             break;        /* Nope, just punt it then    */
  321.  
  322.         patrn++;
  323.         }
  324.     }
  325.  
  326.     /*
  327.      * If the 'next' character didn't fit in the pattern,
  328.      * let's go search for it somewhere else.
  329.      */
  330.     if (sts == FALSE)
  331.         sts = scanmore(dir);
  332.  
  333.     return(sts);        /* And return the status        */
  334. }
  335.  
  336. /*
  337.  * Routine to prompt for I-Search string.
  338.  */
  339.  
  340. int PASCAL NEAR promptpattern(prompt)
  341. char *prompt;
  342. {
  343.     char tpat[NPAT+20];
  344.  
  345.     strcpy(tpat, prompt);        /* copy prompt to output string */
  346.     strcat(tpat, " [");            /* build new prompt string */
  347.     expandp(pat, &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  348.     strcat(tpat, "]<META>: ");
  349.  
  350.     /* check to see if we are executing a command line */
  351.     if (!clexec) {
  352.     mlwrite(tpat);
  353.     }
  354.     return(strlen(tpat));
  355. }
  356.  
  357. /*
  358.  * Routine to echo i-search characters
  359.  */
  360.  
  361. int PASCAL NEAR echochar(c, col)
  362. int c;        /* character to be echoed */
  363. int col;    /* column to be echoed in */
  364. {
  365.     movecursor(term.t_nrow, col);    /* Position the cursor          */
  366.     if ((c < ' ') || (c == 0x7F))    /* Control character?    */
  367.     {
  368.     switch (c)            /* Yes, dispatch special cases*/
  369.     {
  370.       case '\r':            /* Newline          */
  371.         mlout('<');
  372.         mlout('N');
  373.         mlout('L');
  374.         mlout('>');
  375.         col += 3;
  376.         break;
  377.  
  378.       case '\t':            /* Tab              */
  379.         mlout('<');
  380.         mlout('T');
  381.         mlout('A');
  382.         mlout('B');
  383.         mlout('>');
  384.         col += 4;
  385.         break;
  386.  
  387.       default:        /* Vanilla control char and Rubout:   */
  388.         mlout('^');        /* Yes, output prefix          */
  389.         mlout(c ^ 0x40);    /* Make it "^X"              */
  390.         col++;        /* Count this char          */
  391.     }
  392.     } else
  393.     mlout(c);        /* Otherwise, output raw char */
  394.     TTflush();            /* Flush the output          */
  395.     return(++col);        /* return the new column no   */
  396. }
  397.  
  398. /*
  399.  * Routine to get the next character from the input stream.  If we're reading
  400.  * from the real terminal, force a screen update before we get the char.
  401.  * Otherwise, we must be re-executing the command string, so just return the
  402.  * next character.
  403.  */
  404.  
  405. int PASCAL NEAR get_char()
  406. {
  407.     int    c;
  408.  
  409.     /* See if we're re-executing: */
  410.  
  411.     if (cmd_reexecute >= 0)        /* Is there an offset?        */
  412.     if ((c = cmd_buff[cmd_reexecute++]) != 0)
  413.         return(c);            /* Yes, return any character    */
  414.  
  415.     /* We're not re-executing (or aren't any more).  Try for a real char
  416.      */
  417.     cmd_reexecute = -1;            /* Say we're in real mode again    */
  418.     update(FALSE);            /* Pretty up the screen        */
  419.     if (cmd_offset >= CMDBUFLEN-1)    /* If we're getting too big ...    */
  420.     {
  421.     mlwrite (TEXT167);        /* Complain loudly and bitterly    */
  422. /*               "? command too long" */
  423.     return(sterm);            /* And force a quit        */
  424.     }
  425.     c = getkey();            /* Get the next character    */
  426.  
  427.     cmd_buff[cmd_offset++] = c;        /* Save the char for next time    */
  428.     cmd_buff[cmd_offset] = '\0';    /* And terminate the buffer    */
  429.     return(c);                /* Return the character        */
  430. }
  431.  
  432. /*
  433.  * Hacky routine to re-eat a character.  This will save the character to be
  434.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  435.  */
  436.  
  437. /* Come here on the next term.t_getchar call: */
  438.  
  439. int PASCAL NEAR uneat()
  440. {
  441.     int c;
  442.  
  443.     term.t_getchar = saved_get_char;    /* restore the routine address          */
  444.     c = eaten_char;            /* Get the re-eaten char          */
  445.     eaten_char = -1;            /* Clear the old char              */
  446.     return(c);                /* and return the last char          */
  447. }
  448.  
  449. PASCAL NEAR reeat(c)
  450. int    c;
  451. {
  452.     if (eaten_char != -1)        /* If we've already been here          */
  453.     return/*(NULL)*/;        /* Don't do it again              */
  454.     eaten_char = c;            /* Else, save the char for later      */
  455.     saved_get_char = term.t_getchar;    /* Save the char get routine          */
  456.     term.t_getchar = uneat;        /* Replace it with ours              */
  457. }
  458. #else
  459. PASCAL NEAR isearch(dir)
  460.  
  461. int dir;
  462.  
  463. {
  464. }
  465. #endif
  466.