2 * Copyright (c) 1988 Mark Nudleman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 static char sccsid[] = "@(#)tags.c 8.1 (Berkeley) 6/6/93";
40 static const char rcsid[] =
44 #include <sys/types.h>
45 #include <sys/queue.h>
54 #define WHITESP(c) ((c)==' ' || (c)=='\t')
57 char *tagfile; /* Name of source file containing current tag */
59 static enum { CTAGS, GTAGS } tagstyle = GTAGS;
60 static char *findctag(), *findgtag(), *nextgtag(), *prevgtag();
61 static ctagsearch(), gtagsearch();
64 * Load information about the tag. The global variable tagfile will point to
65 * the file that contains the tag, or will be NULL if information could not be
66 * found (in which case an error message will have been printed). After
67 * loading the file named by tagfile, tagsearch() should be called to
68 * set the current position to the tag.
71 char *tag; /* The tag to load */
74 * Try using gtags or ctags first, as indicated by tagstyle. If
75 * that fails, try the other. Someday there may even be a way to
76 * assert a certain tagstyle...
80 tagfile = findctag(tag);
81 if (!tagfile && (tagfile = findgtag(tag))) tagstyle = GTAGS;
85 /* Would be nice to print the number of tag references
86 * we found (for nexttag() and prevtag()) in a (not-)error()
88 tagfile = findgtag(tag);
89 if (!tagfile && (tagfile = findctag(tag))) tagstyle = CTAGS;
94 error("could not find relevent tag information");
98 * Load information about the next number'th tag, if the last findtag() call
99 * found multiple tag references. The global variable tagfile will point to the
100 * file that contains the tag, or will be NULL if information could not be
101 * found (in which case an error message will have been printed). After
102 * loading the file named by tagfile, tagsearch() should be called to set
103 * the current position to the tag.
106 int number; /* How many tags to go forward by */
108 if (number < 0) number = -number; /* positive only, please */
114 while (number--) tagfile = nextgtag();
118 error("no next tag");
122 * The antithesis to nexttag().
125 int number; /* How many tags to go backwards by */
127 if (number < 0) number = -number; /* positive only, please */
133 while (number--) tagfile = prevgtag();
137 error("no previous tag");
141 * Try and position the currently loaded file at the last tag that was
142 * succesfully passed to findtag() or chosen with nexttag() and prevtag().
143 * An error message will be printed if unsuccessful.
150 error("could not locate ctag");
154 error("could not locate gtag");
160 /*******************************************************************************
169 static char *ctagpattern;
170 static int ctagflags;
173 #define START_OF_LINE 0x01
174 #define END_OF_LINE 0x02
177 * Find specified tag in the ctags(1)-format tag file ctagfile. Returns
178 * pointer to a static buffer holding the name of the file containing
179 * the tag. Returns NULL on failure. The next call to ctagsearch() will
180 * position the currently loaded file at the tag.
184 register char *tag; /* tag to search for */
190 static char tline[200]; /* XXX should be dynamic */
191 const char *ctagfile = "tags";
194 if ((f = fopen(ctagfile, "r")) == NULL)
197 taglen = strlen(tag);
200 * Search the tags file for the desired tag.
202 while (fgets(tline, sizeof(tline), f) != NULL)
207 if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
212 * The line contains the tag, the filename and the
213 * pattern, separated by white space.
214 * The pattern is surrounded by a pair of identical
216 * Parse the line and extract these parts.
220 * Skip over the tag and the whitespace after the tag name.
222 for (p = tline; !WHITESP(*p) && *p != '\0'; p++)
227 /* File name is missing! */
231 * Save the file name.
232 * Skip over the filename and whitespace after the file name.
235 while (!WHITESP(*p) && *p != '\0')
241 /* Pattern is missing! */
246 * Skip to the end of the pattern.
247 * Delete the initial "^" and the final "$" from the pattern.
252 ctagflags |= START_OF_LINE;
254 ctagflags &= ~START_OF_LINE;
256 ctagpattern = p; /* cock ctagsearch() */
257 while (*p != search_char && *p != '\0')
263 ctagflags |= END_OF_LINE;
265 ctagflags &= ~END_OF_LINE;
277 * Locate the tag that was loaded by findctag().
278 * This is a stripped-down version of search().
279 * We don't use search() for several reasons:
280 * - We don't want to blow away any search string we may have saved.
281 * - The various regular-expression functions (from different systems:
282 * regcmp vs. re_comp) behave differently in the presence of
283 * parentheses (which are almost always found in a tag).
285 * Returns -1 if it was unable to position at the requested pattern,
291 off_t pos, linepos, forw_raw_line();
295 linenum = find_linenum(pos);
300 * Get lines until we find a matching one or
301 * until we hit end-of-file.
307 * Read the next line, and save the
308 * starting position of that line in linepos.
311 pos = forw_raw_line(pos);
315 if (pos == NULL_POSITION)
316 return (-1); /* Tag not found. */
319 * If we're using line numbers, we might as well
320 * remember the information we have now (the position
321 * and line number of the current line).
324 add_lnum(linenum, pos);
327 * Test the line to see if we have a match. I don't know of
328 * any tags program that would use START_OF_LINE but not
329 * END_OF_LINE, or vice-a-versa, but we handle this case anyway.
332 case 0: /* !START_OF_LINE and !END_OF_LINE */
333 if (strstr(line, ctagpattern))
336 case START_OF_LINE: /* !END_OF_LINE */
337 if (!strncmp(ctagpattern, line, strlen(ctagpattern)))
340 case END_OF_LINE: /* !START_OF_LINE */
342 char *x = strstr(line, ctagpattern);
345 if (x[strlen(ctagpattern)] != '\0')
349 case START_OF_LINE | END_OF_LINE:
350 if (!strcmp(ctagpattern, line))
362 /*******************************************************************************
369 * The findgtag() and getentry() functions are stolen, more or less, from the
370 * patches to nvi-1.79 included in Shigio Yamaguchi's global-3.42 distribution.
374 * The queue of tags generated by the last findgtag() call.
376 static CIRCLEQ_HEAD(gtag_q, struct gtag) gtag_q;
378 CIRCLEQ_ENTRY(struct gtag) ptrs;
379 char *file; /* source file containing the tag */
380 int line; /* appropriate line number of source file */
382 static struct gtag *curgtag;
386 * The findgtag() will try and load information about the requested tag.
387 * It does this by calling "global -x tag; global -xr tag;" and storing the
388 * parsed output for future use by gtagsearch_f() and gtagsearch_b(). A
389 * pointer to a static buffer containing the name of the source file will
390 * be returned, or NULL on failure. The first filename printed by global is
391 * returned (hopefully the function definition) and the other filenames may
392 * be accessed by nextgtag() and prevgtag().
396 char *tag; /* tag to load */
398 struct gtag *gtag_p1, *gtag_p2;
403 if (!tag) return (NULL); /* Sanity check */
405 /* Clear any existing tag circle queue */
406 /* XXX Ideally, we wouldn't do this until after we know that we
407 * can load some other tag information. */
409 gtag_p1 = gtag_q.cqh_first;
410 if (gtag_p1) while (gtag_p1 != (void *)>ag_q) {
411 gtag_p2 = gtag_p1->ptrs.cqe_next;
416 /* Allocate and initialize the tag queue structure. */
417 CIRCLEQ_INIT(>ag_q);
419 /* Get our data from global(1) */
420 snprintf(command, sizeof(command),
421 "(global -x '%s'; global -xr '%s') 2>/dev/null", tag, tag);
422 if (fp = popen(command, "r")) {
423 while (fgets(buf, sizeof(buf), fp)) {
424 char *name, *file, *line;
432 if (buf[strlen(buf) - 1] == '\n')
433 buf[strlen(buf) - 1] = 0;
435 while (fgetc(fp) != '\n')
438 if (getentry(buf, &name, &file, &line)) {
440 * Couldn't parse this line for some reason.
441 * We'll just pretend it never happened.
447 gtag_p1 = malloc(sizeof(struct gtag));
450 error("malloc() failed");
453 gtag_p1->file = malloc(strlen(file) + 1);
454 if (!gtag_p1->file) {
456 error("malloc() failed");
459 strcpy(gtag_p1->file, file);
460 gtag_p1->line = atoi(line);
461 CIRCLEQ_INSERT_TAIL(>ag_q, gtag_p1, ptrs);
466 /* Check to see if we found anything. */
467 if (gtag_q.cqh_first == (void *)>ag_q)
468 return (NULL); /* Nope! */
470 curgtag = gtag_q.cqh_first;
471 return (curgtag->file);
475 * Return the filename required for the next gtag in the queue that was setup
476 * by findgtag(). The next call to gtagsearch() will try to position at the
483 /* No tag stack loaded */
487 curgtag = curgtag->ptrs.cqe_next;
488 if (curgtag == (void *)>ag_q) {
489 /* Wrapped around to the head of the queue */
490 curgtag = ((struct gtag_q *)curgtag)->cqh_first;
493 return (curgtag->file);
497 * Return the filename required for the previous gtag in the queue that was
498 * setup by findgtat(). The next call to gtagsearch() will try to position
499 * at the appropriate tag.
505 /* No tag stack loaded */
509 curgtag = curgtag->ptrs.cqe_prev;
510 if (curgtag == (void *)>ag_q) {
511 /* Wrapped around to the head of the queue */
512 curgtag = ((struct gtag_q *)curgtag)->cqh_last;
515 return (curgtag->file);
519 * Position the current file at at what is hopefully the tag that was chosen
520 * using either findtag() or one of nextgtag() and prevgtag(). Returns -1
521 * if it was unable to position at the tag, 0 if succesful.
527 return (-1); /* No gtags loaded! */
529 jump_back(curgtag->line);
532 * XXX We'll assume we were successful --- jump_back() will call error()
533 * if it fails, so the user will receive some kind of notification.
534 * Eventually, jump_back() should do its work silently and let us
535 * perform the error notification, eventually allowing our caller
536 * (presumably tagsearch()) to go error("Could not locate tag.");
542 * The getentry() parses output from the global(1) command. The output
543 * must be in the format described below. Returns 0 on success, -1 on
544 * error. The tag, file, and line will each be NUL-terminated pointers
547 * gtags temporary file format.
548 * <tag> <lineno> <file> <image>
551 * +------------------------------------------------
552 * |main 30 main.c main(argc, argv)
553 * |func 21 subr.c func(arg)
556 getentry(buf, tag, file, line)
557 char *buf; /* output from global -x */
558 char **tag; /* name of the tag we actually found */
559 char **file; /* file in which to find this tag */
560 char **line; /* line number of file where this tag is found */
564 for (*tag = p; *p && !isspace(*p); p++) /* tag name */
569 for (; *p && isspace(*p); p++) /* (skip blanks) */
573 *line = p; /* line no */
574 for (*line = p; *p && !isspace(*p); p++)
579 for (; *p && isspace(*p); p++) /* (skip blanks) */
583 *file = p; /* file name */
584 for (*file = p; *p && !isspace(*p); p++)
591 if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
594 return (-1); /* ERROR */