GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/less/less/../tags.c Lines: 1 124 0.8 %
Date: 2017-11-07 Branches: 0 82 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (C) 1984-2012  Mark Nudelman
3
 * Modified for use with illumos by Garrett D'Amore.
4
 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5
 *
6
 * You may distribute under the terms of either the GNU General Public
7
 * License or the Less License, as specified in the README file.
8
 *
9
 * For more information, see the README file.
10
 */
11
12
#include "less.h"
13
14
#define	WHITESP(c)	((c) == ' ' || (c) == '\t')
15
16
char *tags = "tags";
17
18
static int total;
19
static int curseq;
20
21
extern int linenums;
22
extern volatile sig_atomic_t sigs;
23
24
enum tag_result {
25
	TAG_FOUND,
26
	TAG_NOFILE,
27
	TAG_NOTAG,
28
	TAG_NOTYPE,
29
	TAG_INTR
30
};
31
32
static enum tag_result findctag(char *);
33
static char *nextctag(void);
34
static char *prevctag(void);
35
static off_t ctagsearch(void);
36
37
/*
38
 * The list of tags generated by the last findctag() call.
39
 */
40
struct taglist {
41
	struct tag *tl_first;
42
	struct tag *tl_last;
43
};
44
#define	TAG_END  ((struct tag *)&taglist)
45
static struct taglist taglist = { TAG_END, TAG_END };
46
struct tag {
47
	struct tag *next, *prev; /* List links */
48
	char *tag_file;		/* Source file containing the tag */
49
	off_t tag_linenum;	/* Appropriate line number in source file */
50
	char *tag_pattern;	/* Pattern used to find the tag */
51
	int tag_endline;	/* True if the pattern includes '$' */
52
};
53
static struct tag *curtag;
54
55
#define	TAG_INS(tp) \
56
	(tp)->next = TAG_END; \
57
	(tp)->prev = taglist.tl_last; \
58
	taglist.tl_last->next = (tp); \
59
	taglist.tl_last = (tp);
60
61
#define	TAG_RM(tp) \
62
	(tp)->next->prev = (tp)->prev; \
63
	(tp)->prev->next = (tp)->next;
64
65
/*
66
 * Delete tag structures.
67
 */
68
void
69
cleantags(void)
70
{
71
	struct tag *tp;
72
73
	/*
74
	 * Delete any existing tag list.
75
	 * {{ Ideally, we wouldn't do this until after we know that we
76
	 *    can load some other tag information. }}
77
	 */
78
	while ((tp = taglist.tl_first) != TAG_END) {
79
		TAG_RM(tp);
80
		free(tp->tag_file);
81
		free(tp->tag_pattern);
82
		free(tp);
83
	}
84
	curtag = NULL;
85
	total = curseq = 0;
86
}
87
88
/*
89
 * Create a new tag entry.
90
 */
91
static struct tag *
92
maketagent(char *file, off_t linenum, char *pattern, int endline)
93
{
94
	struct tag *tp;
95
96
	tp = ecalloc(sizeof (struct tag), 1);
97
	tp->tag_file = estrdup(file);
98
	tp->tag_linenum = linenum;
99
	tp->tag_endline = endline;
100
	if (pattern == NULL)
101
		tp->tag_pattern = NULL;
102
	else
103
		tp->tag_pattern = estrdup(pattern);
104
	return (tp);
105
}
106
107
/*
108
 * Find tags in tag file.
109
 */
110
void
111
findtag(char *tag)
112
{
113
	enum tag_result result;
114
115
	result = findctag(tag);
116
	switch (result) {
117
	case TAG_FOUND:
118
	case TAG_INTR:
119
		break;
120
	case TAG_NOFILE:
121
		error("No tags file", NULL);
122
		break;
123
	case TAG_NOTAG:
124
		error("No such tag in tags file", NULL);
125
		break;
126
	case TAG_NOTYPE:
127
		error("unknown tag type", NULL);
128
		break;
129
	}
130
}
131
132
/*
133
 * Search for a tag.
134
 */
135
off_t
136
tagsearch(void)
137
{
138
	if (curtag == NULL)
139
		return (-1);	/* No tags loaded! */
140
	if (curtag->tag_linenum != 0)
141
		return (find_pos(curtag->tag_linenum));
142
	return (ctagsearch());
143
}
144
145
/*
146
 * Go to the next tag.
147
 */
148
char *
149
nexttag(int n)
150
{
151
	char *tagfile = NULL;
152
153
	while (n-- > 0)
154
		tagfile = nextctag();
155
	return (tagfile);
156
}
157
158
/*
159
 * Go to the previous tag.
160
 */
161
char *
162
prevtag(int n)
163
{
164
	char *tagfile = NULL;
165
166
	while (n-- > 0)
167
		tagfile = prevctag();
168
	return (tagfile);
169
}
170
171
/*
172
 * Return the total number of tags.
173
 */
174
int
175
ntags(void)
176
{
177
1838
	return (total);
178
}
179
180
/*
181
 * Return the sequence number of current tag.
182
 */
183
int
184
curr_tag(void)
185
{
186
	return (curseq);
187
}
188
189
/*
190
 * Find tags in the "tags" file.
191
 * Sets curtag to the first tag entry.
192
 */
193
static enum tag_result
194
findctag(char *tag)
195
{
196
	char *p;
197
	FILE *f;
198
	int taglen;
199
	off_t taglinenum;
200
	char *tagfile;
201
	char *tagpattern;
202
	int tagendline;
203
	int search_char;
204
	int err;
205
	char tline[TAGLINE_SIZE];
206
	struct tag *tp;
207
208
	p = shell_unquote(tags);
209
	f = fopen(p, "r");
210
	free(p);
211
	if (f == NULL)
212
		return (TAG_NOFILE);
213
214
	cleantags();
215
	total = 0;
216
	taglen = strlen(tag);
217
218
	/*
219
	 * Search the tags file for the desired tag.
220
	 */
221
	while (fgets(tline, sizeof (tline), f) != NULL) {
222
		if (tline[0] == '!')
223
			/* Skip header of extended format. */
224
			continue;
225
		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
226
			continue;
227
228
		/*
229
		 * Found it.
230
		 * The line contains the tag, the filename and the
231
		 * location in the file, separated by white space.
232
		 * The location is either a decimal line number,
233
		 * or a search pattern surrounded by a pair of delimiters.
234
		 * Parse the line and extract these parts.
235
		 */
236
		tagpattern = NULL;
237
238
		/*
239
		 * Skip over the whitespace after the tag name.
240
		 */
241
		p = skipsp(tline+taglen);
242
		if (*p == '\0')
243
			/* File name is missing! */
244
			continue;
245
246
		/*
247
		 * Save the file name.
248
		 * Skip over the whitespace after the file name.
249
		 */
250
		tagfile = p;
251
		while (!WHITESP(*p) && *p != '\0')
252
			p++;
253
		*p++ = '\0';
254
		p = skipsp(p);
255
		if (*p == '\0')
256
			/* Pattern is missing! */
257
			continue;
258
259
		/*
260
		 * First see if it is a line number.
261
		 */
262
		tagendline = 0;
263
		taglinenum = getnum(&p, 0, &err);
264
		if (err) {
265
			/*
266
			 * No, it must be a pattern.
267
			 * Delete the initial "^" (if present) and
268
			 * the final "$" from the pattern.
269
			 * Delete any backslash in the pattern.
270
			 */
271
			taglinenum = 0;
272
			search_char = *p++;
273
			if (*p == '^')
274
				p++;
275
			tagpattern = p;
276
			while (*p != search_char && *p != '\0') {
277
				if (*p == '\\')
278
					p++;
279
				p++;
280
			}
281
			tagendline = (p[-1] == '$');
282
			if (tagendline)
283
				p--;
284
			*p = '\0';
285
		}
286
		tp = maketagent(tagfile, taglinenum, tagpattern, tagendline);
287
		TAG_INS(tp);
288
		total++;
289
	}
290
	fclose(f);
291
	if (total == 0)
292
		return (TAG_NOTAG);
293
	curtag = taglist.tl_first;
294
	curseq = 1;
295
	return (TAG_FOUND);
296
}
297
298
/*
299
 * Edit current tagged file.
300
 */
301
int
302
edit_tagfile(void)
303
{
304
	if (curtag == NULL)
305
		return (1);
306
	return (edit(curtag->tag_file));
307
}
308
309
/*
310
 * Search for a tag.
311
 * This is a stripped-down version of search().
312
 * We don't use search() for several reasons:
313
 *   -	We don't want to blow away any search string we may have saved.
314
 *   -	The various regular-expression functions (from different systems:
315
 *	regcmp vs. re_comp) behave differently in the presence of
316
 *	parentheses (which are almost always found in a tag).
317
 */
318
static off_t
319
ctagsearch(void)
320
{
321
	off_t pos, linepos;
322
	off_t linenum;
323
	int len;
324
	char *line;
325
326
	pos = ch_zero();
327
	linenum = find_linenum(pos);
328
329
	for (;;) {
330
		/*
331
		 * Get lines until we find a matching one or
332
		 * until we hit end-of-file.
333
		 */
334
		if (ABORT_SIGS())
335
			return (-1);
336
337
		/*
338
		 * Read the next line, and save the
339
		 * starting position of that line in linepos.
340
		 */
341
		linepos = pos;
342
		pos = forw_raw_line(pos, &line, (int *)NULL);
343
		if (linenum != 0)
344
			linenum++;
345
346
		if (pos == -1) {
347
			/*
348
			 * We hit EOF without a match.
349
			 */
350
			error("Tag not found", NULL);
351
			return (-1);
352
		}
353
354
		/*
355
		 * If we're using line numbers, we might as well
356
		 * remember the information we have now (the position
357
		 * and line number of the current line).
358
		 */
359
		if (linenums)
360
			add_lnum(linenum, pos);
361
362
		/*
363
		 * Test the line to see if we have a match.
364
		 * Use strncmp because the pattern may be
365
		 * truncated (in the tags file) if it is too long.
366
		 * If tagendline is set, make sure we match all
367
		 * the way to end of line (no extra chars after the match).
368
		 */
369
		len = strlen(curtag->tag_pattern);
370
		if (strncmp(curtag->tag_pattern, line, len) == 0 &&
371
		    (!curtag->tag_endline || line[len] == '\0' ||
372
		    line[len] == '\r')) {
373
			curtag->tag_linenum = find_linenum(linepos);
374
			break;
375
		}
376
	}
377
378
	return (linepos);
379
}
380
381
static int circular = 0;	/* 1: circular tag structure */
382
383
/*
384
 * Return the filename required for the next tag in the queue that was setup
385
 * by findctag().  The next call to ctagsearch() will try to position at the
386
 * appropriate tag.
387
 */
388
static char *
389
nextctag(void)
390
{
391
	struct tag *tp;
392
393
	if (curtag == NULL)
394
		/* No tag loaded */
395
		return (NULL);
396
397
	tp = curtag->next;
398
	if (tp == TAG_END) {
399
		if (!circular)
400
			return (NULL);
401
		/* Wrapped around to the head of the queue */
402
		curtag = taglist.tl_first;
403
		curseq = 1;
404
	} else {
405
		curtag = tp;
406
		curseq++;
407
	}
408
	return (curtag->tag_file);
409
}
410
411
/*
412
 * Return the filename required for the previous ctag in the queue that was
413
 * setup by findctag().  The next call to ctagsearch() will try to position
414
 * at the appropriate tag.
415
 */
416
static char *
417
prevctag(void)
418
{
419
	struct tag *tp;
420
421
	if (curtag == NULL)
422
		/* No tag loaded */
423
		return (NULL);
424
425
	tp = curtag->prev;
426
	if (tp == TAG_END) {
427
		if (!circular)
428
			return (NULL);
429
		/* Wrapped around to the tail of the queue */
430
		curtag = taglist.tl_last;
431
		curseq = total;
432
	} else {
433
		curtag = tp;
434
		curseq--;
435
	}
436
	return (curtag->tag_file);
437
}