home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD1.bin / useful / util / edit / vim / src / search.c < prev    next >
C/C++ Source or Header  |  1994-08-09  |  31KB  |  1,376 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8. /*
  9.  * search.c: code for normal mode searching commands
  10.  */
  11.  
  12. #include "vim.h"
  13. #include "globals.h"
  14. #include "proto.h"
  15. #include "param.h"
  16. #include "ops.h"        /* for mincl */
  17.  
  18. /* modified Henry Spencer's regular expression routines */
  19. #include "regexp.h"
  20.  
  21. static int inmacro __ARGS((char_u *, char_u *));
  22. static int cls __ARGS((void));
  23.  
  24. static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
  25. static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
  26.  
  27. /*
  28.  * This file contains various searching-related routines. These fall into
  29.  * three groups:
  30.  * 1. string searches (for /, ?, n, and N)
  31.  * 2. character searches within a single line (for f, F, t, T, etc)
  32.  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
  33.  */
  34.  
  35. /*
  36.  * String searches
  37.  *
  38.  * The string search functions are divided into two levels:
  39.  * lowest:    searchit(); called by dosearch() and edit().
  40.  * Highest: dosearch(); changes curwin->w_cursor, called by normal().
  41.  *
  42.  * The last search pattern is remembered for repeating the same search.
  43.  * This pattern is shared between the :g, :s, ? and / commands.
  44.  * This is in myregcomp().
  45.  *
  46.  * The actual string matching is done using a heavily modified version of
  47.  * Henry Spencer's regular expression library.
  48.  */
  49.  
  50. /*
  51.  * Two search patterns are remembered: One for the :substitute command and
  52.  * one for other searches. last_pattern points to the one that was
  53.  * used the last time.
  54.  */
  55. static char_u     *search_pattern = NULL;
  56. static char_u     *subst_pattern = NULL;
  57. static char_u     *last_pattern = NULL;
  58.  
  59. static int        want_start;                /* looking for start of line? */
  60.  
  61. /*
  62.  * translate search pattern for regcomp()
  63.  *
  64.  * sub_cmd == 0: save pat in search_pattern (normal search command)
  65.  * sub_cmd == 1: save pat in subst_pattern (:substitute command)
  66.  * sub_cmd == 2: save pat in both patterns (:global command)
  67.  * which_pat == 0: use previous search pattern if "pat" is NULL
  68.  * which_pat == 1: use previous sustitute pattern if "pat" is NULL
  69.  * which_pat == 2: use last used pattern if "pat" is NULL
  70.  * 
  71.  */
  72.     regexp *
  73. myregcomp(pat, sub_cmd, which_pat)
  74.     char_u    *pat;
  75.     int        sub_cmd;
  76.     int        which_pat;
  77. {
  78.     regexp *retval;
  79.  
  80.     if (pat == NULL || *pat == NUL)     /* use previous search pattern */
  81.     {
  82.         if (which_pat == 0)
  83.         {
  84.             if (search_pattern == NULL)
  85.             {
  86.                 emsg(e_noprevre);
  87.                 return (regexp *) NULL;
  88.             }
  89.             pat = search_pattern;
  90.         }
  91.         else if (which_pat == 1)
  92.         {
  93.             if (subst_pattern == NULL)
  94.             {
  95.                 emsg(e_nopresub);
  96.                 return (regexp *) NULL;
  97.             }
  98.             pat = subst_pattern;
  99.         }
  100.         else    /* which_pat == 2 */
  101.         {
  102.             if (last_pattern == NULL)
  103.             {
  104.                 emsg(e_noprevre);
  105.                 return (regexp *) NULL;
  106.             }
  107.             pat = last_pattern;
  108.         }
  109.     }
  110.  
  111.     /*
  112.      * save the currently used pattern in the appropriate place,
  113.      * unless the pattern should not be remembered
  114.      */
  115.     if (!keep_old_search_pattern)
  116.     {
  117.         if (sub_cmd == 0 || sub_cmd == 2)    /* search or global command */
  118.         {
  119.             if (search_pattern != pat)
  120.             {
  121.                 free(search_pattern);
  122.                 search_pattern = strsave(pat);
  123.                 last_pattern = search_pattern;
  124.                 reg_magic = p_magic;        /* Magic sticks with the r.e. */
  125.             }
  126.         }
  127.         if (sub_cmd == 1 || sub_cmd == 2)    /* substitute or global command */
  128.         {
  129.             if (subst_pattern != pat)
  130.             {
  131.                 free(subst_pattern);
  132.                 subst_pattern = strsave(pat);
  133.                 last_pattern = subst_pattern;
  134.                 reg_magic = p_magic;        /* Magic sticks with the r.e. */
  135.             }
  136.         }
  137.     }
  138.  
  139.     want_start = (*pat == '^');        /* looking for start of line? */
  140.     reg_ic = p_ic;                    /* tell the regexec routine how to search */
  141.     retval = regcomp(pat);
  142.     return retval;
  143. }
  144.  
  145. /*
  146.  * lowest level search function.
  147.  * Search for 'count'th occurrence of 'str' in direction 'dir'.
  148.  * Start at position 'pos' and return the found position in 'pos'.
  149.  * Return OK for success, FAIL for failure.
  150.  */
  151.     int
  152. searchit(pos, dir, str, count, end, message)
  153.     FPOS    *pos;
  154.     int     dir;
  155.     char_u    *str;
  156.     long    count;
  157.     int        end;
  158.     int        message;
  159. {
  160.     int                 found;
  161.     linenr_t            lnum = 0;            /* init to shut up gcc */
  162.     linenr_t            startlnum;
  163.     regexp                *prog;
  164.     register char_u        *s;
  165.     char_u                *ptr;
  166.     register int        i;
  167.     register char_u        *match, *matchend;
  168.     int                 loop;
  169.  
  170.     if ((prog = myregcomp(str, 0, 2)) == NULL)
  171.     {
  172.         if (message)
  173.             emsg(e_invstring);
  174.         return FAIL;
  175.     }
  176. /*
  177.  * find the string
  178.  */
  179.     found = 1;
  180.     while (count-- && found)    /* stop after count matches, or no more matches */
  181.     {
  182.         startlnum = pos->lnum;    /* remember start of search for detecting no match */
  183.         found = 0;                /* default: not found */
  184.  
  185.         i = pos->col + dir;     /* search starts one postition away */
  186.         lnum = pos->lnum;
  187.  
  188.         if (dir == BACKWARD && i < 0)
  189.             --lnum;
  190.  
  191.         for (loop = 0; loop != 2; ++loop)   /* do this twice if 'wrapscan' is set */
  192.         {
  193.             for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count; lnum += dir, i = -1)
  194.             {
  195.                 s = ptr = ml_get(lnum);
  196.                 if (dir == FORWARD && i > 0)    /* first line for forward search */
  197.                 {
  198.                     if (want_start || STRLEN(s) <= (size_t)i)   /* match not possible */
  199.                         continue;
  200.                     s += i;
  201.                 }
  202.  
  203.                 if (regexec(prog, s, dir == BACKWARD || i <= 0))
  204.                 {                            /* match somewhere on line */
  205.                     match = prog->startp[0];
  206.                     matchend = prog->endp[0];
  207.                     if (dir == BACKWARD && !want_start)
  208.                     {
  209.                         /*
  210.                          * Now, if there are multiple matches on this line,
  211.                          * we have to get the last one. Or the last one before
  212.                          * the cursor, if we're on that line.
  213.                          */
  214.                         while (*match != NUL && regexec(prog, match + 1, (int)FALSE))
  215.                         {
  216.                             if ((i >= 0) && ((prog->startp[0] - s) > i))
  217.                                 break;
  218.                             match = prog->startp[0];
  219.                             matchend = prog->endp[0];
  220.                         }
  221.  
  222.                         if ((i >= 0) && ((match - s) > i))
  223.                             continue;
  224.                     }
  225.  
  226.                     pos->lnum = lnum;
  227.                     if (end)
  228.                         pos->col = (int) (matchend - ptr - 1);
  229.                     else
  230.                         pos->col = (int) (match - ptr);
  231.                     found = 1;
  232.                     break;
  233.                 }
  234.                 /* breakcheck is slow, do it only once in 16 lines */
  235.                 if ((lnum & 15) == 0)
  236.                     breakcheck();       /* stop if ctrl-C typed */
  237.                 if (got_int)
  238.                     break;
  239.  
  240.                 if (loop && lnum == startlnum)  /* if second loop stop where started */
  241.                     break;
  242.             }
  243.     /* stop the search if wrapscan isn't set, after an interrupt and after a match */
  244.             if (!p_ws || got_int || found)
  245.                 break;
  246.  
  247.             /*
  248.              * If 'wrapscan' is set we continue at the other end of the file.
  249.              * If 'terse' is not set, we give a message.
  250.              * This message is also remembered in keep_msg for when the screen
  251.              * is redrawn. The keep_msg is cleared whenever another message is
  252.              * written.
  253.              */
  254.             if (dir == BACKWARD)    /* start second loop at the other end */
  255.             {
  256.                 lnum = curbuf->b_ml.ml_line_count;
  257.                 if (!p_terse && message)
  258.                 {
  259.                     msg(top_bot_msg);
  260.                     keep_msg = top_bot_msg;
  261.                 }
  262.             }
  263.             else
  264.             {
  265.                 lnum = 1;
  266.                 if (!p_terse && message)
  267.                 {
  268.                     msg(bot_top_msg);
  269.                     keep_msg = bot_top_msg;
  270.                 }
  271.             }
  272.         }
  273.         if (got_int)
  274.             break;
  275.     }
  276.  
  277.     free(prog);
  278.  
  279.     if (!found)             /* did not find it */
  280.     {
  281.         if (got_int)
  282.             emsg(e_interr);
  283.         else if (message)
  284.         {
  285.             if (p_ws)
  286.                 emsg(e_patnotf);
  287.             else if (lnum == 0)
  288.                 EMSG("search hit TOP without match");
  289.             else
  290.                 EMSG("search hit BOTTOM without match");
  291.         }
  292.         return FAIL;
  293.     }
  294.  
  295.     return OK;
  296. }
  297.  
  298. /*
  299.  * Highest level string search function.
  300.  * Search for the 'count'th occurence of string 'str' in direction 'dirc'
  301.  *                    If 'dirc' is 0: use previous dir.
  302.  * If 'str' is 0 or 'str' is empty: use previous string.
  303.  *              If 'reverse' is TRUE: go in reverse of previous dir.
  304.  *                 If 'echo' is TRUE: echo the search command and handle options
  305.  *              If 'message' is TRUE: may give error message
  306.  *
  307.  * return 0 for failure, 1 for found, 2 for found and line offset added
  308.  */
  309.     int
  310. dosearch(dirc, str, reverse, count, echo, message)
  311.     int                dirc;
  312.     char_u           *str;
  313.     int                reverse;
  314.     long            count;
  315.     int                echo;
  316.     int                message;
  317. {
  318.     FPOS            pos;        /* position of the last match */
  319.     char_u            *searchstr;
  320.     static int        lastsdir = '/';    /* previous search direction */
  321.     static int        lastoffline;/* previous/current search has line of