tags.c revision 60786
1/*
2 * Copyright (C) 1984-2000  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12#include "less.h"
13
14#define	WHITESP(c)	((c)==' ' || (c)=='\t')
15
16#if TAGS
17
18public char *tags = "tags";
19
20static char *tagfile;
21static char *tagpattern;
22static int taglinenum;
23static int tagendline;
24
25extern int linenums;
26extern int sigs;
27extern int jump_sline;
28
29/*
30 * Find a tag in the "tags" file.
31 * Sets "tagfile" to the name of the file containing the tag,
32 * and "tagpattern" to the search pattern which should be used
33 * to find the tag.
34 */
35	public void
36findtag(tag)
37	register char *tag;
38{
39	char *p;
40	char *q;
41	register FILE *f;
42	register int taglen;
43	int search_char;
44	int err;
45	char tline[TAGLINE_SIZE];
46
47	p = unquote_file(tags);
48	f = fopen(p, "r");
49	free(p);
50	if (f == NULL)
51	{
52		error("No tags file", NULL_PARG);
53		tagfile = NULL;
54		return;
55	}
56
57	taglen = strlen(tag);
58
59	/*
60	 * Search the tags file for the desired tag.
61	 */
62	while (fgets(tline, sizeof(tline), f) != NULL)
63	{
64		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
65			continue;
66
67		/*
68		 * Found it.
69		 * The line contains the tag, the filename and the
70		 * location in the file, separated by white space.
71		 * The location is either a decimal line number,
72		 * or a search pattern surrounded by a pair of delimiters.
73		 * Parse the line and extract these parts.
74		 */
75		tagfile = tagpattern = NULL;
76		taglinenum = 0;
77
78		/*
79		 * Skip over the whitespace after the tag name.
80		 */
81		p = skipsp(tline+taglen);
82		if (*p == '\0')
83			/* File name is missing! */
84			continue;
85
86		/*
87		 * Save the file name.
88		 * Skip over the whitespace after the file name.
89		 */
90		tagfile = p;
91		while (!WHITESP(*p) && *p != '\0')
92			p++;
93		*p++ = '\0';
94		p = skipsp(p);
95		if (*p == '\0')
96			/* Pattern is missing! */
97			continue;
98		tagfile = save(tagfile);
99
100		/*
101		 * First see if it is a line number.
102		 */
103		taglinenum = getnum(&p, 0, &err);
104		if (err)
105		{
106			/*
107			 * No, it must be a pattern.
108			 * Delete the initial "^" (if present) and
109			 * the final "$" from the pattern.
110			 * Delete any backslash in the pattern.
111			 */
112			taglinenum = 0;
113			search_char = *p++;
114			if (*p == '^')
115				p++;
116			tagpattern = (char *) ecalloc(strlen(p)+1, sizeof(char));
117			q = tagpattern;
118			while (*p != search_char && *p != '\0')
119			{
120				if (*p == '\\')
121					p++;
122				*q++ = *p++;
123			}
124			tagendline = (q[-1] == '$');
125			if (tagendline)
126				q--;
127			*q = '\0';
128		}
129
130		fclose(f);
131		return;
132	}
133	fclose(f);
134	error("No such tag in tags file", NULL_PARG);
135	tagfile = NULL;
136}
137
138	public int
139edit_tagfile()
140{
141	int r;
142
143	if (tagfile == NULL)
144		return (1);
145	r = edit(tagfile);
146	free(tagfile);
147	tagfile = NULL;
148	return (r);
149}
150
151/*
152 * Search for a tag.
153 * This is a stripped-down version of search().
154 * We don't use search() for several reasons:
155 *   -	We don't want to blow away any search string we may have saved.
156 *   -	The various regular-expression functions (from different systems:
157 *	regcmp vs. re_comp) behave differently in the presence of
158 *	parentheses (which are almost always found in a tag).
159 */
160	public POSITION
161tagsearch()
162{
163	POSITION pos, linepos;
164	int linenum;
165	int len;
166	char *line;
167
168	/*
169	 * If we have the line number of the tag instead of the pattern,
170	 * just use find_pos.
171	 */
172	if (taglinenum)
173		return (find_pos(taglinenum));
174
175	pos = ch_zero();
176	linenum = find_linenum(pos);
177
178	for (;;)
179	{
180		/*
181		 * Get lines until we find a matching one or
182		 * until we hit end-of-file.
183		 */
184		if (ABORT_SIGS())
185			return (NULL_POSITION);
186
187		/*
188		 * Read the next line, and save the
189		 * starting position of that line in linepos.
190		 */
191		linepos = pos;
192		pos = forw_raw_line(pos, &line);
193		if (linenum != 0)
194			linenum++;
195
196		if (pos == NULL_POSITION)
197		{
198			/*
199			 * We hit EOF without a match.
200			 */
201			error("Tag not found", NULL_PARG);
202			return (NULL_POSITION);
203		}
204
205		/*
206		 * If we're using line numbers, we might as well
207		 * remember the information we have now (the position
208		 * and line number of the current line).
209		 */
210		if (linenums)
211			add_lnum(linenum, pos);
212
213		/*
214		 * Test the line to see if we have a match.
215		 * Use strncmp because the pattern may be
216		 * truncated (in the tags file) if it is too long.
217		 * If tagendline is set, make sure we match all
218		 * the way to end of line (no extra chars after the match).
219		 */
220		len = strlen(tagpattern);
221		if (strncmp(tagpattern, line, len) == 0 &&
222		    (!tagendline || line[len] == '\0' || line[len] == '\r'))
223			break;
224	}
225
226	free(tagpattern);
227	tagpattern = NULL;
228	return (linepos);
229}
230
231#endif
232