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