1238730Sdelphij/*
2330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
3238730Sdelphij *
4238730Sdelphij * You may distribute under the terms of either the GNU General Public
5238730Sdelphij * License or the Less License, as specified in the README file.
6238730Sdelphij *
7238730Sdelphij * For more information, see the README file.
8238730Sdelphij */
960786Sps
1060786Sps
1160786Sps#include "less.h"
1260786Sps
1360786Sps#define	WHITESP(c)	((c)==' ' || (c)=='\t')
1460786Sps
1560786Sps#if TAGS
1660786Sps
17294286Sdelphijpublic char ztags[] = "tags";
18294286Sdelphijpublic char *tags = ztags;
1960786Sps
2089019Spsstatic int total;
2189019Spsstatic int curseq;
2260786Sps
2360786Spsextern int linenums;
2460786Spsextern int sigs;
25330571Sdelphijextern int ctldisp;
2660786Sps
2789019Spsenum tag_result {
2889019Sps	TAG_FOUND,
2989019Sps	TAG_NOFILE,
3089019Sps	TAG_NOTAG,
3189019Sps	TAG_NOTYPE,
3289019Sps	TAG_INTR
3389019Sps};
3489019Sps
3560786Sps/*
3689019Sps * Tag type
3789019Sps */
3889019Spsenum {
3989019Sps	T_CTAGS,	/* 'tags': standard and extended format (ctags) */
4089019Sps	T_CTAGS_X,	/* stdin: cross reference format (ctags) */
4189019Sps	T_GTAGS,	/* 'GTAGS': function defenition (global) */
4289019Sps	T_GRTAGS,	/* 'GRTAGS': function reference (global) */
4389019Sps	T_GSYMS,	/* 'GSYMS': other symbols (global) */
4489019Sps	T_GPATH		/* 'GPATH': path name (global) */
4589019Sps};
4689019Sps
4789019Spsstatic enum tag_result findctag();
4889019Spsstatic enum tag_result findgtag();
4989019Spsstatic char *nextgtag();
5089019Spsstatic char *prevgtag();
5189019Spsstatic POSITION ctagsearch();
5289019Spsstatic POSITION gtagsearch();
5389019Spsstatic int getentry();
5489019Sps
5589019Sps/*
5689019Sps * The list of tags generated by the last findgtag() call.
5789019Sps *
5889019Sps * Use either pattern or line number.
5989019Sps * findgtag() always uses line number, so pattern is always NULL.
60170256Sdelphij * findctag() uses either pattern (in which case line number is 0),
6189019Sps * or line number (in which case pattern is NULL).
6289019Sps */
6389019Spsstruct taglist {
6489019Sps	struct tag *tl_first;
6589019Sps	struct tag *tl_last;
6689019Sps};
6789019Spsstruct tag {
6889019Sps	struct tag *next, *prev; /* List links */
6989019Sps	char *tag_file;		/* Source file containing the tag */
70128345Stjr	LINENUM tag_linenum;	/* Appropriate line number in source file */
7189019Sps	char *tag_pattern;	/* Pattern used to find the tag */
7289019Sps	char tag_endline;	/* True if the pattern includes '$' */
7389019Sps};
74330571Sdelphij#define TAG_END  ((struct tag *) &taglist)
75330571Sdelphijstatic struct taglist taglist = { TAG_END, TAG_END };
7689019Spsstatic struct tag *curtag;
7789019Sps
7889019Sps#define TAG_INS(tp) \
79170256Sdelphij	(tp)->next = TAG_END; \
80170256Sdelphij	(tp)->prev = taglist.tl_last; \
81170256Sdelphij	taglist.tl_last->next = (tp); \
82170256Sdelphij	taglist.tl_last = (tp);
8389019Sps
8489019Sps#define TAG_RM(tp) \
8589019Sps	(tp)->next->prev = (tp)->prev; \
8689019Sps	(tp)->prev->next = (tp)->next;
8789019Sps
8889019Sps/*
8989019Sps * Delete tag structures.
9089019Sps */
9189019Sps	public void
9289019Spscleantags()
9389019Sps{
94330571Sdelphij	struct tag *tp;
9589019Sps
9689019Sps	/*
9789019Sps	 * Delete any existing tag list.
9889019Sps	 * {{ Ideally, we wouldn't do this until after we know that we
9989019Sps	 *    can load some other tag information. }}
10089019Sps	 */
10189019Sps	while ((tp = taglist.tl_first) != TAG_END)
10289019Sps	{
10389019Sps		TAG_RM(tp);
104330571Sdelphij		free(tp->tag_file);
105330571Sdelphij		free(tp->tag_pattern);
10689019Sps		free(tp);
10789019Sps	}
10889019Sps	curtag = NULL;
10989019Sps	total = curseq = 0;
11089019Sps}
11189019Sps
11289019Sps/*
11389019Sps * Create a new tag entry.
11489019Sps */
11589019Sps	static struct tag *
11689019Spsmaketagent(name, file, linenum, pattern, endline)
11789019Sps	char *name;
11889019Sps	char *file;
119128345Stjr	LINENUM linenum;
12089019Sps	char *pattern;
12189019Sps	int endline;
12289019Sps{
123330571Sdelphij	struct tag *tp;
12489019Sps
12589019Sps	tp = (struct tag *) ecalloc(sizeof(struct tag), 1);
12689019Sps	tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char));
12789019Sps	strcpy(tp->tag_file, file);
12889019Sps	tp->tag_linenum = linenum;
12989019Sps	tp->tag_endline = endline;
13089019Sps	if (pattern == NULL)
13189019Sps		tp->tag_pattern = NULL;
13289019Sps	else
13389019Sps	{
13489019Sps		tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char));
13589019Sps		strcpy(tp->tag_pattern, pattern);
13689019Sps	}
13789019Sps	return (tp);
13889019Sps}
13989019Sps
14089019Sps/*
14189019Sps * Get tag mode.
14289019Sps */
14389019Sps	public int
14489019Spsgettagtype()
14589019Sps{
14689019Sps	int f;
14789019Sps
14889019Sps	if (strcmp(tags, "GTAGS") == 0)
14989019Sps		return T_GTAGS;
15089019Sps	if (strcmp(tags, "GRTAGS") == 0)
15189019Sps		return T_GRTAGS;
15289019Sps	if (strcmp(tags, "GSYMS") == 0)
15389019Sps		return T_GSYMS;
15489019Sps	if (strcmp(tags, "GPATH") == 0)
15589019Sps		return T_GPATH;
15689019Sps	if (strcmp(tags, "-") == 0)
15789019Sps		return T_CTAGS_X;
15889019Sps	f = open(tags, OPEN_READ);
15989019Sps	if (f >= 0)
16089019Sps	{
16189019Sps		close(f);
16289019Sps		return T_CTAGS;
16389019Sps	}
16489019Sps	return T_GTAGS;
16589019Sps}
16689019Sps
16789019Sps/*
16889019Sps * Find tags in tag file.
16960786Sps * Find a tag in the "tags" file.
17089019Sps * Sets "tag_file" to the name of the file containing the tag,
17160786Sps * and "tagpattern" to the search pattern which should be used
17260786Sps * to find the tag.
17360786Sps */
17460786Sps	public void
17560786Spsfindtag(tag)
176330571Sdelphij	char *tag;
17760786Sps{
17889019Sps	int type = gettagtype();
17989019Sps	enum tag_result result;
18089019Sps
18189019Sps	if (type == T_CTAGS)
18289019Sps		result = findctag(tag);
18389019Sps	else
18489019Sps		result = findgtag(tag, type);
18589019Sps	switch (result)
18689019Sps	{
18789019Sps	case TAG_FOUND:
18889019Sps	case TAG_INTR:
18989019Sps		break;
19089019Sps	case TAG_NOFILE:
19189019Sps		error("No tags file", NULL_PARG);
19289019Sps		break;
19389019Sps	case TAG_NOTAG:
19489019Sps		error("No such tag in tags file", NULL_PARG);
19589019Sps		break;
19689019Sps	case TAG_NOTYPE:
19789019Sps		error("unknown tag type", NULL_PARG);
19889019Sps		break;
19989019Sps	}
20089019Sps}
20189019Sps
20289019Sps/*
20389019Sps * Search for a tag.
20489019Sps */
20589019Sps	public POSITION
20689019Spstagsearch()
20789019Sps{
20889019Sps	if (curtag == NULL)
20989019Sps		return (NULL_POSITION);  /* No gtags loaded! */
21089019Sps	if (curtag->tag_linenum != 0)
21189019Sps		return gtagsearch();
21289019Sps	else
21389019Sps		return ctagsearch();
21489019Sps}
21589019Sps
21689019Sps/*
21789019Sps * Go to the next tag.
21889019Sps */
21989019Sps	public char *
22089019Spsnexttag(n)
22189019Sps	int n;
22289019Sps{
223128345Stjr	char *tagfile = (char *) NULL;
22489019Sps
22589019Sps	while (n-- > 0)
22689019Sps		tagfile = nextgtag();
22789019Sps	return tagfile;
22889019Sps}
22989019Sps
23089019Sps/*
23189019Sps * Go to the previous tag.
23289019Sps */
23389019Sps	public char *
23489019Spsprevtag(n)
23589019Sps	int n;
23689019Sps{
237128345Stjr	char *tagfile = (char *) NULL;
23889019Sps
23989019Sps	while (n-- > 0)
24089019Sps		tagfile = prevgtag();
24189019Sps	return tagfile;
24289019Sps}
24389019Sps
24489019Sps/*
24589019Sps * Return the total number of tags.
24689019Sps */
24789019Sps	public int
24889019Spsntags()
24989019Sps{
25089019Sps	return total;
25189019Sps}
25289019Sps
25389019Sps/*
25489019Sps * Return the sequence number of current tag.
25589019Sps */
25689019Sps	public int
25789019Spscurr_tag()
25889019Sps{
25989019Sps	return curseq;
26089019Sps}
26189019Sps
26289019Sps/*****************************************************************************
26389019Sps * ctags
26489019Sps */
26589019Sps
26689019Sps/*
26789019Sps * Find tags in the "tags" file.
26889019Sps * Sets curtag to the first tag entry.
26989019Sps */
27089019Sps	static enum tag_result
27189019Spsfindctag(tag)
272330571Sdelphij	char *tag;
27389019Sps{
27460786Sps	char *p;
275330571Sdelphij	FILE *f;
276330571Sdelphij	int taglen;
277128345Stjr	LINENUM taglinenum;
27889019Sps	char *tagfile;
27989019Sps	char *tagpattern;
28089019Sps	int tagendline;
28160786Sps	int search_char;
28260786Sps	int err;
28360786Sps	char tline[TAGLINE_SIZE];
28489019Sps	struct tag *tp;
28560786Sps
286128345Stjr	p = shell_unquote(tags);
28760786Sps	f = fopen(p, "r");
28860786Sps	free(p);
28960786Sps	if (f == NULL)
29089019Sps		return TAG_NOFILE;
29160786Sps
29289019Sps	cleantags();
29389019Sps	total = 0;
294294286Sdelphij	taglen = (int) strlen(tag);
29560786Sps
29660786Sps	/*
29760786Sps	 * Search the tags file for the desired tag.
29860786Sps	 */
29960786Sps	while (fgets(tline, sizeof(tline), f) != NULL)
30060786Sps	{
30189019Sps		if (tline[0] == '!')
30289019Sps			/* Skip header of extended format. */
30389019Sps			continue;
30460786Sps		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
30560786Sps			continue;
30660786Sps
30760786Sps		/*
30860786Sps		 * Found it.
30960786Sps		 * The line contains the tag, the filename and the
31060786Sps		 * location in the file, separated by white space.
31160786Sps		 * The location is either a decimal line number,
31260786Sps		 * or a search pattern surrounded by a pair of delimiters.
31360786Sps		 * Parse the line and extract these parts.
31460786Sps		 */
31589019Sps		tagpattern = NULL;
31660786Sps
31760786Sps		/*
31860786Sps		 * Skip over the whitespace after the tag name.
31960786Sps		 */
32060786Sps		p = skipsp(tline+taglen);
32160786Sps		if (*p == '\0')
32260786Sps			/* File name is missing! */
32360786Sps			continue;
32460786Sps
32560786Sps		/*
32660786Sps		 * Save the file name.
32760786Sps		 * Skip over the whitespace after the file name.
32860786Sps		 */
32960786Sps		tagfile = p;
33060786Sps		while (!WHITESP(*p) && *p != '\0')
33160786Sps			p++;
33260786Sps		*p++ = '\0';
33360786Sps		p = skipsp(p);
33460786Sps		if (*p == '\0')
33560786Sps			/* Pattern is missing! */
33660786Sps			continue;
33760786Sps
33860786Sps		/*
33960786Sps		 * First see if it is a line number.
34060786Sps		 */
341128345Stjr		tagendline = 0;
34260786Sps		taglinenum = getnum(&p, 0, &err);
34360786Sps		if (err)
34460786Sps		{
34560786Sps			/*
34660786Sps			 * No, it must be a pattern.
34760786Sps			 * Delete the initial "^" (if present) and
34860786Sps			 * the final "$" from the pattern.
34960786Sps			 * Delete any backslash in the pattern.
35060786Sps			 */
35160786Sps			taglinenum = 0;
35260786Sps			search_char = *p++;
35360786Sps			if (*p == '^')
35460786Sps				p++;
35589019Sps			tagpattern = p;
35660786Sps			while (*p != search_char && *p != '\0')
35760786Sps			{
35860786Sps				if (*p == '\\')
35960786Sps					p++;
36089019Sps				p++;
36160786Sps			}
36289019Sps			tagendline = (p[-1] == '$');
36360786Sps			if (tagendline)
36489019Sps				p--;
36589019Sps			*p = '\0';
36660786Sps		}
36789019Sps		tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
36889019Sps		TAG_INS(tp);
36989019Sps		total++;
37060786Sps	}
37160786Sps	fclose(f);
37289019Sps	if (total == 0)
37389019Sps		return TAG_NOTAG;
37489019Sps	curtag = taglist.tl_first;
37589019Sps	curseq = 1;
37689019Sps	return TAG_FOUND;
37760786Sps}
37860786Sps
37989019Sps/*
38089019Sps * Edit current tagged file.
38189019Sps */
38260786Sps	public int
38360786Spsedit_tagfile()
38460786Sps{
38589019Sps	if (curtag == NULL)
38660786Sps		return (1);
38789019Sps	return (edit(curtag->tag_file));
38860786Sps}
38960786Sps
390330571Sdelphij	static int
391330571Sdelphijcurtag_match(char const *line, POSITION linepos)
392330571Sdelphij{
393330571Sdelphij	/*
394330571Sdelphij	 * Test the line to see if we have a match.
395330571Sdelphij	 * Use strncmp because the pattern may be
396330571Sdelphij	 * truncated (in the tags file) if it is too long.
397330571Sdelphij	 * If tagendline is set, make sure we match all
398330571Sdelphij	 * the way to end of line (no extra chars after the match).
399330571Sdelphij	 */
400330571Sdelphij	int len = (int) strlen(curtag->tag_pattern);
401330571Sdelphij	if (strncmp(curtag->tag_pattern, line, len) == 0 &&
402330571Sdelphij	    (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
403330571Sdelphij	{
404330571Sdelphij		curtag->tag_linenum = find_linenum(linepos);
405330571Sdelphij		return 1;
406330571Sdelphij	}
407330571Sdelphij	return 0;
408330571Sdelphij}
409330571Sdelphij
41060786Sps/*
41160786Sps * Search for a tag.
41260786Sps * This is a stripped-down version of search().
41360786Sps * We don't use search() for several reasons:
41460786Sps *   -	We don't want to blow away any search string we may have saved.
41560786Sps *   -	The various regular-expression functions (from different systems:
41660786Sps *	regcmp vs. re_comp) behave differently in the presence of
41760786Sps *	parentheses (which are almost always found in a tag).
41860786Sps */
41989019Sps	static POSITION
42089019Spsctagsearch()
42160786Sps{
42260786Sps	POSITION pos, linepos;
423128345Stjr	LINENUM linenum;
424330571Sdelphij	int line_len;
42560786Sps	char *line;
426330571Sdelphij	int found;
42760786Sps
42860786Sps	pos = ch_zero();
42960786Sps	linenum = find_linenum(pos);
43060786Sps
431330571Sdelphij	for (found = 0; !found;)
43260786Sps	{
43360786Sps		/*
43460786Sps		 * Get lines until we find a matching one or
43560786Sps		 * until we hit end-of-file.
43660786Sps		 */
43760786Sps		if (ABORT_SIGS())
43860786Sps			return (NULL_POSITION);
43960786Sps
44060786Sps		/*
44160786Sps		 * Read the next line, and save the
44260786Sps		 * starting position of that line in linepos.
44360786Sps		 */
44460786Sps		linepos = pos;
445330571Sdelphij		pos = forw_raw_line(pos, &line, &line_len);
44660786Sps		if (linenum != 0)
44760786Sps			linenum++;
44860786Sps
44960786Sps		if (pos == NULL_POSITION)
45060786Sps		{
45160786Sps			/*
45260786Sps			 * We hit EOF without a match.
45360786Sps			 */
45460786Sps			error("Tag not found", NULL_PARG);
45560786Sps			return (NULL_POSITION);
45660786Sps		}
45760786Sps
45860786Sps		/*
45960786Sps		 * If we're using line numbers, we might as well
46060786Sps		 * remember the information we have now (the position
46160786Sps		 * and line number of the current line).
46260786Sps		 */
46360786Sps		if (linenums)
46460786Sps			add_lnum(linenum, pos);
46560786Sps
466330571Sdelphij		if (ctldisp != OPT_ONPLUS)
46789019Sps		{
468330571Sdelphij			if (curtag_match(line, linepos))
469330571Sdelphij				found = 1;
470330571Sdelphij		} else
471330571Sdelphij		{
472330571Sdelphij			int cvt_ops = CVT_ANSI;
473330571Sdelphij			int cvt_len = cvt_length(line_len, cvt_ops);
474330571Sdelphij			int *chpos = cvt_alloc_chpos(cvt_len);
475330571Sdelphij			char *cline = (char *) ecalloc(1, cvt_len);
476330571Sdelphij			cvt_text(cline, line, chpos, &line_len, cvt_ops);
477330571Sdelphij			if (curtag_match(cline, linepos))
478330571Sdelphij				found = 1;
479330571Sdelphij			free(chpos);
480330571Sdelphij			free(cline);
48189019Sps		}
48260786Sps	}
48360786Sps
48460786Sps	return (linepos);
48560786Sps}
48660786Sps
48789019Sps/*******************************************************************************
48889019Sps * gtags
48989019Sps */
49089019Sps
49189019Sps/*
49289019Sps * Find tags in the GLOBAL's tag file.
49389019Sps * The findgtag() will try and load information about the requested tag.
49489019Sps * It does this by calling "global -x tag" and storing the parsed output
49589019Sps * for future use by gtagsearch().
49689019Sps * Sets curtag to the first tag entry.
49789019Sps */
49889019Sps	static enum tag_result
49989019Spsfindgtag(tag, type)
50089019Sps	char *tag;		/* tag to load */
50189019Sps	int type;		/* tags type */
50289019Sps{
50389019Sps	char buf[256];
50489019Sps	FILE *fp;
50589019Sps	struct tag *tp;
50689019Sps
50789019Sps	if (type != T_CTAGS_X && tag == NULL)
50889019Sps		return TAG_NOFILE;
50989019Sps
51089019Sps	cleantags();
51189019Sps	total = 0;
51289019Sps
51389019Sps	/*
51489019Sps	 * If type == T_CTAGS_X then read ctags's -x format from stdin
51589019Sps	 * else execute global(1) and read from it.
51689019Sps	 */
51789019Sps	if (type == T_CTAGS_X)
51889019Sps	{
51989019Sps		fp = stdin;
52089019Sps		/* Set tag default because we cannot read stdin again. */
521294286Sdelphij		tags = ztags;
52289019Sps	} else
52389019Sps	{
52489019Sps#if !HAVE_POPEN
52589019Sps		return TAG_NOFILE;
52689019Sps#else
527161475Sdelphij		char *command;
52889019Sps		char *flag;
529128345Stjr		char *qtag;
53089019Sps		char *cmd = lgetenv("LESSGLOBALTAGS");
53189019Sps
53289019Sps		if (cmd == NULL || *cmd == '\0')
53389019Sps			return TAG_NOFILE;
53489019Sps		/* Get suitable flag value for global(1). */
53589019Sps		switch (type)
53689019Sps		{
53789019Sps		case T_GTAGS:
53889019Sps			flag = "" ;
53989019Sps			break;
54089019Sps		case T_GRTAGS:
54189019Sps			flag = "r";
54289019Sps			break;
54389019Sps		case T_GSYMS:
54489019Sps			flag = "s";
54589019Sps			break;
54689019Sps		case T_GPATH:
54789019Sps			flag = "P";
54889019Sps			break;
54989019Sps		default:
55089019Sps			return TAG_NOTYPE;
55189019Sps		}
55289019Sps
55389019Sps		/* Get our data from global(1). */
554128345Stjr		qtag = shell_quote(tag);
555128345Stjr		if (qtag == NULL)
556128345Stjr			qtag = tag;
557161475Sdelphij		command = (char *) ecalloc(strlen(cmd) + strlen(flag) +
558161475Sdelphij				strlen(qtag) + 5, sizeof(char));
559128345Stjr		sprintf(command, "%s -x%s %s", cmd, flag, qtag);
560128345Stjr		if (qtag != tag)
561128345Stjr			free(qtag);
56289019Sps		fp = popen(command, "r");
563161475Sdelphij		free(command);
56460786Sps#endif
56589019Sps	}
56689019Sps	if (fp != NULL)
56789019Sps	{
56889019Sps		while (fgets(buf, sizeof(buf), fp))
56989019Sps		{
57089019Sps			char *name, *file, *line;
571161475Sdelphij			int len;
57289019Sps
57389019Sps			if (sigs)
57489019Sps			{
57589019Sps#if HAVE_POPEN
57689019Sps				if (fp != stdin)
57789019Sps					pclose(fp);
57889019Sps#endif
57989019Sps				return TAG_INTR;
58089019Sps			}
581294286Sdelphij			len = (int) strlen(buf);
582161475Sdelphij			if (len > 0 && buf[len-1] == '\n')
583161475Sdelphij				buf[len-1] = '\0';
58489019Sps			else
58589019Sps			{
58689019Sps				int c;
58789019Sps				do {
58889019Sps					c = fgetc(fp);
58989019Sps				} while (c != '\n' && c != EOF);
59089019Sps			}
59189019Sps
59289019Sps 			if (getentry(buf, &name, &file, &line))
59389019Sps			{
59489019Sps				/*
59589019Sps				 * Couldn't parse this line for some reason.
59689019Sps				 * We'll just pretend it never happened.
59789019Sps				 */
59889019Sps				break;
59989019Sps			}
60089019Sps
60189019Sps			/* Make new entry and add to list. */
602128345Stjr			tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
60389019Sps			TAG_INS(tp);
60489019Sps			total++;
60589019Sps		}
60689019Sps		if (fp != stdin)
60789019Sps		{
60889019Sps			if (pclose(fp))
60989019Sps			{
61089019Sps				curtag = NULL;
61189019Sps				total = curseq = 0;
61289019Sps				return TAG_NOFILE;
61389019Sps			}
61489019Sps		}
61589019Sps	}
61689019Sps
61789019Sps	/* Check to see if we found anything. */
61889019Sps	tp = taglist.tl_first;
61989019Sps	if (tp == TAG_END)
62089019Sps		return TAG_NOTAG;
62189019Sps	curtag = tp;
62289019Sps	curseq = 1;
62389019Sps	return TAG_FOUND;
62489019Sps}
62589019Sps
62689019Spsstatic int circular = 0;	/* 1: circular tag structure */
62789019Sps
62889019Sps/*
62989019Sps * Return the filename required for the next gtag in the queue that was setup
63089019Sps * by findgtag().  The next call to gtagsearch() will try to position at the
63189019Sps * appropriate tag.
63289019Sps */
63389019Sps	static char *
63489019Spsnextgtag()
63589019Sps{
63689019Sps	struct tag *tp;
63789019Sps
63889019Sps	if (curtag == NULL)
63989019Sps		/* No tag loaded */
64089019Sps		return NULL;
64189019Sps
64289019Sps	tp = curtag->next;
64389019Sps	if (tp == TAG_END)
64489019Sps	{
64589019Sps		if (!circular)
64689019Sps			return NULL;
64789019Sps		/* Wrapped around to the head of the queue */
64889019Sps		curtag = taglist.tl_first;
64989019Sps		curseq = 1;
65089019Sps	} else
65189019Sps	{
65289019Sps		curtag = tp;
65389019Sps		curseq++;
65489019Sps	}
65589019Sps	return (curtag->tag_file);
65689019Sps}
65789019Sps
65889019Sps/*
65989019Sps * Return the filename required for the previous gtag in the queue that was
66089019Sps * setup by findgtat().  The next call to gtagsearch() will try to position
66189019Sps * at the appropriate tag.
66289019Sps */
66389019Sps	static char *
66489019Spsprevgtag()
66589019Sps{
66689019Sps	struct tag *tp;
66789019Sps
66889019Sps	if (curtag == NULL)
66989019Sps		/* No tag loaded */
67089019Sps		return NULL;
67189019Sps
67289019Sps	tp = curtag->prev;
67389019Sps	if (tp == TAG_END)
67489019Sps	{
67589019Sps		if (!circular)
67689019Sps			return NULL;
67789019Sps		/* Wrapped around to the tail of the queue */
67889019Sps		curtag = taglist.tl_last;
67989019Sps		curseq = total;
68089019Sps	} else
68189019Sps	{
68289019Sps		curtag = tp;
68389019Sps		curseq--;
68489019Sps	}
68589019Sps	return (curtag->tag_file);
68689019Sps}
68789019Sps
68889019Sps/*
68989019Sps * Position the current file at at what is hopefully the tag that was chosen
69089019Sps * using either findtag() or one of nextgtag() and prevgtag().  Returns -1
691173682Sdelphij * if it was unable to position at the tag, 0 if successful.
69289019Sps */
69389019Sps	static POSITION
69489019Spsgtagsearch()
69589019Sps{
69689019Sps	if (curtag == NULL)
69789019Sps		return (NULL_POSITION);  /* No gtags loaded! */
69889019Sps	return (find_pos(curtag->tag_linenum));
69989019Sps}
70089019Sps
70189019Sps/*
70289019Sps * The getentry() parses both standard and extended ctags -x format.
70389019Sps *
70489019Sps * [standard format]
70589019Sps * <tag>   <lineno>  <file>         <image>
70689019Sps * +------------------------------------------------
70789019Sps * |main     30      main.c         main(argc, argv)
70889019Sps * |func     21      subr.c         func(arg)
70989019Sps *
71089019Sps * The following commands write this format.
71189019Sps *	o Traditinal Ctags with -x option
71289019Sps *	o Global with -x option
71389019Sps *		See <http://www.gnu.org/software/global/global.html>
71489019Sps *
71589019Sps * [extended format]
71689019Sps * <tag>   <type>  <lineno>   <file>        <image>
71789019Sps * +----------------------------------------------------------
71889019Sps * |main     function 30      main.c         main(argc, argv)
71989019Sps * |func     function 21      subr.c         func(arg)
72089019Sps *
72189019Sps * The following commands write this format.
72289019Sps *	o Exuberant Ctags with -x option
72389019Sps *		See <http://ctags.sourceforge.net>
72489019Sps *
72589019Sps * Returns 0 on success, -1 on error.
72689019Sps * The tag, file, and line will each be NUL-terminated pointers
72789019Sps * into buf.
72889019Sps */
72989019Sps	static int
73089019Spsgetentry(buf, tag, file, line)
73189019Sps	char *buf;	/* standard or extended ctags -x format data */
73289019Sps	char **tag;	/* name of the tag we actually found */
73389019Sps	char **file;	/* file in which to find this tag */
73489019Sps	char **line;	/* line number of file where this tag is found */
73589019Sps{
73689019Sps	char *p = buf;
73789019Sps
738161475Sdelphij	for (*tag = p;  *p && !IS_SPACE(*p);  p++)	/* tag name */
73989019Sps		;
74089019Sps	if (*p == 0)
74189019Sps		return (-1);
74289019Sps	*p++ = 0;
743161475Sdelphij	for ( ;  *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
74489019Sps		;
74589019Sps	if (*p == 0)
74689019Sps		return (-1);
74789019Sps	/*
74889019Sps	 * If the second part begin with other than digit,
74989019Sps	 * it is assumed tag type. Skip it.
75089019Sps	 */
751161475Sdelphij	if (!IS_DIGIT(*p))
75289019Sps	{
753161475Sdelphij		for ( ;  *p && !IS_SPACE(*p);  p++)	/* (skip tag type) */
75489019Sps			;
755161475Sdelphij		for (;  *p && IS_SPACE(*p);  p++)	/* (skip blanks) */
75689019Sps			;
75789019Sps	}
758161475Sdelphij	if (!IS_DIGIT(*p))
75989019Sps		return (-1);
76089019Sps	*line = p;					/* line number */
761161475Sdelphij	for (*line = p;  *p && !IS_SPACE(*p);  p++)
76289019Sps		;
76389019Sps	if (*p == 0)
76489019Sps		return (-1);
76589019Sps	*p++ = 0;
766161475Sdelphij	for ( ; *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
76789019Sps		;
76889019Sps	if (*p == 0)
76989019Sps		return (-1);
77089019Sps	*file = p;					/* file name */
771161475Sdelphij	for (*file = p;  *p && !IS_SPACE(*p);  p++)
77289019Sps		;
77389019Sps	if (*p == 0)
77489019Sps		return (-1);
77589019Sps	*p = 0;
77689019Sps
77789019Sps	/* value check */
77889019Sps	if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
77989019Sps		return (0);
78089019Sps	return (-1);
78189019Sps}
78289019Sps
78389019Sps#endif
784