diff options
Diffstat (limited to 'usr.bin/more/tags.c')
-rw-r--r-- | usr.bin/more/tags.c | 426 |
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 *)>ag_q) { + gtag_p2 = gtag_p1->ptrs.cqe_next; + free(gtag_p1); + gtag_p1 = gtag_p2; + } + + /* Allocate and initialize the tag queue structure. */ + CIRCLEQ_INIT(>ag_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(>ag_q, gtag_p1, ptrs); + } + pclose(fp); + } + + /* Check to see if we found anything. */ + if (gtag_q.cqh_first == (void *)>ag_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 *)>ag_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 *)>ag_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 */ +} |