Grok gtags too.

This commit is contained in:
Tim Vanderhoek 1999-06-01 20:02:34 +00:00
parent e6923505ee
commit ac8ab44cf6
7 changed files with 427 additions and 44 deletions

View File

@ -38,7 +38,7 @@ static char sccsid[] = "@(#)command.c 8.1 (Berkeley) 6/6/93";
#ifndef lint
static const char rcsid[] =
"$Id$";
"$Id: command.c,v 1.11 1999/05/30 18:06:52 hoek Exp $";
#endif /* not lint */
#include <sys/param.h>
@ -364,6 +364,7 @@ commands()
register int action;
static int default_hscroll = 1;
static int saved_horiz_off = NO_HORIZ_OFF;
extern char *tagfile;
last_mca = 0;
scroll = (sc_height + 1) / 2;
@ -569,6 +570,24 @@ again: if (sigs)
start_mca(A_TAGFILE, "Tag: ");
c = getcc();
goto again;
case A_NEXTTAG:
if (number <= 0)
number = 1;
nexttag(number);
if (tagfile == NULL)
break;
if (edit(tagfile))
(void)tagsearch();
break;
case A_PREVTAG:
if (number <= 0)
number = 1;
prevtag(number);
if (tagfile == NULL)
break;
if (edit(tagfile))
(void)tagsearch();
break;
case A_FILE_LIST: /* show list of file names */
CMD_EXEC;
showlist();

View File

@ -38,7 +38,7 @@ static char sccsid[] = "@(#)decode.c 8.1 (Berkeley) 6/6/93";
#ifndef lint
static const char rcsid[] =
"$Id$";
"$Id: decode.c,v 1.2 1999/05/30 18:06:53 hoek Exp $";
#endif /* not lint */
/*
@ -133,6 +133,8 @@ static char cmdtable[] = {
'q',0, A_QUIT,
':','q',0, A_QUIT,
':','t',0, A_TAGFILE,
'T',0, A_PREVTAG,
't',0, A_NEXTTAG,
':', 'a', 0, A_FILE_LIST,
'Z','Z',0, A_QUIT,
};

View File

@ -33,7 +33,7 @@
*
* @(#)less.h 8.1 (Berkeley) 6/6/93
*
* $Id$
* $Id: less.h,v 1.3 1999/05/30 18:06:55 hoek Exp $
*/
#define NULL_POSITION ((off_t)(-1))
@ -90,3 +90,5 @@
#define A_L_COL 28
#define A_R_COL 29
#define A_HOME 30
#define A_NEXTTAG 31
#define A_PREVTAG 32

View File

@ -45,7 +45,7 @@ static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/7/93";
#ifndef lint
static const char rcsid[] =
"$Id$";
"$Id: main.c,v 1.11 1999/05/30 18:06:56 hoek Exp $";
#endif /* not lint */
/*
@ -82,7 +82,8 @@ extern int tagoption;
/*
* Edit a new file.
* Filename "-" means standard input.
* No filename means the "current" file, from the command line.
* No filename means the "current" file, from the command line. If called
* with the same filename in succession, filename will be closed and reopened.
*/
edit(filename)
register char *filename;

View File

@ -245,6 +245,10 @@ Examine the previous file.
If a number N is specified, the N-th previous file is examined.
.It Ic \&:t
Go to supplied tag.
.It Ic t
Go forward in tag queue [gtags only].
.It Ic T
Go backward in tag queue [gtags only].
.It Ic v
Invokes an editor to edit the current file being viewed.
The editor is taken from the environment variable
@ -286,6 +290,8 @@ Specifies terminal type, used by more to get the terminal
characteristics necessary to manipulate the screen.
.El
.Sh SEE ALSO
.Xr gtags 1 ,
.Xr global 1 ,
.Xr ctags 1 ,
.Xr vi 1
.Sh BUGS

View File

@ -31,6 +31,7 @@
:n, N * Examine the next file.
:p, P * Examine the previous file.
:t [tag] Examine the tag.
t, T Move forward or backward N tags in the gtags queue.
v Run an editor on the current file.
=, ^G Print current file name and stats.

View File

@ -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;
static char *tags = "tags";
extern int linenums;
extern int sigs;
extern char *line;
char *tagfile; /* Name of source file containing current tag */
static enum { CTAGS, GTAGS } tagstyle = GTAGS;
static char *findctag(), *findgtag(), *nextgtag(), *prevgtag();
static ctagsearch(), gtagsearch();
/*
* 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.
* 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)
register char *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;
}
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 char *line;
static char *ctagpattern;
/*
* 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.
*/
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 */
}