home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / isearch.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  17KB  |  565 lines

  1. /* vile note, 6/1/91, pgf -- I haven't tried this code in a long time */
  2. /*
  3.  * The functions in this file implement commands that perform incremental
  4.  * searches in the forward and backward directions.  This "ISearch" command
  5.  * is intended to emulate the same command from the original EMACS
  6.  * implementation (ITS).  Contains references to routines internal to
  7.  * SEARCH.C.
  8.  * 
  9.  * REVISION HISTORY:
  10.  * 
  11.  * D. R. Banks 9-May-86 - added ITS EMACSlike ISearch
  12.  * 
  13.  * John M. Gamble 5-Oct-86 - Made iterative search use search.c's scanner()
  14.  * routine. This allowed the elimination of bakscan(). - Put isearch
  15.  * constants into estruct.h - Eliminated the passing of 'status' to
  16.  * scanmore() and checknext(), since there were no circumstances where it
  17.  * ever equalled FALSE.
  18.  * 
  19.  * $Log: isearch.c,v $
  20.  * Revision 1.16  1992/05/25  21:07:48  foxharp
  21.  * extern func declarations moved to header
  22.  *
  23.  * Revision 1.15  1992/05/19  08:55:44  foxharp
  24.  * more prototype and shadowed decl fixups
  25.  *
  26.  * Revision 1.14  1992/05/16  12:00:31  pgf
  27.  * prototypes/ansi/void-int stuff/microsoftC
  28.  *
  29.  * Revision 1.13  1992/03/01  18:41:31  pgf
  30.  * took out the checknext optimization, since we no longer move along
  31.  * the found string quite the way we used to
  32.  *
  33.  * Revision 1.12  1992/01/05  00:06:13  pgf
  34.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  35.  * often.  also normalized message appearance somewhat.
  36.  *
  37.  * Revision 1.11  1991/11/16  18:34:31  pgf
  38.  * pass magic mode as flag to regcomp()
  39.  *
  40.  * Revision 1.10  1991/11/01  14:38:00  pgf
  41.  * saber cleanup
  42.  * Revision 1.9  1991/10/30  14:55:43  pgf renamed
  43.  * thescanner to scanner and setboundry to scanboundry
  44.  * 
  45.  * Revision 1.8  1991/10/27  01:46:27  pgf switch to regexp from regex package,
  46.  * and moved expandp to this file
  47.  * 
  48.  * Revision 1.7  1991/10/24  13:05:52  pgf conversion to new regex package --
  49.  * much faster
  50.  * 
  51.  * Revision 1.6  1991/08/07  12:35:07  pgf added RCS log messages
  52.  * 
  53.  * revision 1.5 date: 1991/06/26 09:37:56; renamed an ifdef BEFORE
  54.  * 
  55.  * revision 1.4 date: 1991/06/25 19:52:50; massive data structure restructure
  56.  * 
  57.  * revision 1.3 date: 1991/06/03 10:23:22; cleanup, and made it work with newer
  58.  * scanner (setboundry())
  59.  * 
  60.  * revision 1.2 date: 1990/10/03 16:00:54; make backspace work for everyone
  61.  * 
  62.  * revision 1.1 date: 1990/09/21 10:25:30; initial vile RCS revision
  63.  */
  64.  
  65.  
  66. #include        <stdio.h>
  67. #include    "estruct.h"
  68. #include        "edef.h"
  69.  
  70. #if    ISRCH
  71.  
  72. #ifdef USE_REEAT
  73. /* A couple of "own" variables for re-eat */
  74. int             (*saved_get_char) ();    /* Get character routine */
  75. int             eaten_char = -1;/* Re-eaten char */
  76. #endif
  77.  
  78. /* A couple "own" variables for the command string */
  79.  
  80. int             cmd_buff[CMDBUFLEN];    /* Save the command args here */
  81. int             cmd_offset;    /* Current offset into command buff */
  82. int             cmd_reexecute = -1;    /* > 0 if re-executing command */
  83.  
  84.  
  85. /*
  86.  * Subroutine to do incremental reverse search.  It actually uses the same
  87.  * code as the normal incremental search, as both can go both ways.
  88.  */
  89.  
  90. int 
  91. risearch(f, n)
  92.     int             f, n;
  93. {
  94.     MARK            curpos;    /* Current point on entry */
  95.  
  96.     /* remember the initial . on entry: */
  97.  
  98.     curpos = DOT;        /* Save the current point */
  99.  
  100.     /* Make sure the search doesn't match where we already are: */
  101.  
  102.     backchar(TRUE, 1);    /* Back up a character */
  103.  
  104. #if    NeWS
  105.     newsimmediateon();
  106. #endif
  107.  
  108.     if (!(isearch(f, -n))) {/* Call ISearch backwards */
  109.                 /* If error in search: */
  110.         DOT = curpos;    /* Reset the pointer */
  111.         curwp->w_flag |= WFMOVE;    /* Say we've moved */
  112.         update(FALSE);    /* And force an update */
  113.         mlforce("[I-Search failed]");    /* Say we died */
  114.         TTbeep();
  115.         return FALSE;
  116.     } else
  117.         mlerase();    /* If happy, just erase the cmd line */
  118.  
  119. #if    NeWS
  120.     newsimmediateoff();
  121. #endif
  122.     return TRUE;
  123. }
  124.  
  125. /* Again, but for the forward direction */
  126.  
  127. int
  128. fisearch(f, n)
  129.     int             f, n;
  130. {
  131.     MARK            curpos;    /* current line on entryl */
  132.  
  133.     /* remember the initial . on entry: */
  134.  
  135.     curpos = DOT;        /* save current point */
  136.  
  137.     /* do the search */
  138.  
  139. #if    NeWS
  140.     newsimmediateon();
  141. #endif
  142.  
  143.     if (!(isearch(f, n))) {    /* Call ISearch forwards */
  144.                 /* If error in search: */
  145.         DOT = curpos;    /* reset */
  146.         curwp->w_flag |= WFMOVE;    /* Say we've moved */
  147.         update(FALSE);    /* And force an update */
  148.         mlforce("[I-Search failed]");    /* Say we died */
  149.         TTbeep();
  150.         return FALSE;
  151.     } else
  152.         mlerase();    /* If happy, just erase the cmd line */
  153.  
  154. #if    NeWS
  155.     newsimmediateoff();
  156. #endif
  157.     return TRUE;
  158. }
  159.  
  160. /*
  161.  * Subroutine to do an incremental search.  In general, this works similarly
  162.  * to the older micro-emacs search function, except that the search happens
  163.  * as each character is typed, with the screen and cursor updated with each
  164.  * new search character.
  165.  * 
  166.  * While searching forward, each successive character will leave the cursor at
  167.  * the end of the entire matched string.  Typing a Control-S or Control-X
  168.  * will cause the next occurrence of the string to be searched for (where the
  169.  * next occurrence does NOT overlap the current occurrence).  A Control-R
  170.  * will change to a backwards search, META will terminate the search and
  171.  * Control-G will abort the search.  Rubout will back up to the previous
  172.  * match of the string, or if the starting point is reached first, it will
  173.  * delete the last character from the search string.
  174.  * 
  175.  * While searching backward, each successive character will leave the cursor at
  176.  * the beginning of the matched string.  Typing a Control-R will search
  177.  * backward for the next occurrence of the string.  Control-S or Control-X
  178.  * will revert the search to the forward direction.  In general, the reverse
  179.  * incremental search is just like the forward incremental search inverted.
  180.  * 
  181.  * In all cases, if the search fails, the user will be feeped, and the search
  182.  * will stall until the pattern string is edited back into something that
  183.  * exists (or until the search is aborted).
  184.  */
  185.  
  186. /* ARGSUSED */
  187. int
  188. isearch(f, n)
  189. int f,n;
  190. {
  191.     int             status;    /* Search status */
  192.     int             col;    /* prompt column */
  193.     register int    cpos;    /* character number in search string */
  194.     register int    c;    /* current input character */
  195.     char            pat_save[NPAT];    /* Saved copy of the old pattern str */
  196.     MARK            curpos, curp;    /* Current point on entry */
  197.     int             init_direction;    /* The initial search direction */
  198.  
  199.     /* Initialize starting conditions */
  200.  
  201.     cmd_reexecute = -1;    /* We're not re-executing (yet?) */
  202.     cmd_offset = 0;        /* Start at the beginning of the buff */
  203.     cmd_buff[0] = '\0';    /* Init the command buffer */
  204.     strncpy(pat_save, pat, NPAT);    /* Save the old pattern string */
  205.     curpos = DOT;        /* Save the current pointer */
  206.     init_direction = n;    /* Save the initial search direction */
  207.  
  208.     ignorecase = b_val(curwp->w_bufp, MDIGNCASE);
  209.  
  210.     scanboundry(FALSE, DOT, FORWARD);    /* keep scanner() finite */
  211.  
  212.     /* This is a good place to start a re-execution: */
  213.  
  214. start_over:
  215.  
  216.     /* ask the user for the text of a pattern */
  217.     col = promptpattern("ISearch: ");    /* Prompt, remember the col */
  218.  
  219.     cpos = 0;        /* Start afresh */
  220.     status = TRUE;        /* Assume everything's cool */
  221.  
  222.     /*
  223.      * Get the first character in the pattern.  If we get an initial
  224.      * Control-S or Control-R, re-use the old search string and find the
  225.      * first occurrence
  226.      */
  227.  
  228.     c = kcod2key(get_char());    /* Get the first character */
  229.     if ((c == IS_FORWARD) ||
  230.         (c == IS_REVERSE)) {/* Reuse old search string? */
  231.         for (cpos = 0; pat[cpos] != 0; cpos++)    /* Yup, find the length */
  232.             col = echochar(pat[cpos], col);    /* and re-echo the
  233.                              * string */
  234.         curp = DOT;
  235.         if (c == IS_REVERSE) {    /* forward search? */
  236.             n = -1;    /* No, search in reverse */
  237.             backchar(TRUE, 1);    /* Be defensive about EOB */
  238.         } else {
  239.             n = 1;    /* Yes, search forward */
  240.             forwchar(TRUE, 1);
  241.         }
  242.         --cmd_offset;    /* Back up over the Rubout */
  243.         cmd_buff[--cmd_offset] = '\0';    /* Yes, delete last char */
  244.         status = scanmore(pat, n);    /* Do the search */
  245.         if (status != TRUE)
  246.             DOT = curp;
  247.         c = kcod2key(get_char());    /* Get another character */
  248.     }
  249.     /* Top of the per character loop */
  250.  
  251.     for (;;) {        /* ISearch per character loop */
  252.         /* Check for special characters first: */
  253.         /* Most cases here change the search */
  254.  
  255.         if (c == kcod2key(abortc))    /* Want to quit searching? */
  256.             return (TRUE);    /* Quit searching now */
  257.  
  258.         if (isbackspace(c))
  259.             c = '\b';
  260.  
  261.         if (c == kcod2key(quotec))    /* quote character? */
  262.             c = kcod2key(get_char());    /* Get the next char */
  263.  
  264.         switch (c) {    /* dispatch on the input char */
  265.         case IS_REVERSE:    /* If backward search */
  266.         case IS_FORWARD:    /* If forward search */
  267.             curp = DOT;
  268.             if (c == IS_REVERSE) {    /* forward search? */
  269.                 n = -1;    /* No, search in reverse */
  270.                 backchar(TRUE, 1);    /* Be defensive about
  271.                              * EOB */
  272.             } else {
  273.                 n = 1;    /* Yes, search forward */
  274.                 forwchar(TRUE, 1);
  275.             }
  276.             status = scanmore(pat, n);    /* Do the search */
  277.             if (status != TRUE)
  278.                 DOT = curp;
  279.             c = kcod2key(get_char());    /* Get the next char */
  280.             --cmd_offset;    /* Back up over the Rubout */
  281.             cmd_buff[--cmd_offset] = '\0';    /* Yes, del last char */
  282.             continue;    /* Go continue with the search */
  283.  
  284.         case '\r':    /* Carriage return */
  285.             c = '\n';    /* Make it a new line */
  286.             break;    /* Make sure we use it */
  287.  
  288.         case '\t':    /* Generically allowed */
  289.         case '\n':    /* controlled characters */
  290.             break;    /* Make sure we use it */
  291.  
  292.         case '\b':    /* or if a Rubout: */
  293.             if (cmd_offset <= 1)    /* Anything to delete? */
  294.                 return (TRUE);    /* No, just exit */
  295.             --cmd_offset;    /* Back up over the Rubout */
  296.             cmd_buff[--cmd_offset] = '\0';    /* Yes, del last char */
  297.             DOT = curpos;    /* Reset the pointer */
  298.             n = init_direction;    /* Reset the search direction */
  299.             strncpy(pat, pat_save, NPAT);    /* Restore the old
  300.                              * search str */
  301.             cmd_reexecute = 0;    /* Start the whole mess over */
  302.             goto start_over;    /* Let it take care of itself */
  303.  
  304.             /* Presumably a quasi-normal character comes here */
  305.  
  306.         default:    /* All other chars */
  307.             if (c < ' ') {    /* Is it printable? *//* Nop
  308.                      * e. */
  309.                 tungetc(c);    /* Re-eat the char */
  310.                 return (TRUE);    /* And return the last status */
  311.             }
  312.         }        /* Switch */
  313.  
  314.         /* I guess we got something to search for, so search for it */
  315.  
  316.         pat[cpos++] = c;/* put the char in the buffer */
  317.         if (cpos >= NPAT) {    /* too many chars in string? *//* Yup
  318.                      * .  Complain about it */
  319.             mlforce("[Search string too long]");
  320.             return (TRUE);    /* Return an error */
  321.         }
  322.         pat[cpos] = 0;    /* null terminate the buffer */
  323.         col = echochar(c, col);    /* Echo the character */
  324.         if (!status) {    /* If we lost last time */
  325.             TTbeep();    /* Feep again */
  326.             TTflush();    /* see that the feep feeps */
  327.         } else /* Otherwise, we must have won */
  328.             status = scanmore(pat, n);   /* or find the next
  329.                                   * match */
  330.         c = kcod2key(get_char());    /* Get the next char */
  331.     }            /* for {;;} */
  332. }
  333.  
  334. /*
  335.  * This hack will search for the next occurrence of <pat> in the buffer,
  336.  * either forward or backward.  It is called with the status of the prior
  337.  * search attempt, so that it knows not to bother if it didn't work last
  338.  * time.  If we can't find any more matches, "point" is left where it was
  339.  * before.  If we do find a match, "point" will be at the end of the matched
  340.  * string for forward searches and at the beginning of the matched string for
  341.  * reverse searches.
  342.  */
  343.  
  344. int 
  345. scanmore(patrn, dir)        /* search forward or back for a pattern */
  346.     char           *patrn;    /* string to scan for */
  347.     int             dir;    /* direction to search */
  348. {
  349.     int             sts;    /* search status */
  350.  
  351.     if (gregexp)
  352.         free((char *)gregexp);
  353.     gregexp = regcomp(patrn, b_val(curbp, MDMAGIC));
  354.     if (!gregexp)
  355.         return FALSE;
  356.  
  357.     ignorecase = b_val(curwp->w_bufp, MDIGNCASE);
  358.  
  359.     sts = scanner(gregexp, (dir < 0) ? REVERSE : FORWARD, FALSE);
  360.  
  361.     if (!sts) {
  362.         TTbeep();    /* Feep if search fails */
  363.         TTflush();    /* see that the feep feeps */
  364.     }
  365.     return (sts);        /* else, don't even try */
  366. }
  367.  
  368. /*
  369.  * The following is a worker subroutine used by the reverse search.  It
  370.  * compares the pattern string with the characters at "." for equality. If
  371.  * any characters mismatch, it will return FALSE.
  372.  * 
  373.  * This isn't used for forward searches, because forward searches leave "." at
  374.  * the end of the search string (instead of in front), so all that needs to
  375.  * be done is match the last char input.
  376.  */
  377.  
  378. int 
  379. match_pat(patrn)        /* See if the pattern string matches string
  380.                  * at "." */
  381.     char           *patrn;    /* String to match to buffer */
  382. {
  383.     register int    i;    /* Generic loop index/offset */
  384.     register int    buffchar;    /* character at current position */
  385.     MARK            curpos;
  386.  
  387.     /* setup the local scan pointer to current "." */
  388.     curpos = DOT;        /* Get the current point */
  389.  
  390.     /* top of per character compare loop: */
  391.     for (i = 0; i < strlen(patrn); i++) {    /* Loop for all characters in
  392.                          * patrn */
  393.         if (is_at_end_of_line(curpos)) {    /* If at end of line */
  394.             curpos.l = lforw(curpos.l);    /* Skip to the next line */
  395.             curpos.o = 0;    /* Start at the beginning of the line */
  396.             if (is_header_line(curpos, curbp))
  397.                 return (FALSE);    /* Abort if at end of buffer */
  398.             buffchar = '\n';    /* And say the next char is
  399.                          * NL */
  400.         } else {
  401.             buffchar = char_at(curpos);    /* Get the next char */
  402.             curpos.o++;
  403.         }
  404.         if (!eq(buffchar, patrn[i]))    /* Is it what we're looking
  405.                          * for? */
  406.             return (FALSE);    /* Nope, just punt it then */
  407.     }
  408.     return (TRUE);        /* Everything matched? Let's celebrate */
  409. }
  410.  
  411. /* Routine to prompt for I-Search string. */
  412.  
  413. int 
  414. promptpattern(prompt)
  415.     char           *prompt;
  416. {
  417.     char            tpat[NPAT + 20];
  418.  
  419.     strcpy(tpat, prompt);    /* copy prompt to output string */
  420.     strcat(tpat, " [");    /* build new prompt string */
  421.     (void)expandp(pat, &tpat[strlen(tpat)], NPAT / 2);    /* add old pattern */
  422.     strcat(tpat, "]: ");
  423.  
  424.     /* check to see if we are executing a command line */
  425.     if (!clexec) {
  426.         mlforce(tpat);
  427.     }
  428.     return (strlen(tpat));
  429. }
  430.  
  431. /*
  432.  * expandp -- Expand control key sequences for output.
  433.  */
  434. int
  435. expandp(srcstr, deststr, maxlength)
  436.     char           *srcstr;    /* string to expand */
  437.     char           *deststr;/* destination of expanded string */
  438.     int             maxlength;    /* maximum chars in destination */
  439. {
  440.     unsigned char   c;    /* current char to translate */
  441.  
  442.     /*
  443.      * Scan through the string.
  444.  */
  445.     while ((c = *srcstr++) != 0) {
  446.         if (c == '\n') {/* it's a newline */
  447.             *deststr++ = '<';
  448.             *deststr++ = 'N';
  449.             *deststr++ = 'L';
  450.             *deststr++ = '>';
  451.             maxlength -= 4;
  452.         } else if (!isprint(c)) {    /* control character */
  453.             *deststr++ = '^';
  454.             *deststr++ = toalpha(c);
  455.             maxlength -= 2;
  456.         } else if (c == '%') {
  457.             *deststr++ = '%';
  458.             *deststr++ = '%';
  459.             maxlength -= 2;
  460.         } else {    /* any other character */
  461.             *deststr++ = c;
  462.             maxlength--;
  463.         }
  464.  
  465.         /* check for maxlength */
  466.         if (maxlength < 4) {
  467.             *deststr++ = '$';
  468.             *deststr = '\0';
  469.             return (FALSE);
  470.         }
  471.     }
  472.     *deststr = '\0';
  473.     return (TRUE);
  474. }
  475.  
  476. /* routine to echo i-search characters */
  477.  
  478. int 
  479. echochar(c, col)
  480.     int             c;    /* character to be echoed */
  481.     int             col;    /* column to be echoed in */
  482. {
  483.     movecursor(term.t_nrow, col);    /* Position the cursor */
  484.     if (!isprint(c)) {    /* control char */
  485.         TTputc('^');    /* Yes, output prefix */
  486.         TTputc(toalpha(c));    /* Make it "^X" */
  487.         col++;        /* Count this char */
  488.     } else {
  489.         TTputc(c);    /* Otherwise, output raw char */
  490.     }
  491.     TTflush();        /* Flush the output */
  492.     return (++col);        /* return the new column no */
  493. }
  494.  
  495. /*
  496.  * Routine to get the next character from the input stream.  If we're reading
  497.  * from the real terminal, force a screen update before we get the char.
  498.  * Otherwise, we must be re-executing the command string, so just return the
  499.  * next character.
  500.  */
  501.  
  502. int 
  503. get_char()
  504. {
  505.     int             c;    /* A place to get a character */
  506.  
  507.     /* See if we're re-executing: */
  508.  
  509.     if (cmd_reexecute >= 0)    /* Is there an offset? */
  510.         if ((c = cmd_buff[cmd_reexecute++]) != 0)
  511.             return (c);    /* Yes, return any character */
  512.  
  513.     /* We're not re-executing (or aren't any more).  Try for a real char */
  514.  
  515.     cmd_reexecute = -1;    /* Say we're in real mode again */
  516.     update(FALSE);        /* Pretty up the screen */
  517.     if (cmd_offset >= CMDBUFLEN - 1) {    /* If we're getting too big
  518.                          * ... */
  519.         mlforce("[Command too long]");    /* Complain loudly and
  520.                          * bitterly */
  521.         return (abortc);/* And force a quit */
  522.     }
  523.     c = kbd_key();        /* Get the next character */
  524.     cmd_buff[cmd_offset++] = c;    /* Save the char for next time */
  525.     cmd_buff[cmd_offset] = '\0';    /* And terminate the buffer */
  526.     return (c);        /* Return the character */
  527. }
  528.  
  529. #ifdef USE_REEAT        /* tungetc replaces this */
  530. /*
  531.  * Hacky routine to re-eat a character.  This will save the character to be
  532.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  533.  */
  534.  
  535. /* Come here on the next term.t_getchar call: */
  536.  
  537. int 
  538. uneat()
  539. {
  540.     int             c;
  541.  
  542.     term.t_getchar = saved_get_char;    /* restore the routine
  543.                          * address */
  544.     c = eaten_char;        /* Get the re-eaten char */
  545.     eaten_char = -1;    /* Clear the old char */
  546.     return (c);        /* and return the last char */
  547. }
  548.  
  549. int 
  550. reeat(c)
  551.     int             c;
  552. {
  553.     if (eaten_char != -1)    /* If we've already been here */
  554.         return /* (NULL) */ ;    /* Don't do it again */
  555.     eaten_char = c;        /* Else, save the char for later */
  556.     saved_get_char = term.t_getchar;    /* Save the char get routine */
  557.     term.t_getchar = uneat;    /* Replace it with ours */
  558. }
  559. #endif
  560. #else
  561. isearch()
  562. {
  563. }
  564. #endif
  565.