diff options
author | ps <ps@FreeBSD.org> | 2002-01-07 20:29:38 +0000 |
---|---|---|
committer | ps <ps@FreeBSD.org> | 2002-01-07 20:29:38 +0000 |
commit | c0c85bc41cb6c023adae56cf0a7e3495bd528cb2 (patch) | |
tree | 22ed5b4269586d3d5c21d37a27645897709341e3 /contrib/less/tags.c | |
parent | 5d465776b9e058088e1f29636cf7cdcb0c13eb0d (diff) | |
download | FreeBSD-src-c0c85bc41cb6c023adae56cf0a7e3495bd528cb2.zip FreeBSD-src-c0c85bc41cb6c023adae56cf0a7e3495bd528cb2.tar.gz |
Import less v371
Diffstat (limited to 'contrib/less/tags.c')
-rw-r--r-- | contrib/less/tags.c | 618 |
1 files changed, 571 insertions, 47 deletions
diff --git a/contrib/less/tags.c b/contrib/less/tags.c index cf531a6..e559e77 100644 --- a/contrib/less/tags.c +++ b/contrib/less/tags.c @@ -17,18 +17,154 @@ public char *tags = "tags"; -static char *tagfile; -static char *tagpattern; -static int taglinenum; -static int tagendline; +static int total; +static int curseq; extern int linenums; extern int sigs; -extern int jump_sline; + +enum tag_result { + TAG_FOUND, + TAG_NOFILE, + TAG_NOTAG, + TAG_NOTYPE, + TAG_INTR +}; + +/* + * Tag type + */ +enum { + T_CTAGS, /* 'tags': standard and extended format (ctags) */ + T_CTAGS_X, /* stdin: cross reference format (ctags) */ + T_GTAGS, /* 'GTAGS': function defenition (global) */ + T_GRTAGS, /* 'GRTAGS': function reference (global) */ + T_GSYMS, /* 'GSYMS': other symbols (global) */ + T_GPATH /* 'GPATH': path name (global) */ +}; + +static enum tag_result findctag(); +static enum tag_result findgtag(); +static char *nextgtag(); +static char *prevgtag(); +static POSITION ctagsearch(); +static POSITION gtagsearch(); +static int getentry(); + +/* + * The list of tags generated by the last findgtag() call. + * + * Use either pattern or line number. + * findgtag() always uses line number, so pattern is always NULL. + * findctag() usually either pattern (in which case line number is 0), + * or line number (in which case pattern is NULL). + */ +struct taglist { + struct tag *tl_first; + struct tag *tl_last; +}; +#define TAG_END ((struct tag *) &taglist) +static struct taglist taglist = { TAG_END, TAG_END }; +struct tag { + struct tag *next, *prev; /* List links */ + char *tag_file; /* Source file containing the tag */ + int tag_linenum; /* Appropriate line number in source file */ + char *tag_pattern; /* Pattern used to find the tag */ + char tag_endline; /* True if the pattern includes '$' */ +}; +static struct tag *curtag; + +#define TAG_INS(tp) \ + (tp)->next = taglist.tl_first; \ + (tp)->prev = TAG_END; \ + taglist.tl_first->prev = (tp); \ + taglist.tl_first = (tp); + +#define TAG_RM(tp) \ + (tp)->next->prev = (tp)->prev; \ + (tp)->prev->next = (tp)->next; + +/* + * Delete tag structures. + */ + public void +cleantags() +{ + register struct tag *tp; + + /* + * Delete any existing tag list. + * {{ Ideally, we wouldn't do this until after we know that we + * can load some other tag information. }} + */ + while ((tp = taglist.tl_first) != TAG_END) + { + TAG_RM(tp); + free(tp); + } + curtag = NULL; + total = curseq = 0; +} /* + * Create a new tag entry. + */ + static struct tag * +maketagent(name, file, linenum, pattern, endline) + char *name; + char *file; + int linenum; + char *pattern; + int endline; +{ + register struct tag *tp; + + tp = (struct tag *) ecalloc(sizeof(struct tag), 1); + tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char)); + strcpy(tp->tag_file, file); + tp->tag_linenum = linenum; + tp->tag_endline = endline; + if (pattern == NULL) + tp->tag_pattern = NULL; + else + { + tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char)); + strcpy(tp->tag_pattern, pattern); + } + return (tp); +} + +/* + * Get tag mode. + */ + public int +gettagtype() +{ + int f; + + if (strcmp(tags, "GTAGS") == 0) + return T_GTAGS; + if (strcmp(tags, "GRTAGS") == 0) + return T_GRTAGS; + if (strcmp(tags, "GSYMS") == 0) + return T_GSYMS; + if (strcmp(tags, "GPATH") == 0) + return T_GPATH; + if (strcmp(tags, "-") == 0) + return T_CTAGS_X; + f = open(tags, OPEN_READ); + if (f >= 0) + { + close(f); + return T_CTAGS; + } + return T_GTAGS; +} + +/* + * Find tags in tag file. * Find a tag in the "tags" file. - * Sets "tagfile" to the name of the file containing the tag, + * Sets "tag_file" to the name of the file containing the tag, * and "tagpattern" to the search pattern which should be used * to find the tag. */ @@ -36,24 +172,122 @@ extern int jump_sline; findtag(tag) register char *tag; { + int type = gettagtype(); + enum tag_result result; + + if (type == T_CTAGS) + result = findctag(tag); + else + result = findgtag(tag, type); + switch (result) + { + case TAG_FOUND: + case TAG_INTR: + break; + case TAG_NOFILE: + error("No tags file", NULL_PARG); + break; + case TAG_NOTAG: + error("No such tag in tags file", NULL_PARG); + break; + case TAG_NOTYPE: + error("unknown tag type", NULL_PARG); + break; + } +} + +/* + * Search for a tag. + */ + public POSITION +tagsearch() +{ + if (curtag == NULL) + return (NULL_POSITION); /* No gtags loaded! */ + if (curtag->tag_linenum != 0) + return gtagsearch(); + else + return ctagsearch(); +} + +/* + * Go to the next tag. + */ + public char * +nexttag(n) + int n; +{ + char *tagfile; + + while (n-- > 0) + tagfile = nextgtag(); + return tagfile; +} + +/* + * Go to the previous tag. + */ + public char * +prevtag(n) + int n; +{ + char *tagfile; + + while (n-- > 0) + tagfile = prevgtag(); + return tagfile; +} + +/* + * Return the total number of tags. + */ + public int +ntags() +{ + return total; +} + +/* + * Return the sequence number of current tag. + */ + public int +curr_tag() +{ + return curseq; +} + +/***************************************************************************** + * ctags + */ + +/* + * Find tags in the "tags" file. + * Sets curtag to the first tag entry. + */ + static enum tag_result +findctag(tag) + register char *tag; +{ char *p; - char *q; register FILE *f; register int taglen; + register int taglinenum; + char *tagfile; + char *tagpattern; + int tagendline; int search_char; int err; char tline[TAGLINE_SIZE]; + struct tag *tp; p = unquote_file(tags); f = fopen(p, "r"); free(p); if (f == NULL) - { - error("No tags file", NULL_PARG); - tagfile = NULL; - return; - } + return TAG_NOFILE; + cleantags(); + total = 0; taglen = strlen(tag); /* @@ -61,6 +295,9 @@ findtag(tag) */ while (fgets(tline, sizeof(tline), f) != NULL) { + if (tline[0] == '!') + /* Skip header of extended format. */ + continue; if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) continue; @@ -72,8 +309,7 @@ findtag(tag) * or a search pattern surrounded by a pair of delimiters. * Parse the line and extract these parts. */ - tagfile = tagpattern = NULL; - taglinenum = 0; + tagpattern = NULL; /* * Skip over the whitespace after the tag name. @@ -95,7 +331,6 @@ findtag(tag) if (*p == '\0') /* Pattern is missing! */ continue; - tagfile = save(tagfile); /* * First see if it is a line number. @@ -113,39 +348,39 @@ findtag(tag) search_char = *p++; if (*p == '^') p++; - tagpattern = (char *) ecalloc(strlen(p)+1, sizeof(char)); - q = tagpattern; + tagpattern = p; while (*p != search_char && *p != '\0') { if (*p == '\\') p++; - *q++ = *p++; + p++; } - tagendline = (q[-1] == '$'); + tagendline = (p[-1] == '$'); if (tagendline) - q--; - *q = '\0'; + p--; + *p = '\0'; } - - fclose(f); - return; + tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline); + TAG_INS(tp); + total++; } fclose(f); - error("No such tag in tags file", NULL_PARG); - tagfile = NULL; + if (total == 0) + return TAG_NOTAG; + curtag = taglist.tl_first; + curseq = 1; + return TAG_FOUND; } +/* + * Edit current tagged file. + */ public int edit_tagfile() { - int r; - - if (tagfile == NULL) + if (curtag == NULL) return (1); - r = edit(tagfile); - free(tagfile); - tagfile = NULL; - return (r); + return (edit(curtag->tag_file)); } /* @@ -157,21 +392,14 @@ edit_tagfile() * regcmp vs. re_comp) behave differently in the presence of * parentheses (which are almost always found in a tag). */ - public POSITION -tagsearch() + static POSITION +ctagsearch() { POSITION pos, linepos; int linenum; int len; char *line; - /* - * If we have the line number of the tag instead of the pattern, - * just use find_pos. - */ - if (taglinenum) - return (find_pos(taglinenum)); - pos = ch_zero(); linenum = find_linenum(pos); @@ -217,15 +445,311 @@ tagsearch() * If tagendline is set, make sure we match all * the way to end of line (no extra chars after the match). */ - len = strlen(tagpattern); - if (strncmp(tagpattern, line, len) == 0 && - (!tagendline || line[len] == '\0' || line[len] == '\r')) + len = strlen(curtag->tag_pattern); + if (strncmp(curtag->tag_pattern, line, len) == 0 && + (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r')) + { + curtag->tag_linenum = find_linenum(linepos); break; + } } - free(tagpattern); - tagpattern = NULL; return (linepos); } +/******************************************************************************* + * gtags + */ + +/* + * Find tags in the GLOBAL's tag file. + * The findgtag() will try and load information about the requested tag. + * It does this by calling "global -x tag" and storing the parsed output + * for future use by gtagsearch(). + * Sets curtag to the first tag entry. + */ + static enum tag_result +findgtag(tag, type) + char *tag; /* tag to load */ + int type; /* tags type */ +{ + char buf[256]; + FILE *fp; + struct tag *tp; + + if (type != T_CTAGS_X && tag == NULL) + return TAG_NOFILE; + + cleantags(); + total = 0; + + /* + * If type == T_CTAGS_X then read ctags's -x format from stdin + * else execute global(1) and read from it. + */ + if (type == T_CTAGS_X) + { + fp = stdin; + /* Set tag default because we cannot read stdin again. */ + tags = "tags"; + } else + { +#if !HAVE_POPEN + return TAG_NOFILE; +#else + char command[512]; + char *flag; + char *cmd = lgetenv("LESSGLOBALTAGS"); + + if (cmd == NULL || *cmd == '\0') + return TAG_NOFILE; + /* Get suitable flag value for global(1). */ + switch (type) + { + case T_GTAGS: + flag = "" ; + break; + case T_GRTAGS: + flag = "r"; + break; + case T_GSYMS: + flag = "s"; + break; + case T_GPATH: + flag = "P"; + break; + default: + return TAG_NOTYPE; + } + + /* Get our data from global(1). */ + tag = esc_metachars(tag); + sprintf(command, "%s -x%s %s", cmd, flag, tag); + free(tag); + fp = popen(command, "r"); +#endif + } + if (fp != NULL) + { + while (fgets(buf, sizeof(buf), fp)) + { + char *name, *file, *line; + + if (sigs) + { +#if HAVE_POPEN + if (fp != stdin) + pclose(fp); +#endif + return TAG_INTR; + } + if (buf[strlen(buf) - 1] == '\n') + buf[strlen(buf) - 1] = 0; + else + { + int c; + do { + c = fgetc(fp); + } while (c != '\n' && c != EOF); + } + + if (getentry(buf, &name, &file, &line)) + { + /* + * Couldn't parse this line for some reason. + * We'll just pretend it never happened. + */ + break; + } + + /* Make new entry and add to list. */ + tp = maketagent(name, file, atoi(line), NULL, 0); + TAG_INS(tp); + total++; + } + if (fp != stdin) + { + if (pclose(fp)) + { + curtag = NULL; + total = curseq = 0; + return TAG_NOFILE; + } + } + } + + /* Check to see if we found anything. */ + tp = taglist.tl_first; + if (tp == TAG_END) + return TAG_NOTAG; + curtag = tp; + curseq = 1; + return TAG_FOUND; +} + +static int circular = 0; /* 1: circular tag structure */ + +/* + * 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() +{ + struct tag *tp; + + if (curtag == NULL) + /* No tag loaded */ + return NULL; + + tp = curtag->next; + if (tp == TAG_END) + { + if (!circular) + return NULL; + /* Wrapped around to the head of the queue */ + curtag = taglist.tl_first; + curseq = 1; + } else + { + curtag = tp; + curseq++; + } + return (curtag->tag_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() +{ + struct tag *tp; + + if (curtag == NULL) + /* No tag loaded */ + return NULL; + + tp = curtag->prev; + if (tp == TAG_END) + { + if (!circular) + return NULL; + /* Wrapped around to the tail of the queue */ + curtag = taglist.tl_last; + curseq = total; + } else + { + curtag = tp; + curseq--; + } + return (curtag->tag_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 POSITION +gtagsearch() +{ + if (curtag == NULL) + return (NULL_POSITION); /* No gtags loaded! */ + return (find_pos(curtag->tag_linenum)); +} + +/* + * The getentry() parses both standard and extended ctags -x format. + * + * [standard format] + * <tag> <lineno> <file> <image> + * +------------------------------------------------ + * |main 30 main.c main(argc, argv) + * |func 21 subr.c func(arg) + * + * The following commands write this format. + * o Traditinal Ctags with -x option + * o Global with -x option + * See <http://www.gnu.org/software/global/global.html> + * + * [extended format] + * <tag> <type> <lineno> <file> <image> + * +---------------------------------------------------------- + * |main function 30 main.c main(argc, argv) + * |func function 21 subr.c func(arg) + * + * The following commands write this format. + * o Exuberant Ctags with -x option + * See <http://ctags.sourceforge.net> + * + * Returns 0 on success, -1 on error. + * The tag, file, and line will each be NUL-terminated pointers + * into buf. + */ + +#ifndef isspace +#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == '\f') +#endif +#ifndef isdigit +#define isdigit(c) ((c) >= '0' && (c <= '9')) +#endif + + static int +getentry(buf, tag, file, line) + char *buf; /* standard or extended ctags -x format data */ + 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) + return (-1); + *p++ = 0; + for ( ; *p && isspace(*p); p++) /* (skip blanks) */ + ; + if (*p == 0) + return (-1); + /* + * If the second part begin with other than digit, + * it is assumed tag type. Skip it. + */ + if (!isdigit(*p)) + { + for ( ; *p && !isspace(*p); p++) /* (skip tag type) */ + ; + for (; *p && isspace(*p); p++) /* (skip blanks) */ + ; + } + if (!isdigit(*p)) + return (-1); + *line = p; /* line number */ + for (*line = p; *p && !isspace(*p); p++) + ; + if (*p == 0) + return (-1); + *p++ = 0; + for ( ; *p && isspace(*p); p++) /* (skip blanks) */ + ; + if (*p == 0) + return (-1); + *file = p; /* file name */ + for (*file = p; *p && !isspace(*p); p++) + ; + if (*p == 0) + return (-1); + *p = 0; + + /* value check */ + if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0) + return (0); + return (-1); +} + #endif |