search.c
上传用户:gddssl
上传日期:2007-01-06
资源大小:1003k
文件大小:87k
源码类别:

编辑器/阅读器

开发平台:

DOS

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