160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
7240121Sdelphij * For more information, see the README file.
860786Sps */
960786Sps
1060786Sps
1160786Sps#include "less.h"
1260786Sps
1360786Sps#define	WHITESP(c)	((c)==' ' || (c)=='\t')
1460786Sps
1560786Sps#if TAGS
1660786Sps
1760786Spspublic char *tags = "tags";
1860786Sps
1989019Spsstatic int total;
2089019Spsstatic int curseq;
2160786Sps
2260786Spsextern int linenums;
2360786Spsextern int sigs;
2460786Sps
2589019Spsenum tag_result {
2689019Sps	TAG_FOUND,
2789019Sps	TAG_NOFILE,
2889019Sps	TAG_NOTAG,
2989019Sps	TAG_NOTYPE,
3089019Sps	TAG_INTR
3189019Sps};
3289019Sps
3360786Sps/*
3489019Sps * Tag type
3589019Sps */
3689019Spsenum {
3789019Sps	T_CTAGS,	/* 'tags': standard and extended format (ctags) */
3889019Sps	T_CTAGS_X,	/* stdin: cross reference format (ctags) */
3989019Sps	T_GTAGS,	/* 'GTAGS': function defenition (global) */
4089019Sps	T_GRTAGS,	/* 'GRTAGS': function reference (global) */
4189019Sps	T_GSYMS,	/* 'GSYMS': other symbols (global) */
4289019Sps	T_GPATH		/* 'GPATH': path name (global) */
4389019Sps};
4489019Sps
4589019Spsstatic enum tag_result findctag();
4689019Spsstatic enum tag_result findgtag();
4789019Spsstatic char *nextgtag();
4889019Spsstatic char *prevgtag();
4989019Spsstatic POSITION ctagsearch();
5089019Spsstatic POSITION gtagsearch();
5189019Spsstatic int getentry();
5289019Sps
5389019Sps/*
5489019Sps * The list of tags generated by the last findgtag() call.
5589019Sps *
5689019Sps * Use either pattern or line number.
5789019Sps * findgtag() always uses line number, so pattern is always NULL.
58170256Sdelphij * findctag() uses either pattern (in which case line number is 0),
5989019Sps * or line number (in which case pattern is NULL).
6089019Sps */
6189019Spsstruct taglist {
6289019Sps	struct tag *tl_first;
6389019Sps	struct tag *tl_last;
6489019Sps};
6589019Sps#define TAG_END  ((struct tag *) &taglist)
6689019Spsstatic struct taglist taglist = { TAG_END, TAG_END };
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};
7489019Spsstatic struct tag *curtag;
7589019Sps
7689019Sps#define TAG_INS(tp) \
77170256Sdelphij	(tp)->next = TAG_END; \
78170256Sdelphij	(tp)->prev = taglist.tl_last; \
79170256Sdelphij	taglist.tl_last->next = (tp); \
80170256Sdelphij	taglist.tl_last = (tp);
8189019Sps
8289019Sps#define TAG_RM(tp) \
8389019Sps	(tp)->next->prev = (tp)->prev; \
8489019Sps	(tp)->prev->next = (tp)->next;
8589019Sps
8689019Sps/*
8789019Sps * Delete tag structures.
8889019Sps */
8989019Sps	public void
9089019Spscleantags()
9189019Sps{
9289019Sps	register struct tag *tp;
9389019Sps
9489019Sps	/*
9589019Sps	 * Delete any existing tag list.
9689019Sps	 * {{ Ideally, we wouldn't do this until after we know that we
9789019Sps	 *    can load some other tag information. }}
9889019Sps	 */
9989019Sps	while ((tp = taglist.tl_first) != TAG_END)
10089019Sps	{
10189019Sps		TAG_RM(tp);
10289019Sps		free(tp);
10389019Sps	}
10489019Sps	curtag = NULL;
10589019Sps	total = curseq = 0;
10689019Sps}
10789019Sps
10889019Sps/*
10989019Sps * Create a new tag entry.
11089019Sps */
11189019Sps	static struct tag *
11289019Spsmaketagent(name, file, linenum, pattern, endline)
11389019Sps	char *name;
11489019Sps	char *file;
115128345Stjr	LINENUM linenum;
11689019Sps	char *pattern;
11789019Sps	int endline;
11889019Sps{
11989019Sps	register struct tag *tp;
12089019Sps
12189019Sps	tp = (struct tag *) ecalloc(sizeof(struct tag), 1);
12289019Sps	tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char));
12389019Sps	strcpy(tp->tag_file, file);
12489019Sps	tp->tag_linenum = linenum;
12589019Sps	tp->tag_endline = endline;
12689019Sps	if (pattern == NULL)
12789019Sps		tp->tag_pattern = NULL;
12889019Sps	else
12989019Sps	{
13089019Sps		tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char));
13189019Sps		strcpy(tp->tag_pattern, pattern);
13289019Sps	}
13389019Sps	return (tp);
13489019Sps}
13589019Sps
13689019Sps/*
13789019Sps * Get tag mode.
13889019Sps */
13989019Sps	public int
14089019Spsgettagtype()
14189019Sps{
14289019Sps	int f;
14389019Sps
14489019Sps	if (strcmp(tags, "GTAGS") == 0)
14589019Sps		return T_GTAGS;
14689019Sps	if (strcmp(tags, "GRTAGS") == 0)
14789019Sps		return T_GRTAGS;
14889019Sps	if (strcmp(tags, "GSYMS") == 0)
14989019Sps		return T_GSYMS;
15089019Sps	if (strcmp(tags, "GPATH") == 0)
15189019Sps		return T_GPATH;
15289019Sps	if (strcmp(tags, "-") == 0)
15389019Sps		return T_CTAGS_X;
15489019Sps	f = open(tags, OPEN_READ);
15589019Sps	if (f >= 0)
15689019Sps	{
15789019Sps		close(f);
15889019Sps		return T_CTAGS;
15989019Sps	}
16089019Sps	return T_GTAGS;
16189019Sps}
16289019Sps
16389019Sps/*
16489019Sps * Find tags in tag file.
16560786Sps * Find a tag in the "tags" file.
16689019Sps * Sets "tag_file" to the name of the file containing the tag,
16760786Sps * and "tagpattern" to the search pattern which should be used
16860786Sps * to find the tag.
16960786Sps */
17060786Sps	public void
17160786Spsfindtag(tag)
17260786Sps	register char *tag;
17360786Sps{
17489019Sps	int type = gettagtype();
17589019Sps	enum tag_result result;
17689019Sps
17789019Sps	if (type == T_CTAGS)
17889019Sps		result = findctag(tag);
17989019Sps	else
18089019Sps		result = findgtag(tag, type);
18189019Sps	switch (result)
18289019Sps	{
18389019Sps	case TAG_FOUND:
18489019Sps	case TAG_INTR:
18589019Sps		break;
18689019Sps	case TAG_NOFILE:
18789019Sps		error("No tags file", NULL_PARG);
18889019Sps		break;
18989019Sps	case TAG_NOTAG:
19089019Sps		error("No such tag in tags file", NULL_PARG);
19189019Sps		break;
19289019Sps	case TAG_NOTYPE:
19389019Sps		error("unknown tag type", NULL_PARG);
19489019Sps		break;
19589019Sps	}
19689019Sps}
19789019Sps
19889019Sps/*
19989019Sps * Search for a tag.
20089019Sps */
20189019Sps	public POSITION
20289019Spstagsearch()
20389019Sps{
20489019Sps	if (curtag == NULL)
20589019Sps		return (NULL_POSITION);  /* No gtags loaded! */
20689019Sps	if (curtag->tag_linenum != 0)
20789019Sps		return gtagsearch();
20889019Sps	else
20989019Sps		return ctagsearch();
21089019Sps}
21189019Sps
21289019Sps/*
21389019Sps * Go to the next tag.
21489019Sps */
21589019Sps	public char *
21689019Spsnexttag(n)
21789019Sps	int n;
21889019Sps{
219128345Stjr	char *tagfile = (char *) NULL;
22089019Sps
22189019Sps	while (n-- > 0)
22289019Sps		tagfile = nextgtag();
22389019Sps	return tagfile;
22489019Sps}
22589019Sps
22689019Sps/*
22789019Sps * Go to the previous tag.
22889019Sps */
22989019Sps	public char *
23089019Spsprevtag(n)
23189019Sps	int n;
23289019Sps{
233128345Stjr	char *tagfile = (char *) NULL;
23489019Sps
23589019Sps	while (n-- > 0)
23689019Sps		tagfile = prevgtag();
23789019Sps	return tagfile;
23889019Sps}
23989019Sps
24089019Sps/*
24189019Sps * Return the total number of tags.
24289019Sps */
24389019Sps	public int
24489019Spsntags()
24589019Sps{
24689019Sps	return total;
24789019Sps}
24889019Sps
24989019Sps/*
25089019Sps * Return the sequence number of current tag.
25189019Sps */
25289019Sps	public int
25389019Spscurr_tag()
25489019Sps{
25589019Sps	return curseq;
25689019Sps}
25789019Sps
25889019Sps/*****************************************************************************
25989019Sps * ctags
26089019Sps */
26189019Sps
26289019Sps/*
26389019Sps * Find tags in the "tags" file.
26489019Sps * Sets curtag to the first tag entry.
26589019Sps */
26689019Sps	static enum tag_result
26789019Spsfindctag(tag)
26889019Sps	register char *tag;
26989019Sps{
27060786Sps	char *p;
27160786Sps	register FILE *f;
27260786Sps	register int taglen;
273128345Stjr	LINENUM taglinenum;
27489019Sps	char *tagfile;
27589019Sps	char *tagpattern;
27689019Sps	int tagendline;
27760786Sps	int search_char;
27860786Sps	int err;
27960786Sps	char tline[TAGLINE_SIZE];
28089019Sps	struct tag *tp;
28160786Sps
282128345Stjr	p = shell_unquote(tags);
28360786Sps	f = fopen(p, "r");
28460786Sps	free(p);
28560786Sps	if (f == NULL)
28689019Sps		return TAG_NOFILE;
28760786Sps
28889019Sps	cleantags();
28989019Sps	total = 0;
29060786Sps	taglen = strlen(tag);
29160786Sps
29260786Sps	/*
29360786Sps	 * Search the tags file for the desired tag.
29460786Sps	 */
29560786Sps	while (fgets(tline, sizeof(tline), f) != NULL)
29660786Sps	{
29789019Sps		if (tline[0] == '!')
29889019Sps			/* Skip header of extended format. */
29989019Sps			continue;
30060786Sps		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
30160786Sps			continue;
30260786Sps
30360786Sps		/*
30460786Sps		 * Found it.
30560786Sps		 * The line contains the tag, the filename and the
30660786Sps		 * location in the file, separated by white space.
30760786Sps		 * The location is either a decimal line number,
30860786Sps		 * or a search pattern surrounded by a pair of delimiters.
30960786Sps		 * Parse the line and extract these parts.
31060786Sps		 */
31189019Sps		tagpattern = NULL;
31260786Sps
31360786Sps		/*
31460786Sps		 * Skip over the whitespace after the tag name.
31560786Sps		 */
31660786Sps		p = skipsp(tline+taglen);
31760786Sps		if (*p == '\0')
31860786Sps			/* File name is missing! */
31960786Sps			continue;
32060786Sps
32160786Sps		/*
32260786Sps		 * Save the file name.
32360786Sps		 * Skip over the whitespace after the file name.
32460786Sps		 */
32560786Sps		tagfile = p;
32660786Sps		while (!WHITESP(*p) && *p != '\0')
32760786Sps			p++;
32860786Sps		*p++ = '\0';
32960786Sps		p = skipsp(p);
33060786Sps		if (*p == '\0')
33160786Sps			/* Pattern is missing! */
33260786Sps			continue;
33360786Sps
33460786Sps		/*
33560786Sps		 * First see if it is a line number.
33660786Sps		 */
337128345Stjr		tagendline = 0;
33860786Sps		taglinenum = getnum(&p, 0, &err);
33960786Sps		if (err)
34060786Sps		{
34160786Sps			/*
34260786Sps			 * No, it must be a pattern.
34360786Sps			 * Delete the initial "^" (if present) and
34460786Sps			 * the final "$" from the pattern.
34560786Sps			 * Delete any backslash in the pattern.
34660786Sps			 */
34760786Sps			taglinenum = 0;
34860786Sps			search_char = *p++;
34960786Sps			if (*p == '^')
35060786Sps				p++;
35189019Sps			tagpattern = p;
35260786Sps			while (*p != search_char && *p != '\0')
35360786Sps			{
35460786Sps				if (*p == '\\')
35560786Sps					p++;
35689019Sps				p++;
35760786Sps			}
35889019Sps			tagendline = (p[-1] == '$');
35960786Sps			if (tagendline)
36089019Sps				p--;
36189019Sps			*p = '\0';
36260786Sps		}
36389019Sps		tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
36489019Sps		TAG_INS(tp);
36589019Sps		total++;
36660786Sps	}
36760786Sps	fclose(f);
36889019Sps	if (total == 0)
36989019Sps		return TAG_NOTAG;
37089019Sps	curtag = taglist.tl_first;
37189019Sps	curseq = 1;
37289019Sps	return TAG_FOUND;
37360786Sps}
37460786Sps
37589019Sps/*
37689019Sps * Edit current tagged file.
37789019Sps */
37860786Sps	public int
37960786Spsedit_tagfile()
38060786Sps{
38189019Sps	if (curtag == NULL)
38260786Sps		return (1);
38389019Sps	return (edit(curtag->tag_file));
38460786Sps}
38560786Sps
38660786Sps/*
38760786Sps * Search for a tag.
38860786Sps * This is a stripped-down version of search().
38960786Sps * We don't use search() for several reasons:
39060786Sps *   -	We don't want to blow away any search string we may have saved.
39160786Sps *   -	The various regular-expression functions (from different systems:
39260786Sps *	regcmp vs. re_comp) behave differently in the presence of
39360786Sps *	parentheses (which are almost always found in a tag).
39460786Sps */
39589019Sps	static POSITION
39689019Spsctagsearch()
39760786Sps{
39860786Sps	POSITION pos, linepos;
399128345Stjr	LINENUM linenum;
40060786Sps	int len;
40160786Sps	char *line;
40260786Sps
40360786Sps	pos = ch_zero();
40460786Sps	linenum = find_linenum(pos);
40560786Sps
40660786Sps	for (;;)
40760786Sps	{
40860786Sps		/*
40960786Sps		 * Get lines until we find a matching one or
41060786Sps		 * until we hit end-of-file.
41160786Sps		 */
41260786Sps		if (ABORT_SIGS())
41360786Sps			return (NULL_POSITION);
41460786Sps
41560786Sps		/*
41660786Sps		 * Read the next line, and save the
41760786Sps		 * starting position of that line in linepos.
41860786Sps		 */
41960786Sps		linepos = pos;
420170256Sdelphij		pos = forw_raw_line(pos, &line, (int *)NULL);
42160786Sps		if (linenum != 0)
42260786Sps			linenum++;
42360786Sps
42460786Sps		if (pos == NULL_POSITION)
42560786Sps		{
42660786Sps			/*
42760786Sps			 * We hit EOF without a match.
42860786Sps			 */
42960786Sps			error("Tag not found", NULL_PARG);
43060786Sps			return (NULL_POSITION);
43160786Sps		}
43260786Sps
43360786Sps		/*
43460786Sps		 * If we're using line numbers, we might as well
43560786Sps		 * remember the information we have now (the position
43660786Sps		 * and line number of the current line).
43760786Sps		 */
43860786Sps		if (linenums)
43960786Sps			add_lnum(linenum, pos);
44060786Sps
44160786Sps		/*
44260786Sps		 * Test the line to see if we have a match.
44360786Sps		 * Use strncmp because the pattern may be
44460786Sps		 * truncated (in the tags file) if it is too long.
44560786Sps		 * If tagendline is set, make sure we match all
44660786Sps		 * the way to end of line (no extra chars after the match).
44760786Sps		 */
44889019Sps		len = strlen(curtag->tag_pattern);
44989019Sps		if (strncmp(curtag->tag_pattern, line, len) == 0 &&
45089019Sps		    (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
45189019Sps		{
45289019Sps			curtag->tag_linenum = find_linenum(linepos);
45360786Sps			break;
45489019Sps		}
45560786Sps	}
45660786Sps
45760786Sps	return (linepos);
45860786Sps}
45960786Sps
46089019Sps/*******************************************************************************
46189019Sps * gtags
46289019Sps */
46389019Sps
46489019Sps/*
46589019Sps * Find tags in the GLOBAL's tag file.
46689019Sps * The findgtag() will try and load information about the requested tag.
46789019Sps * It does this by calling "global -x tag" and storing the parsed output
46889019Sps * for future use by gtagsearch().
46989019Sps * Sets curtag to the first tag entry.
47089019Sps */
47189019Sps	static enum tag_result
47289019Spsfindgtag(tag, type)
47389019Sps	char *tag;		/* tag to load */
47489019Sps	int type;		/* tags type */
47589019Sps{
47689019Sps	char buf[256];
47789019Sps	FILE *fp;
47889019Sps	struct tag *tp;
47989019Sps
48089019Sps	if (type != T_CTAGS_X && tag == NULL)
48189019Sps		return TAG_NOFILE;
48289019Sps
48389019Sps	cleantags();
48489019Sps	total = 0;
48589019Sps
48689019Sps	/*
48789019Sps	 * If type == T_CTAGS_X then read ctags's -x format from stdin
48889019Sps	 * else execute global(1) and read from it.
48989019Sps	 */
49089019Sps	if (type == T_CTAGS_X)
49189019Sps	{
49289019Sps		fp = stdin;
49389019Sps		/* Set tag default because we cannot read stdin again. */
49489019Sps		tags = "tags";
49589019Sps	} else
49689019Sps	{
49789019Sps#if !HAVE_POPEN
49889019Sps		return TAG_NOFILE;
49989019Sps#else
500161475Sdelphij		char *command;
50189019Sps		char *flag;
502128345Stjr		char *qtag;
50389019Sps		char *cmd = lgetenv("LESSGLOBALTAGS");
50489019Sps
50589019Sps		if (cmd == NULL || *cmd == '\0')
50689019Sps			return TAG_NOFILE;
50789019Sps		/* Get suitable flag value for global(1). */
50889019Sps		switch (type)
50989019Sps		{
51089019Sps		case T_GTAGS:
51189019Sps			flag = "" ;
51289019Sps			break;
51389019Sps		case T_GRTAGS:
51489019Sps			flag = "r";
51589019Sps			break;
51689019Sps		case T_GSYMS:
51789019Sps			flag = "s";
51889019Sps			break;
51989019Sps		case T_GPATH:
52089019Sps			flag = "P";
52189019Sps			break;
52289019Sps		default:
52389019Sps			return TAG_NOTYPE;
52489019Sps		}
52589019Sps
52689019Sps		/* Get our data from global(1). */
527128345Stjr		qtag = shell_quote(tag);
528128345Stjr		if (qtag == NULL)
529128345Stjr			qtag = tag;
530161475Sdelphij		command = (char *) ecalloc(strlen(cmd) + strlen(flag) +
531161475Sdelphij				strlen(qtag) + 5, sizeof(char));
532128345Stjr		sprintf(command, "%s -x%s %s", cmd, flag, qtag);
533128345Stjr		if (qtag != tag)
534128345Stjr			free(qtag);
53589019Sps		fp = popen(command, "r");
536161475Sdelphij		free(command);
53760786Sps#endif
53889019Sps	}
53989019Sps	if (fp != NULL)
54089019Sps	{
54189019Sps		while (fgets(buf, sizeof(buf), fp))
54289019Sps		{
54389019Sps			char *name, *file, *line;
544161475Sdelphij			int len;
54589019Sps
54689019Sps			if (sigs)
54789019Sps			{
54889019Sps#if HAVE_POPEN
54989019Sps				if (fp != stdin)
55089019Sps					pclose(fp);
55189019Sps#endif
55289019Sps				return TAG_INTR;
55389019Sps			}
554161475Sdelphij			len = strlen(buf);
555161475Sdelphij			if (len > 0 && buf[len-1] == '\n')
556161475Sdelphij				buf[len-1] = '\0';
55789019Sps			else
55889019Sps			{
55989019Sps				int c;
56089019Sps				do {
56189019Sps					c = fgetc(fp);
56289019Sps				} while (c != '\n' && c != EOF);
56389019Sps			}
56489019Sps
56589019Sps 			if (getentry(buf, &name, &file, &line))
56689019Sps			{
56789019Sps				/*
56889019Sps				 * Couldn't parse this line for some reason.
56989019Sps				 * We'll just pretend it never happened.
57089019Sps				 */
57189019Sps				break;
57289019Sps			}
57389019Sps
57489019Sps			/* Make new entry and add to list. */
575128345Stjr			tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
57689019Sps			TAG_INS(tp);
57789019Sps			total++;
57889019Sps		}
57989019Sps		if (fp != stdin)
58089019Sps		{
58189019Sps			if (pclose(fp))
58289019Sps			{
58389019Sps				curtag = NULL;
58489019Sps				total = curseq = 0;
58589019Sps				return TAG_NOFILE;
58689019Sps			}
58789019Sps		}
58889019Sps	}
58989019Sps
59089019Sps	/* Check to see if we found anything. */
59189019Sps	tp = taglist.tl_first;
59289019Sps	if (tp == TAG_END)
59389019Sps		return TAG_NOTAG;
59489019Sps	curtag = tp;
59589019Sps	curseq = 1;
59689019Sps	return TAG_FOUND;
59789019Sps}
59889019Sps
59989019Spsstatic int circular = 0;	/* 1: circular tag structure */
60089019Sps
60189019Sps/*
60289019Sps * Return the filename required for the next gtag in the queue that was setup
60389019Sps * by findgtag().  The next call to gtagsearch() will try to position at the
60489019Sps * appropriate tag.
60589019Sps */
60689019Sps	static char *
60789019Spsnextgtag()
60889019Sps{
60989019Sps	struct tag *tp;
61089019Sps
61189019Sps	if (curtag == NULL)
61289019Sps		/* No tag loaded */
61389019Sps		return NULL;
61489019Sps
61589019Sps	tp = curtag->next;
61689019Sps	if (tp == TAG_END)
61789019Sps	{
61889019Sps		if (!circular)
61989019Sps			return NULL;
62089019Sps		/* Wrapped around to the head of the queue */
62189019Sps		curtag = taglist.tl_first;
62289019Sps		curseq = 1;
62389019Sps	} else
62489019Sps	{
62589019Sps		curtag = tp;
62689019Sps		curseq++;
62789019Sps	}
62889019Sps	return (curtag->tag_file);
62989019Sps}
63089019Sps
63189019Sps/*
63289019Sps * Return the filename required for the previous gtag in the queue that was
63389019Sps * setup by findgtat().  The next call to gtagsearch() will try to position
63489019Sps * at the appropriate tag.
63589019Sps */
63689019Sps	static char *
63789019Spsprevgtag()
63889019Sps{
63989019Sps	struct tag *tp;
64089019Sps
64189019Sps	if (curtag == NULL)
64289019Sps		/* No tag loaded */
64389019Sps		return NULL;
64489019Sps
64589019Sps	tp = curtag->prev;
64689019Sps	if (tp == TAG_END)
64789019Sps	{
64889019Sps		if (!circular)
64989019Sps			return NULL;
65089019Sps		/* Wrapped around to the tail of the queue */
65189019Sps		curtag = taglist.tl_last;
65289019Sps		curseq = total;
65389019Sps	} else
65489019Sps	{
65589019Sps		curtag = tp;
65689019Sps		curseq--;
65789019Sps	}
65889019Sps	return (curtag->tag_file);
65989019Sps}
66089019Sps
66189019Sps/*
66289019Sps * Position the current file at at what is hopefully the tag that was chosen
66389019Sps * using either findtag() or one of nextgtag() and prevgtag().  Returns -1
664173682Sdelphij * if it was unable to position at the tag, 0 if successful.
66589019Sps */
66689019Sps	static POSITION
66789019Spsgtagsearch()
66889019Sps{
66989019Sps	if (curtag == NULL)
67089019Sps		return (NULL_POSITION);  /* No gtags loaded! */
67189019Sps	return (find_pos(curtag->tag_linenum));
67289019Sps}
67389019Sps
67489019Sps/*
67589019Sps * The getentry() parses both standard and extended ctags -x format.
67689019Sps *
67789019Sps * [standard format]
67889019Sps * <tag>   <lineno>  <file>         <image>
67989019Sps * +------------------------------------------------
68089019Sps * |main     30      main.c         main(argc, argv)
68189019Sps * |func     21      subr.c         func(arg)
68289019Sps *
68389019Sps * The following commands write this format.
68489019Sps *	o Traditinal Ctags with -x option
68589019Sps *	o Global with -x option
68689019Sps *		See <http://www.gnu.org/software/global/global.html>
68789019Sps *
68889019Sps * [extended format]
68989019Sps * <tag>   <type>  <lineno>   <file>        <image>
69089019Sps * +----------------------------------------------------------
69189019Sps * |main     function 30      main.c         main(argc, argv)
69289019Sps * |func     function 21      subr.c         func(arg)
69389019Sps *
69489019Sps * The following commands write this format.
69589019Sps *	o Exuberant Ctags with -x option
69689019Sps *		See <http://ctags.sourceforge.net>
69789019Sps *
69889019Sps * Returns 0 on success, -1 on error.
69989019Sps * The tag, file, and line will each be NUL-terminated pointers
70089019Sps * into buf.
70189019Sps */
70289019Sps	static int
70389019Spsgetentry(buf, tag, file, line)
70489019Sps	char *buf;	/* standard or extended ctags -x format data */
70589019Sps	char **tag;	/* name of the tag we actually found */
70689019Sps	char **file;	/* file in which to find this tag */
70789019Sps	char **line;	/* line number of file where this tag is found */
70889019Sps{
70989019Sps	char *p = buf;
71089019Sps
711161475Sdelphij	for (*tag = p;  *p && !IS_SPACE(*p);  p++)	/* tag name */
71289019Sps		;
71389019Sps	if (*p == 0)
71489019Sps		return (-1);
71589019Sps	*p++ = 0;
716161475Sdelphij	for ( ;  *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
71789019Sps		;
71889019Sps	if (*p == 0)
71989019Sps		return (-1);
72089019Sps	/*
72189019Sps	 * If the second part begin with other than digit,
72289019Sps	 * it is assumed tag type. Skip it.
72389019Sps	 */
724161475Sdelphij	if (!IS_DIGIT(*p))
72589019Sps	{
726161475Sdelphij		for ( ;  *p && !IS_SPACE(*p);  p++)	/* (skip tag type) */
72789019Sps			;
728161475Sdelphij		for (;  *p && IS_SPACE(*p);  p++)	/* (skip blanks) */
72989019Sps			;
73089019Sps	}
731161475Sdelphij	if (!IS_DIGIT(*p))
73289019Sps		return (-1);
73389019Sps	*line = p;					/* line number */
734161475Sdelphij	for (*line = p;  *p && !IS_SPACE(*p);  p++)
73589019Sps		;
73689019Sps	if (*p == 0)
73789019Sps		return (-1);
73889019Sps	*p++ = 0;
739161475Sdelphij	for ( ; *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
74089019Sps		;
74189019Sps	if (*p == 0)
74289019Sps		return (-1);
74389019Sps	*file = p;					/* file name */
744161475Sdelphij	for (*file = p;  *p && !IS_SPACE(*p);  p++)
74589019Sps		;
74689019Sps	if (*p == 0)
74789019Sps		return (-1);
74889019Sps	*p = 0;
74989019Sps
75089019Sps	/* value check */
75189019Sps	if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
75289019Sps		return (0);
75389019Sps	return (-1);
75489019Sps}
75589019Sps
75689019Sps#endif
757