home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / isearch.c < prev    next >
C/C++ Source or Header  |  1997-10-31  |  12KB  |  404 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.  * original author: D. R. Banks 9-May-86
  9.  *
  10.  * $Header: /usr/build/vile/vile/RCS/isearch.c,v 1.49 1997/11/01 00:18:26 tom Exp $
  11.  *
  12.  */
  13.  
  14. #include    "estruct.h"
  15. #include        "edef.h"
  16.  
  17. #if    OPT_ISRCH
  18.  
  19. static    char *    expandp(char *deststr, char *srcstr, int maxlength);
  20. static    int    get_char (void);
  21. static    int    isearch(int f, int n);
  22. static    int    promptpattern(const char *prompt);
  23. static    int    scanmore(char *patrn, int dir);
  24. static    void    echochar(int c);
  25.  
  26. /* A couple "own" variables for the command string */
  27.  
  28. static    int    cmd_buff[CMDBUFLEN];    /* Save the command args here */
  29. static    int    cmd_offset;        /* Current offset into command buff */
  30. static    int    cmd_reexecute = -1;    /* > 0 if re-executing command */
  31.  
  32.  
  33. /*
  34.  * Subroutine to do incremental reverse search.  It actually uses the same
  35.  * code as the normal incremental search, as both can go both ways.
  36.  */
  37.  
  38. int
  39. risearch(int f, int n)
  40. {
  41.     MARK            curpos;    /* Current point on entry */
  42.  
  43.     /* remember the initial . on entry: */
  44.  
  45.     curpos = DOT;        /* Save the current point */
  46.  
  47.     /* Save direction */
  48.     last_srch_direc =  REVERSE;
  49.  
  50.     /* Make sure the search doesn't match where we already are: */
  51.  
  52.     backchar(TRUE, 1);    /* Back up a character */
  53.  
  54.  
  55.     if (!(isearch(f, -n))) {/* Call ISearch backwards */
  56.                 /* If error in search: */
  57.         DOT = curpos;    /* Reset the pointer */
  58.         curwp->w_flag |= WFMOVE;    /* Say we've moved */
  59.         (void)update(FALSE);        /* And force an update */
  60.         mlwarn("[I-Search failed]");    /* Say we died */
  61.         return FALSE;
  62.     } else
  63.         mlerase();    /* If happy, just erase the cmd line */
  64.  
  65.     return TRUE;
  66. }
  67.  
  68. /* Again, but for the forward direction */
  69.  
  70. int
  71. fisearch(int f, int n)
  72. {
  73.     MARK            curpos;    /* current line on entryl */
  74.  
  75.     /* remember the initial . on entry: */
  76.  
  77.     curpos = DOT;        /* save current point */
  78.  
  79.     /* Save direction */
  80.     last_srch_direc = FORWARD;
  81.  
  82.     /* do the search */
  83.  
  84.  
  85.     if (!(isearch(f, n))) {    /* Call ISearch forwards */
  86.                 /* If error in search: */
  87.         DOT = curpos;    /* reset */
  88.         curwp->w_flag |= WFMOVE;    /* Say we've moved */
  89.         (void)update(FALSE);        /* And force an update */
  90.         mlwarn("[I-Search failed]");    /* Say we died */
  91.         return FALSE;
  92.     } else
  93.         mlerase();    /* If happy, just erase the cmd line */
  94.  
  95.     return TRUE;
  96. }
  97.  
  98. /*
  99.  * Subroutine to do an incremental search.  In general, this works similarly
  100.  * to the older micro-emacs search function, except that the search happens
  101.  * as each character is typed, with the screen and cursor updated with each
  102.  * new search character.
  103.  *
  104.  * While searching forward, each successive character will leave the cursor at
  105.  * the end of the entire matched string.  Typing a Control-S or Control-X
  106.  * will cause the next occurrence of the string to be searched for (where the
  107.  * next occurrence does NOT overlap the current occurrence).  A Control-R
  108.  * will change to a backwards search, META will terminate the search and
  109.  * Control-G will abort the search.  Rubout will back up to the previous
  110.  * match of the string, or if the starting point is reached first, it will
  111.  * delete the last character from the search string.
  112.  *
  113.  * While searching backward, each successive character will leave the cursor at
  114.  * the beginning of the matched string.  Typing a Control-R will search
  115.  * backward for the next occurrence of the string.  Control-S or Control-X
  116.  * will revert the search to the forward direction.  In general, the reverse
  117.  * incremental search is just like the forward incremental search inverted.
  118.  *
  119.  * In all cases, if the search fails, the user will be feeped, and the search
  120.  * will stall until the pattern string is edited back into something that
  121.  * exists (or until the search is aborted).
  122.  */
  123.  
  124. /* ARGSUSED */
  125. static int
  126. isearch(int f GCC_UNUSED, int n)
  127. {
  128.     int             status;    /* Search status */
  129.     register int    cpos;    /* character number in search string */
  130.     register int    c;    /* current input character */
  131.     char            pat_save[NPAT];    /* Saved copy of the old pattern str */
  132.     MARK            curpos, curp;    /* Current point on entry */
  133.     int             init_direction;    /* The initial search direction */
  134.  
  135.     /* Initialize starting conditions */
  136.  
  137.     cmd_reexecute = -1;    /* We're not re-executing (yet?) */
  138.     cmd_offset = 0;        /* Start at the beginning of the buff */
  139.     cmd_buff[0] = EOS;    /* Init the command buffer */
  140.     (void)strncpy0(pat_save, pat, NPAT); /* Save the old pattern string */
  141.     curpos = DOT;        /* Save the current pointer */
  142.     init_direction = n;    /* Save the initial search direction */
  143.  
  144.     ignorecase = window_b_val(curwp, MDIGNCASE);
  145.  
  146.     scanboundry(FALSE, DOT, FORWARD);    /* keep scanner() finite */
  147.  
  148.     /* This is a good place to start a re-execution: */
  149.  
  150. start_over:
  151.  
  152.     /* ask the user for the text of a pattern */
  153.     promptpattern("ISearch: ");
  154.  
  155.     cpos = 0;        /* Start afresh */
  156.     status = TRUE;        /* Assume everything's cool */
  157.  
  158.     /*
  159.      * Get the first character in the pattern.  If we get an initial
  160.      * Control-S or Control-R, re-use the old search string and find the
  161.      * first occurrence
  162.      */
  163.  
  164.     c = kcod2key(get_char());    /* Get the first character */
  165.     if ((c == IS_FORWARD) ||
  166.         (c == IS_REVERSE)) {/* Reuse old search string? */
  167.         for (cpos = 0; pat[cpos] != 0; cpos++)    /* Yup, find the length */
  168.             echochar(pat[cpos]); /* and re-echo the string */
  169.         curp = DOT;
  170.         if (c == IS_REVERSE) {    /* forward search? */
  171.             n = -1;    /* No, search in reverse */
  172.             last_srch_direc = REVERSE;
  173.             backchar(TRUE, 1);    /* Be defensive about EOB */
  174.         } else {
  175.             n = 1;    /* Yes, search forward */
  176.             last_srch_direc = FORWARD;
  177.             forwchar(TRUE, 1);
  178.         }
  179.         --cmd_offset;    /* Back up over the Rubout */
  180.         cmd_buff[--cmd_offset] = EOS;    /* Yes, delete last char */
  181.         status = scanmore(pat, n);    /* Do the search */
  182.         if (status != TRUE)
  183.             DOT = curp;
  184.         c = kcod2key(get_char());    /* Get another character */
  185.     }
  186.     /* Top of the per character loop */
  187.  
  188.     for_ever {        /* ISearch per character loop */
  189.         /* Check for special characters first: */
  190.         /* Most cases here change the search */
  191.  
  192.         if (ABORTED(c) || c == '\r')    /* Want to quit searching? */
  193.             return (TRUE);    /* Quit searching now */
  194.  
  195.         if (isbackspace(c))
  196.             c = '\b';
  197.  
  198.         if (c == quotec)    /* quote character? */
  199.             c = kcod2key(get_char());    /* Get the next char */
  200.  
  201.         switch (c) {    /* dispatch on the input char */
  202.         case IS_REVERSE:    /* If backward search */
  203.         case IS_FORWARD:    /* If forward search */
  204.             curp = DOT;
  205.             if (c == IS_REVERSE) {    /* forward search? */
  206.                 last_srch_direc = REVERSE;
  207.                 n = -1;    /* No, search in reverse */
  208.                 backchar(TRUE, 1);    /* Be defensive about
  209.                              * EOB */
  210.             } else {
  211.                 n = 1;    /* Yes, search forward */
  212.                 last_srch_direc = FORWARD;
  213.                 forwchar(TRUE, 1);
  214.             }
  215.             status = scanmore(pat, n);    /* Do the search */
  216.             if (status != TRUE)
  217.                 DOT = curp;
  218.             c = kcod2key(get_char());    /* Get the next char */
  219.             --cmd_offset;    /* Back up over the Rubout */
  220.             cmd_buff[--cmd_offset] = EOS;    /* Yes, del last char */
  221.             continue;    /* Go continue with the search */
  222.  
  223.         case '\t':    /* Generically allowed */
  224.         case '\n':    /* controlled characters */
  225.             break;    /* Make sure we use it */
  226.  
  227.         case '\b':    /* or if a Rubout: */
  228.             if (cmd_offset <= 1)    /* Anything to delete? */
  229.                 return (TRUE);    /* No, just exit */
  230.             --cmd_offset;    /* Back up over the Rubout */
  231.             cmd_buff[--cmd_offset] = EOS;    /* Yes, del last char */
  232.             DOT = curpos;    /* Reset the pointer */
  233.             n = init_direction;    /* Reset the search direction */
  234.             (void)strncpy0(pat, pat_save, NPAT);
  235.                         /* Restore the old search str */
  236.             cmd_reexecute = 0;    /* Start the whole mess over */
  237.             goto start_over;    /* Let it take care of itself */
  238.  
  239.             /* Presumably a quasi-normal character comes here */
  240.  
  241.         default:    /* All other chars */
  242.             if (c < ' ') {    /* Is it printable? *//* Nop
  243.                      * e. */
  244.                 unkeystroke(c);    /* Re-eat the char */
  245.                 return (TRUE);    /* And return the last status */
  246.             }
  247.         }        /* Switch */
  248.  
  249.         /* I guess we got something to search for, so search for it */
  250.  
  251.         pat[cpos++] = (char)c;    /* put the char in the buffer */
  252.         if (cpos >= NPAT) {    /* too many chars in string? *//* Yup
  253.                      * .  Complain about it */
  254.             mlforce("[Search string too long]");
  255.             return (TRUE);    /* Return an error */
  256.         }
  257.         pat[cpos] = 0;    /* null terminate the buffer */
  258.         echochar(c);    /* Echo the character */
  259.         if (!status) {    /* If we lost last time */
  260.             kbd_alarm();    /* Feep again */
  261.         } else /* Otherwise, we must have won */
  262.             status = scanmore(pat, n);   /* or find the next
  263.                                   * match */
  264.         c = kcod2key(get_char());    /* Get the next char */
  265.     }    /* for_ever */
  266. }
  267.  
  268. /*
  269.  * This hack will search for the next occurrence of <pat> in the buffer,
  270.  * either forward or backward.  It is called with the status of the prior
  271.  * search attempt, so that it knows not to bother if it didn't work last
  272.  * time.  If we can't find any more matches, "point" is left where it was
  273.  * before.  If we do find a match, "point" will be at the end of the matched
  274.  * string for forward searches and at the beginning of the matched string for
  275.  * reverse searches.
  276.  */
  277.  
  278. static int
  279. scanmore(            /* search forward or back for a pattern */
  280.     char           *patrn,    /* string to scan for */
  281.     int             dir)    /* direction to search */
  282. {
  283.     int             sts;    /* search status */
  284.  
  285.     FreeIfNeeded(gregexp);
  286.     gregexp = regcomp(patrn, b_val(curbp, MDMAGIC));
  287.     if (!gregexp)
  288.         return FALSE;
  289.  
  290.     ignorecase = window_b_val(curwp, MDIGNCASE);
  291.  
  292.     sts = scanner(gregexp, (dir < 0) ? REVERSE : FORWARD, FALSE, (int *)0);
  293.  
  294.     if (!sts)
  295.         kbd_alarm();    /* Feep if search fails */
  296.     return (sts);        /* else, don't even try */
  297. }
  298.  
  299.  
  300. /* Routine to prompt for I-Search string. */
  301.  
  302. static int
  303. promptpattern(const char *prompt)
  304. {
  305.     static    const char fmt[] = "%s [%s]: ";
  306.     char            tpat[NPAT],
  307.             temp[NPAT];
  308.  
  309.     (void)lsprintf(tpat, fmt, prompt,
  310.         expandp(temp, pat, (int)(NPAT-sizeof(fmt)-strlen(prompt))));
  311.  
  312.     /* check to see if we are executing a command line */
  313.     if (!clexec) {
  314.         mlforce("%s", tpat);
  315.     }
  316.     return (strlen(tpat));
  317. }
  318.  
  319. /*
  320.  * expandp -- Expand control key sequences for output.
  321.  */
  322. static char *
  323. expandp(
  324.     char           *deststr,    /* destination of expanded string */
  325.     char           *srcstr,        /* string to expand */
  326.     int             maxlength)    /* maximum chars in destination */
  327. {
  328.     char    *base = deststr;
  329.     UCHAR   c;    /* current char to translate */
  330.  
  331.     /*
  332.      * Scan through the string.
  333.      */
  334.     while ((c = *srcstr++) != 0) {
  335.         if (c == '\n') {/* it's a newline */
  336.             *deststr++ = '<';
  337.             *deststr++ = 'N';
  338.             *deststr++ = 'L';
  339.             *deststr++ = '>';
  340.             maxlength -= 4;
  341.         } else if (!isPrint(c)) {    /* control character */
  342.             *deststr++ = '^';
  343.             *deststr++ = toalpha(c);
  344.             maxlength -= 2;
  345.         } else {    /* any other character */
  346.             *deststr++ = c;
  347.             maxlength--;
  348.         }
  349.  
  350.         /* check for maxlength */
  351.         if (maxlength < 4) {
  352.             *deststr++ = '$';
  353.             break;
  354.         }
  355.     }
  356.     *deststr = EOS;
  357.     return base;
  358. }
  359.  
  360. /* routine to echo i-search characters */
  361.  
  362. static void
  363. echochar(int c) /* character to be echoed */
  364. {
  365.     kbd_putc(c);
  366.     kbd_flush();
  367. }
  368.  
  369. /*
  370.  * Routine to get the next character from the input stream.  If we're reading
  371.  * from the real terminal, force a screen update before we get the char.
  372.  * Otherwise, we must be re-executing the command string, so just return the
  373.  * next character.
  374.  */
  375.  
  376. static int
  377. get_char(void)
  378. {
  379.     int             c;    /* A place to get a character */
  380.  
  381.     /* See if we're re-executing: */
  382.  
  383.     if (cmd_reexecute >= 0)    /* Is there an offset? */
  384.         if ((c = cmd_buff[cmd_reexecute++]) != 0)
  385.             return (c);    /* Yes, return any character */
  386.  
  387.     /* We're not re-executing (or aren't any more).  Try for a real char */
  388.  
  389.     cmd_reexecute = -1;    /* Say we're in real mode again */
  390.     (void)update(FALSE);    /* Pretty up the screen */
  391.     if (cmd_offset >= CMDBUFLEN - 1) {    /* If we're getting too big
  392.                          * ... */
  393.         mlforce("[Command too long]");    /* Complain loudly and
  394.                          * bitterly */
  395.         return (abortc);/* And force a quit */
  396.     }
  397.     c = keystroke8();        /* Get the next character */
  398.     cmd_buff[cmd_offset++] = c;    /* Save the char for next time */
  399.     cmd_buff[cmd_offset] = EOS;    /* And terminate the buffer */
  400.     return (c);        /* Return the character */
  401. }
  402.  
  403. #endif
  404.