[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     ! 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: }