home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / amiga / vim46src.lha / vim-4.6 / src / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-24  |  70.3 KB  |  3,130 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  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 "option.h"
  16. #include "ops.h"        /* for op_inclusive */
  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. static int skip_chars __ARGS((int, int));
  24. static void back_in_line __ARGS((void));
  25. static void find_first_blank __ARGS((FPOS *));
  26. static void show_pat_in_path __ARGS((char_u *, int,
  27.                                          int, int, FILE *, linenr_t *, long));
  28. static void give_warning __ARGS((char_u *));
  29.  
  30. static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
  31. static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
  32.  
  33. /*
  34.  * This file contains various searching-related routines. These fall into
  35.  * three groups:
  36.  * 1. string searches (for /, ?, n, and N)
  37.  * 2. character searches within a single line (for f, F, t, T, etc)
  38.  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
  39.  */
  40.  
  41. /*
  42.  * String searches
  43.  *
  44.  * The string search functions are divided into two levels:
  45.  * lowest:    searchit(); called by do_search() and edit().
  46.  * Highest: do_search(); changes curwin->w_cursor, called by normal().
  47.  *
  48.  * The last search pattern is remembered for repeating the same search.
  49.  * This pattern is shared between the :g, :s, ? and / commands.
  50.  * This is in myregcomp().
  51.  *
  52.  * The actual string matching is done using a heavily modified version of
  53.  * Henry Spencer's regular expression library.
  54.  */
  55.  
  56. /*
  57.  * Two search patterns are remembered: One for the :substitute command and
  58.  * one for other searches. last_pattern points to the one that was
  59.  * used the last time.
  60.  */
  61. static char_u    *search_pattern = NULL;
  62. static int        search_magic = TRUE;
  63. static int        search_no_scs = FALSE;
  64. static char_u    *subst_pattern = NULL;
  65. static int        subst_magic = TRUE;
  66. static int        subst_no_scs = FALSE;
  67. static char_u    *last_pattern = NULL;
  68. static int        last_magic = TRUE;
  69. static int        last_no_scs = FALSE;
  70. static char_u    *mr_pattern = NULL;        /* pattern used by myregcomp() */
  71.  
  72. /*
  73.  * Type used by find_pattern_in_path() to remember which included files have
  74.  * been searched already.
  75.  */
  76. typedef struct SearchedFile
  77. {
  78.     FILE        *fp;        /* File pointer */
  79.     char_u        *name;        /* Full name of file */
  80.     linenr_t    lnum;        /* Line we were up to in file */
  81.     int            matched;    /* Found a match in this file */
  82. } SearchedFile;
  83.  
  84. /*
  85.  * translate search pattern for vim_regcomp()
  86.  *
  87.  * sub_cmd == RE_SEARCH: save pat in search_pattern (normal search command)
  88.  * sub_cmd == RE_SUBST: save pat in subst_pattern (:substitute command)
  89.  * sub_cmd == RE_BOTH: save pat in both patterns (:global command)
  90.  * which_pat == RE_SEARCH: use previous search pattern if "pat" is NULL
  91.  * which_pat == RE_SUBST: use previous sustitute pattern if "pat" is NULL
  92.  * which_pat == RE_LAST: use last used pattern if "pat" is NULL
  93.  * options & SEARCH_HIS: put search string in history
  94.  * options & SEARCH_KEEP: keep previous search pattern
  95.  * 
  96.  */
  97.     regexp *
  98. myregcomp(pat, sub_cmd, which_pat, options)
  99.     char_u    *pat;
  100.     int        sub_cmd;
  101.     int        which_pat;
  102.     int        options;
  103. {
  104.     rc_did_emsg = FALSE;
  105.     reg_magic = p_magic;
  106.     if (pat == NULL || *pat == NUL)        /* use previous search pattern */
  107.     {
  108.         if (which_pat == RE_SEARCH)
  109.         {
  110.             if (search_pattern == NULL)
  111.             {
  112.                 emsg(e_noprevre);
  113.                 rc_did_emsg = TRUE;
  114.                 return (regexp *) NULL;
  115.             }
  116.             pat = search_pattern;
  117.             reg_magic = search_magic;
  118.             no_smartcase = search_no_scs;
  119.         }
  120.         else if (which_pat == RE_SUBST)
  121.         {
  122.             if (subst_pattern == NULL)
  123.             {
  124.                 emsg(e_nopresub);
  125.                 rc_did_emsg = TRUE;
  126.                 return (regexp *) NULL;
  127.             }
  128.             pat = subst_pattern;
  129.             reg_magic = subst_magic;
  130.             no_smartcase = subst_no_scs;
  131.         }
  132.         else    /* which_pat == RE_LAST */
  133.         {
  134.             if (last_pattern == NULL)
  135.             {
  136.                 emsg(e_noprevre);
  137.                 rc_did_emsg = TRUE;
  138.                 return (regexp *) NULL;
  139.             }
  140.             pat = last_pattern;
  141.             reg_magic = last_magic;
  142.             no_smartcase = last_no_scs;
  143.         }
  144.     }
  145.     else if (options & SEARCH_HIS)
  146.         add_to_history(1, pat);            /* put new pattern in history */
  147.  
  148.     mr_pattern = pat;
  149.  
  150.     /*
  151.      * save the currently used pattern in the appropriate place,
  152.      * unless the pattern should not be remembered
  153.      */
  154.     if (!(options & SEARCH_KEEP))
  155.     {
  156.         /*
  157.          * search or global command
  158.          */
  159.         if (sub_cmd == RE_SEARCH || sub_cmd == RE_BOTH)
  160.         {
  161.             if (search_pattern != pat)
  162.             {
  163.                 vim_free(search_pattern);
  164.                 search_pattern = strsave(pat);
  165.                 last_pattern = search_pattern;
  166.                 search_magic = reg_magic;
  167.                 last_magic = reg_magic;        /* Magic sticks with the r.e. */
  168.                 search_no_scs = no_smartcase;
  169.                 last_no_scs = no_smartcase;
  170.             }
  171.         }
  172.         /*
  173.          * substitute or global command
  174.          */
  175.         if (sub_cmd == RE_SUBST || sub_cmd == RE_BOTH)
  176.         {
  177.             if (subst_pattern != pat)
  178.             {
  179.                 vim_free(subst_pattern);
  180.                 subst_pattern = strsave(pat);
  181.                 last_pattern = subst_pattern;
  182.                 subst_magic = reg_magic;
  183.                 last_magic = reg_magic;        /* Magic sticks with the r.e. */
  184.                 subst_no_scs = no_smartcase;
  185.                 last_no_scs = no_smartcase;
  186.             }
  187.         }
  188.     }
  189.  
  190.     set_reg_ic(pat);            /* tell the vim_regexec routine how to search */
  191.     return vim_regcomp(pat);
  192. }
  193.  
  194. /*
  195.  * Set reg_ic according to p_ic, p_scs and the search pattern.
  196.  */
  197.     void
  198. set_reg_ic(pat)
  199.     char_u    *pat;
  200. {
  201.     char_u *p;
  202.  
  203.     reg_ic = p_ic;
  204.     if (!no_smartcase && p_scs
  205. #ifdef INSERT_EXPAND
  206.                                 && !(ctrl_x_mode && curbuf->b_p_inf)
  207. #endif
  208.                                                                     )
  209.     {
  210.         /* don't ignore case if pattern has uppercase */
  211.         for (p = pat; *p; )
  212.             if (isupper(*p++))
  213.                 reg_ic = FALSE;
  214.     }
  215.     no_smartcase = FALSE;
  216. }
  217.  
  218. /*
  219.  * lowest level search function.
  220.  * Search for 'count'th occurrence of 'str' in direction 'dir'.
  221.  * Start at position 'pos' and return the found position in 'pos'.
  222.  *
  223.  * if (options & SEARCH_MSG) == 0 don't give any messages
  224.  * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
  225.  * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
  226.  * if (options & SEARCH_HIS) put search pattern in history
  227.  * if (options & SEARCH_END) return position at end of match
  228.  * if (options & SEARCH_START) accept match at pos itself
  229.  * if (options & SEARCH_KEEP) keep previous search pattern
  230.  *
  231.  * Return OK for success, FAIL for failure.
  232.  */
  233.     int
  234. searchit(pos, dir, str, count, options, which_pat)
  235.     FPOS    *pos;
  236.     int        dir;
  237.     char_u    *str;
  238.     long    count;
  239.     int        options;
  240.     int        which_pat;
  241. {
  242.     int                    found;
  243.     linenr_t            lnum;            /* no init to shut up Apollo cc */
  244.     regexp                *prog;
  245.     char_u                *ptr;
  246.     register char_u        *match = NULL, *matchend = NULL;    /* init for GCC */
  247.     int                    loop;
  248.     FPOS                start_pos;
  249.     int                    at_first_line;
  250.     int                    extra_col;
  251.     int                    match_ok;
  252.     char_u                *p;
  253.  
  254.     if ((prog = myregcomp(str, RE_SEARCH, which_pat,
  255.                              (options & (SEARCH_HIS + SEARCH_KEEP)))) == NULL)
  256.     {
  257.         if ((options & SEARCH_MSG) && !rc_did_emsg)
  258.             emsg2((char_u *)"Invalid search string: %s", mr_pattern);
  259.         return FAIL;
  260.     }
  261.  
  262.     if (options & SEARCH_START)
  263.         extra_col = 0;
  264.     else
  265.         extra_col = 1;
  266.  
  267.  
  268. /*
  269.  * find the string
  270.  */
  271.     do                            /* loop for count */
  272.     {
  273.         start_pos = *pos;        /* remember start pos for detecting no match */
  274.         found = 0;                /* default: not found */
  275.  
  276.         /*
  277.          * Start searching in current line, unless searching backwards and
  278.          * we're in column 0 or searching forward and we're past the end of
  279.          * the line
  280.          */
  281.         if (dir == BACKWARD && start_pos.col == 0)
  282.         {
  283.             lnum = pos->lnum - 1;
  284.             at_first_line = FALSE;
  285.         }
  286.         else
  287.         {
  288.             lnum = pos->lnum;
  289.             at_first_line = TRUE;
  290.         }
  291.  
  292.         for (loop = 0; loop <= 1; ++loop)    /* loop twice if 'wrapscan' set */
  293.         {
  294.             for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count;
  295.                                            lnum += dir, at_first_line = FALSE)
  296.             {
  297.                 /*
  298.                  * Look for a match somewhere in the line.
  299.                  */
  300.                 ptr = ml_get(lnum);
  301.                 if (vim_regexec(prog, ptr, TRUE))
  302.                 {
  303.                     match = prog->startp[0];
  304.                     matchend = prog->endp[0];
  305.                     
  306.                     /*
  307.                      * Forward search in the first line: match should be after
  308.                      * the start position. If not, continue at the end of the
  309.                      * match (this is vi compatible).
  310.                      */
  311.                     if (dir == FORWARD && at_first_line)
  312.                     {
  313.                         match_ok = TRUE;
  314.                         /*
  315.                          * When *match == NUL the cursor will be put one back
  316.                          * afterwards, compare with that position, otherwise
  317.                          * "/$" will get stuck on end of line.
  318.                          */
  319.                         while ((options & SEARCH_END) ?
  320.                                                  ((int)(matchend - ptr) - 1  <
  321.                                              (int)start_pos.col + extra_col) :
  322.                                   ((int)(match - ptr) - (int)(*match == NUL) <
  323.                                               (int)start_pos.col + extra_col))
  324.                         {
  325.                             /*
  326.                              * If vi-compatible searching, continue at the end
  327.                              * of the match, otherwise continue one position
  328.                              * forward.
  329.                              */
  330.                             if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
  331.                             {
  332.                                 p = matchend;
  333.                                 if (match == p && *p != NUL)
  334.                                     ++p;
  335.                             }
  336.                             else
  337.                             {
  338.                                 p = match;
  339.                                 if (*p != NUL)
  340.                                     ++p;
  341.                             }
  342.                             if (*p != NUL && vim_regexec(prog, p, FALSE))
  343.                             {
  344.                                 match = prog->startp[0];
  345.                                 matchend = prog->endp[0];
  346.                             }
  347.                             else
  348.                             {
  349.                                 match_ok = FALSE;
  350.                                 break;
  351.                             }
  352.                         }
  353.                         if (!match_ok)
  354.                             continue;
  355.                     }
  356.                     if (dir == BACKWARD)
  357.                     {
  358.                         /*
  359.                          * Now, if there are multiple matches on this line,
  360.                          * we have to get the last one. Or the last one before
  361.                          * the cursor, if we're on that line.
  362.                          * When putting the new cursor at the end, compare
  363.                          * relative to the end of the match.
  364.                          */
  365.                         match_ok = FALSE;
  366.                         for (;;)
  367.                         {
  368.                             if (!at_first_line || ((options & SEARCH_END) ?
  369.                                         ((prog->endp[0] - ptr) - 1 + extra_col
  370.                                                       <= (int)start_pos.col) :
  371.                                           ((prog->startp[0] - ptr) + extra_col
  372.                                                       <= (int)start_pos.col)))
  373.                             {
  374.                                 match_ok = TRUE;
  375.                                 match = prog->startp[0];
  376.                                 matchend = prog->endp[0];
  377.                             }
  378.                             else
  379.                                 break;
  380.                             /*
  381.                              * If vi-compatible searching, continue at the end
  382.                              * of the match, otherwise continue one position
  383.                              * forward.
  384.                              */
  385.                             if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
  386.                             {
  387.                                 p = matchend;
  388.                                 if (p == match && *p != NUL)
  389.                                     ++p;
  390.                             }
  391.                             else
  392.                             {
  393.                                 p = match;
  394.                                 if (*p != NUL)
  395.                                     ++p;
  396.                             }
  397.                             if (*p == NUL || !vim_regexec(prog, p, (int)FALSE))
  398.                                 break;
  399.                         }
  400.  
  401.                         /*
  402.                          * If there is only a match after the cursor, skip
  403.                          * this match.
  404.                          */
  405.                         if (!match_ok)
  406.                             continue;
  407.                     }
  408.  
  409.                     pos->lnum = lnum;
  410.                     if (options & SEARCH_END && !(options & SEARCH_NOOF))
  411.                         pos->col = (int) (matchend - ptr - 1);
  412.                     else
  413.                         pos->col = (int) (match - ptr);
  414.                     found = 1;
  415.                     break;
  416.                 }
  417.                 line_breakcheck();        /* stop if ctrl-C typed */
  418.                 if (got_int)
  419.                     break;
  420.  
  421.                 if (loop && lnum == start_pos.lnum)
  422.                     break;            /* if second loop, stop where started */
  423.             }
  424.             at_first_line = FALSE;
  425.  
  426.             /*
  427.              * stop the search if wrapscan isn't set, after an interrupt and
  428.              * after a match
  429.              */
  430.             if (!p_ws || got_int || found)
  431.                 break;
  432.  
  433.             /*
  434.              * If 'wrapscan' is set we continue at the other end of the file.
  435.              * If 'shortmess' does not contain 's', we give a message.
  436.              * This message is also remembered in keep_msg for when the screen
  437.              * is redrawn. The keep_msg is cleared whenever another message is
  438.              * written.
  439.              */
  440.             if (dir == BACKWARD)    /* start second loop at the other end */
  441.             {
  442.                 lnum = curbuf->b_ml.ml_line_count;
  443.                 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
  444.                     give_warning(top_bot_msg);
  445.             }
  446.             else
  447.             {
  448.                 lnum = 1;
  449.                 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
  450.                     give_warning(bot_top_msg);
  451.             }
  452.         }
  453.         if (got_int)
  454.             break;
  455.     }
  456.     while (--count > 0 && found);    /* stop after count matches or no match */
  457.  
  458.     vim_free(prog);
  459.  
  460.     if (!found)                /* did not find it */
  461.     {
  462.         if (got_int)
  463.             emsg(e_interr);
  464.         else if ((options & SEARCH_MSG) == SEARCH_MSG)
  465.         {
  466.             if (p_ws)
  467.                 EMSG2("Pattern not found: %s", mr_pattern);
  468.             else if (lnum == 0)
  469.                 EMSG2("search hit TOP without match for: %s", mr_pattern);
  470.             else
  471.                 EMSG2("search hit BOTTOM without match for: %s", mr_pattern);
  472.         }
  473.         return FAIL;
  474.     }
  475.     search_match_len = matchend - match;
  476.  
  477.     return OK;
  478. }
  479.  
  480. /*
  481.  * Highest level string search function.
  482.  * Search for the 'count'th occurence of string 'str' in direction 'dirc'
  483.  *                  If 'dirc' is 0: use previous dir.
  484.  *    If 'str' is NULL or empty : use previous string.
  485.  *      If 'options & SEARCH_REV' : go in reverse of previous dir.
  486.  *      If 'options & SEARCH_ECHO': echo the search command and handle options
  487.  *      If 'options & SEARCH_MSG' : may give error message
  488.  *      If 'options & SEARCH_OPT' : interpret optional flags
  489.  *      If 'options & SEARCH_HIS' : put search pattern in history
  490.  *      If 'options & SEARCH_NOOF': don't add offset to position
  491.  *      If 'options & SEARCH_MARK': set previous context mark
  492.  *      If 'options & SEARCH_KEEP': keep previous search pattern
  493.  *    If 'options & SEARCH_START': accept match at curpos itself
  494.  *
  495.  * Careful: If lastoffline == TRUE and lastoff == 0 this makes the
  496.  * movement linewise without moving the match position.
  497.  *
  498.  * return 0 for failure, 1 for found, 2 for found and line offset added
  499.  */
  500.     int
  501. do_search(dirc, str, count, options)
  502.     int                dirc;
  503.     char_u           *str;
  504.     long            count;
  505.     int                options;
  506. {
  507.     FPOS            pos;        /* position of the last match */
  508.     char_u            *searchstr;
  509.     static int        lastsdir = '/';    /* previous search direction */
  510.     static int        lastoffline;/* previous/current search has line offset */
  511.     static int        lastend;    /* previous/current search set cursor at end */
  512.     static long        lastoff;    /* previous/current line or char offset */
  513.     int                old_lastsdir;
  514.     int                old_lastoffline;
  515.     int                old_lastend;
  516.     long            old_lastoff;
  517.     int                retval;        /* Return value */
  518.     register char_u    *p;
  519.     register long    c;
  520.     char_u            *dircp;
  521.     int                i = 0;        /* init for GCC */
  522.  
  523.     /*
  524.      * A line offset is not remembered, this is vi compatible.
  525.      */
  526.     if (lastoffline && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
  527.     {
  528.         lastoffline = FALSE;
  529.         lastoff = 0;
  530.     }
  531.  
  532.     /*
  533.      * Save the values for when (options & SEARCH_KEEP) is used.
  534.      * (there is no "if ()" around this because gcc wants them initialized)
  535.      */
  536.     old_lastsdir = lastsdir;
  537.     old_lastoffline = lastoffline;
  538.     old_lastend = lastend;
  539.     old_lastoff = lastoff;
  540.  
  541.     pos = curwin->w_cursor;        /* start searching at the cursor position */
  542.  
  543.     /*
  544.      * Find out the direction of the search.
  545.      */
  546.     if (dirc == 0)
  547.         dirc = lastsdir;
  548.     else
  549.         lastsdir = dirc;
  550.     if (options & SEARCH_REV)
  551.     {
  552. #ifdef WIN32
  553.         /* There is a bug in the Visual C++ 2.2 compiler which means that
  554.          * dirc always ends up being '/' */
  555.         dirc = (dirc == '/')  ?  '?'  :  '/';
  556. #else
  557.         if (dirc == '/')
  558.             dirc = '?';
  559.         else
  560.             dirc = '/';
  561. #endif
  562.     }
  563.  
  564.     /*
  565.      * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
  566.      */
  567.     for (;;)
  568.     {
  569.         searchstr = str;
  570.         dircp = NULL;
  571.                                             /* use previous pattern */
  572.         if (str == NULL || *str == NUL || *str == dirc)
  573.         {
  574.             if (search_pattern == NULL)        /* no previous pattern */
  575.             {
  576.                 emsg(e_noprevre);
  577.                 retval = 0;
  578.                 goto end_do_search;
  579.             }
  580.             searchstr = (char_u *)"";  /* make myregcomp() use search_pattern */
  581.         }
  582.  
  583.         if (str != NULL && *str != NUL)    /* look for (new) offset */
  584.         {
  585.             /*
  586.              * Find end of regular expression.
  587.              * If there is a matching '/' or '?', toss it.
  588.              */
  589.             p = skip_regexp(str, dirc);
  590.             if (*p == dirc)
  591.             {
  592.                 dircp = p;        /* remember where we put the NUL */
  593.                 *p++ = NUL;
  594.             }
  595.             lastoffline = FALSE;
  596.             lastend = FALSE;
  597.             lastoff = 0;
  598.             /*
  599.              * Check for a line offset or a character offset.
  600.              * For get_address (echo off) we don't check for a character
  601.              * offset, because it is meaningless and the 's' could be a
  602.              * substitute command.
  603.              */
  604.             if (*p == '+' || *p == '-' || isdigit(*p))
  605.                 lastoffline = TRUE;
  606.             else if ((options & SEARCH_OPT) &&
  607.                                         (*p == 'e' || *p == 's' || *p == 'b'))
  608.             {
  609.                 if (*p == 'e')            /* end */
  610.                     lastend = SEARCH_END;
  611.                 ++p;
  612.             }
  613.             if (isdigit(*p) || *p == '+' || *p == '-')       /* got an offset */
  614.             {
  615.                 if (isdigit(*p) || isdigit(*(p + 1)))
  616.                     lastoff = atol((char *)p);        /* 'nr' or '+nr' or '-nr' */
  617.                 else if (*p == '-')            /* single '-' */
  618.                     lastoff = -1;
  619.                 else                        /* single '+' */
  620.                     lastoff = 1;
  621.                 ++p;
  622.                 while (isdigit(*p))            /* skip number */
  623.                     ++p;
  624.             }
  625.             searchcmdlen = p - str;            /* compute length of search command
  626.                                                             for get_address() */
  627.             str = p;                        /* put str after search command */
  628.         }
  629.  
  630.         if (options & SEARCH_ECHO)
  631.         {
  632.             msg_start();
  633.             msg_outchar(dirc);
  634.             msg_outtrans(*searchstr == NUL ? search_pattern : searchstr);
  635.             if (lastoffline || lastend || lastoff)
  636.             {
  637.                 msg_outchar(dirc);
  638.                 if (lastend)
  639.                     msg_outchar('e');
  640.                 else if (!lastoffline)
  641.                     msg_outchar('s');
  642.                 if (lastoff < 0)
  643.                 {
  644.                     msg_outchar('-');
  645.                     msg_outnum((long)-lastoff);
  646.                 }
  647.                 else if (lastoff > 0 || lastoffline)
  648.                 {
  649.                     msg_outchar('+');
  650.                     msg_outnum((long)lastoff);
  651.                 }
  652.             }
  653.             msg_clr_eos();
  654.             (void)msg_check();
  655.  
  656.             gotocmdline(FALSE);
  657.             flushbuf();
  658.         }
  659.  
  660.         /*
  661.          * If there is a character offset, subtract it from the current
  662.          * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
  663.          * This is not done for a line offset, because then we would not be vi
  664.          * compatible.
  665.          */
  666.         if (!lastoffline && lastoff)
  667.         {
  668.             if (lastoff > 0)
  669.             {
  670.                 c = lastoff;
  671.                 while (c--)
  672.                     if ((i = dec(&pos)) != 0)
  673.                         break;
  674.                 if (i == -1)                /* at start of buffer */
  675.                     goto_endofbuf(&pos);
  676.             }
  677.             else
  678.             {
  679.                 c = -lastoff;
  680.                 while (c--)
  681.                     if ((i = inc(&pos)) != 0)
  682.                         break;
  683.                 if (i == -1)                /* at end of buffer */
  684.                 {
  685.                     pos.lnum = 1;
  686.                     pos.col = 0;
  687.                 }
  688.             }
  689.         }
  690.  
  691.         c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count,
  692.                 lastend + (options &
  693.                        (SEARCH_KEEP + SEARCH_HIS + SEARCH_MSG + SEARCH_START +
  694.                            ((str != NULL && *str == ';') ? 0 : SEARCH_NOOF))),
  695.                 2);
  696.         if (dircp != NULL)
  697.             *dircp = dirc;        /* put second '/' or '?' back for normal() */
  698.         if (c == FAIL)
  699.         {
  700.             retval = 0;
  701.             goto end_do_search;
  702.         }
  703.         if (lastend)
  704.             op_inclusive = TRUE;    /* 'e' includes last character */
  705.  
  706.         retval = 1;                    /* pattern found */
  707.  
  708.         /*
  709.          * Add character and/or line offset
  710.          */
  711.         if (!(options & SEARCH_NOOF) || *str == ';')
  712.         {
  713.             if (lastoffline)            /* Add the offset to the line number. */
  714.             {
  715.                 c = pos.lnum + lastoff;
  716.                 if (c < 1)
  717.                     pos.lnum = 1;
  718.                 else if (c > curbuf->b_ml.ml_line_count)
  719.                     pos.lnum = curbuf->b_ml.ml_line_count;
  720.                 else
  721.                     pos.lnum = c;
  722.                 pos.col = 0;
  723.  
  724.                 retval = 2;                /* pattern found, line offset added */
  725.             }
  726.             else
  727.             {
  728.                 if (lastoff > 0)    /* to the right, check for end of line */
  729.                 {
  730.                     p = ml_get_pos(&pos) + 1;
  731.                     c = lastoff;
  732.                     while (c-- && *p++ != NUL)
  733.                         ++pos.col;
  734.                 }
  735.                 else                /* to the left, check for start of line */
  736.                 {
  737.                     if ((c = pos.col + lastoff) < 0)
  738.                         c = 0;
  739.                     pos.col = c;
  740.                 }
  741.             }
  742.         }
  743.  
  744.         /*
  745.          * The search command can be followed by a ';' to do another search.
  746.          * For example: "/pat/;/foo/+3;?bar"
  747.          * This is like doing another search command, except:
  748.          * - The remembered direction '/' or '?' is from the first search.
  749.          * - When an error happens the cursor isn't moved at all.
  750.          * Don't do this when called by get_address() (it handles ';' itself).
  751.          */
  752.         if (!(options & SEARCH_OPT) || str == NULL || *str != ';')
  753.             break;
  754.  
  755.         dirc = *++str;
  756.         if (dirc != '?' && dirc != '/')
  757.         {
  758.             retval = 0;
  759.             EMSG("Expected '?' or '/'  after ';'");
  760.             goto end_do_search;
  761.         }
  762.         ++str;
  763.     }
  764.  
  765.     if (options & SEARCH_MARK)
  766.         setpcmark();
  767.     curwin->w_cursor = pos;
  768.     curwin->w_set_curswant = TRUE;
  769.  
  770. end_do_search:
  771.     if (options & SEARCH_KEEP)
  772.     {
  773.         lastsdir = old_lastsdir;
  774.         lastoffline = old_lastoffline;
  775.         lastend = old_lastend;
  776.         lastoff = old_lastoff;
  777.     }
  778.     return retval;
  779. }
  780.  
  781. /*
  782.  * search_for_exact_line(pos, dir, pat)
  783.  *
  784.  * Search for a line starting with the given pattern (ignoring leading
  785.  * white-space), starting from pos and going in direction dir.    pos will
  786.  * contain the position of the match found.    Blank lines will never match.
  787.  * Return OK for success, or FAIL if no line found.
  788.  */
  789.     int
  790. search_for_exact_line(pos, dir, pat)
  791.     FPOS        *pos;
  792.     int            dir;
  793.     char_u        *pat;
  794. {
  795.     linenr_t    start = 0;
  796.     char_u        *ptr;
  797.     char_u        *p;
  798.  
  799.     if (curbuf->b_ml.ml_line_count == 0)
  800.         return FAIL;
  801.     for (;;)
  802.     {
  803.         pos->lnum += dir;
  804.         if (pos->lnum < 1)
  805.         {
  806.             if (p_ws)
  807.             {
  808.                 pos->lnum = curbuf->b_ml.ml_line_count;
  809.                 if (!shortmess(SHM_SEARCH))
  810.                     give_warning(top_bot_msg);
  811.             }
  812.             else
  813.             {
  814.                 pos->lnum = 1;
  815.                 break;
  816.             }
  817.         }
  818.         else if (pos->lnum > curbuf->b_ml.ml_line_count)
  819.         {
  820.             if (p_ws)
  821.             {
  822.                 pos->lnum = 1;
  823.                 if (!shortmess(SHM_SEARCH))
  824.                     give_warning(bot_top_msg);
  825.             }
  826.             else
  827.             {
  828.                 pos->lnum = 1;
  829.                 break;
  830.             }
  831.         }
  832.         if (pos->lnum == start)
  833.             break;
  834.         if (start == 0)
  835.             start = pos->lnum;
  836.         ptr = ml_get(pos->lnum);
  837.         p = skipwhite(ptr);
  838.         pos->col = p - ptr;
  839.         if (*p != NUL && STRNCMP(p, pat, STRLEN(pat)) == 0)
  840.             return OK;
  841.         else if (*p != NUL && p_ic)
  842.         {
  843.             ptr = pat;
  844.             while (*p && TO_LOWER(*p) == TO_LOWER(*ptr))
  845.             {
  846.                 ++p;
  847.                 ++ptr;
  848.             }
  849.             if (*ptr == NUL)
  850.                 return OK;
  851.         }
  852.     }
  853.     return FAIL;
  854. }
  855.  
  856. /*
  857.  * Character Searches
  858.  */
  859.  
  860. /*
  861.  * searchc(c, dir, type, count)
  862.  *
  863.  * Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
  864.  * position of the character, otherwise move to just before the char.
  865.  * Repeat this 'count' times.
  866.  */
  867.     int
  868. searchc(c, dir, type, count)
  869.     int                c;
  870.     register int    dir;
  871.     int                type;
  872.     long            count;
  873. {
  874.     static int        lastc = NUL;    /* last character searched for */
  875.     static int        lastcdir;        /* last direction of character search */
  876.     static int        lastctype;        /* last type of search ("find" or "to") */
  877.     register int    col;
  878.     char_u            *p;
  879.     int                len;
  880.  
  881.     if (c != NUL)        /* normal search: remember args for repeat */
  882.     {
  883.         if (!KeyStuffed)    /* don't remember when redoing */
  884.         {
  885.             lastc = c;
  886.             lastcdir = dir;
  887.             lastctype = type;
  888.         }
  889.     }
  890.     else                /* repeat previous search */
  891.     {
  892.         if (lastc == NUL)
  893.             return FALSE;
  894.         if (dir)        /* repeat in opposite direction */
  895.             dir = -lastcdir;
  896.         else
  897.             dir = lastcdir;
  898.         type = lastctype;
  899.         c = lastc;
  900.     }
  901.  
  902.     p = ml_get_curline();
  903.     col = curwin->w_cursor.col;
  904.     len = STRLEN(p);
  905.  
  906.     while (count--)
  907.     {
  908.         for (;;)
  909.         {
  910.             if ((col += dir) < 0 || col >= len)
  911.                 return FALSE;
  912.             if (p[col] == c)
  913.                 break;
  914.         }
  915.     }
  916.     if (type)
  917.         col -= dir;
  918.     curwin->w_cursor.col = col;
  919.     return TRUE;
  920. }
  921.  
  922. /*
  923.  * "Other" Searches
  924.  */
  925.  
  926. /*
  927.  * findmatch - find the matching paren or brace
  928.  *
  929.  * Improvement over vi: Braces inside quotes are ignored.
  930.  */
  931.     FPOS *
  932. findmatch(initc)
  933.     int        initc;
  934. {
  935.     return findmatchlimit(initc, 0, 0);
  936. }
  937.  
  938. /*
  939.  * findmatchlimit -- find the matching paren or brace, if it exists within
  940.  * maxtravel lines of here.  A maxtravel of 0 means search until you fall off
  941.  * the edge of the file.
  942.  *
  943.  * flags: FM_BACKWARD    search backwards (when initc is '/', '*' or '#')
  944.  *           FM_FORWARD    search forwards (when initc is '/', '*' or '#')
  945.  *          FM_BLOCKSTOP    stop at start/end of block ({ or } in column 0)
  946.  *          FM_SKIPCOMM    skip comments (not implemented yet!)
  947.  */
  948.  
  949.     FPOS *
  950. findmatchlimit(initc, flags, maxtravel)
  951.     int        initc;
  952.     int        flags;
  953.     int        maxtravel;
  954. {
  955.     static FPOS        pos;                /* current search position */
  956.     int                findc;                /* matching brace */
  957.     int                c;
  958.     int                count = 0;            /* cumulative number of braces */
  959.     int                idx = 0;            /* init for gcc */
  960.     static char_u    table[6] = {'(', ')', '[', ']', '{', '}'};
  961. #ifdef RIGHTLEFT
  962.     static char_u   table_rl[6] = {')', '(', ']', '[', '}', '{'};
  963. #endif
  964.     int                inquote = FALSE;    /* TRUE when inside quotes */
  965.     register char_u    *linep;                /* pointer to current line */
  966.     char_u            *ptr;
  967.     int                do_quotes;            /* check for quotes in current line */
  968.     int                at_start;            /* do_quotes value at start position */
  969.     int                hash_dir = 0;        /* Direction searched for # things */
  970.     int                comment_dir = 0;    /* Direction searched for comments */
  971.     FPOS            match_pos;            /* Where last slash-star was found */
  972.     int                start_in_quotes;    /* start position is in quotes */
  973.     int                traveled = 0;        /* how far we've searched so far */
  974.     int                ignore_cend = FALSE;    /* ignore comment end */
  975.     int                cpo_match;            /* vi compatible matching */
  976.     int                dir;                /* Direction to search */
  977.  
  978.     pos = curwin->w_cursor;
  979.     linep = ml_get(pos.lnum); 
  980.  
  981.     cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
  982.  
  983.     /* Direction to search when initc is '/', '*' or '#' */
  984.     if (flags & FM_BACKWARD)
  985.         dir = BACKWARD;
  986.     else if (flags & FM_FORWARD)
  987.         dir = FORWARD;
  988.     else
  989.         dir = 0;
  990.  
  991.     /*
  992.      * if initc given, look in the table for the matching character
  993.      * '/' and '*' are special cases: look for start or end of comment.
  994.      * When '/' is used, we ignore running backwards into an star-slash, for
  995.      * "[*" command, we just want to find any comment.
  996.      */
  997.     if (initc == '/' || initc == '*')
  998.     {
  999.         comment_dir = dir;
  1000.         if (initc == '/')
  1001.             ignore_cend = TRUE;
  1002.         idx = (dir == FORWARD) ? 0 : 1;
  1003.         initc = NUL;
  1004.     }
  1005.     else if (initc != '#' && initc != NUL)
  1006.     {
  1007.         for (idx = 0; idx < 6; ++idx)
  1008. #ifdef RIGHTLEFT
  1009.             if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
  1010.             {
  1011.                 initc = (curwin->w_p_rl ? table_rl : table)[idx = idx ^ 1];
  1012.                 
  1013. #else
  1014.             if (table[idx] == initc)
  1015.             {
  1016.                 initc = table[idx = idx ^ 1];
  1017. #endif
  1018.                 break;
  1019.             }
  1020.         if (idx == 6)            /* invalid initc! */
  1021.             return NULL;
  1022.     }
  1023.     /*
  1024.      * Either initc is '#', or no initc was given and we need to look under the
  1025.      * cursor.
  1026.      */
  1027.     else
  1028.     {
  1029.         if (initc == '#')
  1030.         {
  1031.             hash_dir = dir;
  1032.         }
  1033.         else
  1034.         {
  1035.             /*
  1036.              * initc was not given, must look for something to match under
  1037.              * or near the cursor.
  1038.              */
  1039.             if (linep[0] == '#' && pos.col == 0)
  1040.             {
  1041.                 /* If it's not #if, #else etc, we should look for a brace
  1042.                  * instead */
  1043.                 for (c = 1; vim_iswhite(linep[c]); c++)
  1044.                     ;
  1045.                 if (STRNCMP(linep + c, "if", (size_t)2) == 0 ||
  1046.                     STRNCMP(linep + c, "endif", (size_t)5) == 0 ||
  1047.                     STRNCMP(linep + c, "el", (size_t)2) == 0)
  1048.                         hash_dir = 1;
  1049.             }
  1050.  
  1051.             /*
  1052.              * Are we on a comment?
  1053.              */
  1054.             else if (linep[pos.col] == '/')
  1055.             {
  1056.                 if (linep[pos.col + 1] == '*')
  1057.                 {
  1058.                     comment_dir = 1;
  1059.                     idx = 0;
  1060.                     pos.col++;
  1061.                 }
  1062.                 else if (pos.col > 0 && linep[pos.col - 1] == '*')
  1063.                 {
  1064.                     comment_dir = -1;
  1065.                     idx = 1;
  1066.                     pos.col--;
  1067.                 }
  1068.             }
  1069.             else if (linep[pos.col] == '*')
  1070.             {
  1071.                 if (linep[pos.col + 1] == '/')
  1072.                 {
  1073.                     comment_dir = -1;
  1074.                     idx = 1;
  1075.                 }
  1076.                 else if (pos.col > 0 && linep[pos.col - 1] == '/')
  1077.                 {
  1078.                     comment_dir = 1;
  1079.                     idx = 0;
  1080.                 }
  1081.             }
  1082.  
  1083.             /*
  1084.              * If we are not on a comment or the # at the start of a line, then
  1085.              * look for brace anywhere on this line after the cursor.
  1086.              */
  1087.             if (!hash_dir && !comment_dir)
  1088.             {
  1089.                 /*
  1090.                  * find the brace under or after the cursor
  1091.                  */
  1092.                 linep = ml_get(pos.lnum); 
  1093.                 idx = 6;                    /* error if this line is empty */
  1094.                 for (;;)
  1095.                 {
  1096.                     initc = linep[pos.col];
  1097.                     if (initc == NUL)
  1098.                         break;
  1099.  
  1100.                     for (idx = 0; idx < 6; ++idx)
  1101. #ifdef RIGHTLEFT
  1102.                         if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
  1103. #else
  1104.                          if (table[idx] == initc)
  1105. #endif
  1106.                             break;
  1107.                     if (idx != 6)
  1108.                         break;
  1109.                     ++pos.col;
  1110.                 }
  1111.                 if (idx == 6)
  1112.                 {
  1113.                     if (linep[0] == '#')
  1114.                         hash_dir = 1;
  1115.                     else
  1116.                         return NULL;
  1117.                 }
  1118.             }
  1119.         }
  1120.         if (hash_dir)
  1121.         {
  1122.             /*
  1123.              * Look for matching #if, #else, #elif, or #endif
  1124.              */
  1125.             op_motion_type = MLINE;        /* Linewise for this case only */
  1126.             if (initc != '#')
  1127.             {
  1128.                 ptr = skipwhite(linep + 1);
  1129.                 if (STRNCMP(ptr, "if", (size_t)2) == 0 ||
  1130.                                        STRNCMP(ptr, "el", (size_t)2) == 0)
  1131.                     hash_dir = 1;
  1132.                 else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
  1133.                     hash_dir = -1;
  1134.                 else
  1135.                     return NULL;
  1136.             }
  1137.             pos.col = 0;
  1138.             while (!got_int)
  1139.             {
  1140.                 if (hash_dir > 0)
  1141.                 {
  1142.                     if (pos.lnum == curbuf->b_ml.ml_line_count)
  1143.                         break;
  1144.                 }
  1145.                 else if (pos.lnum == 1)
  1146.                     break;
  1147.                 pos.lnum += hash_dir;
  1148.                 linep = ml_get(pos.lnum);
  1149.                 line_breakcheck();
  1150.                 if (linep[0] != '#')
  1151.                     continue;
  1152.                 ptr = linep + 1;
  1153.                 while (*ptr == ' ' || *ptr == TAB)
  1154.                     ptr++;
  1155.                 if (hash_dir > 0)
  1156.                 {
  1157.                     if (STRNCMP(ptr, "if", (size_t)2) == 0)
  1158.                         count++;
  1159.                     else if (STRNCMP(ptr, "el", (size_t)2) == 0)
  1160.                     {
  1161.                         if (count == 0)
  1162.                             return &pos;
  1163.                     }
  1164.                     else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
  1165.                     {
  1166.                         if (count == 0)
  1167.                             return &pos;
  1168.                         count--;
  1169.                     }
  1170.                 }
  1171.                 else
  1172.                 {
  1173.                     if (STRNCMP(ptr, "if", (size_t)2) == 0)
  1174.                     {
  1175.                         if (count == 0)
  1176.                             return &pos;
  1177.                         count--;
  1178.                     }
  1179.                     else if (initc == '#' && STRNCMP(ptr, "el", (size_t)2) == 0)
  1180.                     {
  1181.                         if (count == 0)
  1182.                             return &pos;
  1183.                     }
  1184.                     else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
  1185.                         count++;
  1186.                 }
  1187.             }
  1188.             return NULL;
  1189.         }
  1190.     }
  1191.  
  1192. #ifdef RIGHTLEFT
  1193.     findc = (curwin->w_p_rl ? table_rl : table)[idx ^ 1];
  1194. #else
  1195.      findc = table[idx ^ 1];        /* get matching brace */
  1196. #endif
  1197.     idx &= 1;
  1198.  
  1199.     do_quotes = -1;
  1200.     start_in_quotes = MAYBE;
  1201.     while (!got_int)
  1202.     {
  1203.         /*
  1204.          * Go to the next position, forward or backward. We could use
  1205.          * inc() and dec() here, but that is much slower
  1206.          */
  1207.         if (idx)                        /* backward search */
  1208.         {
  1209.             if (pos.col == 0)            /* at start of line, go to prev. one */
  1210.             {
  1211.                 if (pos.lnum == 1)        /* start of file */
  1212.                     break;
  1213.                 --pos.lnum;
  1214.  
  1215.                 if (maxtravel && traveled++ > maxtravel)
  1216.                     break;
  1217.  
  1218.                 linep = ml_get(pos.lnum);
  1219.                 pos.col = STRLEN(linep);    /* put pos.col on trailing NUL */
  1220.                 do_quotes = -1;
  1221.                 line_breakcheck();
  1222.             }
  1223.             else
  1224.                 --pos.col;
  1225.         }
  1226.         else                            /* forward search */
  1227.         {
  1228.             if (linep[pos.col] == NUL)    /* at end of line, go to next one */
  1229.             {
  1230.                 if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
  1231.                     break;
  1232.                 ++pos.lnum;
  1233.  
  1234.                 if (maxtravel && traveled++ > maxtravel)
  1235.                     break;
  1236.  
  1237.                 linep = ml_get(pos.lnum);
  1238.                 pos.col = 0;
  1239.                 do_quotes = -1;
  1240.                 line_breakcheck();
  1241.             }
  1242.             else
  1243.                 ++pos.col;
  1244.         }
  1245.  
  1246.         /*
  1247.          * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
  1248.          */
  1249.         if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
  1250.                                          (linep[0] == '{' || linep[0] == '}'))
  1251.         {
  1252.             if (linep[0] == findc && count == 0)        /* match! */
  1253.                 return &pos;
  1254.             break;                                        /* out of scope */
  1255.         }
  1256.  
  1257.         if (comment_dir)
  1258.         {
  1259.             /* Note: comments do not nest, and we ignore quotes in them */
  1260.             if (comment_dir == 1)
  1261.             {
  1262.                 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
  1263.                 {
  1264.                     pos.col++;
  1265.                     return &pos;
  1266.                 }
  1267.             }
  1268.             else    /* Searching backwards */
  1269.             {
  1270.                 /*
  1271.                  * A comment may contain slash-star, it may also start or end
  1272.                  * with slash-star-slash.  I'm not using real examples though
  1273.                  * because "gcc -Wall" would complain -- webb
  1274.                  */
  1275.                 if (pos.col == 0)
  1276.                     continue;
  1277.                 else if (linep[pos.col - 1] == '/' && linep[pos.col] == '*')
  1278.                 {
  1279.                     count++;
  1280.                     match_pos = pos;
  1281.                     match_pos.col--;
  1282.                 }
  1283.                 else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
  1284.                 {
  1285.                     if (count > 0)
  1286.                         pos = match_pos;
  1287.                     else if (pos.col > 1 && linep[pos.col - 2] == '/')
  1288.                         pos.col -= 2;
  1289.                     else if (ignore_cend)
  1290.                         continue;
  1291.                     else
  1292.                         return NULL;
  1293.                     return &pos;
  1294.                 }
  1295.             }
  1296.             continue;
  1297.         }
  1298.  
  1299.         /*
  1300.          * If smart matching ('cpoptions' does not contain '%'), braces inside
  1301.          * of quotes are ignored, but only if there is an even number of
  1302.          * quotes in the line.
  1303.          */
  1304.         if (cpo_match)
  1305.             do_quotes = 0;
  1306.         else if (do_quotes == -1)
  1307.         {
  1308.             /*
  1309.              * count the number of quotes in the line, skipping \" and '"'
  1310.              */
  1311.             at_start = do_quotes;
  1312.             for (ptr = linep; *ptr; ++ptr)
  1313.             {
  1314.                 if (ptr == linep + curwin->w_cursor.col)
  1315.                     at_start = (do_quotes & 1);
  1316.                 if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
  1317.                             (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
  1318.                     ++do_quotes;
  1319.             }
  1320.             do_quotes &= 1;            /* result is 1 with even number of quotes */
  1321.  
  1322.             /*
  1323.              * If we find an uneven count, check current line and previous
  1324.              * one for a '\' at the end.
  1325.              */
  1326.             if (!do_quotes)
  1327.             {
  1328.                 inquote = FALSE;
  1329.                 if (ptr[-1] == '\\')
  1330.                 {
  1331.                     do_quotes = 1;
  1332.                     if (start_in_quotes == MAYBE)
  1333.                     {
  1334.                         inquote = !at_start;
  1335.                         if (inquote)
  1336.                             start_in_quotes = TRUE;
  1337.                     }
  1338.                     else if (idx)                /* backward search */
  1339.                         inquote = TRUE;
  1340.                 }
  1341.                 if (pos.lnum > 1)
  1342.                 {
  1343.                     ptr = ml_get(pos.lnum - 1);
  1344.                     if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
  1345.                     {
  1346.                         do_quotes = 1;
  1347.                         if (start_in_quotes == MAYBE)
  1348.                         {
  1349.                             inquote = at_start;
  1350.                             if (inquote)
  1351.                                 start_in_quotes = TRUE;
  1352.                         }
  1353.                         else if (!idx)                /* forward search */
  1354.                             inquote = TRUE;
  1355.                     }
  1356.                 }
  1357.             }
  1358.         }
  1359.         if (start_in_quotes == MAYBE)
  1360.             start_in_quotes = FALSE;
  1361.  
  1362.         /*
  1363.          * If 'smartmatch' is set:
  1364.          *     Things inside quotes are ignored by setting 'inquote'.  If we
  1365.          *     find a quote without a preceding '\' invert 'inquote'.  At the
  1366.          *     end of a line not ending in '\' we reset 'inquote'.
  1367.          *
  1368.          *     In lines with an uneven number of quotes (without preceding '\')
  1369.          *     we do not know which part to ignore. Therefore we only set
  1370.          *     inquote if the number of quotes in a line is even, unless this
  1371.          *     line or the previous one ends in a '\'.  Complicated, isn't it?
  1372.          */
  1373.         switch (c = linep[pos.col])
  1374.         {
  1375.         case NUL:
  1376.                 /* at end of line without trailing backslash, reset inquote */
  1377.             if (pos.col == 0 || linep[pos.col - 1] != '\\')
  1378.             {
  1379.                 inquote = FALSE;
  1380.                 start_in_quotes = FALSE;
  1381.             }
  1382.             break;
  1383.  
  1384.         case '"':
  1385.                 /* a quote that is preceded with a backslash is ignored */
  1386.             if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
  1387.             {
  1388.                 inquote = !inquote;
  1389.                 start_in_quotes = FALSE;
  1390.             }
  1391.             break;
  1392.  
  1393.         /*
  1394.          * If smart matching ('cpoptions' does not contain '%'):
  1395.          *     Skip things in single quotes: 'x' or '\x'.  Be careful for single
  1396.          *     single quotes, eg jon's.  Things like '\233' or '\x3f' are not
  1397.          *     skipped, there is never a brace in them.
  1398.          */
  1399.         case '\'':
  1400.             if (vim_strchr(p_cpo, CPO_MATCH) == NULL)
  1401.             {
  1402.                 if (idx)                        /* backward search */
  1403.                 {
  1404.                     if (pos.col > 1)
  1405.                     {
  1406.                         if (linep[pos.col - 2] == '\'')
  1407.                             pos.col -= 2;
  1408.                         else if (linep[pos.col - 2] == '\\' &&
  1409.                                     pos.col > 2 && linep[pos.col - 3] == '\'')
  1410.                             pos.col -= 3;
  1411.                     }
  1412.                 }
  1413.                 else if (linep[pos.col + 1])    /* forward search */
  1414.                 {
  1415.                     if (linep[pos.col + 1] == '\\' &&
  1416.                             linep[pos.col + 2] && linep[pos.col + 3] == '\'')
  1417.                         pos.col += 3;
  1418.                     else if (linep[pos.col + 2] == '\'')
  1419.                         pos.col += 2;
  1420.                 }
  1421.             }
  1422.             break;
  1423.  
  1424.         default:
  1425.                     /* Check for match outside of quotes, and inside of
  1426.                      * quotes when the start is also inside of quotes */
  1427.             if (!inquote || start_in_quotes == TRUE)
  1428.             {
  1429.                 if (c == initc)
  1430.                     count++;
  1431.                 else if (c == findc)
  1432.                 {
  1433.                     if (count == 0)
  1434.                         return &pos;
  1435.                     count--;
  1436.                 }
  1437.             }
  1438.         }
  1439.     }
  1440.  
  1441.     if (comment_dir == -1 && count > 0)
  1442.     {
  1443.         pos = match_pos;
  1444.         return &pos;
  1445.     }
  1446.     return (FPOS *) NULL;        /* never found it */
  1447. }
  1448.  
  1449. /*
  1450.  * Move cursor briefly to character matching the one under the cursor.
  1451.  * Show the match only if it is visible on the screen.
  1452.  */
  1453.     void
  1454. showmatch()
  1455. {
  1456.     FPOS           *lpos, csave;
  1457.     colnr_t            vcol;
  1458.  
  1459.     if ((lpos = findmatch(NUL)) == NULL)        /* no match, so beep */
  1460.         vim_beep();
  1461.     else if (lpos->lnum >= curwin->w_topline)
  1462.     {
  1463.         if (!curwin->w_p_wrap)
  1464.             getvcol(curwin, lpos, NULL, &vcol, NULL);
  1465.         if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol &&
  1466.                                           vcol < curwin->w_leftcol + Columns))
  1467.         {
  1468.             updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
  1469.             csave = curwin->w_cursor;
  1470.             curwin->w_cursor = *lpos;    /* move to matching char */
  1471.             cursupdate();
  1472.             showruler(0);
  1473.             setcursor();
  1474.             cursor_on();                /* make sure that the cursor is shown */
  1475.             flushbuf();
  1476.  
  1477.             /*
  1478.              * brief pause, unless 'm' is present in 'cpo' and a character is
  1479.              * available.
  1480.              */
  1481.             if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
  1482.                 mch_delay(500L, TRUE);
  1483.             else if (!char_avail())
  1484.                 mch_delay(500L, FALSE);
  1485.             curwin->w_cursor = csave;    /* restore cursor position */
  1486.             cursupdate();
  1487.         }
  1488.     }
  1489. }
  1490.  
  1491. /*
  1492.  * findsent(dir, count) - Find the start of the next sentence in direction
  1493.  * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
  1494.  * space or a line break. Also stop at an empty line.
  1495.  * Return OK if the next sentence was found.
  1496.  */
  1497.     int
  1498. findsent(dir, count)
  1499.     int        dir;
  1500.     long    count;
  1501. {
  1502.     FPOS            pos, tpos;
  1503.     register int    c;
  1504.     int                (*func) __PARMS((FPOS *));
  1505.     int                startlnum;
  1506.     int                noskip = FALSE;            /* do not skip blanks */
  1507.  
  1508.     pos = curwin->w_cursor;
  1509.     if (dir == FORWARD)
  1510.         func = incl;
  1511.     else
  1512.         func = decl;
  1513.  
  1514.     while (count--)
  1515.     {
  1516.         /*
  1517.          * if on an empty line, skip upto a non-empty line
  1518.          */
  1519.         if (gchar(&pos) == NUL)
  1520.         {
  1521.             do
  1522.                 if ((*func)(&pos) == -1)
  1523.                     break;
  1524.             while (gchar(&pos) == NUL);
  1525.             if (dir == FORWARD)
  1526.                 goto found;
  1527.         }
  1528.         /*
  1529.          * if on the start of a paragraph or a section and searching forward,
  1530.          * go to the next line
  1531.          */
  1532.         else if (dir == FORWARD && pos.col == 0 &&
  1533.                                                 startPS(pos.lnum, NUL, FALSE))
  1534.         {
  1535.             if (pos.lnum == curbuf->b_ml.ml_line_count)
  1536.                 return FAIL;
  1537.             ++pos.lnum;
  1538.             goto found;
  1539.         }
  1540.         else if (dir == BACKWARD)
  1541.             decl(&pos);
  1542.  
  1543.         /* go back to the previous non-blank char */
  1544.         while ((c = gchar(&pos)) == ' ' || c == '\t' ||
  1545.              (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
  1546.         {
  1547.             if (decl(&pos) == -1)
  1548.                 break;
  1549.             /* when going forward: Stop in front of empty line */
  1550.             if (lineempty(pos.lnum) && dir == FORWARD)
  1551.             {
  1552.                 incl(&pos);
  1553.                 goto found;
  1554.             }
  1555.         }
  1556.  
  1557.         /* remember the line where the search started */
  1558.         startlnum = pos.lnum;
  1559.  
  1560.         for (;;)                /* find end of sentence */
  1561.         {
  1562.             c = gchar(&pos);
  1563.             if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
  1564.             {
  1565.                 if (dir == BACKWARD && pos.lnum != startlnum)
  1566.                     ++pos.lnum;
  1567.                 break;
  1568.             }
  1569.             if (c == '.' || c == '!' || c == '?')
  1570.             {
  1571.                 tpos = pos;
  1572.                 do
  1573.                     if ((c = inc(&tpos)) == -1)
  1574.                         break;
  1575.                 while (vim_strchr((char_u *)")]\"'", c = gchar(&tpos)) != NULL);
  1576.                 if (c == -1  || c == ' ' || c == '\t' || c == NUL)
  1577.                 {
  1578.                     pos = tpos;
  1579.                     if (gchar(&pos) == NUL) /* skip NUL at EOL */
  1580.                         inc(&pos);
  1581.                     break;
  1582.                 }
  1583.             }
  1584.             if ((*func)(&pos) == -1)
  1585.             {
  1586.                 if (count)
  1587.                     return FAIL;
  1588.                 noskip = TRUE;
  1589.                 break;
  1590.             }
  1591.         }
  1592. found:
  1593.             /* skip white space */
  1594.         while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
  1595.             if (incl(&pos) == -1)
  1596.                 break;
  1597.     }
  1598.  
  1599.     setpcmark();
  1600.     curwin->w_cursor = pos;
  1601.     return OK;
  1602. }
  1603.  
  1604. /*
  1605.  * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
  1606.  * Paragraphs are currently supposed to be separated by empty lines.
  1607.  * Return TRUE if the next paragraph was found.
  1608.  * If 'what' is '{' or '}' we go to the next section.
  1609.  * If 'both' is TRUE also stop at '}'.
  1610.  */
  1611.     int
  1612. findpar(dir, count, what, both)
  1613.     register int    dir;
  1614.     long            count;
  1615.     int                what;
  1616.     int                both;
  1617. {
  1618.     register linenr_t    curr;
  1619.     int                    did_skip;        /* TRUE after separating lines have
  1620.                                                 been skipped */
  1621.     int                    first;            /* TRUE on first line */
  1622.  
  1623.     curr = curwin->w_cursor.lnum;
  1624.  
  1625.     while (count--)
  1626.     {
  1627.         did_skip = FALSE;
  1628.         for (first = TRUE; ; first = FALSE)
  1629.         {
  1630.             if (*ml_get(curr) != NUL)
  1631.                 did_skip = TRUE;
  1632.  
  1633.             if (!first && did_skip && startPS(curr, what, both))
  1634.                 break;
  1635.  
  1636.             if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
  1637.             {
  1638.                 if (count)
  1639.                     return FALSE;
  1640.                 curr -= dir;
  1641.                 break;
  1642.             }
  1643.         }
  1644.     }
  1645.     setpcmark();
  1646.     if (both && *ml_get(curr) == '}')    /* include line with '}' */
  1647.         ++curr;
  1648.     curwin->w_cursor.lnum = curr;
  1649.     if (curr == curbuf->b_ml.ml_line_count)
  1650.     {
  1651.         if ((curwin->w_cursor.col = STRLEN(ml_get(curr))) != 0)
  1652.         {
  1653.             --curwin->w_cursor.col;
  1654.             op_inclusive = TRUE;
  1655.         }
  1656.     }
  1657.     else
  1658.         curwin->w_cursor.col = 0;
  1659.     return TRUE;
  1660. }
  1661.  
  1662. /*
  1663.  * check if the string 's' is a nroff macro that is in option 'opt'
  1664.  */
  1665.     static int
  1666. inmacro(opt, s)
  1667.     char_u            *opt;
  1668.     register char_u *s;
  1669. {
  1670.     register char_u *macro;
  1671.  
  1672.     for (macro = opt; macro[0]; ++macro)
  1673.     {
  1674.         if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ') &&
  1675.                    (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
  1676.             break;
  1677.         ++macro;
  1678.         if (macro[0] == NUL)
  1679.             break;
  1680.     }
  1681.     return (macro[0] != NUL);
  1682. }
  1683.  
  1684. /*
  1685.  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
  1686.  * If 'para' is '{' or '}' only check for sections.
  1687.  * If 'both' is TRUE also stop at '}'
  1688.  */
  1689.     int
  1690. startPS(lnum, para, both)
  1691.     linenr_t    lnum;
  1692.     int            para;
  1693.     int            both;
  1694. {
  1695.     register char_u *s;
  1696.  
  1697.     s = ml_get(lnum);
  1698.     if (*s == para || *s == '\f' || (both && *s == '}'))
  1699.         return TRUE;
  1700.     if (*s == '.' && (inmacro(p_sections, s + 1) ||
  1701.                                            (!para && inmacro(p_para, s + 1))))
  1702.         return TRUE;
  1703.     return FALSE;
  1704. }
  1705.  
  1706. /*
  1707.  * The following routines do the word searches performed by the 'w', 'W',
  1708.  * 'b', 'B', 'e', and 'E' commands.
  1709.  */
  1710.  
  1711. /*
  1712.  * To perform these searches, characters are placed into one of three
  1713.  * classes, and transitions between classes determine word boundaries.
  1714.  *
  1715.  * The classes are:
  1716.  *
  1717.  * 0 - white space
  1718.  * 1 - keyword charactes (letters, digits and underscore)
  1719.  * 2 - everything else
  1720.  */
  1721.  
  1722. static int        stype;            /* type of the word motion being performed */
  1723.  
  1724. /*
  1725.  * cls() - returns the class of character at curwin->w_cursor
  1726.  *
  1727.  * The 'type' of the current search modifies the classes of characters if a
  1728.  * 'W', 'B', or 'E' motion is being done. In this case, chars. from class 2
  1729.  * are reported as class 1 since only white space boundaries are of interest.
  1730.  */
  1731.     static int
  1732. cls()
  1733. {
  1734.     register int c;
  1735.  
  1736.     c = gchar_cursor();
  1737.     if (c == ' ' || c == '\t' || c == NUL)
  1738.         return 0;
  1739.  
  1740.     if (iswordchar(c))
  1741.         return 1;
  1742.  
  1743.     /*
  1744.      * If stype is non-zero, report these as class 1.
  1745.      */
  1746.     return (stype == 0) ? 2 : 1;
  1747. }
  1748.  
  1749.  
  1750. /*
  1751.  * fwd_word(count, type, eol) - move forward one word
  1752.  *
  1753.  * Returns FAIL if the cursor was already at the end of the file.
  1754.  * If eol is TRUE, last word stops at end of line (for operators).
  1755.  */
  1756.     int
  1757. fwd_word(count, type, eol)
  1758.     long        count;
  1759.     int            type;
  1760.     int            eol;
  1761. {
  1762.     int            sclass;        /* starting class */
  1763.     int            i;
  1764.     int            last_line;
  1765.  
  1766.     stype = type;
  1767.     while (--count >= 0)
  1768.     {
  1769.         sclass = cls();
  1770.  
  1771.         /*
  1772.          * We always move at least one character, unless on the last character
  1773.          * in the buffer.
  1774.          */
  1775.         last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
  1776.         i = inc_cursor();
  1777.         if (i == -1 || (i == 1 && last_line))
  1778.                                             /* started at last char in file */
  1779.             return FAIL;
  1780.         if (i == 1 && eol && count == 0)    /* started at last char in line */
  1781.             return OK;
  1782.  
  1783.         /*
  1784.          * Go one char past end of current word (if any)
  1785.          */
  1786.         if (sclass != 0)
  1787.             while (cls() == sclass)
  1788.             {
  1789.                 i = inc_cursor();
  1790.                 if (i == -1 || (i == 1 && eol && count == 0))
  1791.                     return OK;
  1792.             }
  1793.  
  1794.         /*
  1795.          * go to next non-white
  1796.          */
  1797.         while (cls() == 0)
  1798.         {
  1799.             /*
  1800.              * We'll stop if we land on a blank line
  1801.              */
  1802.             if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
  1803.                 break;
  1804.  
  1805.             i = inc_cursor();
  1806.             if (i == -1 || (i == 1 && eol && count == 0))
  1807.                 return OK;
  1808.         }
  1809.     }
  1810.     return OK;
  1811. }
  1812.  
  1813. /*
  1814.  * bck_word() - move backward 'count' words
  1815.  *
  1816.  * If stop is TRUE and we are already on the start of a word, move one less.
  1817.  *
  1818.  * Returns FAIL if top of the file was reached.
  1819.  */
  1820.     int
  1821. bck_word(count, type, stop)
  1822.     long        count;
  1823.     int            type;
  1824.     int            stop;
  1825. {
  1826.     int            sclass;        /* starting class */
  1827.  
  1828.     stype = type;
  1829.     while (--count >= 0)
  1830.     {
  1831.         sclass = cls();
  1832.         if (dec_cursor() == -1)        /* started at start of file */
  1833.             return FAIL;
  1834.  
  1835.         if (!stop || sclass == cls() || sclass == 0)
  1836.         {
  1837.             /*
  1838.              * Skip white space before the word.
  1839.              * Stop on an empty line.
  1840.              */
  1841.             while (cls() == 0)
  1842.             {
  1843.                 if (curwin->w_cursor.col == 0 &&
  1844.                                              lineempty(curwin->w_cursor.lnum))
  1845.                     goto finished;
  1846.  
  1847.                 if (dec_cursor() == -1)        /* hit start of file, stop here */
  1848.                     return OK;
  1849.             }
  1850.  
  1851.             /*
  1852.              * Move backward to start of this word.
  1853.              */
  1854.             if (skip_chars(cls(), BACKWARD))
  1855.                 return OK;
  1856.         }
  1857.  
  1858.         inc_cursor();                     /* overshot - forward one */
  1859. finished:
  1860.         stop = FALSE;
  1861.     }
  1862.     return OK;
  1863. }
  1864.  
  1865. /*
  1866.  * end_word() - move to the end of the word
  1867.  *
  1868.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  1869.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  1870.  * motion crosses blank lines. When the real vi crosses a blank line in an
  1871.  * 'e' motion, the cursor is placed on the FIRST character of the next
  1872.  * non-blank line. The 'E' command, however, works correctly. Since this
  1873.  * appears to be a bug, I have not duplicated it here.
  1874.  *
  1875.  * Returns FAIL if end of the file was reached.
  1876.  *
  1877.  * If stop is TRUE and we are already on the end of a word, move one less.
  1878.  * If empty is TRUE stop on an empty line.
  1879.  */
  1880.     int
  1881. end_word(count, type, stop, empty)
  1882.     long        count;
  1883.     int            type;
  1884.     int            stop;
  1885.     int            empty;
  1886. {
  1887.     int            sclass;        /* starting class */
  1888.  
  1889.     stype = type;
  1890.     while (--count >= 0)
  1891.     {
  1892.         sclass = cls();
  1893.         if (inc_cursor() == -1)
  1894.             return FAIL;
  1895.  
  1896.         /*
  1897.          * If we're in the middle of a word, we just have to move to the end
  1898.          * of it.
  1899.          */
  1900.         if (cls() == sclass && sclass != 0)
  1901.         {
  1902.             /*
  1903.              * Move forward to end of the current word
  1904.              */
  1905.             if (skip_chars(sclass, FORWARD))
  1906.                 return FAIL;
  1907.         }
  1908.         else if (!stop || sclass == 0)
  1909.         {
  1910.             /*
  1911.              * We were at the end of a word. Go to the end of the next word.
  1912.              * First skip white space, if 'empty' is TRUE, stop at empty line.
  1913.              */
  1914.             while (cls() == 0)
  1915.             {
  1916.                 if (empty && curwin->w_cursor.col == 0 &&
  1917.                                              lineempty(curwin->w_cursor.lnum))
  1918.                     goto finished;
  1919.                 if (inc_cursor() == -1)        /* hit end of file, stop here */
  1920.                     return FAIL;
  1921.             }
  1922.  
  1923.             /*
  1924.              * Move forward to the end of this word.
  1925.              */
  1926.             if (skip_chars(cls(), FORWARD))
  1927.                 return FAIL;
  1928.         }
  1929.         dec_cursor();                    /* overshot - one char backward */
  1930. finished:
  1931.         stop = FALSE;                    /* we move only one word less */
  1932.     }
  1933.     return OK;
  1934. }
  1935.  
  1936. /*
  1937.  * bckend_word(count, type) - move back to the end of the word
  1938.  *
  1939.  * If 'eol' is TRUE, stop at end of line.
  1940.  *
  1941.  * Returns FAIL if start of the file was reached.
  1942.  */
  1943.     int
  1944. bckend_word(count, type, eol)
  1945.     long        count;
  1946.     int            type;
  1947.     int            eol;
  1948. {
  1949.     int            sclass;        /* starting class */
  1950.     int            i;
  1951.  
  1952.     stype = type;
  1953.     while (--count >= 0)
  1954.     {
  1955.         sclass = cls();
  1956.         if ((i = dec_cursor()) == -1)
  1957.             return FAIL;
  1958.         if (eol && i == 1)
  1959.             return OK;
  1960.  
  1961.         /*
  1962.          * Move backward to before the start of this word.
  1963.          */
  1964.         if (sclass != 0)
  1965.         {
  1966.             while (cls() == sclass)
  1967.                 if ((i = dec_cursor()) == -1 || (eol && i == 1))
  1968.                     return OK;
  1969.         }
  1970.  
  1971.         /*
  1972.          * Move backward to end of the previous word
  1973.          */
  1974.         while (cls() == 0)
  1975.         {
  1976.             if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
  1977.                 break;
  1978.             if ((i = dec_cursor()) == -1 || (eol && i == 1))
  1979.                 return OK;
  1980.         }
  1981.     }
  1982.     return OK;
  1983. }
  1984.  
  1985.     static int
  1986. skip_chars(cclass, dir)
  1987.     int        cclass;
  1988.     int        dir;
  1989. {
  1990.     while (cls() == cclass)
  1991.         if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
  1992.             return TRUE;
  1993.     return FALSE;
  1994. }
  1995.  
  1996. /*
  1997.  * Go back to the start of the word or the start of white space
  1998.  */
  1999.     static void
  2000. back_in_line()
  2001. {
  2002.     int            sclass;                /* starting class */
  2003.  
  2004.     sclass = cls();
  2005.     for (;;)
  2006.     {
  2007.         if (curwin->w_cursor.col == 0)        /* stop at start of line */
  2008.             break;
  2009.         --curwin->w_cursor.col;
  2010.         if (cls() != sclass)                /* stop at start of word */
  2011.         {
  2012.             ++curwin->w_cursor.col;
  2013.             break;
  2014.         }
  2015.     }
  2016. }
  2017.  
  2018. /*
  2019.  * Find word under cursor, cursor at end
  2020.  */
  2021.     int
  2022. current_word(count, type)
  2023.     long        count;
  2024.     int            type;
  2025. {
  2026.     FPOS        start;
  2027.     FPOS        pos;
  2028.     int            inclusive = TRUE;
  2029.  
  2030.     stype = type;
  2031.  
  2032.     /*
  2033.      * When visual area is bigger than one character: Extend it.
  2034.      */
  2035.     if (VIsual_active && !equal(curwin->w_cursor, VIsual))
  2036.     {
  2037.         if (lt(curwin->w_cursor, VIsual))
  2038.         {
  2039.             if (decl(&curwin->w_cursor) == -1)
  2040.                 return FAIL;
  2041.             if (cls() == 0)
  2042.             {
  2043.                 if (bckend_word(count, type, TRUE) == FAIL)
  2044.                     return FAIL;
  2045.                 (void)incl(&curwin->w_cursor);
  2046.             }
  2047.             else
  2048.             {
  2049.                 if (bck_word(count, type, TRUE) == FAIL)
  2050.                     return FAIL;
  2051.             }
  2052.         }
  2053.         else
  2054.         {
  2055.             if (incl(&curwin->w_cursor) == -1)
  2056.                 return FAIL;
  2057.             if (cls() == 0)
  2058.             {
  2059.                 if (fwd_word(count, type, TRUE) == FAIL)
  2060.                     return FAIL;
  2061.                 (void)oneleft();
  2062.             }
  2063.             else
  2064.             {
  2065.                 if (end_word(count, type, TRUE, TRUE) == FAIL)
  2066.                     return FAIL;
  2067.             }
  2068.         }
  2069.         return OK;
  2070.     }
  2071.  
  2072.     /*
  2073.      * Go to start of current word or white space.
  2074.      */
  2075.     back_in_line();
  2076.     start = curwin->w_cursor;
  2077.  
  2078.     /*
  2079.      * If the start is on white space, find end of word.
  2080.      * Otherwise find start of next word.
  2081.      */
  2082.     if (cls() == 0)
  2083.     {
  2084.         if (end_word(count, type, TRUE, TRUE) == FAIL)
  2085.             return FAIL;
  2086.     }
  2087.     else
  2088.     {
  2089.         if (fwd_word(count, type, TRUE) == FAIL)
  2090.             return FAIL;
  2091.         /*
  2092.          * If end is just past a new-line, we don't want to include the first
  2093.          * character on the line
  2094.          */
  2095.         if (oneleft() == FAIL)            /* put cursor on last char of area */
  2096.             inclusive = FALSE;
  2097.         else
  2098.         {
  2099.             pos = curwin->w_cursor;        /* save cursor position */
  2100.             /*
  2101.              * If we don't include white space at the end, move the start to 
  2102.              * include some white space there. This makes "d." work better on
  2103.              * the last word in a sentence. Don't delete white space at start
  2104.              * of line (indent).
  2105.              */
  2106.             if (cls() != 0)
  2107.             {
  2108.                 curwin->w_cursor = start;
  2109.                 if (oneleft() == OK)
  2110.                 {
  2111.                     back_in_line();
  2112.                     if (cls() == 0 && curwin->w_cursor.col > 0)
  2113.                         start = curwin->w_cursor;
  2114.                 }
  2115.             }
  2116.             curwin->w_cursor = pos;        /* put cursor back at end */
  2117.         }
  2118.     }
  2119.     if (VIsual_active)
  2120.     {
  2121.         /* should do something when inclusive == FALSE ! */
  2122.         VIsual = start;
  2123.         VIsual_mode = 'v';
  2124.         update_curbuf(NOT_VALID);        /* update the inversion */
  2125.     }
  2126.     else
  2127.     {
  2128.         curbuf->b_op_start = start;
  2129.         op_motion_type = MCHAR;
  2130.         op_inclusive = inclusive;
  2131.     }
  2132.     return OK;
  2133. }
  2134.  
  2135. /*
  2136.  * Find sentence under the cursor, cursor at end.
  2137.  */
  2138.     int
  2139. current_sent(count)
  2140.     long    count;
  2141. {
  2142.     FPOS    start;
  2143.     FPOS    pos;
  2144.     int        start_blank;
  2145.     int        c;
  2146.  
  2147.     pos = curwin->w_cursor;
  2148.     start = pos;
  2149.  
  2150.     /*
  2151.      * When visual area is bigger than one character: Extend it.
  2152.      */
  2153.     if (VIsual_active && !equal(curwin->w_cursor, VIsual))
  2154.     {
  2155.         if (lt(pos, VIsual))
  2156.         {
  2157.             /*
  2158.              * Do a "sentence backward" on the next character.
  2159.              * If we end up on the same position, we were already at the start
  2160.              * of a sentence
  2161.              */
  2162.             if (incl(&curwin->w_cursor) == -1)
  2163.                 return FAIL;
  2164.             findsent(BACKWARD, 1L);
  2165.             start = curwin->w_cursor;
  2166.             if (count > 1)
  2167.                 findsent(BACKWARD, count - 1);
  2168.             /*
  2169.              * When at start of sentence: Include blanks in front of sentence.
  2170.              * Use current_word() to cross line boundaries.
  2171.              * If we don't end up on a blank or on an empty line, go back to
  2172.              * the start of the previous sentence.
  2173.              */
  2174.             if (equal(pos, start))
  2175.             {
  2176.                 current_word(1L, 0);
  2177.                 c = gchar_cursor();
  2178.                 if (c != NUL && !vim_iswhite(c))
  2179.                     findsent(BACKWARD, 1L);
  2180.             }
  2181.  
  2182.         }
  2183.         else
  2184.         {
  2185.             /*
  2186.              * When one char before start of sentence: Don't include blanks in
  2187.              * front of next sentence.
  2188.              * Else go count sentences forward.
  2189.              */
  2190.             findsent(FORWARD, 1L);
  2191.             incl(&pos);
  2192.             if (equal(pos, curwin->w_cursor))
  2193.             {
  2194.                 findsent(FORWARD, count);
  2195.                 find_first_blank(&curwin->w_cursor);
  2196.             }
  2197.             else if (count > 1)
  2198.                 findsent(FORWARD, count - 1);
  2199.             decl(&curwin->w_cursor);
  2200.         }
  2201.         return OK;
  2202.     }
  2203.  
  2204.     /*
  2205.      * Find start of next sentence.
  2206.      */
  2207.     findsent(FORWARD, 1L);
  2208.  
  2209.     /*
  2210.      * If cursor started on blank, check if it is just before the start of the
  2211.      * next sentence.
  2212.      */
  2213.     while (vim_iswhite(gchar(&pos)))
  2214.         incl(&pos);
  2215.     if (equal(pos, curwin->w_cursor))
  2216.     {
  2217.         start_blank = TRUE;
  2218.         /*
  2219.          * go back to first blank
  2220.          */
  2221.         while (decl(&start) != -1)
  2222.         {
  2223.             if (!vim_iswhite(gchar(&start)))
  2224.             {
  2225.                 incl(&start);
  2226.                 break;
  2227.             }
  2228.         }
  2229.     }
  2230.     else
  2231.     {
  2232.         start_blank = FALSE;
  2233.         findsent(BACKWARD, 1L);
  2234.         start = curwin->w_cursor;
  2235.     }
  2236.     findsent(FORWARD, count);
  2237.  
  2238.     /*
  2239.      * If the blank in front of the sentence is included, exclude the blanks
  2240.      * at the end of the sentence, go back to the first blank.
  2241.      */
  2242.     if (start_blank)
  2243.         find_first_blank(&curwin->w_cursor);
  2244.     else
  2245.     {
  2246.         /*
  2247.          * If there are no trailing blanks, try to include leading blanks
  2248.          */
  2249.         pos = curwin->w_cursor;
  2250.         decl(&pos);
  2251.         if (!vim_iswhite(gchar(&pos)))
  2252.             find_first_blank(&start);
  2253.     }
  2254.  
  2255.     if (VIsual_active)
  2256.     {
  2257.         VIsual = start;
  2258.         VIsual_mode = 'v';
  2259.         decl(&curwin->w_cursor);        /* don't include the cursor char */
  2260.         update_curbuf(NOT_VALID);        /* update the inversion */
  2261.     }
  2262.     else
  2263.     {
  2264.         curbuf->b_op_start = start;
  2265.         op_motion_type = MCHAR;
  2266.         op_inclusive = FALSE;
  2267.     }
  2268.     return OK;
  2269. }
  2270.  
  2271.     int
  2272. current_block(what, count)
  2273.     int        what;            /* '(' or '{' */
  2274.     long    count;
  2275. {
  2276.     FPOS    old_pos;
  2277.     FPOS    *pos = NULL;
  2278.     FPOS    start_pos;
  2279.     FPOS    *end_pos;
  2280.     FPOS    old_start, old_end;
  2281.     int        inclusive = FALSE;
  2282.     int        other;
  2283.  
  2284.     old_pos = curwin->w_cursor;
  2285.     if (what == '{')
  2286.         other = '}';
  2287.     else
  2288.         other = ')';
  2289.  
  2290.     old_end = curwin->w_cursor;                /* remember where we started */
  2291.     old_start = old_end;
  2292.  
  2293.     /*
  2294.      * If we start on '(', '{', ')' or '}', use the whole block inclusive.
  2295.      */
  2296.     if (!VIsual_active || (VIsual.lnum == curwin->w_cursor.lnum &&
  2297.                                           VIsual.col == curwin->w_cursor.col))
  2298.     {
  2299.         if (what == '{')                    /* ignore indent */
  2300.             while (inindent(1))
  2301.                 if (inc_cursor() != 0)
  2302.                     break;
  2303.         if (gchar_cursor() == what)            /* cursor on '(' or '{' */
  2304.         {
  2305.             ++curwin->w_cursor.col;
  2306.             inclusive = TRUE;
  2307.         }
  2308.         else if (gchar_cursor() == other)    /* cursor on ')' or '}' */
  2309.             inclusive = TRUE;
  2310.     }
  2311.     else if (lt(VIsual, curwin->w_cursor))
  2312.     {
  2313.         old_start = VIsual;
  2314.         curwin->w_cursor = VIsual;            /* cursor at low end of Visual */
  2315.     }
  2316.     else
  2317.         old_end = VIsual;
  2318.  
  2319.     /*
  2320.      * Search backwards for unclosed '(' or '{'.
  2321.      * put this position in start_pos.
  2322.      */
  2323.     while (count--)
  2324.     {
  2325.         if ((pos = findmatch(what)) == NULL)
  2326.             break;
  2327.         curwin->w_cursor = *pos;
  2328.         start_pos = *pos;    /* the findmatch for end_pos will overwrite *pos */
  2329.     }
  2330.  
  2331.     /*
  2332.      * Search for matching ')' or '}'.
  2333.      * Put this position in curwin->w_cursor.
  2334.      */
  2335.     if (pos == NULL || (end_pos = findmatch(other)) == NULL)
  2336.     {
  2337.         curwin->w_cursor = old_pos;
  2338.         return FAIL;
  2339.     }
  2340.     curwin->w_cursor = *end_pos;
  2341.  
  2342.     /*
  2343.      * Try to exclude the '(', '{', ')' and '}'.
  2344.      * If the ending '}' is only preceded by indent, skip that indent.
  2345.      * But only if the resulting area is not smaller than what we started with.
  2346.      */
  2347.     if (!inclusive)
  2348.     {
  2349.         incl(&start_pos);
  2350.         old_pos = curwin->w_cursor;
  2351.         decl(&curwin->w_cursor);
  2352.         if (what == '{')
  2353.             while (inindent(1))
  2354.                 if (decl(&curwin->w_cursor) != 0)
  2355.                     break;
  2356.         if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor))
  2357.         {
  2358.             decl(&start_pos);
  2359.             curwin->w_cursor = old_pos;
  2360.         }
  2361.     }
  2362.  
  2363.     if (VIsual_active)
  2364.     {
  2365.         VIsual = start_pos;
  2366.         VIsual_mode = 'v';
  2367.         update_curbuf(NOT_VALID);        /* update the inversion */
  2368.         showmode();
  2369.     }
  2370.     else
  2371.     {
  2372.         curbuf->b_op_start = start_pos;
  2373.         op_motion_type = MCHAR;
  2374.         op_inclusive = TRUE;
  2375.     }
  2376.  
  2377.     return OK;
  2378. }
  2379.  
  2380.     int
  2381. current_par(type, count)
  2382.     int        type;            /* 'p' for paragraph, 'S' for section */
  2383.     long    count;
  2384. {
  2385.     linenr_t    start;
  2386.     linenr_t    end;
  2387.     int            white_in_front;
  2388.     int            dir;
  2389.     int            start_is_white;
  2390.     int            retval = OK;
  2391.  
  2392.     if (type == 'S')        /* not implemented yet */
  2393.         return FAIL;
  2394.     
  2395.     start = curwin->w_cursor.lnum;
  2396.  
  2397.     /*
  2398.      * When visual area is more than one line: extend it.
  2399.      */
  2400.     if (VIsual_active && start != VIsual.lnum)
  2401.     {
  2402.         if (start < VIsual.lnum)
  2403.             dir = BACKWARD;
  2404.         else
  2405.             dir = FORWARD;
  2406.         while (count--)
  2407.         {
  2408.             if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
  2409.                 retval = FAIL;
  2410.  
  2411.             start_is_white = -1;
  2412.             for (;;)
  2413.             {
  2414.                 if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
  2415.                     break;
  2416.                 if (start_is_white >= 0 &&
  2417.                                   (start_is_white != linewhite(start + dir) ||
  2418.                                     startPS(start + (dir > 0 ? 1 : 0), 0, 0)))
  2419.                     break;
  2420.                 start += dir;
  2421.                 if (start_is_white < 0)
  2422.                     start_is_white = linewhite(start);
  2423.             }
  2424.         }
  2425.         curwin->w_cursor.lnum = start;
  2426.         curwin->w_cursor.col = 0;
  2427.         return retval;
  2428.     }
  2429.  
  2430.     /*
  2431.      * First move back to the start of the paragraph or white lines
  2432.      */
  2433.     white_in_front = linewhite(start);
  2434.     while (start > 1)
  2435.     {
  2436.         if (white_in_front)            /* stop at first white line */
  2437.         {
  2438.             if (!linewhite(start - 1))
  2439.                 break;
  2440.         }
  2441.         else            /* stop at first non-white line of start of paragraph */
  2442.         {
  2443.             if (linewhite(start - 1) || startPS(start, 0, 0))
  2444.                 break;
  2445.         }
  2446.         --start;
  2447.     }
  2448.  
  2449.     /*
  2450.      * Move past the end of the white lines.
  2451.      */
  2452.     end = start;
  2453.     while (linewhite(end) && end < curbuf->b_ml.ml_line_count)
  2454.         ++end;
  2455.     
  2456.     --end;
  2457.     while (count--)
  2458.     {
  2459.         if (end == curbuf->b_ml.ml_line_count)
  2460.             return FAIL;
  2461.  
  2462.         ++end;
  2463.         /*
  2464.          * skip to end of paragraph
  2465.          */
  2466.         while (end < curbuf->b_ml.ml_line_count &&
  2467.                                !linewhite(end + 1) && !startPS(end + 1, 0, 0))
  2468.             ++end;
  2469.  
  2470.         if (count == 0 && white_in_front)
  2471.             break;
  2472.  
  2473.         /*
  2474.          * skip to end of white lines after paragraph
  2475.          */
  2476.         while (end < curbuf->b_ml.ml_line_count && linewhite(end + 1))
  2477.             ++end;
  2478.     }
  2479.  
  2480.     /*
  2481.      * If there are no empty lines at the end, try to find some empty lines at
  2482.      * the start (unless that has been done already).
  2483.      */
  2484.     if (!white_in_front && !linewhite(end))
  2485.         while (start > 1 && linewhite(start - 1))
  2486.             --start;
  2487.  
  2488.     if (VIsual_active)
  2489.     {
  2490.         VIsual.lnum = start;
  2491.         VIsual_mode = 'V';
  2492.         update_curbuf(NOT_VALID);        /* update the inversion */
  2493.         showmode();
  2494.     }
  2495.     else
  2496.     {
  2497.         curbuf->b_op_start.lnum = start;
  2498.         op_motion_type = MLINE;
  2499.     }
  2500.     curwin->w_cursor.lnum = end;
  2501.     curwin->w_cursor.col = 0;
  2502.  
  2503.     return OK;
  2504. }
  2505.  
  2506. /*
  2507.  * linewhite -- return TRUE if line 'lnum' is empty or has white chars only.
  2508.  */
  2509.     int
  2510. linewhite(lnum)
  2511.     linenr_t    lnum;
  2512. {
  2513.     char_u    *p;
  2514.  
  2515.     p = skipwhite(ml_get(lnum));
  2516.     return (*p == NUL);
  2517. }
  2518.  
  2519.     static void
  2520. find_first_blank(posp)
  2521.     FPOS    *posp;
  2522. {
  2523.     while (decl(posp) != -1)
  2524.     {
  2525.         if (!vim_iswhite(gchar(posp)))
  2526.         {
  2527.             incl(posp);
  2528.             break;
  2529.         }
  2530.     }
  2531. }
  2532.  
  2533.     void
  2534. find_pattern_in_path(ptr, len, whole, skip_comments,
  2535.                                     type, count, action, start_lnum, end_lnum)
  2536.     char_u    *ptr;            /* pointer to search pattern */
  2537.     int        len;            /* length of search pattern */
  2538.     int        whole;            /* match whole words only */
  2539.     int        skip_comments;    /* don't match inside comments */
  2540.     int        type;            /* Type of search; are we looking for a type?  a
  2541.                                 macro? */
  2542.     long    count;
  2543.     int        action;            /* What to do when we find it */
  2544.     linenr_t    start_lnum;    /* first line to start searching */
  2545.     linenr_t    end_lnum;    /* last line for searching */
  2546. {
  2547.     SearchedFile *files;                /* Stack of included files */
  2548.     SearchedFile *bigger;                /* When we need more space */
  2549.     int            max_path_depth = 50;
  2550.     long        match_count = 1;
  2551.  
  2552.     char_u        *pat;
  2553.     char_u        *new_fname;
  2554.     char_u        *curr_fname = curbuf->b_xfilename;
  2555.     char_u        *prev_fname = NULL;
  2556.     linenr_t    lnum;
  2557.     int            depth;
  2558.     int            depth_displayed;        /* For type==CHECK_PATH */
  2559.     int            old_files;
  2560.     int            already_searched;
  2561.     char_u        *file_line;
  2562.     char_u        *line;
  2563.     char_u        *p;
  2564.     char_u        *p2 = NUL;                /* Init for gcc */
  2565.     char_u        save_char = NUL;
  2566.     int            define_matched;
  2567.     struct regexp *prog = NULL;
  2568.     struct regexp *include_prog = NULL;
  2569.     struct regexp *define_prog = NULL;
  2570.     int            matched = FALSE;
  2571.     int            did_show = FALSE;
  2572.     int            found = FALSE;
  2573.     int            i;
  2574.  
  2575.     file_line = alloc(LSIZE);
  2576.     if (file_line == NULL)
  2577.         return;
  2578.  
  2579.     reg_magic = p_magic;
  2580.     if (type != CHECK_PATH)
  2581.     {
  2582.         pat = alloc(len + 5);
  2583.         if (pat == NULL)
  2584.             goto fpip_end;
  2585.         sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
  2586.         set_reg_ic(pat);    /* set reg_ic according to p_ic, p_scs and pat */
  2587.         prog = vim_regcomp(pat);
  2588.         vim_free(pat);
  2589.         if (prog == NULL)
  2590.             goto fpip_end;
  2591.     }
  2592.     reg_ic = FALSE;        /* don't ignore case in include and define patterns */
  2593.     if (*p_inc != NUL)
  2594.     {
  2595.         include_prog = vim_regcomp(p_inc);
  2596.         if (include_prog == NULL)
  2597.             goto fpip_end;
  2598.     }
  2599.     if (type == FIND_DEFINE && *p_def != NUL)
  2600.     {
  2601.         define_prog = vim_regcomp(p_def);
  2602.         if (define_prog == NULL)
  2603.             goto fpip_end;
  2604.     }
  2605.     files = (SearchedFile *)lalloc(max_path_depth * sizeof(SearchedFile), TRUE);
  2606.     if (files == NULL)
  2607.         goto fpip_end;
  2608.     for (i = 0; i < max_path_depth; i++)
  2609.     {
  2610.         files[i].fp = NULL;
  2611.         files[i].name = NULL;
  2612.         files[i].lnum = 0;
  2613.         files[i].matched = FALSE;
  2614.     }
  2615.     old_files = max_path_depth;
  2616.     depth = depth_displayed = -1;
  2617.  
  2618.     lnum = start_lnum;
  2619.     if (end_lnum > curbuf->b_ml.ml_line_count)
  2620.         end_lnum = curbuf->b_ml.ml_line_count;
  2621.     if (lnum > end_lnum)                /* do at least one line */
  2622.         lnum = end_lnum;
  2623.     line = ml_get(lnum);
  2624.  
  2625.     for (;;)
  2626.     {
  2627.         if (include_prog != NULL && vim_regexec(include_prog, line, TRUE))
  2628.         {
  2629.             new_fname = get_file_name_in_path(include_prog->endp[0] + 1,
  2630.                                                                 0, FNAME_EXP);
  2631.             already_searched = FALSE;
  2632.             if (new_fname != NULL)
  2633.             {
  2634.                 /* Check whether we have already searched in this file */
  2635.                 for (i = 0;; i++)
  2636.                 {
  2637.                     if (i == depth + 1)
  2638.                         i = old_files;
  2639.                     if (i == max_path_depth)
  2640.                         break;
  2641.                     if (STRCMP(new_fname, files[i].name) == 0)
  2642.                     {
  2643.                         if (type != CHECK_PATH &&
  2644.                                 action == ACTION_SHOW_ALL && files[i].matched)
  2645.                         {
  2646.                             msg_outchar('\n');        /* cursor below last one */
  2647.                             if (!got_int)            /* don't display if 'q'
  2648.                                                        typed at "--more--"
  2649.                                                        mesage */
  2650.                             {
  2651.                                 set_highlight('d');    /* Same as for dirs */
  2652.                                 start_highlight();
  2653.                                 msg_home_replace(new_fname);
  2654.                                 stop_highlight();
  2655.                                 MSG_OUTSTR(" (includes previously listed match)");
  2656.                                 prev_fname = NULL;
  2657.                             }
  2658.                         }
  2659.                         vim_free(new_fname);
  2660.                         new_fname = NULL;
  2661.                         already_searched = TRUE;
  2662.                         break;
  2663.                     }
  2664.                 }
  2665.             }
  2666.  
  2667.             if (type == CHECK_PATH && (action == ACTION_SHOW_ALL ||
  2668.                                     (new_fname == NULL && !already_searched)))
  2669.             {
  2670.                 if (did_show)
  2671.                     msg_outchar('\n');        /* cursor below last one */
  2672.                 else
  2673.                 {
  2674.                     gotocmdline(TRUE);        /* cursor at status line */
  2675.                     set_highlight('t');        /* Highlight title */
  2676.                     start_highlight();
  2677.                     MSG_OUTSTR("--- Included files ");
  2678.                     if (action != ACTION_SHOW_ALL)
  2679.                         MSG_OUTSTR("not found ");
  2680.                     MSG_OUTSTR("in path ---\n");
  2681.                     stop_highlight();
  2682.                 }
  2683.                 did_show = TRUE;
  2684.                 while (depth_displayed < depth && !got_int)
  2685.                 {
  2686.                     ++depth_displayed;
  2687.                     for (i = 0; i < depth_displayed; i++)
  2688.                         MSG_OUTSTR("  ");
  2689.                     msg_home_replace(files[depth_displayed].name);
  2690.                     MSG_OUTSTR(" -->\n");
  2691.                 }
  2692.                 if (!got_int)                /* don't display if 'q' typed
  2693.                                                for "--more--" message */
  2694.                 {
  2695.                     for (i = 0; i <= depth_displayed; i++)
  2696.                         MSG_OUTSTR("  ");
  2697.                     set_highlight('d');        /* Same as for directories */
  2698.                     start_highlight();
  2699.                     /*
  2700.                      * Isolate the file name.
  2701.                      * Include the surrounding "" or <> if present.
  2702.                      */
  2703.                     for (p = include_prog->endp[0] + 1; !isfilechar(*p); p++)
  2704.                         ;
  2705.                     for (i = 0; isfilechar(p[i]); i++)
  2706.                         ;
  2707.                     if (p[-1] == '"' || p[-1] == '<')
  2708.                     {
  2709.                         --p;
  2710.                         ++i;
  2711.                     }
  2712.                     if (p[i] == '"' || p[i] == '>')
  2713.                         ++i;
  2714.                     save_char = p[i];
  2715.                     p[i] = NUL;
  2716.                     msg_outstr(p);
  2717.                     p[i] = save_char;
  2718.                     stop_highlight();
  2719.                     if (new_fname == NULL && action == ACTION_SHOW_ALL)
  2720.                     {
  2721.                         if (already_searched)
  2722.                             MSG_OUTSTR("  (Already listed)");
  2723.                         else
  2724.                             MSG_OUTSTR("  NOT FOUND");
  2725.                     }
  2726.                 }
  2727.                 flushbuf();            /* output each line directly */
  2728.             }
  2729.  
  2730.             if (new_fname != NULL)
  2731.             {
  2732.                 /* Push the new file onto the file stack */
  2733.                 if (depth + 1 == old_files)
  2734.                 {
  2735.                     bigger = (SearchedFile *)lalloc(max_path_depth * 2
  2736.                                                 * sizeof(SearchedFile), TRUE);
  2737.                     if (bigger != NULL)
  2738.                     {
  2739.                         for (i = 0; i <= depth; i++)
  2740.                             bigger[i] = files[i];
  2741.                         for (i = depth + 1; i < old_files + max_path_depth; i++)
  2742.                         {
  2743.                             bigger[i].fp = NULL;
  2744.                             bigger[i].name = NULL;
  2745.                             bigger[i].lnum = 0;
  2746.                             bigger[i].matched = FALSE;
  2747.                         }
  2748.                         for (i = old_files; i < max_path_depth; i++)
  2749.                             bigger[i + max_path_depth] = files[i];
  2750.                         old_files += max_path_depth;
  2751.                         max_path_depth *= 2;
  2752.                         vim_free(files);
  2753.                         files = bigger;
  2754.                     }
  2755.                 }
  2756.                 if ((files[depth + 1].fp = fopen((char *)new_fname, "r"))
  2757.                                                                     == NULL)
  2758.                     vim_free(new_fname);
  2759.                 else
  2760.                 {
  2761.                     if (++depth == old_files)
  2762.                     {
  2763.                         /*
  2764.                          * lalloc() for 'bigger' must have failed above.  We
  2765.                          * will forget one of our already visited files now.
  2766.                          */
  2767.                         vim_free(files[old_files].name);
  2768.                         ++old_files;
  2769.                     }
  2770.                     files[depth].name = curr_fname = new_fname;
  2771.                     files[depth].lnum = 0;
  2772.                     files[depth].matched = FALSE;
  2773.                 }
  2774.             }
  2775.         }
  2776.         else
  2777.         {
  2778.             /*
  2779.              * Check if the line is a define (type == FIND_DEFINE)
  2780.              */
  2781.             p = line;
  2782.             define_matched = FALSE;
  2783.             if (define_prog != NULL && vim_regexec(define_prog, line, TRUE))
  2784.             {
  2785.                 /*
  2786.                  * Pattern must be first identifier after 'define', so skip
  2787.                  * to that position before checking for match of pattern.  Also
  2788.                  * don't let it match beyond the end of this identifier.
  2789.                  */
  2790.                 p = define_prog->endp[0] + 1;
  2791.                 while (*p && !isidchar(*p))
  2792.                     p++;
  2793.                 p2 = p;
  2794.                 while (*p2 && isidchar(*p2))
  2795.                     p2++;
  2796.                 save_char = *p2;
  2797.                 *p2 = NUL;
  2798.                 define_matched = TRUE;
  2799.             }
  2800.  
  2801.             /*
  2802.              * Look for a match.  Don't do this if we are looking for a
  2803.              * define and this line didn't match define_prog above.
  2804.              */
  2805.             if ((define_prog == NULL || define_matched) &&
  2806.                               prog != NULL && vim_regexec(prog, p, p == line))
  2807.             {
  2808.                 matched = TRUE;
  2809.                 /*
  2810.                  * Check if the line is not a comment line (unless we are
  2811.                  * looking for a define).  A line starting with "# define" is
  2812.                  * not considered to be a comment line.
  2813.                  */
  2814.                 if (!define_matched && skip_comments)
  2815.                 {
  2816.                     fo_do_comments = TRUE;
  2817.                     if ((*line != '#' ||
  2818.                             STRNCMP(skipwhite(line + 1), "define", 6) != 0) &&
  2819.                                                    get_leader_len(line, NULL))
  2820.                         matched = FALSE;
  2821.  
  2822.                     /*
  2823.                      * Also check for a "/ *" or "/ /" before the match.
  2824.                      * Skips lines like "int idx;  / * normal index * /" when
  2825.                      * looking for "normal".
  2826.                      */
  2827.                     else
  2828.                         for (p = line; *p && p < prog->startp[0]; ++p)
  2829.                             if (p[0] == '/' && (p[1] == '*' || p[1] == '/'))
  2830.                             {
  2831.                                 matched = FALSE;
  2832.                                 break;
  2833.                             }
  2834.                     fo_do_comments = FALSE;
  2835.                 }
  2836.             }
  2837.             if (define_matched)
  2838.                 *p2 = save_char;
  2839.         }
  2840.         if (matched)
  2841.         {
  2842. #ifdef INSERT_EXPAND
  2843.             if (action == ACTION_EXPAND)
  2844.             {
  2845.                 if (depth == -1 && lnum == curwin->w_cursor.lnum)
  2846.                     break;
  2847.                 found = TRUE;
  2848.                 p = prog->startp[0];
  2849.                 while (iswordchar(*p))
  2850.                     ++p;
  2851.                 if (add_completion_and_infercase(prog->startp[0],
  2852.                                                    (int)(p - prog->startp[0]),
  2853.                         curr_fname == curbuf->b_xfilename ? NULL : curr_fname,
  2854.                                                         FORWARD) == RET_ERROR)
  2855.                     break;
  2856.             }
  2857.             else
  2858. #endif
  2859.                  if (action == ACTION_SHOW_ALL)
  2860.             {
  2861.                 found = TRUE;
  2862.                 if (!did_show)
  2863.                     gotocmdline(TRUE);            /* cursor at status line */
  2864.                 if (curr_fname != prev_fname)
  2865.                 {
  2866.                     if (did_show)
  2867.                         msg_outchar('\n');        /* cursor below last one */
  2868.                     if (!got_int)                /* don't display if 'q' typed
  2869.                                                     at "--more--" mesage */
  2870.                     {
  2871.                         set_highlight('d');        /* Same as for directories */
  2872.                         start_highlight();
  2873.                         msg_home_replace(curr_fname);
  2874.                         stop_highlight();
  2875.                     }
  2876.                     prev_fname = curr_fname;
  2877.                 }
  2878.                 did_show = TRUE;
  2879.                 if (!got_int)
  2880.                     show_pat_in_path(line, type, TRUE, action,
  2881.                             (depth == -1) ? NULL : files[depth].fp,
  2882.                             (depth == -1) ? &lnum : &files[depth].lnum,
  2883.                             match_count++);
  2884.  
  2885.                 /* Set matched flag for this file and all the ones that
  2886.                  * include it */
  2887.                 for (i = 0; i <= depth; ++i)
  2888.                     files[i].matched = TRUE;
  2889.             }
  2890.             else if (--count <= 0)
  2891.             {
  2892.                 found = TRUE;
  2893.                 if (depth == -1 && lnum == curwin->w_cursor.lnum)
  2894.                     EMSG("Match is on current line");
  2895.                 else if (action == ACTION_SHOW)
  2896.                 {
  2897.                     show_pat_in_path(line, type, did_show, action,
  2898.                         (depth == -1) ? NULL : files[depth].fp,
  2899.                         (depth == -1) ? &lnum : &files[depth].lnum, 1L);
  2900.                     did_show = TRUE;
  2901.                 }
  2902.                 else
  2903.                 {
  2904.                     if (action == ACTION_SPLIT)
  2905.                     {
  2906.                         if (win_split(0, FALSE) == FAIL)
  2907.                             break;
  2908.                     }
  2909.                     if (depth == -1)
  2910.                     {
  2911.                         setpcmark();
  2912.                         curwin->w_cursor.lnum = lnum;
  2913.                     }
  2914.                     else
  2915.                         if (getfile(0, files[depth].name, NULL, TRUE,
  2916.                                                 files[depth].lnum, FALSE) > 0)
  2917.                             break;        /* failed to jump to file */
  2918.                 }
  2919.                 if (action != ACTION_SHOW)
  2920.                 {
  2921.                     curwin->w_cursor.col = prog->startp[0] - line;
  2922.                     curwin->w_set_curswant = TRUE;
  2923.                 }
  2924.                 break;
  2925.             }
  2926.             matched = FALSE;
  2927.         }
  2928.         line_breakcheck();
  2929.         if (got_int)
  2930.             break;
  2931.         while (depth >= 0)
  2932.         {
  2933.             if (!vim_fgets(line = file_line, LSIZE, files[depth].fp))
  2934.             {
  2935.                 ++files[depth].lnum;
  2936.                 break;
  2937.             }
  2938.             fclose(files[depth].fp);
  2939.             --old_files;
  2940.             files[old_files].name = files[depth].name;
  2941.             files[old_files].matched = files[depth].matched;
  2942.             --depth;
  2943.             curr_fname = (depth == -1) ? curbuf->b_xfilename
  2944.                                        : files[depth].name;
  2945.             if (depth < depth_displayed)
  2946.                 depth_displayed = depth;
  2947.         }
  2948.         if (depth < 0)
  2949.         {
  2950.             if (++lnum > end_lnum)
  2951.                 break;
  2952.             line = ml_get(lnum);
  2953.         }
  2954.     }
  2955.     for (i = 0; i <= depth; i++)
  2956.     {
  2957.         fclose(files[i].fp);
  2958.         vim_free(files[i].name);
  2959.     }
  2960.     for (i = old_files; i < max_path_depth; i++)
  2961.         vim_free(files[i].name);
  2962.     vim_free(files);
  2963.  
  2964.     if (type == CHECK_PATH)
  2965.     {
  2966.         if (!did_show)
  2967.         {
  2968.             if (action != ACTION_SHOW_ALL)
  2969.                 MSG("All included files were found");
  2970.             else
  2971.                 MSG("No included files");
  2972.         }
  2973.     }
  2974.     else if (!found
  2975. #ifdef INSERT_EXPAND
  2976.                     && action != ACTION_EXPAND
  2977. #endif
  2978.                                                 )
  2979.     {
  2980.         if (got_int)
  2981.             emsg(e_interr);
  2982.         else if (type == FIND_DEFINE)
  2983.             EMSG("Couldn't find definition");
  2984.         else
  2985.             EMSG("Couldn't find pattern");
  2986.     }
  2987.     if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
  2988.         msg_end();
  2989.  
  2990. fpip_end:
  2991.     vim_free(file_line);
  2992.     vim_free(prog);
  2993.     vim_free(include_prog);
  2994.     vim_free(define_prog);
  2995. }
  2996.  
  2997.     static void
  2998. show_pat_in_path(line, type, did_show, action, fp, lnum, count)
  2999.     char_u    *line;
  3000.     int        type;
  3001.     int        did_show;
  3002.     int        action;
  3003.     FILE    *fp;
  3004.     linenr_t *lnum;
  3005.     long    count;
  3006. {
  3007.     char_u    *p;
  3008.  
  3009.     if (did_show)
  3010.         msg_outchar('\n');        /* cursor below last one */
  3011.     else
  3012.         gotocmdline(TRUE);        /* cursor at status line */
  3013.     if (got_int)                /* 'q' typed at "--more--" message */
  3014.         return;
  3015.     for (;;)
  3016.     {
  3017.         p = line + STRLEN(line) - 1;
  3018.         if (fp != NULL)
  3019.         {
  3020.             /* We used fgets(), so get rid of newline at end */
  3021.             if (p >= line && *p == '\n')
  3022.                 --p;
  3023.             if (p >= line && *p == '\r')
  3024.                 --p;
  3025.             *(p + 1) = NUL;
  3026.         }
  3027.         if (action == ACTION_SHOW_ALL)
  3028.         {
  3029.             sprintf((char *)IObuff, "%3ld: ", count);    /* show match nr */
  3030.             msg_outstr(IObuff);
  3031.             set_highlight('n');                    /* Highlight line numbers */
  3032.             start_highlight();
  3033.             sprintf((char *)IObuff, "%4ld", *lnum);        /* show line nr */
  3034.             msg_outstr(IObuff);
  3035.             stop_highlight();
  3036.             MSG_OUTSTR(" ");
  3037.         }
  3038.         msg_prt_line(line);
  3039.         flushbuf();                        /* show one line at a time */
  3040.  
  3041.         /* Definition continues until line that doesn't end with '\' */
  3042.         if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
  3043.             break;
  3044.         
  3045.         if (fp != NULL)
  3046.         {
  3047.             if (vim_fgets(line, LSIZE, fp))    /* end of file */
  3048.                 break;
  3049.             ++*lnum;
  3050.         }
  3051.         else
  3052.         {
  3053.             if (++*lnum > curbuf->b_ml.ml_line_count)
  3054.                 break;
  3055.             line = ml_get(*lnum);
  3056.         }
  3057.         msg_outchar('\n');
  3058.     }
  3059. }
  3060.  
  3061. #ifdef VIMINFO
  3062.     int
  3063. read_viminfo_search_pattern(line, fp, force)
  3064.     char_u    *line;
  3065.     FILE    *fp;
  3066.     int        force;
  3067. {
  3068.     char_u    *lp;
  3069.     char_u    **pattern;
  3070.  
  3071.     lp = line;
  3072.     if (lp[0] == '~')
  3073.         lp++;
  3074.     if (lp[0] == '/')
  3075.         pattern = &search_pattern;
  3076.     else
  3077.         pattern = &subst_pattern;
  3078.     if (*pattern != NULL && force)
  3079.         vim_free(*pattern);
  3080.     if (force || *pattern == NULL)
  3081.     {
  3082.         viminfo_readstring(lp);
  3083.         *pattern = strsave(lp + 1);
  3084.         if (line[0] == '~')
  3085.             last_pattern = *pattern;
  3086.     }
  3087.     return vim_fgets(line, LSIZE, fp);
  3088. }
  3089.  
  3090.     void
  3091. write_viminfo_search_pattern(fp)
  3092.     FILE    *fp;
  3093. {
  3094.     if (get_viminfo_parameter('/') != 0)
  3095.     {
  3096.         if (search_pattern != NULL)
  3097.         {
  3098.             fprintf(fp, "\n# Last Search Pattern:\n");
  3099.             fprintf(fp, "%s/", (last_pattern == search_pattern) ? "~" : "");
  3100.             viminfo_writestring(fp, search_pattern);
  3101.         }
  3102.         if (subst_pattern != NULL)
  3103.         {
  3104.             fprintf(fp, "\n# Last Substitute Search Pattern:\n");
  3105.             fprintf(fp, "%s&", (last_pattern == subst_pattern) ? "~" : "");
  3106.             viminfo_writestring(fp, subst_pattern);
  3107.         }
  3108.     }
  3109. }
  3110. #endif /* VIMINFO */
  3111.  
  3112. /*
  3113.  * Give a warning message.
  3114.  * Use 'w' highlighting and may repeat the message after redrawing
  3115.  */
  3116.     static void
  3117. give_warning(message)
  3118.     char_u    *message;
  3119. {
  3120.     (void)set_highlight('w');
  3121.     msg_highlight = TRUE;
  3122.     if (msg(message) && !msg_scroll)
  3123.     {
  3124.         keep_msg = message;
  3125.         keep_msg_highlight = 'w';
  3126.     }
  3127.     msg_didout = FALSE;        /* overwrite this message */
  3128.     msg_col = 0;
  3129. }
  3130.