[BACK]Return to search.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

Annotation of src/usr.bin/vim/search.c, Revision 1.2

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