[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.1.1.1

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