summaryrefslogtreecommitdiffstats
path: root/usr.bin/more/tags.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/more/tags.c')
-rw-r--r--usr.bin/more/tags.c426
1 files changed, 389 insertions, 37 deletions
diff --git a/usr.bin/more/tags.c b/usr.bin/more/tags.c
index 5486567..706a2e7 100644
--- a/usr.bin/more/tags.c
+++ b/usr.bin/more/tags.c
@@ -2,6 +2,7 @@
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
+ * Portions copyright (c) 1996, 1997, 1998 Shigio Yamaguchi.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,43 +37,158 @@
static char sccsid[] = "@(#)tags.c 8.1 (Berkeley) 6/6/93";
#endif /* not lint */
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <less.h>
+
+#include "less.h"
#define WHITESP(c) ((c)==' ' || (c)=='\t')
-char *tagfile;
-char *tagpattern;
+extern int sigs;
+char *tagfile; /* Name of source file containing current tag */
+
+static enum { CTAGS, GTAGS } tagstyle = GTAGS;
+static char *findctag(), *findgtag(), *nextgtag(), *prevgtag();
+static ctagsearch(), gtagsearch();
+
+/*
+ * Load information about the tag. The global variable tagfile will point to
+ * the file that contains the tag, or will be NULL if information could not be
+ * found (in which case an error message will have been printed). After
+ * loading the file named by tagfile, tagsearch() should be called to
+ * set the current position to the tag.
+ */
+findtag(tag)
+ char *tag; /* The tag to load */
+{
+ /*
+ * Try using gtags or ctags first, as indicated by tagstyle. If
+ * that fails, try the other. Someday there may even be a way to
+ * assert a certain tagstyle...
+ */
+ switch(tagstyle) {
+ case CTAGS:
+ tagfile = findctag(tag);
+ if (!tagfile && (tagfile = findgtag(tag))) tagstyle = GTAGS;
+ if (tagfile) return;
+ break;
+ case GTAGS:
+ /* Would be nice to print the number of tag references
+ * we found (for nexttag() and prevtag()) in a (not-)error()
+ * message. */
+ tagfile = findgtag(tag);
+ if (!tagfile && (tagfile = findctag(tag))) tagstyle = CTAGS;
+ if (tagfile) return;
+ break;
+ }
-static char *tags = "tags";
+ error("could not find relevent tag information");
+}
+
+/*
+ * Load information about the next number'th tag, if the last findtag() call
+ * found multiple tag references. The global variable tagfile will point to the
+ * file that contains the tag, or will be NULL if information could not be
+ * found (in which case an error message will have been printed). After
+ * loading the file named by tagfile, tagsearch() should be called to set
+ * the current position to the tag.
+ */
+nexttag(number)
+ int number; /* How many tags to go forward by */
+{
+ if (number < 0) number = -number; /* positive only, please */
+
+ switch(tagstyle) {
+ case CTAGS:
+ break;
+ case GTAGS:
+ while (number--) tagfile = nextgtag();
+ break;
+ }
+ if (!tagfile)
+ error("no next tag");
+}
+
+/*
+ * The antithesis to nexttag().
+ */
+prevtag(number)
+ int number; /* How many tags to go backwards by */
+{
+ if (number < 0) number = -number; /* positive only, please */
+
+ switch(tagstyle) {
+ case CTAGS:
+ break;
+ case GTAGS:
+ while (number--) tagfile = prevgtag();
+ break;
+ }
+ if (!tagfile)
+ error("no previous tag");
+}
+
+/*
+ * Try and position the currently loaded file at the last tag that was
+ * succesfully passed to findtag() or chosen with nexttag() and prevtag().
+ * An error message will be printed if unsuccessful.
+ */
+tagsearch()
+{
+ switch(tagstyle) {
+ case CTAGS:
+ if (ctagsearch())
+ error("could not locate ctag");
+ return;
+ case GTAGS:
+ if (gtagsearch())
+ error("could not locate gtag");
+ return;
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * ctags
+ *
+ */
extern int linenums;
-extern int sigs;
extern char *line;
+static char *ctagpattern;
+
/*
- * Find a tag in the "tags" file.
- * Sets "tagfile" to the name of the file containing the tag,
- * and "tagpattern" to the search pattern which should be used
- * to find the tag.
+ * Find specified tag in the ctags(1)-format tag file ctagfile. Returns
+ * pointer to a static buffer holding the name of the file containing
+ * the tag. Returns NULL on failure. The next call to ctagsearch() will
+ * position the currently loaded file at the tag.
*/
-findtag(tag)
- register char *tag;
+static char *
+findctag(tag)
+ register char *tag; /* tag to search for */
{
register char *p;
register FILE *f;
register int taglen;
int search_char;
- static char tline[200];
+ static char tline[200]; /* XXX should be dynamic */
+ const char *ctagfile = "tags";
+ char *retr;
- if ((f = fopen(tags, "r")) == NULL)
- {
- error("No tags file");
- tagfile = NULL;
- return;
- }
+ if ((f = fopen(ctagfile, "r")) == NULL)
+ return (NULL);
taglen = strlen(tag);
@@ -81,6 +197,9 @@ findtag(tag)
*/
while (fgets(tline, sizeof(tline), f) != NULL)
{
+ if (sigs)
+ break; /* abandon */
+
if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
continue;
@@ -92,10 +211,9 @@ findtag(tag)
* search characters.
* Parse the line and extract these parts.
*/
- tagfile = tagpattern = NULL;
/*
- * Skip over the whitespace after the tag name.
+ * Skip over the tag and the whitespace after the tag name.
*/
for (p = tline; !WHITESP(*p) && *p != '\0'; p++)
continue;
@@ -107,9 +225,9 @@ findtag(tag)
/*
* Save the file name.
- * Skip over the whitespace after the file name.
+ * Skip over the filename and whitespace after the file name.
*/
- tagfile = p;
+ retr = p;
while (!WHITESP(*p) && *p != '\0')
p++;
*p++ = '\0';
@@ -127,7 +245,7 @@ findtag(tag)
search_char = *p++;
if (*p == '^')
p++;
- tagpattern = p;
+ ctagpattern = p; /* cock ctagsearch() */
while (*p != search_char && *p != '\0')
p++;
if (p[-1] == '$')
@@ -135,23 +253,26 @@ findtag(tag)
*p = '\0';
(void)fclose(f);
- return;
+ return (retr);
}
(void)fclose(f);
- error("No such tag in tags file");
- tagfile = NULL;
+ return (NULL);
}
/*
- * Search for a tag.
+ * Locate the tag that was loaded by findctag().
* This is a stripped-down version of search().
* We don't use search() for several reasons:
* - We don't want to blow away any search string we may have saved.
* - The various regular-expression functions (from different systems:
* regcmp vs. re_comp) behave differently in the presence of
* parentheses (which are almost always found in a tag).
+ *
+ * Returns -1 if it was unable to position at the requested pattern,
+ * 0 otherwise.
*/
-tagsearch()
+static
+ctagsearch()
{
off_t pos, linepos, forw_raw_line();
int linenum;
@@ -166,7 +287,7 @@ tagsearch()
* until we hit end-of-file.
*/
if (sigs)
- return (1);
+ return (-1);
/*
* Read the next line, and save the
@@ -178,13 +299,7 @@ tagsearch()
linenum++;
if (pos == NULL_POSITION)
- {
- /*
- * We hit EOF without a match.
- */
- error("Tag not found");
- return (1);
- }
+ return (-1); /* Tag not found. */
/*
* If we're using line numbers, we might as well
@@ -197,10 +312,247 @@ tagsearch()
/*
* Test the line to see if we have a match.
*/
- if (strcmp(tagpattern, line) == 0)
+ if (strcmp(ctagpattern, line) == 0)
break;
}
jump_loc(linepos);
return (0);
}
+
+
+/*******************************************************************************
+ *
+ * gtags
+ *
+ */
+
+/*
+ * The findgtag() and getentry() functions are stolen, more or less, from the
+ * patches to nvi-1.79 included in Shigio Yamaguchi's global-3.42 distribution.
+ */
+
+/*
+ * The queue of tags generated by the last findgtag() call.
+ */
+static CIRCLEQ_HEAD(gtag_q, gtag) gtag_q;
+struct gtag {
+ CIRCLEQ_ENTRY(gtag) ptrs;
+ char *file; /* source file containing the tag */
+ int line; /* appropriate line number of source file */
+};
+static struct gtag *curgtag;
+static getentry();
+
+/*
+ * The findgtag() will try and load information about the requested tag.
+ * It does this by calling "global -x tag; global -xr tag;" and storing the
+ * parsed output for future use by gtagsearch_f() and gtagsearch_b(). A
+ * pointer to a static buffer containing the name of the source file will
+ * be returned, or NULL on failure. The first filename printed by global is
+ * returned (hopefully the function definition) and the other filenames may
+ * be accessed by nextgtag() and prevgtag().
+ */
+static char *
+findgtag(tag)
+ char *tag; /* tag to load */
+{
+ struct gtag *gtag_p1, *gtag_p2;
+ char command[512];
+ char buf[256];
+ FILE *fp;
+
+ if (!tag) return (NULL); /* Sanity check */
+
+ /* Clear any existing tag circle queue */
+ /* XXX Ideally, we wouldn't do this until after we know that we
+ * can load some other tag information. */
+ curgtag = NULL;
+ gtag_p1 = gtag_q.cqh_first;
+ if (gtag_p1) while (gtag_p1 != (void *)&gtag_q) {
+ gtag_p2 = gtag_p1->ptrs.cqe_next;
+ free(gtag_p1);
+ gtag_p1 = gtag_p2;
+ }
+
+ /* Allocate and initialize the tag queue structure. */
+ CIRCLEQ_INIT(&gtag_q);
+
+ /* Get our data from global(1) */
+ snprintf(command, sizeof(command),
+ "(global -x '%s'; global -xr '%s') 2>/dev/null", tag, tag);
+ if (fp = popen(command, "r")) {
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *name, *file, *line;
+
+ if (sigs) {
+ pclose(fp);
+ return (NULL);
+ }
+
+ /* chop(buf) */
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = 0;
+ else
+ while (fgetc(fp) != '\n')
+ ;
+
+ if (getentry(buf, &name, &file, &line)) {
+ /*
+ * Couldn't parse this line for some reason.
+ * We'll just pretend it never happened.
+ */
+ break;
+ }
+
+ /* Add to queue */
+ gtag_p1 = malloc(sizeof(struct gtag));
+ if (!gtag_p1) {
+ pclose(fp);
+ error("malloc() failed");
+ return (NULL);
+ }
+ gtag_p1->file = file;
+ gtag_p1->file = malloc(strlen(file));
+ if (!gtag_p1->file) {
+ pclose(fp);
+ error("malloc() failed");
+ return (NULL);
+ }
+ strcpy(gtag_p1->file, file);
+ gtag_p1->line = atoi(line);
+ CIRCLEQ_INSERT_TAIL(&gtag_q, gtag_p1, ptrs);
+ }
+ pclose(fp);
+ }
+
+ /* Check to see if we found anything. */
+ if (gtag_q.cqh_first == (void *)&gtag_q)
+ return (NULL); /* Nope! */
+
+ curgtag = gtag_q.cqh_first;
+ return (curgtag->file);
+}
+
+/*
+ * Return the filename required for the next gtag in the queue that was setup
+ * by findgtag(). The next call to gtagsearch() will try to position at the
+ * appropriate tag.
+ */
+static char *
+nextgtag()
+{
+ if (!curgtag) {
+ /* No tag stack loaded */
+ return (NULL);
+ }
+
+ curgtag = curgtag->ptrs.cqe_next;
+ if (curgtag == (void *)&gtag_q) {
+ /* Wrapped around to the head of the queue */
+ curgtag = ((struct gtag_q *)curgtag)->cqh_first;
+ }
+
+ return (curgtag->file);
+}
+
+/*
+ * Return the filename required for the previous gtag in the queue that was
+ * setup by findgtat(). The next call to gtagsearch() will try to position
+ * at the appropriate tag.
+ */
+static char *
+prevgtag()
+{
+ if (!curgtag) {
+ /* No tag stack loaded */
+ return (NULL);
+ }
+
+ curgtag = curgtag->ptrs.cqe_prev;
+ if (curgtag == (void *)&gtag_q) {
+ /* Wrapped around to the head of the queue */
+ curgtag = ((struct gtag_q *)curgtag)->cqh_last;
+ }
+
+ return (curgtag->file);
+}
+
+/*
+ * Position the current file at at what is hopefully the tag that was chosen
+ * using either findtag() or one of nextgtag() and prevgtag(). Returns -1
+ * if it was unable to position at the tag, 0 if succesful.
+ */
+static
+gtagsearch()
+{
+ if (!curgtag)
+ return (-1); /* No gtags loaded! */
+
+ jump_back(curgtag->line);
+
+ /*
+ * XXX We'll assume we were successful --- jump_back() will call error()
+ * if it fails, so the user will receive some kind of notification.
+ * Eventually, jump_back() should do its work silently and let us
+ * perform the error notification, eventually allowing our caller
+ * (presumably tagsearch()) to go error("Could not locate tag.");
+ */
+ return (0);
+}
+
+/*
+ * The getentry() parses output from the global(1) command. The output
+ * must be in the format described below. Returns 0 on success, -1 on
+ * error. The tag, file, and line will each be NUL-terminated pointers
+ * into buf.
+ *
+ * gtags temporary file format.
+ * <tag> <lineno> <file> <image>
+ *
+ * sample.
+ * +------------------------------------------------
+ * |main 30 main.c main(argc, argv)
+ * |func 21 subr.c func(arg)
+ */
+static
+getentry(buf, tag, file, line)
+ char *buf; /* output from global -x */
+ char **tag; /* name of the tag we actually found */
+ char **file; /* file in which to find this tag */
+ char **line; /* line number of file where this tag is found */
+{
+ char *p = buf;
+
+ for (*tag = p; *p && !isspace(*p); p++) /* tag name */
+ ;
+ if (*p == 0)
+ goto err;
+ *p++ = 0;
+ for (; *p && isspace(*p); p++) /* (skip blanks) */
+ ;
+ if (*p == 0)
+ goto err;
+ *line = p; /* line no */
+ for (*line = p; *p && !isspace(*p); p++)
+ ;
+ if (*p == 0)
+ goto err;
+ *p++ = 0;
+ for (; *p && isspace(*p); p++) /* (skip blanks) */
+ ;
+ if (*p == 0)
+ goto err;
+ *file = p; /* file name */
+ for (*file = p; *p && !isspace(*p); p++)
+ ;
+ if (*p == 0)
+ goto err;
+ *p = 0;
+
+ /* value check */
+ if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
+ return (0); /* OK */
+err:
+ return (-1); /* ERROR */
+}
OpenPOWER on IntegriCloud