home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 198_02 / isearch.c < prev    next >
C/C++ Source or Header  |  1990-01-23  |  19KB  |  509 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.  */
  21.  
  22. #include        <stdio.h>
  23. #include    "estruct.h"
  24. #include        "edef.h"
  25.  
  26. #if    ISRCH
  27.  
  28. extern int scanner();            /* Handy search routine */
  29. extern int eq();            /* Compare chars, match case */
  30.  
  31. /* A couple of "own" variables for re-eat */
  32.  
  33. int    (*saved_get_char)();        /* Get character routine */
  34. int    eaten_char = -1;        /* Re-eaten char */
  35.  
  36. /* A couple more "own" variables for the command string */
  37.  
  38. int    cmd_buff[CMDBUFLEN];        /* Save the command args here */
  39. int    cmd_offset;            /* Current offset into command buff */
  40. int    cmd_reexecute = -1;        /* > 0 if re-executing command */
  41.  
  42.  
  43. /*
  44.  * Subroutine to do incremental reverse search.  It actually uses the
  45.  * same code as the normal incremental search, as both can go both ways.
  46.  */
  47.  
  48. int risearch(f, n)
  49. {
  50.     LINE *curline;            /* Current line on entry          */
  51.     int  curoff;            /* Current offset on entry          */
  52.  
  53.     /* remember the initial . on entry: */
  54.  
  55.     curline = curwp->w_dotp;        /* Save the current line pointer      */
  56.     curoff  = curwp->w_doto;        /* Save the current offset          */
  57.  
  58.     /* Make sure the search doesn't match where we already are:              */
  59.  
  60.     backchar(TRUE, 1);            /* Back up a character              */
  61.  
  62.     if (!(isearch(f, -n)))        /* Call ISearch backwards          */
  63.     {                    /* If error in search:              */
  64.     curwp->w_dotp = curline;    /* Reset the line pointer          */
  65.     curwp->w_doto = curoff;        /*  and the offset to original value  */
  66.     curwp->w_flag |= WFMOVE;    /* Say we've moved              */
  67.     update(FALSE);            /* And force an update              */
  68.     mlwrite ("[search failed]");    /* Say we died                  */
  69.     } else mlerase ();            /* If happy, just erase the cmd line  */
  70. }
  71.  
  72. /* Again, but for the forward direction */
  73.  
  74. int fisearch(f, n)
  75. {
  76.     LINE *curline;            /* Current line on entry          */
  77.     int  curoff;            /* Current offset on entry          */
  78.  
  79.     /* remember the initial . on entry: */
  80.  
  81.     curline = curwp->w_dotp;        /* Save the current line pointer      */
  82.     curoff  = curwp->w_doto;        /* Save the current offset          */
  83.  
  84.     /* do the search */
  85.  
  86.     if (!(isearch(f, n)))        /* Call ISearch forwards          */
  87.     {                    /* If error in search:              */
  88.     curwp->w_dotp = curline;    /* Reset the line pointer          */
  89.     curwp->w_doto = curoff;        /*  and the offset to original value  */
  90.     curwp->w_flag |= WFMOVE;    /* Say we've moved              */
  91.     update(FALSE);            /* And force an update              */
  92.     mlwrite ("[search failed]");    /* Say we died                  */
  93.     } else mlerase ();            /* If happy, just erase the cmd line  */
  94. }
  95.  
  96. /*
  97.  * Subroutine to do an incremental search.  In general, this works similarly
  98.  * to the older micro-emacs search function, except that the search happens
  99.  * as each character is typed, with the screen and cursor updated with each
  100.  * new search character.
  101.  *
  102.  * While searching forward, each successive character will leave the cursor
  103.  * at the end of the entire matched string.  Typing a Control-S or Control-X
  104.  * will cause the next occurrence of the string to be searched for (where the
  105.  * next occurrence does NOT overlap the current occurrence).  A Control-R will
  106.  * change to a backwards search, META will terminate the search and Control-G
  107.  * will abort the search.  Rubout will back up to the previous match of the
  108.  * string, or if the starting point is reached first, it will delete the
  109.  * last character from the search string.
  110.  *
  111.  * While searching backward, each successive character will leave the cursor
  112.  * at the beginning of the matched string.  Typing a Control-R will search
  113.  * backward for the next occurrence of the string.  Control-S or Control-X
  114.  * will revert the search to the forward direction.  In general, the reverse
  115.  * incremental search is just like the forward incremental search inverted.
  116.  *
  117.  * In all cases, if the search fails, the user will be feeped, and the search
  118.  * will stall until the pattern string is edited back into something that
  119.  * exists (or until the search is aborted).
  120.  */
  121.  
  122. isearch(f, n)
  123. {
  124.     int            status;        /* Search status */
  125.     int            col;        /* prompt column */
  126.     register int    cpos;        /* character number in search string  */
  127.     register int    c;        /* current input character */
  128.     register int    expc;        /* function expanded input char          */
  129.     char        pat_save[NPAT];    /* Saved copy of the old pattern str  */
  130.     LINE        *curline;    /* Current line on entry          */
  131.     int            curoff;        /* Current offset on entry          */
  132.     int            init_direction;    /* The initial search direction          */
  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 the buff */
  138.     cmd_buff[0] = '\0';        /* Init the command buffer          */
  139.     bytecopy (pat_save, pat, NPAT-1);    /* 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 = n;            /* Save the initial search direction  */
  143.  
  144.     /* This is a good place to start a re-execution: */
  145.  
  146. start_over:
  147.  
  148.     /* ask the user for the text of a pattern */
  149.     col = promptpattern("ISearch: ");        /* Prompt, remember the col   */
  150.  
  151.     cpos = 0;                    /* Start afresh              */
  152.     status = TRUE;                /* Assume everything's cool   */
  153.  
  154.     /*
  155.        Get the first character in the pattern.  If we get an initial Control-S
  156.        or Control-R, re-use the old search string and find the first occurrence
  157.      */
  158.  
  159.     c = ectoc(expc = get_char());        /* Get the first character    */
  160.     if ((c == IS_FORWARD) ||
  161.         (c == IS_REVERSE) ||
  162.         (c == IS_VMSFORW))            /* Reuse old search string?   */
  163.     {
  164.         for (cpos = 0; pat[cpos] != 0; cpos++)    /* Yup, find the length          */
  165.             col = echochar(pat[cpos],col);    /*  and re-echo the string    */
  166.     if (c == IS_REVERSE) {            /* forward search?          */
  167.         n = -1;                /* No, search in reverse      */
  168.         backchar (TRUE, 1);            /* Be defensive about EOB     */
  169.     } else
  170.         n = 1;                /* Yes, search forward          */
  171.     status = scanmore(pat, n);        /* Do the search          */
  172.     c = ectoc(expc = get_char());        /* Get another character      */
  173.     }
  174.  
  175.     /* Top of the per character loop */
  176.             
  177.     for (;;)                    /* ISearch per character loop */
  178.     {
  179.     /* Check for special characters first: */
  180.     /* Most cases here change the search */
  181.  
  182.     if (expc == metac)            /* Want to quit searching?    */
  183.         return (TRUE);            /* Quit searching now          */
  184.  
  185.     switch (c)                /* dispatch on the input char */
  186.     {
  187.       case IS_ABORT:            /* If abort search request    */
  188.         return(FALSE);            /* Quit searching again          */
  189.  
  190.       case IS_REVERSE:            /* If backward search          */
  191.       case IS_FORWARD:            /* If forward search          */
  192.       case IS_VMSFORW:            /*  of either flavor          */
  193.         if (c == IS_REVERSE)        /* If reverse search          */
  194.         n = -1;                /* Set the reverse direction  */
  195.         else                /* Otherwise,               */
  196.         n = 1;                /*  go forward              */
  197.         status = scanmore(pat, n);        /* Start the search again     */
  198.         c = ectoc(expc = get_char());    /* Get the next char          */
  199.         continue;                /* Go continue with the search*/
  200.  
  201.       case IS_NEWLINE:            /* Carriage return          */
  202.         c = '\n';                /* Make it a new line          */
  203.         break;                /* Make sure we use it          */
  204.  
  205.       case IS_QUOTE:            /* Quote character          */
  206.       case IS_VMSQUOTE:            /*  of either variety          */
  207.         c = ectoc(expc = get_char());    /* Get the next char          */
  208.  
  209.       case IS_TAB:                /* Generically allowed          */
  210.       case '\n':                /*  controlled characters     */
  211.         break;                /* Make sure we use it          */
  212.  
  213.       case IS_BACKSP:            /* If a backspace:            */
  214.       case IS_RUBOUT:            /*  or if a Rubout:          */
  215.         if (cmd_offset <= 1)        /* Anything to delete?          */
  216.         return (TRUE);            /* No, just exit          */
  217.         --cmd_offset;            /* Back up over the Rubout    */
  218.         cmd_buff[--cmd_offset] = '\0';    /* Yes, delete last char   */
  219.         curwp->w_dotp = curline;        /* Reset the line pointer     */
  220.         curwp->w_doto = curoff;        /*  and the offset          */
  221.         n = init_direction;            /* Reset the search direction */
  222.         bytecopy (pat, pat_save, NPAT-1);    /* Restore the old search str */
  223.         cmd_reexecute = 0;            /* Start the whole mess over  */
  224.         goto start_over;            /* Let it take care of itself */
  225.  
  226.       /* Presumably a quasi-normal character comes here */
  227.  
  228.       default:                /* All other chars              */
  229.         if (c < ' ')            /* Is it printable?          */
  230.         {                    /* Nope.              */
  231.         reeat (c);            /* Re-eat the char          */
  232.         return (TRUE);            /* And return the last status */
  233.         }
  234.     }  /* Switch */
  235.  
  236.     /* I guess we got something to search for, so search for it          */
  237.  
  238.     pat[cpos++] = c;            /* put the char in the buffer */
  239.     if (cpos >= NPAT)            /* too many chars in string?  */
  240.     {                    /* Yup.  Complain about it    */
  241.         mlwrite("? Search string too long");
  242.         return(TRUE);            /* Return an error          */
  243.     }
  244.     pat[cpos] = 0;                /* null terminate the buffer  */
  245.     col = echochar(c,col);            /* Echo the character          */
  246.     if (!status) {                /* If we lost last time          */
  247.         TTputc(BELL);        /* Feep again              */
  248.         TTflush();            /* see that the feep feeps    */
  249.     } else                    /* Otherwise, we must have won*/
  250.         if (!(status = checknext(c, pat, n))) /* See if match          */
  251.         status = scanmore(pat, n);    /*  or find the next match    */
  252.     c = ectoc(expc = get_char());        /* Get the next char          */
  253.     } /* for {;;} */
  254. }
  255.  
  256. /*
  257.  * Trivial routine to insure that the next character in the search string is
  258.  * still true to whatever we're pointing to in the buffer.  This routine will
  259.  * not attempt to move the "point" if the match fails, although it will 
  260.  * implicitly move the "point" if we're forward searching, and find a match,
  261.  * since that's the way forward isearch works.
  262.  *
  263.  * If the compare fails, we return FALSE and assume the caller will call
  264.  * scanmore or something.
  265.  */
  266.  
  267. int checknext (chr, patrn, dir)    /* Check next character in search string */
  268. char    chr;            /* Next char to look for         */
  269. char    *patrn;            /* The entire search string (incl chr)   */
  270. int    dir;            /* Search direction             */
  271. {
  272.     register LINE *curline;        /* current line during scan          */
  273.     register int curoff;        /* position within current line          */
  274.     register int buffchar;        /* character at current position      */
  275.     int status;                /* how well things go              */
  276.  
  277.  
  278.     /* setup the local scan pointer to current "." */
  279.  
  280.     curline = curwp->w_dotp;        /* Get the current line structure     */
  281.     curoff  = curwp->w_doto;        /* Get the offset within that line    */
  282.  
  283.     if (dir > 0)            /* If searching forward              */
  284.     {
  285.         if (curoff == llength(curline)) /* If at end of line              */
  286.         {
  287.         curline = lforw(curline);    /* Skip to the next line          */
  288.         if (curline == curbp->b_linep)
  289.         return (FALSE);        /* Abort if at end of buffer          */
  290.         curoff = 0;            /* Start at the beginning of the line */
  291.         buffchar = '\n';        /* And say the next char is NL          */
  292.     } else
  293.         buffchar = lgetc(curline, curoff++); /* Get the next char          */
  294.     if ((status=eq(buffchar, chr)) != 0) /* Is it what we're looking for? */
  295.     {
  296.         curwp->w_dotp = curline;    /* Yes, set the buffer's point          */
  297.         curwp->w_doto = curoff;    /*  to the matched character          */
  298.         curwp->w_flag |= WFMOVE;    /* Say that we've moved              */
  299.     }
  300.     return (status);        /* And return the status          */
  301.     } else                /* Else, if reverse search:          */
  302.     return (match_pat (patrn));    /* See if we're in the right place    */
  303. }
  304.  
  305. /*
  306.  * This hack will search for the next occurrence of <pat> in the buffer, either
  307.  * forward or backward.  It is called with the status of the prior search
  308.  * attempt, so that it knows not to bother if it didn't work last time.  If
  309.  * we can't find any more matches, "point" is left where it was before.  If
  310.  * we do find a match, "point" will be at the end of the matched string for
  311.  * forward searches and at the beginning of the matched string for reverse
  312.  * searches.
  313.  */
  314.  
  315. int scanmore(patrn, dir)    /* search forward or back for a pattern          */
  316. char    *patrn;            /* string to scan for                  */
  317. int    dir;            /* direction to search                  */
  318. {
  319.     int    sts;            /* search status              */
  320.  
  321.     setjtable(patrn);        /* Set up fast search array */
  322.  
  323.         if (dir < 0)                /* reverse search?          */
  324.         {
  325.         rvstrcpy(tap, patrn);        /* Put reversed string in tap */
  326.         sts = scanner(tap, REVERSE, PTBEG);
  327.     }
  328.     else
  329.         sts = scanner(patrn, FORWARD, PTEND);    /* Nope. Go forward   */
  330.  
  331.     if (!sts)
  332.     {
  333.         TTputc(BELL);    /* Feep if search fails       */
  334.         TTflush();        /* see that the feep feeps    */
  335.     }
  336.  
  337.     return(sts);                /* else, don't even try          */
  338. }
  339.  
  340. /*
  341.  * The following is a worker subroutine used by the reverse search.  It
  342.  * compares the pattern string with the characters at "." for equality. If
  343.  * any characters mismatch, it will return FALSE.
  344.  *
  345.  * This isn't used for forward searches, because forward searches leave "."
  346.  * at the end of the search string (instead of in front), so all that needs to
  347.  * be done is match the last char input.
  348.  */
  349.  
  350. int match_pat (patrn)    /* See if the pattern string matches string at "."   */
  351. char    *patrn;        /* String to match to buffer                 */
  352. {
  353.     register int  i;            /* Generic loop index/offset          */
  354.     register int buffchar;        /* character at current position      */
  355.     register LINE *curline;        /* current line during scan          */
  356.     register int curoff;        /* position within current line          */
  357.  
  358.     /* setup the local scan pointer to current "." */
  359.  
  360.     curline = curwp->w_dotp;        /* Get the current line structure     */
  361.     curoff  = curwp->w_doto;        /* Get the offset within that line    */
  362.  
  363.     /* top of per character compare loop: */
  364.  
  365.     for (i = 0; i < strlen(patrn); i++)    /* Loop for all characters in patrn   */
  366.     {
  367.         if (curoff == llength(curline)) /* If at end of line              */
  368.         {
  369.         curline = lforw(curline);    /* Skip to the next line          */
  370.         curoff = 0;            /* Start at the beginning of the line */
  371.         if (curline == curbp->b_linep)
  372.         return (FALSE);        /* Abort if at end of buffer          */
  373.         buffchar = '\n';        /* And say the next char is NL          */
  374.     } else
  375.         buffchar = lgetc(curline, curoff++); /* Get the next char          */
  376.     if (!eq(buffchar, patrn[i]))    /* Is it what we're looking for?      */
  377.         return (FALSE);        /* Nope, just punt it then          */
  378.     }
  379.     return (TRUE);            /* Everything matched? Let's celebrate*/
  380. }
  381.  
  382. /* Routine to prompt for I-Search string. */
  383.  
  384. int promptpattern(prompt)
  385. char *prompt;
  386. {
  387.     char tpat[NPAT+20];
  388.  
  389.     strcpy(tpat, prompt);        /* copy prompt to output string */
  390.     strcat(tpat, " [");            /* build new prompt string */
  391.     expandp(pat, &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  392.     strcat(tpat, "]<META>: ");
  393.  
  394.     /* check to see if we are executing a command line */
  395.     if (!clexec) {
  396.     mlwrite(tpat);
  397.     }
  398.     return(strlen(tpat));
  399. }
  400.  
  401. /* routine to echo i-search characters */
  402.  
  403. int echochar(c,col)
  404. int    c;    /* character to be echoed */
  405. int    col;    /* column to be echoed in */
  406. {
  407.     movecursor(term.t_nrow,col);        /* Position the cursor          */
  408.     if ((c < ' ') || (c == 0x7F))        /* Control character?          */
  409.     {
  410.     switch (c)                /* Yes, dispatch special cases*/
  411.     {
  412.       case '\n':                /* Newline              */
  413.         TTputc('<');
  414.         TTputc('N');
  415.         TTputc('L');
  416.         TTputc('>');
  417.         col += 3;
  418.         break;
  419.  
  420.       case '\t':                /* Tab                  */
  421.         TTputc('<');
  422.         TTputc('T');
  423.         TTputc('A');
  424.         TTputc('B');
  425.         TTputc('>');
  426.         col += 4;
  427.         break;
  428.  
  429.       case 0x7F:                /* Rubout:              */
  430.         TTputc('^');        /* Output a funny looking     */
  431.         TTputc('?');        /*  indication of Rubout      */
  432.         col++;                /* Count the extra char       */
  433.         break;
  434.  
  435.       default:                /* Vanilla control char       */
  436.         TTputc('^');        /* Yes, output prefix          */
  437.             TTputc(c+0x40);        /* Make it "^X"              */
  438.         col++;                /* Count this char          */
  439.     }
  440.     } else
  441.     TTputc(c);            /* Otherwise, output raw char */
  442.     TTflush();                /* Flush the output          */
  443.     return(++col);                /* return the new column no   */
  444. }
  445.  
  446. /*
  447.  * Routine to get the next character from the input stream.  If we're reading
  448.  * from the real terminal, force a screen update before we get the char. 
  449.  * Otherwise, we must be re-executing the command string, so just return the
  450.  * next character.
  451.  */
  452.  
  453. int get_char ()
  454. {
  455.     int    c;                /* A place to get a character          */
  456.  
  457.     /* See if we're re-executing: */
  458.  
  459.     if (cmd_reexecute >= 0)        /* Is there an offset?              */
  460.     if ((c = cmd_buff[cmd_reexecute++]) != 0)
  461.         return (c);            /* Yes, return any character          */
  462.  
  463.     /* We're not re-executing (or aren't any more).  Try for a real char      */
  464.  
  465.     cmd_reexecute = -1;        /* Say we're in real mode again          */
  466.     update(FALSE);            /* Pretty up the screen              */
  467.     if (cmd_offset >= CMDBUFLEN-1)    /* If we're getting too big ...          */
  468.     {
  469.     mlwrite ("? command too long");    /* Complain loudly and bitterly          */
  470.     return (metac);            /* And force a quit              */
  471.     }
  472.     c = get1key();        /* Get the next character          */
  473.     cmd_buff[cmd_offset++] = c; /* Save the char for next time        */
  474.     cmd_buff[cmd_offset] = '\0';/* And terminate the buffer          */
  475.     return (c);                /* Return the character              */
  476. }
  477.  
  478. /*
  479.  * Hacky routine to re-eat a character.  This will save the character to be
  480.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  481.  */
  482.  
  483. /* Come here on the next term.t_getchar call: */
  484.  
  485. int uneat()
  486. {
  487.     int c;
  488.  
  489.     term.t_getchar = saved_get_char;    /* restore the routine address          */
  490.     c = eaten_char;            /* Get the re-eaten char          */
  491.     eaten_char = -1;            /* Clear the old char              */
  492.     return(c);                /* and return the last char          */
  493. }
  494.  
  495. int reeat(c)
  496. int    c;
  497. {
  498.     if (eaten_char != -1)        /* If we've already been here          */
  499.     return/*(NULL)*/;        /* Don't do it again              */
  500.     eaten_char = c;            /* Else, save the char for later      */
  501.     saved_get_char = term.t_getchar;    /* Save the char get routine          */
  502.     term.t_getchar = uneat;        /* Replace it with ours              */
  503. }
  504. #else
  505. isearch()
  506. {
  507. }
  508. #endif
  509.