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