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