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