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