tags.c revision 195941
1266423Sjfv/*
2266423Sjfv * Copyright (C) 1984-2009  Mark Nudelman
3279033Sjfv *
4266423Sjfv * You may distribute under the terms of either the GNU General Public
5266423Sjfv * License or the Less License, as specified in the README file.
6266423Sjfv *
7266423Sjfv * For more information about less, or for information on how to
8266423Sjfv * contact the author, see the README file.
9266423Sjfv */
10266423Sjfv
11266423Sjfv
12266423Sjfv#include "less.h"
13266423Sjfv
14266423Sjfv#define	WHITESP(c)	((c)==' ' || (c)=='\t')
15266423Sjfv
16266423Sjfv#if TAGS
17266423Sjfv
18266423Sjfvpublic char *tags = "tags";
19266423Sjfv
20266423Sjfvstatic int total;
21266423Sjfvstatic int curseq;
22266423Sjfv
23266423Sjfvextern int linenums;
24266423Sjfvextern int sigs;
25266423Sjfv
26266423Sjfvenum tag_result {
27266423Sjfv	TAG_FOUND,
28266423Sjfv	TAG_NOFILE,
29266423Sjfv	TAG_NOTAG,
30266423Sjfv	TAG_NOTYPE,
31266423Sjfv	TAG_INTR
32266423Sjfv};
33266423Sjfv
34266423Sjfv/*
35266423Sjfv * Tag type
36266423Sjfv */
37266423Sjfvenum {
38266423Sjfv	T_CTAGS,	/* 'tags': standard and extended format (ctags) */
39266423Sjfv	T_CTAGS_X,	/* stdin: cross reference format (ctags) */
40279033Sjfv	T_GTAGS,	/* 'GTAGS': function defenition (global) */
41266423Sjfv	T_GRTAGS,	/* 'GRTAGS': function reference (global) */
42266423Sjfv	T_GSYMS,	/* 'GSYMS': other symbols (global) */
43266423Sjfv	T_GPATH		/* 'GPATH': path name (global) */
44266423Sjfv};
45266423Sjfv
46266423Sjfvstatic enum tag_result findctag();
47266423Sjfvstatic enum tag_result findgtag();
48270346Sjfvstatic char *nextgtag();
49266423Sjfvstatic char *prevgtag();
50266423Sjfvstatic POSITION ctagsearch();
51266423Sjfvstatic POSITION gtagsearch();
52266423Sjfvstatic int getentry();
53266423Sjfv
54266423Sjfv/*
55266423Sjfv * The list of tags generated by the last findgtag() call.
56266423Sjfv *
57266423Sjfv * Use either pattern or line number.
58266423Sjfv * findgtag() always uses line number, so pattern is always NULL.
59266423Sjfv * findctag() uses either pattern (in which case line number is 0),
60266423Sjfv * or line number (in which case pattern is NULL).
61266423Sjfv */
62266423Sjfvstruct taglist {
63270346Sjfv	struct tag *tl_first;
64284049Sjfv	struct tag *tl_last;
65279033Sjfv};
66284049Sjfv#define TAG_END  ((struct tag *) &taglist)
67303967Ssbrunostatic struct taglist taglist = { TAG_END, TAG_END };
68303967Ssbrunostruct tag {
69266423Sjfv	struct tag *next, *prev; /* List links */
70266423Sjfv	char *tag_file;		/* Source file containing the tag */
71303967Ssbruno	LINENUM tag_linenum;	/* Appropriate line number in source file */
72303967Ssbruno	char *tag_pattern;	/* Pattern used to find the tag */
73303967Ssbruno	char tag_endline;	/* True if the pattern includes '$' */
74303967Ssbruno};
75303967Ssbrunostatic struct tag *curtag;
76303967Ssbruno
77303967Ssbruno#define TAG_INS(tp) \
78303967Ssbruno	(tp)->next = TAG_END; \
79303967Ssbruno	(tp)->prev = taglist.tl_last; \
80303967Ssbruno	taglist.tl_last->next = (tp); \
81303967Ssbruno	taglist.tl_last = (tp);
82303967Ssbruno
83303967Ssbruno#define TAG_RM(tp) \
84303967Ssbruno	(tp)->next->prev = (tp)->prev; \
85266423Sjfv	(tp)->prev->next = (tp)->next;
86266423Sjfv
87266423Sjfv/*
88266423Sjfv * Delete tag structures.
89266423Sjfv */
90266423Sjfv	public void
91266423Sjfvcleantags()
92266423Sjfv{
93266423Sjfv	register struct tag *tp;
94266423Sjfv
95266423Sjfv	/*
96266423Sjfv	 * Delete any existing tag list.
97266423Sjfv	 * {{ Ideally, we wouldn't do this until after we know that we
98266423Sjfv	 *    can load some other tag information. }}
99266423Sjfv	 */
100266423Sjfv	while ((tp = taglist.tl_first) != TAG_END)
101266423Sjfv	{
102266423Sjfv		TAG_RM(tp);
103284049Sjfv		free(tp);
104284049Sjfv	}
105284049Sjfv	curtag = NULL;
106284049Sjfv	total = curseq = 0;
107299545Serj}
108284049Sjfv
109284049Sjfv/*
110284049Sjfv * Create a new tag entry.
111284049Sjfv */
112284049Sjfv	static struct tag *
113284049Sjfvmaketagent(name, file, linenum, pattern, endline)
114284049Sjfv	char *name;
115284049Sjfv	char *file;
116284049Sjfv	LINENUM linenum;
117284049Sjfv	char *pattern;
118284049Sjfv	int endline;
119284049Sjfv{
120284049Sjfv	register struct tag *tp;
121284049Sjfv
122284049Sjfv	tp = (struct tag *) ecalloc(sizeof(struct tag), 1);
123284049Sjfv	tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char));
124284049Sjfv	strcpy(tp->tag_file, file);
125284049Sjfv	tp->tag_linenum = linenum;
126284049Sjfv	tp->tag_endline = endline;
127284049Sjfv	if (pattern == NULL)
128284049Sjfv		tp->tag_pattern = NULL;
129284049Sjfv	else
130284049Sjfv	{
131284049Sjfv		tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char));
132284049Sjfv		strcpy(tp->tag_pattern, pattern);
133284049Sjfv	}
134284049Sjfv	return (tp);
135284049Sjfv}
136284049Sjfv
137284049Sjfv/*
138284049Sjfv * Get tag mode.
139284049Sjfv */
140284049Sjfv	public int
141284049Sjfvgettagtype()
142284049Sjfv{
143284049Sjfv	int f;
144284049Sjfv
145284049Sjfv	if (strcmp(tags, "GTAGS") == 0)
146284049Sjfv		return T_GTAGS;
147284049Sjfv	if (strcmp(tags, "GRTAGS") == 0)
148284049Sjfv		return T_GRTAGS;
149284049Sjfv	if (strcmp(tags, "GSYMS") == 0)
150284049Sjfv		return T_GSYMS;
151284049Sjfv	if (strcmp(tags, "GPATH") == 0)
152284049Sjfv		return T_GPATH;
153284049Sjfv	if (strcmp(tags, "-") == 0)
154284049Sjfv		return T_CTAGS_X;
155284049Sjfv	f = open(tags, OPEN_READ);
156284049Sjfv	if (f >= 0)
157284049Sjfv	{
158284049Sjfv		close(f);
159284049Sjfv		return T_CTAGS;
160284049Sjfv	}
161284049Sjfv	return T_GTAGS;
162284049Sjfv}
163284049Sjfv
164284049Sjfv/*
165284049Sjfv * Find tags in tag file.
166284049Sjfv * Find a tag in the "tags" file.
167299545Serj * Sets "tag_file" to the name of the file containing the tag,
168284049Sjfv * and "tagpattern" to the search pattern which should be used
169284049Sjfv * to find the tag.
170284049Sjfv */
171284049Sjfv	public void
172284049Sjfvfindtag(tag)
173284049Sjfv	register char *tag;
174284049Sjfv{
175284049Sjfv	int type = gettagtype();
176284049Sjfv	enum tag_result result;
177284049Sjfv
178284049Sjfv	if (type == T_CTAGS)
179284049Sjfv		result = findctag(tag);
180284049Sjfv	else
181284049Sjfv		result = findgtag(tag, type);
182284049Sjfv	switch (result)
183284049Sjfv	{
184284049Sjfv	case TAG_FOUND:
185284049Sjfv	case TAG_INTR:
186284049Sjfv		break;
187284049Sjfv	case TAG_NOFILE:
188284049Sjfv		error("No tags file", NULL_PARG);
189284049Sjfv		break;
190284049Sjfv	case TAG_NOTAG:
191284049Sjfv		error("No such tag in tags file", NULL_PARG);
192284049Sjfv		break;
193284049Sjfv	case TAG_NOTYPE:
194284049Sjfv		error("unknown tag type", NULL_PARG);
195284049Sjfv		break;
196284049Sjfv	}
197284049Sjfv}
198284049Sjfv
199284049Sjfv/*
200284049Sjfv * Search for a tag.
201284049Sjfv */
202284049Sjfv	public POSITION
203284049Sjfvtagsearch()
204284049Sjfv{
205284049Sjfv	if (curtag == NULL)
206284049Sjfv		return (NULL_POSITION);  /* No gtags loaded! */
207284049Sjfv	if (curtag->tag_linenum != 0)
208284049Sjfv		return gtagsearch();
209284049Sjfv	else
210284049Sjfv		return ctagsearch();
211284049Sjfv}
212284049Sjfv
213284049Sjfv/*
214284049Sjfv * Go to the next tag.
215284049Sjfv */
216284049Sjfv	public char *
217284049Sjfvnexttag(n)
218284049Sjfv	int n;
219284049Sjfv{
220284049Sjfv	char *tagfile = (char *) NULL;
221284049Sjfv
222284049Sjfv	while (n-- > 0)
223284049Sjfv		tagfile = nextgtag();
224284049Sjfv	return tagfile;
225284049Sjfv}
226284049Sjfv
227284049Sjfv/*
228284049Sjfv * Go to the previous tag.
229284049Sjfv */
230284049Sjfv	public char *
231284049Sjfvprevtag(n)
232284049Sjfv	int n;
233284049Sjfv{
234284049Sjfv	char *tagfile = (char *) NULL;
235284049Sjfv
236284049Sjfv	while (n-- > 0)
237284049Sjfv		tagfile = prevgtag();
238284049Sjfv	return tagfile;
239284049Sjfv}
240284049Sjfv
241284049Sjfv/*
242284049Sjfv * Return the total number of tags.
243284049Sjfv */
244284049Sjfv	public int
245284049Sjfvntags()
246284049Sjfv{
247284049Sjfv	return total;
248284049Sjfv}
249284049Sjfv
250284049Sjfv/*
251284049Sjfv * Return the sequence number of current tag.
252284049Sjfv */
253284049Sjfv	public int
254284049Sjfvcurr_tag()
255284049Sjfv{
256284049Sjfv	return curseq;
257284049Sjfv}
258284049Sjfv
259284049Sjfv/*****************************************************************************
260284049Sjfv * ctags
261284049Sjfv */
262284049Sjfv
263284049Sjfv/*
264284049Sjfv * Find tags in the "tags" file.
265284049Sjfv * Sets curtag to the first tag entry.
266284049Sjfv */
267284049Sjfv	static enum tag_result
268284049Sjfvfindctag(tag)
269284049Sjfv	register char *tag;
270284049Sjfv{
271284049Sjfv	char *p;
272284049Sjfv	register FILE *f;
273284049Sjfv	register int taglen;
274284049Sjfv	LINENUM taglinenum;
275284049Sjfv	char *tagfile;
276284049Sjfv	char *tagpattern;
277284049Sjfv	int tagendline;
278284049Sjfv	int search_char;
279284049Sjfv	int err;
280284049Sjfv	char tline[TAGLINE_SIZE];
281284049Sjfv	struct tag *tp;
282284049Sjfv
283284049Sjfv	p = shell_unquote(tags);
284284049Sjfv	f = fopen(p, "r");
285284049Sjfv	free(p);
286284049Sjfv	if (f == NULL)
287284049Sjfv		return TAG_NOFILE;
288284049Sjfv
289284049Sjfv	cleantags();
290284049Sjfv	total = 0;
291284049Sjfv	taglen = strlen(tag);
292284049Sjfv
293284049Sjfv	/*
294284049Sjfv	 * Search the tags file for the desired tag.
295284049Sjfv	 */
296284049Sjfv	while (fgets(tline, sizeof(tline), f) != NULL)
297284049Sjfv	{
298284049Sjfv		if (tline[0] == '!')
299284049Sjfv			/* Skip header of extended format. */
300284049Sjfv			continue;
301284049Sjfv		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
302284049Sjfv			continue;
303284049Sjfv
304284049Sjfv		/*
305284049Sjfv		 * Found it.
306284049Sjfv		 * The line contains the tag, the filename and the
307284049Sjfv		 * location in the file, separated by white space.
308284049Sjfv		 * The location is either a decimal line number,
309266423Sjfv		 * or a search pattern surrounded by a pair of delimiters.
310266423Sjfv		 * Parse the line and extract these parts.
311266423Sjfv		 */
312266423Sjfv		tagpattern = NULL;
313266423Sjfv
314269198Sjfv		/*
315266423Sjfv		 * Skip over the whitespace after the tag name.
316266423Sjfv		 */
317266423Sjfv		p = skipsp(tline+taglen);
318266423Sjfv		if (*p == '\0')
319269198Sjfv			/* File name is missing! */
320266423Sjfv			continue;
321266423Sjfv
322269198Sjfv		/*
323277082Sjfv		 * Save the file name.
324277082Sjfv		 * Skip over the whitespace after the file name.
325266423Sjfv		 */
326266423Sjfv		tagfile = p;
327266423Sjfv		while (!WHITESP(*p) && *p != '\0')
328266423Sjfv			p++;
329266423Sjfv		*p++ = '\0';
330266423Sjfv		p = skipsp(p);
331277082Sjfv		if (*p == '\0')
332277082Sjfv			/* Pattern is missing! */
333277082Sjfv			continue;
334277082Sjfv
335266423Sjfv		/*
336277082Sjfv		 * First see if it is a line number.
337277082Sjfv		 */
338266423Sjfv		tagendline = 0;
339277082Sjfv		taglinenum = getnum(&p, 0, &err);
340277082Sjfv		if (err)
341266423Sjfv		{
342277082Sjfv			/*
343277082Sjfv			 * No, it must be a pattern.
344266423Sjfv			 * Delete the initial "^" (if present) and
345266423Sjfv			 * the final "$" from the pattern.
346266423Sjfv			 * Delete any backslash in the pattern.
347269198Sjfv			 */
348269198Sjfv			taglinenum = 0;
349277082Sjfv			search_char = *p++;
350277082Sjfv			if (*p == '^')
351277082Sjfv				p++;
352277082Sjfv			tagpattern = p;
353277082Sjfv			while (*p != search_char && *p != '\0')
354277082Sjfv			{
355277082Sjfv				if (*p == '\\')
356277082Sjfv					p++;
357299546Serj				p++;
358277082Sjfv			}
359299546Serj			tagendline = (p[-1] == '$');
360303967Ssbruno			if (tagendline)
361277082Sjfv				p--;
362303967Ssbruno			*p = '\0';
363277082Sjfv		}
364299546Serj		tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
365299546Serj		TAG_INS(tp);
366299546Serj		total++;
367299546Serj	}
368303967Ssbruno	fclose(f);
369299546Serj	if (total == 0)
370299546Serj		return TAG_NOTAG;
371299546Serj	curtag = taglist.tl_first;
372266423Sjfv	curseq = 1;
373266423Sjfv	return TAG_FOUND;
374266423Sjfv}
375266423Sjfv
376266423Sjfv/*
377266423Sjfv * Edit current tagged file.
378266423Sjfv */
379266423Sjfv	public int
380266423Sjfvedit_tagfile()
381266423Sjfv{
382266423Sjfv	if (curtag == NULL)
383266423Sjfv		return (1);
384266423Sjfv	return (edit(curtag->tag_file));
385284049Sjfv}
386284049Sjfv
387284049Sjfv/*
388284049Sjfv * Search for a tag.
389284049Sjfv * This is a stripped-down version of search().
390284049Sjfv * We don't use search() for several reasons:
391284049Sjfv *   -	We don't want to blow away any search string we may have saved.
392266423Sjfv *   -	The various regular-expression functions (from different systems:
393266423Sjfv *	regcmp vs. re_comp) behave differently in the presence of
394266423Sjfv *	parentheses (which are almost always found in a tag).
395266423Sjfv */
396266423Sjfv	static POSITION
397266423Sjfvctagsearch()
398266423Sjfv{
399266423Sjfv	POSITION pos, linepos;
400266423Sjfv	LINENUM linenum;
401266423Sjfv	int len;
402266423Sjfv	char *line;
403266423Sjfv
404266423Sjfv	pos = ch_zero();
405266423Sjfv	linenum = find_linenum(pos);
406266423Sjfv
407266423Sjfv	for (;;)
408266423Sjfv	{
409266423Sjfv		/*
410266423Sjfv		 * Get lines until we find a matching one or
411266423Sjfv		 * until we hit end-of-file.
412266423Sjfv		 */
413266423Sjfv		if (ABORT_SIGS())
414266423Sjfv			return (NULL_POSITION);
415266423Sjfv
416266423Sjfv		/*
417266423Sjfv		 * Read the next line, and save the
418266423Sjfv		 * starting position of that line in linepos.
419266423Sjfv		 */
420303967Ssbruno		linepos = pos;
421303967Ssbruno		pos = forw_raw_line(pos, &line, (int *)NULL);
422303967Ssbruno		if (linenum != 0)
423303967Ssbruno			linenum++;
424303967Ssbruno
425303967Ssbruno		if (pos == NULL_POSITION)
426303967Ssbruno		{
427303967Ssbruno			/*
428303967Ssbruno			 * We hit EOF without a match.
429303967Ssbruno			 */
430303967Ssbruno			error("Tag not found", NULL_PARG);
431303967Ssbruno			return (NULL_POSITION);
432303967Ssbruno		}
433303967Ssbruno
434303967Ssbruno		/*
435303967Ssbruno		 * If we're using line numbers, we might as well
436303967Ssbruno		 * remember the information we have now (the position
437303967Ssbruno		 * and line number of the current line).
438303967Ssbruno		 */
439303967Ssbruno		if (linenums)
440303967Ssbruno			add_lnum(linenum, pos);
441303967Ssbruno
442303967Ssbruno		/*
443303967Ssbruno		 * Test the line to see if we have a match.
444303967Ssbruno		 * Use strncmp because the pattern may be
445303967Ssbruno		 * truncated (in the tags file) if it is too long.
446303967Ssbruno		 * If tagendline is set, make sure we match all
447303967Ssbruno		 * the way to end of line (no extra chars after the match).
448303967Ssbruno		 */
449303967Ssbruno		len = strlen(curtag->tag_pattern);
450303967Ssbruno		if (strncmp(curtag->tag_pattern, line, len) == 0 &&
451303967Ssbruno		    (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
452303967Ssbruno		{
453303967Ssbruno			curtag->tag_linenum = find_linenum(linepos);
454303967Ssbruno			break;
455303967Ssbruno		}
456303967Ssbruno	}
457303967Ssbruno
458303967Ssbruno	return (linepos);
459303967Ssbruno}
460303967Ssbruno
461303967Ssbruno/*******************************************************************************
462303967Ssbruno * gtags
463303967Ssbruno */
464303967Ssbruno
465303967Ssbruno/*
466303967Ssbruno * Find tags in the GLOBAL's tag file.
467303967Ssbruno * The findgtag() will try and load information about the requested tag.
468303967Ssbruno * It does this by calling "global -x tag" and storing the parsed output
469303967Ssbruno * for future use by gtagsearch().
470303967Ssbruno * Sets curtag to the first tag entry.
471303967Ssbruno */
472303967Ssbruno	static enum tag_result
473303967Ssbrunofindgtag(tag, type)
474303967Ssbruno	char *tag;		/* tag to load */
475303967Ssbruno	int type;		/* tags type */
476303967Ssbruno{
477303967Ssbruno	char buf[256];
478303967Ssbruno	FILE *fp;
479303967Ssbruno	struct tag *tp;
480303967Ssbruno
481303967Ssbruno	if (type != T_CTAGS_X && tag == NULL)
482303967Ssbruno		return TAG_NOFILE;
483303967Ssbruno
484303967Ssbruno	cleantags();
485303967Ssbruno	total = 0;
486303967Ssbruno
487303967Ssbruno	/*
488303967Ssbruno	 * If type == T_CTAGS_X then read ctags's -x format from stdin
489303967Ssbruno	 * else execute global(1) and read from it.
490303967Ssbruno	 */
491303967Ssbruno	if (type == T_CTAGS_X)
492303967Ssbruno	{
493303967Ssbruno		fp = stdin;
494303967Ssbruno		/* Set tag default because we cannot read stdin again. */
495303967Ssbruno		tags = "tags";
496303967Ssbruno	} else
497303967Ssbruno	{
498303967Ssbruno#if !HAVE_POPEN
499303967Ssbruno		return TAG_NOFILE;
500303967Ssbruno#else
501303967Ssbruno		char *command;
502303967Ssbruno		char *flag;
503303967Ssbruno		char *qtag;
504303967Ssbruno		char *cmd = lgetenv("LESSGLOBALTAGS");
505303967Ssbruno
506303967Ssbruno		if (cmd == NULL || *cmd == '\0')
507303967Ssbruno			return TAG_NOFILE;
508303967Ssbruno		/* Get suitable flag value for global(1). */
509303967Ssbruno		switch (type)
510303967Ssbruno		{
511303967Ssbruno		case T_GTAGS:
512303967Ssbruno			flag = "" ;
513303967Ssbruno			break;
514303967Ssbruno		case T_GRTAGS:
515303967Ssbruno			flag = "r";
516303967Ssbruno			break;
517303967Ssbruno		case T_GSYMS:
518303967Ssbruno			flag = "s";
519303967Ssbruno			break;
520303967Ssbruno		case T_GPATH:
521303967Ssbruno			flag = "P";
522303967Ssbruno			break;
523303967Ssbruno		default:
524303967Ssbruno			return TAG_NOTYPE;
525303967Ssbruno		}
526303967Ssbruno
527303967Ssbruno		/* Get our data from global(1). */
528303967Ssbruno		qtag = shell_quote(tag);
529303967Ssbruno		if (qtag == NULL)
530303967Ssbruno			qtag = tag;
531303967Ssbruno		command = (char *) ecalloc(strlen(cmd) + strlen(flag) +
532303967Ssbruno				strlen(qtag) + 5, sizeof(char));
533303967Ssbruno		sprintf(command, "%s -x%s %s", cmd, flag, qtag);
534303967Ssbruno		if (qtag != tag)
535303967Ssbruno			free(qtag);
536303967Ssbruno		fp = popen(command, "r");
537303967Ssbruno		free(command);
538303967Ssbruno#endif
539303967Ssbruno	}
540303967Ssbruno	if (fp != NULL)
541303967Ssbruno	{
542303967Ssbruno		while (fgets(buf, sizeof(buf), fp))
543303967Ssbruno		{
544303967Ssbruno			char *name, *file, *line;
545303967Ssbruno			int len;
546303967Ssbruno
547303967Ssbruno			if (sigs)
548303967Ssbruno			{
549303967Ssbruno#if HAVE_POPEN
550303967Ssbruno				if (fp != stdin)
551303967Ssbruno					pclose(fp);
552303967Ssbruno#endif
553303967Ssbruno				return TAG_INTR;
554303967Ssbruno			}
555303967Ssbruno			len = strlen(buf);
556303967Ssbruno			if (len > 0 && buf[len-1] == '\n')
557303967Ssbruno				buf[len-1] = '\0';
558303967Ssbruno			else
559303967Ssbruno			{
560303967Ssbruno				int c;
561303967Ssbruno				do {
562303967Ssbruno					c = fgetc(fp);
563303967Ssbruno				} while (c != '\n' && c != EOF);
564303967Ssbruno			}
565303967Ssbruno
566303967Ssbruno 			if (getentry(buf, &name, &file, &line))
567303967Ssbruno			{
568303967Ssbruno				/*
569303967Ssbruno				 * Couldn't parse this line for some reason.
570303967Ssbruno				 * We'll just pretend it never happened.
571303967Ssbruno				 */
572303967Ssbruno				break;
573303967Ssbruno			}
574303967Ssbruno
575303967Ssbruno			/* Make new entry and add to list. */
576303967Ssbruno			tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
577303967Ssbruno			TAG_INS(tp);
578266423Sjfv			total++;
579266423Sjfv		}
580266423Sjfv		if (fp != stdin)
581266423Sjfv		{
582266423Sjfv			if (pclose(fp))
583266423Sjfv			{
584266423Sjfv				curtag = NULL;
585266423Sjfv				total = curseq = 0;
586266423Sjfv				return TAG_NOFILE;
587266423Sjfv			}
588266423Sjfv		}
589266423Sjfv	}
590266423Sjfv
591266423Sjfv	/* Check to see if we found anything. */
592266423Sjfv	tp = taglist.tl_first;
593266423Sjfv	if (tp == TAG_END)
594266423Sjfv		return TAG_NOTAG;
595266423Sjfv	curtag = tp;
596266423Sjfv	curseq = 1;
597266423Sjfv	return TAG_FOUND;
598266423Sjfv}
599266423Sjfv
600266423Sjfvstatic int circular = 0;	/* 1: circular tag structure */
601266423Sjfv
602266423Sjfv/*
603266423Sjfv * Return the filename required for the next gtag in the queue that was setup
604266423Sjfv * by findgtag().  The next call to gtagsearch() will try to position at the
605266423Sjfv * appropriate tag.
606266423Sjfv */
607266423Sjfv	static char *
608266423Sjfvnextgtag()
609266423Sjfv{
610266423Sjfv	struct tag *tp;
611266423Sjfv
612266423Sjfv	if (curtag == NULL)
613266423Sjfv		/* No tag loaded */
614266423Sjfv		return NULL;
615266423Sjfv
616266423Sjfv	tp = curtag->next;
617266423Sjfv	if (tp == TAG_END)
618266423Sjfv	{
619266423Sjfv		if (!circular)
620266423Sjfv			return NULL;
621266423Sjfv		/* Wrapped around to the head of the queue */
622266423Sjfv		curtag = taglist.tl_first;
623266423Sjfv		curseq = 1;
624266423Sjfv	} else
625266423Sjfv	{
626266423Sjfv		curtag = tp;
627266423Sjfv		curseq++;
628266423Sjfv	}
629266423Sjfv	return (curtag->tag_file);
630266423Sjfv}
631266423Sjfv
632266423Sjfv/*
633266423Sjfv * Return the filename required for the previous gtag in the queue that was
634266423Sjfv * setup by findgtat().  The next call to gtagsearch() will try to position
635266423Sjfv * at the appropriate tag.
636266423Sjfv */
637266423Sjfv	static char *
638266423Sjfvprevgtag()
639266423Sjfv{
640266423Sjfv	struct tag *tp;
641266423Sjfv
642266423Sjfv	if (curtag == NULL)
643266423Sjfv		/* No tag loaded */
644266423Sjfv		return NULL;
645266423Sjfv
646266423Sjfv	tp = curtag->prev;
647266423Sjfv	if (tp == TAG_END)
648266423Sjfv	{
649266423Sjfv		if (!circular)
650266423Sjfv			return NULL;
651266423Sjfv		/* Wrapped around to the tail of the queue */
652266423Sjfv		curtag = taglist.tl_last;
653266423Sjfv		curseq = total;
654266423Sjfv	} else
655266423Sjfv	{
656266423Sjfv		curtag = tp;
657266423Sjfv		curseq--;
658266423Sjfv	}
659266423Sjfv	return (curtag->tag_file);
660266423Sjfv}
661266423Sjfv
662266423Sjfv/*
663266423Sjfv * Position the current file at at what is hopefully the tag that was chosen
664266423Sjfv * using either findtag() or one of nextgtag() and prevgtag().  Returns -1
665266423Sjfv * if it was unable to position at the tag, 0 if successful.
666266423Sjfv */
667266423Sjfv	static POSITION
668266423Sjfvgtagsearch()
669266423Sjfv{
670266423Sjfv	if (curtag == NULL)
671266423Sjfv		return (NULL_POSITION);  /* No gtags loaded! */
672266423Sjfv	return (find_pos(curtag->tag_linenum));
673266423Sjfv}
674266423Sjfv
675266423Sjfv/*
676266423Sjfv * The getentry() parses both standard and extended ctags -x format.
677266423Sjfv *
678266423Sjfv * [standard format]
679266423Sjfv * <tag>   <lineno>  <file>         <image>
680266423Sjfv * +------------------------------------------------
681266423Sjfv * |main     30      main.c         main(argc, argv)
682266423Sjfv * |func     21      subr.c         func(arg)
683266423Sjfv *
684266423Sjfv * The following commands write this format.
685266423Sjfv *	o Traditinal Ctags with -x option
686266423Sjfv *	o Global with -x option
687266423Sjfv *		See <http://www.gnu.org/software/global/global.html>
688266423Sjfv *
689266423Sjfv * [extended format]
690266423Sjfv * <tag>   <type>  <lineno>   <file>        <image>
691266423Sjfv * +----------------------------------------------------------
692266423Sjfv * |main     function 30      main.c         main(argc, argv)
693266423Sjfv * |func     function 21      subr.c         func(arg)
694266423Sjfv *
695266423Sjfv * The following commands write this format.
696266423Sjfv *	o Exuberant Ctags with -x option
697266423Sjfv *		See <http://ctags.sourceforge.net>
698266423Sjfv *
699266423Sjfv * Returns 0 on success, -1 on error.
700266423Sjfv * The tag, file, and line will each be NUL-terminated pointers
701266423Sjfv * into buf.
702266423Sjfv */
703266423Sjfv	static int
704266423Sjfvgetentry(buf, tag, file, line)
705266423Sjfv	char *buf;	/* standard or extended ctags -x format data */
706266423Sjfv	char **tag;	/* name of the tag we actually found */
707266423Sjfv	char **file;	/* file in which to find this tag */
708266423Sjfv	char **line;	/* line number of file where this tag is found */
709266423Sjfv{
710266423Sjfv	char *p = buf;
711266423Sjfv
712266423Sjfv	for (*tag = p;  *p && !IS_SPACE(*p);  p++)	/* tag name */
713266423Sjfv		;
714266423Sjfv	if (*p == 0)
715266423Sjfv		return (-1);
716266423Sjfv	*p++ = 0;
717266423Sjfv	for ( ;  *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
718266423Sjfv		;
719266423Sjfv	if (*p == 0)
720266423Sjfv		return (-1);
721266423Sjfv	/*
722266423Sjfv	 * If the second part begin with other than digit,
723266423Sjfv	 * it is assumed tag type. Skip it.
724266423Sjfv	 */
725266423Sjfv	if (!IS_DIGIT(*p))
726266423Sjfv	{
727266423Sjfv		for ( ;  *p && !IS_SPACE(*p);  p++)	/* (skip tag type) */
728266423Sjfv			;
729266423Sjfv		for (;  *p && IS_SPACE(*p);  p++)	/* (skip blanks) */
730266423Sjfv			;
731266423Sjfv	}
732266423Sjfv	if (!IS_DIGIT(*p))
733266423Sjfv		return (-1);
734266423Sjfv	*line = p;					/* line number */
735266423Sjfv	for (*line = p;  *p && !IS_SPACE(*p);  p++)
736266423Sjfv		;
737266423Sjfv	if (*p == 0)
738266423Sjfv		return (-1);
739266423Sjfv	*p++ = 0;
740266423Sjfv	for ( ; *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
741303967Ssbruno		;
742266423Sjfv	if (*p == 0)
743266423Sjfv		return (-1);
744266423Sjfv	*file = p;					/* file name */
745266423Sjfv	for (*file = p;  *p && !IS_SPACE(*p);  p++)
746266423Sjfv		;
747266423Sjfv	if (*p == 0)
748266423Sjfv		return (-1);
749266423Sjfv	*p = 0;
750266423Sjfv
751266423Sjfv	/* value check */
752266423Sjfv	if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
753266423Sjfv		return (0);
754266423Sjfv	return (-1);
755266423Sjfv}
756266423Sjfv
757266423Sjfv#endif
758266423Sjfv