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

Annotation of src/usr.bin/vim/tag.c, Revision 1.3

1.3     ! downsj      1: /* $OpenBSD: tag.c,v 1.2 1996/09/21 06:23:21 downsj Exp $  */
1.1       downsj      2: /* vi:set ts=4 sw=4:
                      3:  *
                      4:  * VIM - Vi IMproved       by Bram Moolenaar
                      5:  *
                      6:  * Do ":help uganda"  in Vim to read copying and usage conditions.
                      7:  * Do ":help credits" in Vim to see a list of people who contributed.
                      8:  */
                      9:
                     10: /*
                     11:  * Code to handle tags and the tag stack
                     12:  */
                     13:
                     14: #include "vim.h"
                     15: #include "globals.h"
                     16: #include "proto.h"
                     17: #include "option.h"
                     18:
                     19: static int get_tagfname __ARGS((int, char_u    *));
                     20:
                     21: #ifdef EMACS_TAGS
                     22: static int parse_tag_line __ARGS((char_u *, int,
                     23:                      char_u **, char_u **, char_u **, char_u **, char_u **));
1.2       downsj     24: static int jumpto_tag __ARGS((char_u *, int, char_u *, char_u *, int));
1.1       downsj     25: #else
                     26: static int parse_tag_line __ARGS((char_u *,
                     27:                      char_u **, char_u **, char_u **, char_u **, char_u **));
1.2       downsj     28: static int jumpto_tag __ARGS((char_u *, char_u *, int));
1.1       downsj     29: #endif
                     30: static int test_for_static __ARGS((char_u **, char_u *, char_u *, char_u *));
                     31: static char_u *expand_rel_name __ARGS((char_u *fname, char_u *tag_fname));
                     32: static void simplify_filename __ARGS((char_u *filename));
                     33: #ifdef EMACS_TAGS
                     34: static int test_for_current __ARGS((int, char_u *, char_u *, char_u *));
                     35: #else
                     36: static int test_for_current __ARGS((char_u *, char_u *, char_u *));
                     37: #endif
                     38:
                     39: static char_u *bottommsg = (char_u *)"at bottom of tag stack";
                     40: static char_u *topmsg = (char_u *)"at top of tag stack";
                     41:
                     42: /*
                     43:  * Jump to tag; handling of tag stack
                     44:  *
                     45:  * *tag != NUL (:tag): jump to new tag, add to tag stack
                     46:  * type == 1 (:pop) || type == 2 (CTRL-T): jump to old position
                     47:  * type == 0 (:tag): jump to old tag
                     48:  */
                     49:    void
1.2       downsj     50: do_tag(tag, type, count, forceit)
1.1       downsj     51:    char_u  *tag;
                     52:    int     type;
                     53:    int     count;
1.2       downsj     54:    int     forceit;        /* :ta with ! */
1.1       downsj     55: {
                     56:    int             i;
                     57:    struct taggy    *tagstack = curwin->w_tagstack;
                     58:    int             tagstackidx = curwin->w_tagstackidx;
                     59:    int             tagstacklen = curwin->w_tagstacklen;
                     60:    int             oldtagstackidx = tagstackidx;
                     61:
                     62:    if (*tag != NUL)                        /* new pattern, add to the stack */
                     63:    {
                     64:        /*
                     65:         * if last used entry is not at the top, delete all tag stack entries
                     66:         * above it.
                     67:         */
                     68:        while (tagstackidx < tagstacklen)
                     69:            vim_free(tagstack[--tagstacklen].tagname);
                     70:
                     71:                /* if tagstack is full: remove oldest entry */
                     72:        if (++tagstacklen > TAGSTACKSIZE)
                     73:        {
                     74:            tagstacklen = TAGSTACKSIZE;
                     75:            vim_free(tagstack[0].tagname);
                     76:            for (i = 1; i < tagstacklen; ++i)
                     77:                tagstack[i - 1] = tagstack[i];
                     78:            --tagstackidx;
                     79:        }
                     80:
                     81:        /*
                     82:         * put the tag name in the tag stack
                     83:         * the position is added below
                     84:         */
                     85:        tagstack[tagstackidx].tagname = strsave(tag);
                     86:    }
                     87:    else if (tagstacklen == 0)                  /* empty stack */
                     88:    {
                     89:        EMSG("tag stack empty");
                     90:        goto end_do_tag;
                     91:    }
                     92:    else if (type)                              /* go to older position */
                     93:    {
                     94:        if ((tagstackidx -= count) < 0)
                     95:        {
                     96:            emsg(bottommsg);
                     97:            if (tagstackidx + count == 0)
                     98:            {
                     99:                /* We did ^T (or <num>^T) from the bottom of the stack */
                    100:                tagstackidx = 0;
                    101:                goto end_do_tag;
                    102:            }
                    103:            /* We weren't at the bottom of the stack, so jump all the way to
                    104:             * the bottom.
                    105:             */
                    106:            tagstackidx = 0;
                    107:        }
                    108:        else if (tagstackidx >= tagstacklen)    /* must have been count == 0 */
                    109:        {
                    110:            emsg(topmsg);
                    111:            goto end_do_tag;
                    112:        }
                    113:        if (tagstack[tagstackidx].fmark.fnum != curbuf->b_fnum)
                    114:        {
                    115:            /*
                    116:             * Jump to other file. If this fails (e.g. because the file was
                    117:             * changed) keep original position in tag stack.
                    118:             */
                    119:            if (buflist_getfile(tagstack[tagstackidx].fmark.fnum,
1.2       downsj    120:                    tagstack[tagstackidx].fmark.mark.lnum,
                    121:                    GETF_SETMARK, forceit) == FAIL)
1.1       downsj    122:            {
                    123:                tagstackidx = oldtagstackidx;   /* back to old position */
                    124:                goto end_do_tag;
                    125:            }
                    126:        }
                    127:        else
                    128:            curwin->w_cursor.lnum = tagstack[tagstackidx].fmark.mark.lnum;
                    129:        curwin->w_cursor.col = tagstack[tagstackidx].fmark.mark.col;
                    130:        curwin->w_set_curswant = TRUE;
                    131:        goto end_do_tag;
                    132:    }
                    133:    else                                    /* go to newer pattern */
                    134:    {
                    135:        if ((tagstackidx += count - 1) >= tagstacklen)
                    136:        {
                    137:            /*
                    138:             * beyond the last one, just give an error message and go to the
                    139:             * last one
                    140:             */
                    141:            tagstackidx = tagstacklen - 1;
                    142:            emsg(topmsg);
                    143:        }
                    144:        else if (tagstackidx < 0)           /* must have been count == 0 */
                    145:        {
                    146:            emsg(bottommsg);
                    147:            tagstackidx = 0;
                    148:            goto end_do_tag;
                    149:        }
                    150:    }
                    151:    /*
                    152:     * For :tag [arg], remember position before the jump
                    153:     */
                    154:    if (type == 0)
                    155:    {
                    156:        tagstack[tagstackidx].fmark.mark = curwin->w_cursor;
                    157:        tagstack[tagstackidx].fmark.fnum = curbuf->b_fnum;
                    158:    }
                    159:    /* curwin will change in the call to find_tags() if ^W^] was used -- webb */
                    160:    curwin->w_tagstackidx = tagstackidx;
                    161:    curwin->w_tagstacklen = tagstacklen;
1.2       downsj    162:    if (find_tags(tagstack[tagstackidx].tagname, NULL, NULL, NULL,
                    163:                                                          FALSE, forceit) > 0)
1.1       downsj    164:        ++tagstackidx;
                    165:
                    166: end_do_tag:
                    167:    curwin->w_tagstackidx = tagstackidx;
                    168:    curwin->w_tagstacklen = tagstacklen;
                    169: }
                    170:
                    171: /*
                    172:  * Print the tag stack
                    173:  */
                    174:    void
                    175: do_tags()
                    176: {
                    177:    int             i;
                    178:    char_u          *name;
                    179:    struct taggy    *tagstack = curwin->w_tagstack;
                    180:    int             tagstackidx = curwin->w_tagstackidx;
                    181:    int             tagstacklen = curwin->w_tagstacklen;
                    182:
                    183:    set_highlight('t');     /* Highlight title */
                    184:    start_highlight();
                    185:    MSG_OUTSTR("\n  # TO tag      FROM line in file");
                    186:    stop_highlight();
                    187:    for (i = 0; i < tagstacklen; ++i)
                    188:    {
                    189:        if (tagstack[i].tagname != NULL)
                    190:        {
                    191:            name = fm_getname(&(tagstack[i].fmark));
                    192:            if (name == NULL)       /* file name not available */
                    193:                continue;
                    194:
                    195:            msg_outchar('\n');
                    196:            sprintf((char *)IObuff, "%c%2d %-15s %4ld  %s",
                    197:                i == tagstackidx ? '>' : ' ',
                    198:                i + 1,
                    199:                tagstack[i].tagname,
                    200:                tagstack[i].fmark.mark.lnum,
                    201:                name);
                    202:            msg_outtrans(IObuff);
                    203:        }
                    204:        flushbuf();                 /* show one line at a time */
                    205:    }
                    206:    if (tagstackidx == tagstacklen)     /* idx at top of stack */
                    207:        MSG_OUTSTR("\n>");
                    208: }
                    209:
                    210: /*
                    211:  * find_tags() - goto tag or search for tags in tags files
                    212:  *
                    213:  * If "tag" is not NULL, search for a single tag and jump to it.
                    214:  *   return FAIL for failure, OK for success
                    215:  * If "tag" is NULL, find all tags matching the regexp given with 'prog'.
                    216:  *   return FAIL if search completely failed, OK otherwise.
                    217:  *
                    218:  * There is a priority in which type of tag is recognized. It is computed
                    219:  * from the PRI_ defines below.
                    220:  *
                    221:  *  6.  A static or global tag with a full matching tag for the current file.
                    222:  *  5.  A global tag with a full matching tag for another file.
                    223:  *  4.  A static tag with a full matching tag for another file.
                    224:  *  2.  A static or global tag with an ignore-case matching tag for the
                    225:  *     current file.
                    226:  *  1.  A global tag with an ignore-case matching tag for another file.
                    227:  *  0.  A static tag with an ignore-case matching tag for another file.
                    228:  *
                    229:  *  Tags in an emacs-style tags file are always global.
                    230:  */
                    231: #define PRI_GLOBAL         1       /* global or emacs tag */
                    232: #define PRI_CURRENT            2       /* tag for current file */
                    233: #define PRI_FULL_MATCH     4       /* case of tag matches */
                    234:
                    235:    int
1.2       downsj    236: find_tags(tag, prog, num_file, file, help_only, forceit)
1.1       downsj    237:    char_u      *tag;               /* NULL or tag to search for */
                    238:    regexp      *prog;              /* regexp program or NULL */
                    239:    int         *num_file;          /* return value: number of matches found */
                    240:    char_u      ***file;            /* return value: array of matches found */
                    241:    int         help_only;          /* if TRUE: only tags for help files */
1.2       downsj    242:    int         forceit;            /* :ta with ! */
1.1       downsj    243: {
                    244:    FILE       *fp;
                    245:    char_u     *lbuf;                   /* line buffer */
                    246:    char_u     *tag_fname;              /* name of tag file */
                    247:    int         first_file;             /* trying first tag file */
                    248:    char_u     *tagname, *tagname_end;  /* name of tag in current line */
                    249:    char_u     *fname, *fname_end;      /* fname in current line */
                    250:    int         did_open = FALSE;       /* did open a tag file */
                    251:    int         stop_searching = FALSE; /* stop when match found or error */
                    252:    int         retval = FAIL;          /* return value */
                    253:    int         is_static;              /* current tag line is static */
                    254:    int         is_current;             /* file name matches */
                    255:    register char_u *p;
                    256: #ifdef EMACS_TAGS
                    257:    char_u     *ebuf;                   /* aditional buffer for etag fname */
                    258:    int         is_etag;                /* current file is emaces style */
                    259: #endif
                    260:
                    261: /*
                    262:  * Variables used only when "tag" != NULL
                    263:  */
                    264:    int         taglen = 0;             /* init for GCC */
                    265:    int         cmplen;
                    266:    int         full_match;
                    267:    int         icase_match;
                    268:    int         priority;               /* priority of current line */
                    269:
                    270:    char_u      *bestmatch_line = NULL; /* saved line for best match found so
                    271:                                            far */
                    272:    char_u      *bestmatch_tag_fname = NULL;
                    273:                                        /* copy of tag_fname for best match */
                    274:    int         bestmatch_priority = 0; /* best match priority */
                    275:
                    276: #ifdef EMACS_TAGS
                    277:    /*
                    278:     * Stack for included emacs-tags file.
                    279:     * It has a fixed size, to truncate cyclic includes. jw
                    280:     */
                    281: # define INCSTACK_SIZE 42
                    282:    struct
                    283:    {
                    284:        FILE    *fp;
                    285:        char_u  *tag_fname;
                    286:    } incstack[INCSTACK_SIZE];
                    287:
                    288:    int         incstack_idx = 0;           /* index in incstack */
                    289:    char_u     *bestmatch_ebuf = NULL;      /* copy of ebuf for best match */
                    290:    int         bestmatch_is_etag = FALSE;  /* copy of is_etag for best match */
                    291: #endif
                    292:
                    293: /*
                    294:  * Variables used when "tag" == NULL
                    295:  */
                    296:    char_u  **matches = NULL;           /* array of matches found */
                    297:    char_u  **new_matches;
                    298:    int     match_limit = 100;          /* max. number of matches stored */
                    299:    int     match_count = 0;            /* number of matches found */
                    300:    int     i;
                    301:    int     help_save;
                    302:
                    303:    help_save = curbuf->b_help;
                    304: /*
                    305:  * Allocate memory for the buffers that are used
                    306:  */
                    307:    lbuf = alloc(LSIZE);
                    308: #ifdef EMACS_TAGS
                    309:    ebuf = alloc(LSIZE);
                    310: #endif
                    311:    tag_fname = alloc(LSIZE + 1);
                    312:                                    /* check for out of memory situation */
                    313:    if ((tag == NULL && prog == NULL) || lbuf == NULL || tag_fname == NULL
                    314: #ifdef EMACS_TAGS
                    315:                                                         || ebuf == NULL
                    316: #endif
                    317:                                                                        )
                    318:        goto findtag_end;
                    319:    if (tag == NULL)
                    320:    {
                    321:        matches = (char_u **)alloc((unsigned)(match_limit * sizeof(char_u *)));
                    322:        if (matches == NULL)
                    323:            goto findtag_end;
                    324:    }
                    325:
                    326: /*
                    327:  * Initialize a few variables
                    328:  */
                    329:    if (tag != NULL)
                    330:    {
                    331:        taglen = STRLEN(tag);
                    332:        if (p_tl != 0 && taglen > p_tl)     /* adjust for 'taglength' */
                    333:            taglen = p_tl;
                    334:    }
                    335:    else if (help_only)             /* want tags from help file */
                    336:        curbuf->b_help = TRUE;
                    337:
                    338: /*
                    339:  * Try tag file names from tags option one by one.
                    340:  */
                    341:    for (first_file = TRUE; get_tagfname(first_file, tag_fname) == OK;
                    342:                                                            first_file = FALSE)
                    343:    {
                    344:        /*
                    345:         * A file that doesn't exist is silently ignored.
                    346:         */
                    347:        if ((fp = fopen((char *)tag_fname, "r")) == NULL)
                    348:            continue;
                    349:        did_open = TRUE;    /* remember that we found at least one file */
                    350:
                    351: #ifdef EMACS_TAGS
                    352:        is_etag = 0;        /* default is: not emacs style */
                    353: #endif
                    354:        /*
                    355:         * Read and parse the lines in the file one by one
                    356:         */
                    357:        while (!got_int)
                    358:        {
                    359:            line_breakcheck();
                    360:
                    361:            if (vim_fgets(lbuf, LSIZE, fp))
                    362: #ifdef EMACS_TAGS
                    363:                if (incstack_idx)       /* this was an included file */
                    364:                {
                    365:                    --incstack_idx;
                    366:                    fclose(fp);         /* end of this file ... */
                    367:                    fp = incstack[incstack_idx].fp;
                    368:                    STRCPY(tag_fname, incstack[incstack_idx].tag_fname);
                    369:                    vim_free(incstack[incstack_idx].tag_fname);
                    370:                    is_etag = 1;        /* (only etags can include) */
                    371:                    continue;           /* ... continue with parent file */
                    372:                }
                    373:                else
                    374: #endif
                    375:                    break;                          /* end of file */
                    376:
                    377: #ifdef EMACS_TAGS
                    378:            /*
                    379:             * Emacs tags line with CTRL-L: New file name on next line.
                    380:             * The file name is followed by a ','.
                    381:             */
                    382:            if (*lbuf == Ctrl('L'))     /* remember etag filename in ebuf */
                    383:            {
                    384:                is_etag = 1;
                    385:                if (!vim_fgets(ebuf, LSIZE, fp))
                    386:                {
                    387:                    for (p = ebuf; *p && *p != ','; p++)
                    388:                        ;
                    389:                    *p = NUL;
                    390:
                    391:                    /*
                    392:                     * atoi(p+1) is the number of bytes before the next ^L
                    393:                     * unless it is an include statement.
                    394:                     */
                    395:                    if (STRNCMP(p + 1, "include", 7) == 0 &&
                    396:                                                 incstack_idx < INCSTACK_SIZE)
                    397:                    {
                    398:                        if ((incstack[incstack_idx].tag_fname =
                    399:                                                  strsave(tag_fname)) != NULL)
                    400:                        {
                    401:                            incstack[incstack_idx].fp = fp;
                    402:                            if ((fp = fopen((char *)ebuf, "r")) == NULL)
                    403:                            {
                    404:                                fp = incstack[incstack_idx].fp;
                    405:                                vim_free(incstack[incstack_idx].tag_fname);
                    406:                            }
                    407:                            else
                    408:                            {
                    409:                                STRCPY(tag_fname, ebuf);
                    410:                                ++incstack_idx;
                    411:                            }
                    412:                            is_etag = 0;    /* we can include anything */
                    413:                        }
                    414:                    }
                    415:                }
                    416:                continue;
                    417:            }
                    418: #endif
                    419:
                    420:            /*
                    421:             * Figure out where the different strings are in this line.
                    422:             * For "normal" tags: Do a quick check if the tag matches.
                    423:             * This speeds up tag searching a lot!
                    424:             */
                    425:            if (tag != NULL
                    426: #ifdef EMACS_TAGS
                    427:                            && !is_etag
                    428: #endif
                    429:                                        )
                    430:            {
                    431:                tagname = lbuf;
                    432:                fname = NULL;
                    433:                for (tagname_end = lbuf; *tagname_end &&
                    434:                                    !vim_iswhite(*tagname_end); ++tagname_end)
                    435:                {
                    436:                    if (*tagname_end == ':')
                    437:                    {
                    438:                        if (fname == NULL)
                    439:                            fname = skipwhite(skiptowhite(tagname_end));
                    440:                        if (fnamencmp(lbuf, fname, tagname_end - lbuf) == 0 &&
                    441:                                       vim_iswhite(fname[tagname_end - lbuf]))
                    442:                            tagname = tagname_end + 1;
                    443:                    }
                    444:                }
                    445:
                    446:                /*
                    447:                 * Skip this line if the lenght of the tag is different.
                    448:                 */
                    449:                cmplen = tagname_end - tagname;
                    450:                if (p_tl != 0 && cmplen > p_tl)     /* adjust for 'taglength' */
                    451:                    cmplen = p_tl;
                    452:                if (taglen != cmplen)
                    453:                    continue;
                    454:
                    455:                /*
                    456:                 * Skip this line if the tag does not match (ignoring case).
                    457:                 */
                    458:                if (vim_strnicmp(tagname, tag, (size_t)cmplen))
                    459:                    continue;
                    460:
                    461:                /*
                    462:                 * This is a useful tag, isolate the filename.
                    463:                 */
                    464:                if (fname == NULL)
                    465:                    fname = skipwhite(skiptowhite(tagname_end));
                    466:                fname_end = skiptowhite(fname);
                    467:                if (*fname_end == NUL)
                    468:                    i = FAIL;
                    469:                else
                    470:                    i = OK;
                    471:            }
                    472:            else
                    473:                i = parse_tag_line(lbuf,
                    474: #ifdef EMACS_TAGS
                    475:                                   is_etag,
                    476: #endif
                    477:                            &tagname, &tagname_end, &fname, &fname_end, NULL);
                    478:            if (i == FAIL)
                    479:            {
                    480:                EMSG2("Format error in tags file \"%s\"", tag_fname);
                    481:                stop_searching = TRUE;
                    482:                break;
                    483:            }
                    484:
                    485: #ifdef EMACS_TAGS
                    486:            is_static = FALSE;
                    487:            if (!is_etag)       /* emacs tags are never static */
                    488: #endif
                    489:                is_static = test_for_static(&tagname, tagname_end,
                    490:                                                            fname, fname_end);
                    491: #ifdef EMACS_TAGS
                    492:            if (is_etag)
                    493:                fname = ebuf;
                    494: #endif
                    495:            /*
                    496:             * "tag" == NULL: find tags matching regexp "prog"
                    497:             */
                    498:            if (tag == NULL)
                    499:            {
                    500:                *tagname_end = NUL;
                    501:                if (vim_regexec(prog, tagname, TRUE))
                    502:                {
                    503:                    is_current = test_for_current(
                    504: #ifdef EMACS_TAGS
                    505:                            is_etag,
                    506: #endif
                    507:                                     fname, fname_end, tag_fname);
                    508:                    if (!is_static || is_current)
                    509:                    {
                    510:                        /*
                    511:                         * Found a match, add it to matches[].
                    512:                         * May need to make matches[] larger.
                    513:                         */
                    514:                        if (match_count == match_limit)
                    515:                        {
                    516:                            match_limit += 100;
                    517:                            new_matches = (char_u **)alloc(
                    518:                                    (unsigned)(match_limit * sizeof(char_u *)));
                    519:                            if (new_matches == NULL)
                    520:                            {
                    521:                                /* Out of memory! Just forget about the rest
                    522:                                 * of the matches. */
                    523:                                retval = OK;
                    524:                                stop_searching = TRUE;
                    525:                                break;
                    526:                            }
                    527:                            for (i = 0; i < match_count; i++)
                    528:                                new_matches[i] = matches[i];
                    529:                            vim_free(matches);
                    530:                            matches = new_matches;
                    531:                        }
                    532:                        if (help_only)
                    533:                        {
                    534:                            int     len;
                    535:
                    536:                            /*
                    537:                             * Append the help-heuristic number after the
                    538:                             * tagname, for sorting it later.
                    539:                             */
                    540:                            len = STRLEN(tagname);
                    541:                            p = alloc(len + 10);
                    542:                            if (p != NULL)
                    543:                            {
                    544:                                STRCPY(p, tagname);
                    545:                                sprintf((char *)p + len + 1, "%06d",
                    546:                                                       help_heuristic(tagname,
                    547:                                           (int)(prog->startp[0] - tagname)));
                    548:                            }
                    549:                            matches[match_count++] = p;
                    550:                        }
                    551:                        else
                    552:                            matches[match_count++] = strsave(tagname);
                    553:                    }
                    554:                }
                    555:            }
                    556:            /*
                    557:             * "tag" != NULL: find tag and jump to it
                    558:             */
                    559:            else
                    560:            {
                    561:                /*
                    562:                 * If tag length does not match, skip the rest
                    563:                 */
                    564:                cmplen = tagname_end - tagname;
                    565:                if (p_tl != 0 && cmplen > p_tl)     /* adjust for 'taglength' */
                    566:                    cmplen = p_tl;
                    567:                if (taglen == cmplen)
                    568:                {
                    569:                    /*
                    570:                     * Check for match (ignoring case).
                    571:                     */
                    572:                    icase_match = (vim_strnicmp(tagname, tag,
                    573:                                                        (size_t)cmplen) == 0);
                    574:                    if (icase_match)    /* Tag matches somehow */
                    575:                    {
                    576:                        /*
                    577:                         * If it's a full match for the current file, jump to
                    578:                         * it now.
                    579:                         */
                    580:                        full_match = (STRNCMP(tagname, tag, cmplen) == 0);
                    581:                        is_current = test_for_current(
                    582: #ifdef EMACS_TAGS
                    583:                                is_etag,
                    584: #endif
                    585:                                         fname, fname_end, tag_fname);
                    586:                        if (full_match && is_current)
                    587:                        {
                    588:                            retval = jumpto_tag(lbuf,
                    589: #ifdef EMACS_TAGS
                    590:                                                    is_etag, ebuf,
                    591: #endif
1.2       downsj    592:                                                          tag_fname, forceit);
1.1       downsj    593:                            stop_searching = TRUE;
                    594:                            break;
                    595:                        }
                    596:
                    597:                        /*
                    598:                         * If the priority of the current line is higher than
                    599:                         * the best match so far, store it as the best match
                    600:                         */
                    601:                        if (full_match)
                    602:                            priority = PRI_FULL_MATCH;
                    603:                        else
                    604:                            priority = 0;
                    605:                        if (is_current)
                    606:                            priority += PRI_CURRENT;
                    607:                        if (!is_static)
                    608:                            priority += PRI_GLOBAL;
                    609:
                    610:                        if (priority > bestmatch_priority)
                    611:                        {
                    612:                            vim_free(bestmatch_line);
                    613:                            bestmatch_line = strsave(lbuf);
                    614:                            vim_free(bestmatch_tag_fname);
                    615:                            bestmatch_tag_fname = strsave(tag_fname);
                    616:                            bestmatch_priority = priority;
                    617: #ifdef EMACS_TAGS
                    618:                            bestmatch_is_etag = is_etag;
                    619:                            if (is_etag)
                    620:                            {
                    621:                                vim_free(bestmatch_ebuf);
                    622:                                bestmatch_ebuf = strsave(ebuf);
                    623:                            }
                    624: #endif
                    625:                        }
                    626:                    }
                    627:                }
                    628:            }
                    629:        }
                    630:        fclose(fp);
                    631: #ifdef EMACS_TAGS
                    632:        while (incstack_idx)
                    633:        {
                    634:            --incstack_idx;
                    635:            fclose(incstack[incstack_idx].fp);
                    636:            vim_free(incstack[incstack_idx].tag_fname);
                    637:        }
                    638: #endif
                    639:        if (stop_searching)
                    640:            break;
                    641:
                    642:        /*
                    643:         * Stop searching if a tag was found in the current tags file and
                    644:         * we got a global match with matching case or 'ignorecase' is set.
                    645:         */
                    646:        if (tag != NULL && bestmatch_line != NULL &&
                    647:               bestmatch_priority >= (p_ic ? 0 : PRI_FULL_MATCH) + PRI_GLOBAL)
                    648:            break;
                    649:    }
                    650:
                    651:    if (!stop_searching)
                    652:    {
                    653:        if (!did_open)                      /* never opened any tags file */
                    654:            EMSG("No tags file");
                    655:        else if (tag == NULL)
                    656:        {
                    657:            retval = OK;        /* It's OK even when no tag found */
                    658:        }
                    659:        else
                    660:        {
                    661:            /*
                    662:             * If we didn't find a static full match, use the best match found.
                    663:             */
                    664:            if (bestmatch_line != NULL)
                    665:            {
                    666:                if (bestmatch_priority < PRI_FULL_MATCH)
                    667:                {
                    668:                    MSG("Only found tag with different case!");
                    669:                    if (!msg_scrolled)
                    670:                    {
                    671:                        flushbuf();
                    672:                        mch_delay(1000L, TRUE);
                    673:                    }
                    674:                }
                    675:                retval = jumpto_tag(bestmatch_line,
                    676: #ifdef EMACS_TAGS
                    677:                        bestmatch_is_etag, bestmatch_ebuf,
                    678: #endif
1.2       downsj    679:                        bestmatch_tag_fname, forceit);
1.1       downsj    680:            }
                    681:            else
                    682:                EMSG("tag not found");
                    683:        }
                    684:    }
                    685:
                    686: findtag_end:
                    687:    vim_free(lbuf);
                    688:    vim_free(tag_fname);
                    689:    vim_free(bestmatch_line);
                    690:    vim_free(bestmatch_tag_fname);
                    691: #ifdef EMACS_TAGS
                    692:    vim_free(ebuf);
                    693:    vim_free(bestmatch_ebuf);
                    694: #endif
                    695:
                    696:    if (tag == NULL)
                    697:    {
                    698:        if (retval == FAIL)         /* free all matches found */
                    699:            while (match_count > 0)
                    700:                vim_free(matches[--match_count]);
                    701:        if (match_count == 0)       /* nothing found, free matches[] */
                    702:        {
                    703:            vim_free(matches);
                    704:            matches = NULL;
                    705:        }
                    706:        *file = matches;
                    707:        *num_file = match_count;
                    708:    }
                    709:    curbuf->b_help = help_save;
                    710:
                    711:    return retval;
                    712: }
                    713:
                    714: /*
                    715:  * Get the next name of a tag file from the tag file list.
                    716:  * For help files, use "vim_tags" file only.
                    717:  *
                    718:  * Return FAIL if no more tag file names, OK otherwise.
                    719:  */
                    720:    static int
                    721: get_tagfname(first, buf)
                    722:    int     first;          /* TRUE when first file name is wanted */
                    723:    char_u  *buf;           /* pointer to buffer of LSIZE chars */
                    724: {
                    725:    static char_u   *np = NULL;
                    726:    char_u          *fname;
                    727:    size_t          path_len, fname_len;
                    728:    /*
                    729:     * A list is made of the files that have been visited.
                    730:     */
                    731:    struct visited
                    732:    {
                    733:        struct visited  *v_next;
                    734: #if defined(UNIX)
                    735:        struct stat     v_st;
                    736: #else
                    737:        char_u          v_fname[1];     /* actually longer */
                    738: #endif
                    739:    };
                    740:    static struct visited   *first_visited = NULL;
                    741:    struct visited          *vp;
                    742: #if defined(UNIX)
                    743:    struct stat             st;
                    744: #endif
                    745:
                    746:    if (first)
                    747:    {
                    748:        np = p_tags;
                    749:        while (first_visited != NULL)
                    750:        {
                    751:            vp = first_visited->v_next;
                    752:            vim_free(first_visited);
                    753:            first_visited = vp;
                    754:        }
                    755:    }
                    756:
                    757:    if (np == NULL)         /* tried allready (or bogus call) */
                    758:        return FAIL;
                    759:
                    760:    /*
                    761:     * For a help window only try the file 'vim_tags' in the same
                    762:     * directory as 'helpfile'.
                    763:     */
                    764:    if (curbuf->b_help)
                    765:    {
                    766:        path_len = gettail(p_hf) - p_hf;
                    767:        if (path_len + 9 >= LSIZE)
                    768:            return FAIL;
                    769:        vim_memmove(buf, p_hf, path_len);
                    770:        STRCPY(buf + path_len, "vim_tags");
                    771:
                    772:        np = NULL;              /* try only once */
                    773:    }
                    774:
                    775:    else
                    776:    {
                    777:        /*
                    778:         * Loop until we have found a file name that can be used.
                    779:         */
                    780:        for (;;)
                    781:        {
                    782:            if (*np == NUL)         /* tried all possibilities */
                    783:                return FAIL;
                    784:
                    785:            /*
                    786:             * Copy next file name into buf.
                    787:             */
                    788:            (void)copy_option_part(&np, buf, LSIZE, " ,");
                    789:
                    790:            /*
                    791:             * Tag file name starting with "./": Replace '.' with path of
                    792:             * current file.
                    793:             */
                    794:            if (buf[0] == '.' && ispathsep(buf[1]))
                    795:            {
                    796:                if (curbuf->b_filename == NULL) /* skip if no filename */
                    797:                    continue;
                    798:
                    799:                path_len = gettail(curbuf->b_filename) - curbuf->b_filename;
                    800:                fname = buf + 1;
                    801:                while (ispathsep(*fname))       /* skip '/' and the like */
                    802:                    ++fname;
                    803:                fname_len = STRLEN(fname);
                    804:                if (fname_len + path_len + 1 > LSIZE)
                    805:                    continue;
                    806:                vim_memmove(buf + path_len, fname, fname_len + 1);
                    807:                vim_memmove(buf, curbuf->b_filename, path_len);
                    808:            }
                    809:
                    810:            /*
                    811:             * Check if this tags file has been used already.
                    812:             * If file doesn't exist, skip it.
                    813:             */
                    814: #if defined(UNIX)
                    815:            if (stat((char *)buf, &st) < 0)
                    816: #else
                    817:            if (FullName(buf, NameBuff, MAXPATHL, TRUE) == FAIL)
                    818: #endif
                    819:                continue;
                    820:
                    821:            for (vp = first_visited; vp != NULL; vp = vp->v_next)
                    822: #if defined(UNIX)
                    823:                if (vp->v_st.st_dev == st.st_dev &&
                    824:                                                 vp->v_st.st_ino == st.st_ino)
                    825: #else
                    826:                if (fnamecmp(vp->v_fname, NameBuff) == 0)
                    827: #endif
                    828:                    break;
                    829:
                    830:            if (vp != NULL)         /* already visited, skip it */
                    831:                continue;
                    832:
                    833:            /*
                    834:             * Found the next name.  Add it to the list of visited files.
                    835:             */
                    836: #if defined(UNIX)
                    837:            vp = (struct visited *)alloc((unsigned)sizeof(struct visited));
                    838: #else
                    839:            vp = (struct visited *)alloc((unsigned)(sizeof(struct visited) +
                    840:                                                           STRLEN(NameBuff)));
                    841: #endif
                    842:            if (vp != NULL)
                    843:            {
                    844: #if defined(UNIX)
                    845:                vp->v_st = st;
                    846: #else
                    847:                STRCPY(vp->v_fname, NameBuff);
                    848: #endif
                    849:                vp->v_next = first_visited;
                    850:                first_visited = vp;
                    851:            }
                    852:            break;
                    853:        }
                    854:    }
                    855:    return OK;
                    856: }
                    857:
                    858: /*
                    859:  * Parse one line from the tags file. Find start/end of tag name, start/end of
                    860:  * file name and start of search pattern.
                    861:  *
                    862:  * If is_etag is TRUE, fname and fname_end are not set.
                    863:  * If command == NULL it is not set.
                    864:  *
                    865:  * Return FAIL if there is a format error in this line, OK otherwise.
                    866:  */
                    867:    static int
                    868: parse_tag_line(lbuf,
                    869: #ifdef EMACS_TAGS
                    870:                    is_etag,
                    871: #endif
                    872:                              tagname, tagname_end, fname, fname_end, command)
                    873:    char_u      *lbuf;
                    874: #ifdef EMACS_TAGS
                    875:    int         is_etag;
                    876: #endif
                    877:    char_u      **tagname;
                    878:    char_u      **tagname_end;
                    879:    char_u      **fname;
                    880:    char_u      **fname_end;
                    881:    char_u      **command;
                    882: {
                    883:    char_u      *p;
                    884:
                    885: #ifdef EMACS_TAGS
                    886:    char_u      *p_7f;
                    887:
                    888:    if (is_etag)
                    889:    {
                    890:        /*
                    891:         * There are two formats for an emacs tag line:
                    892:         * 1:  struct EnvBase ^?EnvBase^A139,4627
                    893:         * 2: #define   ARPB_WILD_WORLD ^?153,5194
                    894:         */
                    895:        p_7f = vim_strchr(lbuf, 0x7f);
                    896:        if (p_7f == NULL)
                    897:            return FAIL;
                    898:                                        /* find start of line number */
                    899:        for (p = p_7f + 1; *p < '0' || *p > '9'; ++p)
                    900:            if (*p == NUL)
                    901:                return FAIL;
                    902:        if (command != NULL)
                    903:            *command = p;
                    904:
                    905:                                /* first format: explicit tagname given */
                    906:        if (p[-1] == Ctrl('A'))
                    907:        {
                    908:            *tagname = p_7f + 1;
                    909:            *tagname_end = p - 1;
                    910:        }
                    911:        else
                    912:                                /* second format: isolate tagname */
                    913:        {
                    914:            /* find end of tagname */
                    915:            for (p = p_7f - 1; *p == ' ' || *p == '\t' ||
                    916:                                                  *p == '(' || *p == ';'; --p)
                    917:                if (p == lbuf)
                    918:                    return FAIL;
                    919:            *tagname_end = p + 1;
                    920:            while (p >= lbuf && *p != ' ' && *p != '\t')
                    921:                --p;
                    922:            *tagname = p + 1;
                    923:        }
                    924:    }
                    925:    else
                    926:    {
                    927: #endif
                    928:            /* Isolate the tagname, from lbuf up to the first white */
                    929:        *tagname = lbuf;
                    930:        p = skiptowhite(lbuf);
                    931:        if (*p == NUL)
                    932:            return FAIL;
                    933:        *tagname_end = p;
                    934:
                    935:            /* Isolate file name, from first to second white space */
                    936:        p = skipwhite(p);
                    937:        *fname = p;
                    938:        p = skiptowhite(p);
                    939:        if (*p == NUL)
                    940:            return FAIL;
                    941:        *fname_end = p;
                    942:
                    943:            /* find start of search command, after second white space */
                    944:        if (command != NULL)
                    945:        {
                    946:            p = skipwhite(p);
                    947:            if (*p == NUL)
                    948:                return FAIL;
                    949:            *command = p;
                    950:        }
                    951: #ifdef EMACS_TAGS
                    952:    }
                    953: #endif
                    954:
                    955:    return OK;
                    956: }
                    957:
                    958: /*
                    959:  * Check if tagname is a static tag
                    960:  *
                    961:  * Static tags produced by the ctags program have the
                    962:  * format: 'file:tag  file  /pattern'.
                    963:  * This is only recognized when both occurences of 'file'
                    964:  * are the same, to avoid recognizing "string::string" or
                    965:  * ":exit".
                    966:  *
                    967:  * Return TRUE if it is a static tag and adjust *tagname to the real tag.
                    968:  * Return FALSE if it is not a static tag.
                    969:  */
                    970:    static int
                    971: test_for_static(tagname, tagname_end, fname, fname_end)
                    972:    char_u      **tagname;
                    973:    char_u      *tagname_end;
                    974:    char_u      *fname;
                    975:    char_u      *fname_end;
                    976: {
                    977:    char_u      *p;
                    978:
                    979:    p = *tagname + (fname_end - fname);
                    980:    if (p < tagname_end && *p == ':' &&
                    981:                            fnamencmp(*tagname, fname, fname_end - fname) == 0)
                    982:    {
                    983:        *tagname = p + 1;
                    984:        return TRUE;
                    985:    }
                    986:    return FALSE;
                    987: }
                    988:
                    989: /*
                    990:  * Jump to a tag that has been found in one of the tag files
                    991:  */
                    992:    static int
                    993: jumpto_tag(lbuf,
                    994: #ifdef EMACS_TAGS
                    995:                is_etag, etag_fname,
                    996: #endif
1.2       downsj    997:                                    tag_fname, forceit)
1.1       downsj    998:    char_u      *lbuf;          /* line from the tags file for this tag */
                    999: #ifdef EMACS_TAGS
                   1000:    int         is_etag;        /* TRUE if it's from an emacs tags file */
                   1001:    char_u      *etag_fname;    /* file name for tag if is_etag is TRUE */
                   1002: #endif
                   1003:    char_u      *tag_fname;     /* file name of the tags file itself */
1.2       downsj   1004:    int         forceit;        /* :ta with ! */
1.1       downsj   1005: {
                   1006:    int         save_secure;
                   1007:    int         save_p_ws, save_p_scs, save_p_ic;
                   1008:    char_u      *str;
                   1009:    char_u      *pbuf;                  /* search pattern buffer */
                   1010:    char_u      *p;
                   1011:    char_u      *expanded_fname = NULL;
                   1012:    char_u      *tagname, *tagname_end;
                   1013:    char_u      *fname, *fname_end;
                   1014:    char_u      *orig_fname;
                   1015:    int         retval = FAIL;
                   1016:    int         getfile_result;
                   1017:    int         search_options;
                   1018:
                   1019:    pbuf = alloc(LSIZE);
                   1020:
                   1021:    if (pbuf == NULL
                   1022: #ifdef EMACS_TAGS
                   1023:                    || (is_etag && etag_fname == NULL)
                   1024: #endif
                   1025:                                                        || tag_fname == NULL)
                   1026:        goto erret;
                   1027:
                   1028:    /*
                   1029:     * find the search pattern (caller should check it is there)
                   1030:     */
                   1031:    if (parse_tag_line(lbuf,
                   1032: #ifdef EMACS_TAGS
                   1033:                               is_etag,
                   1034: #endif
                   1035:                    &tagname, &tagname_end, &fname, &fname_end, &str) == FAIL)
                   1036:        goto erret;
                   1037:    orig_fname = fname;     /* remember for test_for_static() below */
                   1038:
                   1039: #ifdef EMACS_TAGS
                   1040:    if (is_etag)
                   1041:        fname = etag_fname;
                   1042:    else
                   1043: #endif
                   1044:        *fname_end = NUL;
                   1045:
                   1046:    /*
                   1047:     * If the command is a string like "/^function fname"
                   1048:     * scan through the search string. If we see a magic
                   1049:     * char, we have to quote it. This lets us use "real"
                   1050:     * implementations of ctags.
                   1051:     */
                   1052:    if (*str == '/' || *str == '?')
                   1053:    {
                   1054:        p = pbuf;
                   1055:        *p++ = *str++;          /* copy the '/' or '?' */
                   1056:        if (*str == '^')
                   1057:            *p++ = *str++;          /* copy the '^' */
                   1058:
                   1059:        while (*str)
                   1060:        {
                   1061:            switch (*str)
                   1062:            {
                   1063:                        /* Always remove '\' before '('.
                   1064:                         * Remove a '\' befor '*' if 'nomagic'.
                   1065:                         * Otherwise just copy the '\' and don't look at the
                   1066:                         * next character
                   1067:                         */
                   1068:            case '\\':  if (str[1] == '(' || (!p_magic && str[1] == '*'))
                   1069:                            ++str;
                   1070:                        else
                   1071:                            *p++ = *str++;
                   1072:                        break;
                   1073:
                   1074:            case '\r':
                   1075:            case '\n':  *str = pbuf[0]; /* copy '/' or '?' */
                   1076:                        str[1] = NUL;   /* delete NL after CR */
                   1077:                        break;
                   1078:
                   1079:                        /*
                   1080:                         * if string ends in search character: skip it
                   1081:                         * else escape it with '\'
                   1082:                         */
                   1083:            case '/':
                   1084:            case '?':   if (*str != pbuf[0])    /* not the search char */
                   1085:                            break;
                   1086:                                                /* last char */
                   1087:                        if (str[1] == '\n' || str[1] == '\r')
                   1088:                        {
                   1089:                            ++str;
                   1090:                            continue;
                   1091:                        }
                   1092:            case '[':
                   1093:                        if (!p_magic)
                   1094:                            break;
                   1095:            case '^':
                   1096:            case '*':
                   1097:            case '~':
                   1098:            case '.':   *p++ = '\\';
                   1099:                        break;
                   1100:            }
                   1101:            *p++ = *str++;
                   1102:        }
                   1103:    }
                   1104:    else        /* not a search command, just copy it */
                   1105:    {
1.3     ! downsj   1106:        for (p = pbuf; *str && *str != '\n' && *str != '\r'; )
1.1       downsj   1107:        {
                   1108: #ifdef EMACS_TAGS
                   1109:            if (is_etag && *str == ',')     /* stop at ',' after line number */
                   1110:                break;
                   1111: #endif
                   1112:            *p++ = *str++;
                   1113:        }
                   1114:    }
                   1115:    *p = NUL;
                   1116:
                   1117:    /*
                   1118:     * expand filename (for environment variables)
                   1119:     */
                   1120:    expanded_fname = ExpandOne((char_u *)fname, NULL, WILD_LIST_NOTFOUND,
                   1121:                                                            WILD_EXPAND_FREE);
                   1122:    if (expanded_fname != NULL)
                   1123:        fname = expanded_fname;
                   1124:
                   1125:    /*
                   1126:     * if 'tagrelative' option set, may change file name
                   1127:     */
                   1128:    fname = expand_rel_name(fname, tag_fname);
                   1129:
                   1130:    /*
                   1131:     * check if file for tag exists before abandoning current file
                   1132:     */
                   1133:    if (getperm(fname) < 0)
                   1134:    {
                   1135:        EMSG2("File \"%s\" does not exist", fname);
                   1136:        goto erret;
                   1137:    }
                   1138:
                   1139:    ++RedrawingDisabled;
                   1140:    /*
                   1141:     * if it was a CTRL-W CTRL-] command split window now
                   1142:     */
                   1143:    if (postponed_split)
                   1144:        win_split(0, FALSE);
                   1145:    /*
                   1146:     * A :ta from a help file will keep the b_help flag set.
                   1147:     */
                   1148:    keep_help_flag = curbuf->b_help;
1.2       downsj   1149:    getfile_result = getfile(0, fname, NULL, TRUE, (linenr_t)0, forceit);
1.1       downsj   1150:
                   1151:    if (getfile_result <= 0)            /* got to the right file */
                   1152:    {
                   1153:        curwin->w_set_curswant = TRUE;
                   1154:        postponed_split = FALSE;
                   1155:
                   1156:        save_secure = secure;
                   1157:        secure = 1;
                   1158:        /*
                   1159:         * If 'cpoptions' contains 't', store the search pattern for the "n"
                   1160:         * command.  If 'cpoptions' does not contain 't', the search pattern
                   1161:         * is not stored.
                   1162:         */
                   1163:        if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL)
                   1164:            search_options = 0;
                   1165:        else
                   1166:            search_options = SEARCH_KEEP;
                   1167:
                   1168:        /*
                   1169:         * if the command is a search, try here
                   1170:         *
                   1171:         * Rather than starting at line one, just turn wrap-scan
                   1172:         * on temporarily, this ensures that tags on line 1 will
                   1173:         * be found, and makes sure our guess searches search the
                   1174:         * whole file when repeated -- webb.
                   1175:         * Also reset 'smartcase' for the search, since the search
                   1176:         * pattern was not typed by the user.
                   1177:         */
                   1178:        if (pbuf[0] == '/' || pbuf[0] == '?')
                   1179:        {
                   1180:            save_p_ws = p_ws;
                   1181:            save_p_ic = p_ic;
                   1182:            save_p_scs = p_scs;
                   1183:            p_ws = TRUE;    /* Switch wrap-scan on temporarily */
                   1184:            p_ic = FALSE;   /* don't ignore case now */
                   1185:            p_scs = FALSE;
                   1186:            add_to_history(1, pbuf + 1);    /* put pattern in search history */
                   1187:
                   1188:            if (do_search(pbuf[0], pbuf + 1, (long)1, search_options))
                   1189:                retval = OK;
                   1190:            else
                   1191:            {
                   1192:                register int notfound = FALSE;
                   1193:
                   1194:                /*
                   1195:                 * try again, ignore case now
                   1196:                 */
                   1197:                p_ic = TRUE;
                   1198:                if (!do_search(pbuf[0], pbuf + 1, (long)1, search_options))
                   1199:                {
                   1200:                    /*
                   1201:                     * Failed to find pattern, take a guess: "^func  ("
                   1202:                     */
                   1203:                    (void)test_for_static(&tagname, tagname_end,
                   1204:                                                        orig_fname, fname_end);
                   1205:                    *tagname_end = NUL;
                   1206:                    sprintf((char *)pbuf, "^%s[ \t]*(", tagname);
                   1207:                    if (!do_search('/', pbuf, (long)1, search_options))
                   1208:                    {
                   1209:                        /* Guess again: "^char * func  (" */
                   1210:                        sprintf((char *)pbuf, "^[#a-zA-Z_].*%s[ \t]*(",
                   1211:                                                                     tagname);
                   1212:                        if (!do_search('/', pbuf, (long)1, search_options))
                   1213:                            notfound = TRUE;
                   1214:                    }
                   1215:                }
                   1216:                if (notfound)
                   1217:                    EMSG("Can't find tag pattern");
                   1218:                else
                   1219:                {
                   1220:                    MSG("Couldn't find tag, just guessing!");
                   1221:                    if (!msg_scrolled)
                   1222:                    {
                   1223:                        flushbuf();
                   1224:                        mch_delay(1000L, TRUE);
                   1225:                    }
                   1226:                    retval = OK;
                   1227:                }
                   1228:            }
                   1229:            p_ws = save_p_ws;
                   1230:            p_ic = save_p_ic;
                   1231:            p_scs = save_p_scs;
                   1232:        }
                   1233:        else
                   1234:        {                           /* start command in line 1 */
                   1235:            curwin->w_cursor.lnum = 1;
                   1236:            do_cmdline(pbuf, TRUE, TRUE);
                   1237:            retval = OK;
                   1238:        }
                   1239:
                   1240:        if (secure == 2)            /* done something that is not allowed */
                   1241:            wait_return(TRUE);
                   1242:        secure = save_secure;
                   1243:
                   1244:        /*
                   1245:         * Print the file message after redraw if jumped to another file.
                   1246:         * Don't do this for help files (avoid a hit-return message).
                   1247:         */
                   1248:        if (getfile_result == -1)
                   1249:        {
                   1250:            if (!curbuf->b_help)
                   1251:                need_fileinfo = TRUE;
                   1252:            retval = OK;            /* always return OK if jumped to another
                   1253:                                       file (at least we found the file!) */
                   1254:        }
                   1255:
                   1256:        /*
                   1257:         * For a help buffer: Put the cursor line at the top of the window,
                   1258:         * the help subject will be below it.
                   1259:         */
                   1260:        if (curbuf->b_help)
                   1261:        {
                   1262:            curwin->w_topline = curwin->w_cursor.lnum;
                   1263:            comp_Botline(curwin);
                   1264:            cursupdate();       /* take care of 'scrolloff' */
                   1265:            updateScreen(NOT_VALID);
                   1266:        }
                   1267:        --RedrawingDisabled;
                   1268:    }
                   1269:    else
                   1270:    {
                   1271:        --RedrawingDisabled;
                   1272:        if (postponed_split)            /* close the window */
                   1273:        {
                   1274:            close_window(curwin, FALSE);
                   1275:            postponed_split = FALSE;
                   1276:        }
                   1277:    }
                   1278:
                   1279: erret:
                   1280:    vim_free(pbuf);
                   1281:    vim_free(expanded_fname);
                   1282:
                   1283:    return retval;
                   1284: }
                   1285:
                   1286: /*
                   1287:  * If 'tagrelative' option set, change fname (name of file containing tag)
                   1288:  * according to tag_fname (name of tag file containing fname).
                   1289:  */
                   1290:    static char_u *
                   1291: expand_rel_name(fname, tag_fname)
                   1292:    char_u      *fname;
                   1293:    char_u      *tag_fname;
                   1294: {
                   1295:    char_u      *p;
                   1296:
                   1297:    if ((p_tr || curbuf->b_help) && !isFullName(fname) &&
                   1298:                                       (p = gettail(tag_fname)) != tag_fname)
                   1299:    {
                   1300:        STRCPY(NameBuff, tag_fname);
                   1301:        STRNCPY(NameBuff + (p - tag_fname), fname,
                   1302:                                                 MAXPATHL - (p - tag_fname));
                   1303:        /*
                   1304:         * Translate names like "src/a/../b/file.c" into "src/b/file.c".
                   1305:         */
                   1306:        simplify_filename(NameBuff);
                   1307:        fname = NameBuff;
                   1308:    }
                   1309:    return fname;
                   1310: }
                   1311:
                   1312: /*
                   1313:  * Moves the tail part of the path (including the terminating NUL) pointed to
                   1314:  * by "tail" to the new location pointed to by "here". This should accomodate
                   1315:  * an overlapping move.
                   1316:  */
                   1317: #define movetail(here, tail)  vim_memmove(here, tail, STRLEN(tail) + (size_t)1)
                   1318:
                   1319: /*
                   1320:  * For MS-DOS we should check for backslash too, but that is complicated.
                   1321:  */
                   1322: #define DIR_SEP        '/'         /* the directory separator character */
                   1323:
                   1324: /*
                   1325:  * Converts a filename into a canonical form. It simplifies a filename into
                   1326:  * its simplest form by stripping out unneeded components, if any.  The
                   1327:  * resulting filename is simplified in place and will either be the same
                   1328:  * length as that supplied, or shorter.
                   1329:  */
                   1330:    static void
                   1331: simplify_filename(filename)
                   1332:    char_u *filename;
                   1333: {
                   1334:    int     absolute = FALSE;
                   1335:    int     components = 0;
                   1336:    char_u  *p, *tail;
                   1337:
                   1338:    p = filename;
                   1339:    if (*p == DIR_SEP)
                   1340:    {
                   1341:        absolute = TRUE;
                   1342:        ++p;
                   1343:    }
                   1344:    do
                   1345:    {
                   1346:        /*  Always leave "p" pointing to character following next "/". */
                   1347:        if (*p == DIR_SEP)
                   1348:            movetail(p, p+1);               /* strip duplicate "/" */
                   1349:        else if (STRNCMP(p, "./", 2) == 0)
                   1350:            movetail(p, p+2);               /* strip "./" */
                   1351:        else if (STRNCMP(p, "../", 3) == 0)
                   1352:        {
                   1353:            if (components > 0)             /* strip any prev. component */
                   1354:            {
                   1355:                *(p - 1) = 0;               /* delete "/" before  "../" */
                   1356:                tail  = p + 2;              /* skip to "/" of "../" */
                   1357:                p = vim_strrchr(filename, DIR_SEP);  /* find preceding sep. */
                   1358:                if (p != NULL)              /* none found */
                   1359:                    ++p;                    /* skip to char after "/" */
                   1360:                else
                   1361:                {
                   1362:                    ++tail;                 /* strip leading "/" from tail*/
                   1363:                    p = filename;           /* go back to beginning */
                   1364:                    if (absolute)           /* skip over any leading "/" */
                   1365:                        ++p;
                   1366:                }
                   1367:                movetail(p, tail);          /* strip previous component */
                   1368:                --components;
                   1369:            }
                   1370:            else if (absolute)              /* no parent to root... */
                   1371:                movetail(p, p+3);           /*   so strip "../" */
                   1372:            else                            /* leading series of "../" */
                   1373:            {
                   1374:                p = vim_strchr(p, DIR_SEP); /* skip to next "/" */
                   1375:                if (p != NULL)
                   1376:                    ++p;                    /* skip to char after "/" */
                   1377:            }
                   1378:        }
                   1379:        else
                   1380:        {
                   1381:            ++components;                   /* simple path component */
                   1382:            p = vim_strchr(p, DIR_SEP);         /* skip to next "/" */
                   1383:            if (p != NULL)
                   1384:                ++p;                        /* skip to char after "/" */
                   1385:        }
                   1386:    } while (p != NULL && *p != NUL);
                   1387: }
                   1388:
                   1389: /*
                   1390:  * Check if we have a tag for the current file.
                   1391:  * This is a bit slow, because of the full path compare in fullpathcmp().
                   1392:  * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
                   1393:  * file.
                   1394:  */
                   1395:    static int
                   1396: #ifdef EMACS_TAGS
                   1397: test_for_current(is_etag, fname, fname_end, tag_fname)
                   1398:    int     is_etag;
                   1399: #else
                   1400: test_for_current(fname, fname_end, tag_fname)
                   1401: #endif
                   1402:    char_u  *fname;
                   1403:    char_u  *fname_end;
                   1404:    char_u  *tag_fname;
                   1405: {
                   1406:    int     c;
                   1407:    int     retval;
                   1408:
                   1409:    if (curbuf->b_filename == NULL)
                   1410:        retval = FALSE;
                   1411:    else
                   1412:    {
                   1413: #ifdef EMACS_TAGS
                   1414:        if (is_etag)
                   1415:            c = 0;          /* to shut up GCC */
                   1416:        else
                   1417: #endif
                   1418:        {
                   1419:            c = *fname_end;
                   1420:            *fname_end = NUL;
                   1421:        }
                   1422:        retval = (fullpathcmp(expand_rel_name(fname, tag_fname),
                   1423:                                            curbuf->b_xfilename) == FPC_SAME);
                   1424: #ifdef EMACS_TAGS
                   1425:        if (!is_etag)
                   1426: #endif
                   1427:            *fname_end = c;
                   1428:    }
                   1429:
                   1430:    return retval;
                   1431: }