[BACK]Return to tags.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

File: [local] / src / usr.bin / less / tags.c (download)

Revision 1.20, Tue Sep 3 23:08:42 2019 UTC (4 years, 8 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, HEAD
Changes since 1.19: +1 -2 lines

less uses a correct raceless signal method of indicating signal events in
a volatile sig_atomic_t variable, and then processing events in the mainloop.
But only one variable was used for 3 signals, with |= bit operations which
are signal interruptable!  Rewrite the code to use 3 independent variables
and cleanup how the mainloop observes indications.
ok schwarze

/*
 * Copyright (C) 1984-2012  Mark Nudelman
 * Modified for use with illumos by Garrett D'Amore.
 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Less License, as specified in the README file.
 *
 * For more information, see the README file.
 */

#include "less.h"

#define	WHITESP(c)	((c) == ' ' || (c) == '\t')

char *tags = "tags";

static int total;
static int curseq;

extern int linenums;

enum tag_result {
	TAG_FOUND,
	TAG_NOFILE,
	TAG_NOTAG,
	TAG_NOTYPE,
	TAG_INTR
};

static enum tag_result findctag(char *);
static char *nextctag(void);
static char *prevctag(void);
static off_t ctagsearch(void);

/*
 * The list of tags generated by the last findctag() call.
 */
struct taglist {
	struct tag *tl_first;
	struct tag *tl_last;
};
#define	TAG_END  ((struct tag *)&taglist)
static struct taglist taglist = { TAG_END, TAG_END };
struct tag {
	struct tag *next, *prev; /* List links */
	char *tag_file;		/* Source file containing the tag */
	off_t tag_linenum;	/* Appropriate line number in source file */
	char *tag_pattern;	/* Pattern used to find the tag */
	int tag_endline;	/* True if the pattern includes '$' */
};
static struct tag *curtag;

#define	TAG_INS(tp) \
	(tp)->next = TAG_END; \
	(tp)->prev = taglist.tl_last; \
	taglist.tl_last->next = (tp); \
	taglist.tl_last = (tp);

#define	TAG_RM(tp) \
	(tp)->next->prev = (tp)->prev; \
	(tp)->prev->next = (tp)->next;

/*
 * Delete tag structures.
 */
void
cleantags(void)
{
	struct tag *tp;

	/*
	 * Delete any existing tag list.
	 * {{ Ideally, we wouldn't do this until after we know that we
	 *    can load some other tag information. }}
	 */
	while ((tp = taglist.tl_first) != TAG_END) {
		TAG_RM(tp);
		free(tp->tag_file);
		free(tp->tag_pattern);
		free(tp);
	}
	curtag = NULL;
	total = curseq = 0;
}

/*
 * Create a new tag entry.
 */
static struct tag *
maketagent(char *file, off_t linenum, char *pattern, int endline)
{
	struct tag *tp;

	tp = ecalloc(sizeof (struct tag), 1);
	tp->tag_file = estrdup(file);
	tp->tag_linenum = linenum;
	tp->tag_endline = endline;
	if (pattern == NULL)
		tp->tag_pattern = NULL;
	else
		tp->tag_pattern = estrdup(pattern);
	return (tp);
}

/*
 * Find tags in tag file.
 */
void
findtag(char *tag)
{
	enum tag_result result;

	result = findctag(tag);
	switch (result) {
	case TAG_FOUND:
	case TAG_INTR:
		break;
	case TAG_NOFILE:
		error("No tags file", NULL);
		break;
	case TAG_NOTAG:
		error("No such tag in tags file", NULL);
		break;
	case TAG_NOTYPE:
		error("unknown tag type", NULL);
		break;
	}
}

/*
 * Search for a tag.
 */
off_t
tagsearch(void)
{
	if (curtag == NULL)
		return (-1);	/* No tags loaded! */
	if (curtag->tag_linenum != 0)
		return (find_pos(curtag->tag_linenum));
	return (ctagsearch());
}

/*
 * Go to the next tag.
 */
char *
nexttag(int n)
{
	char *tagfile = NULL;

	while (n-- > 0)
		tagfile = nextctag();
	return (tagfile);
}

/*
 * Go to the previous tag.
 */
char *
prevtag(int n)
{
	char *tagfile = NULL;

	while (n-- > 0)
		tagfile = prevctag();
	return (tagfile);
}

/*
 * Return the total number of tags.
 */
int
ntags(void)
{
	return (total);
}

/*
 * Return the sequence number of current tag.
 */
int
curr_tag(void)
{
	return (curseq);
}

/*
 * Find tags in the "tags" file.
 * Sets curtag to the first tag entry.
 */
static enum tag_result
findctag(char *tag)
{
	char *p;
	FILE *f;
	int taglen;
	off_t taglinenum;
	char *tagfile;
	char *tagpattern;
	int tagendline;
	int search_char;
	int err;
	char tline[TAGLINE_SIZE];
	struct tag *tp;

	p = shell_unquote(tags);
	f = fopen(p, "r");
	free(p);
	if (f == NULL)
		return (TAG_NOFILE);

	cleantags();
	total = 0;
	taglen = strlen(tag);

	/*
	 * Search the tags file for the desired tag.
	 */
	while (fgets(tline, sizeof (tline), f) != NULL) {
		if (tline[0] == '!')
			/* Skip header of extended format. */
			continue;
		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
			continue;

		/*
		 * Found it.
		 * The line contains the tag, the filename and the
		 * location in the file, separated by white space.
		 * The location is either a decimal line number,
		 * or a search pattern surrounded by a pair of delimiters.
		 * Parse the line and extract these parts.
		 */
		tagpattern = NULL;

		/*
		 * Skip over the whitespace after the tag name.
		 */
		p = skipsp(tline+taglen);
		if (*p == '\0')
			/* File name is missing! */
			continue;

		/*
		 * Save the file name.
		 * Skip over the whitespace after the file name.
		 */
		tagfile = p;
		while (!WHITESP(*p) && *p != '\0')
			p++;
		*p++ = '\0';
		p = skipsp(p);
		if (*p == '\0')
			/* Pattern is missing! */
			continue;

		/*
		 * First see if it is a line number.
		 */
		tagendline = 0;
		taglinenum = getnum(&p, 0, &err);
		if (err) {
			/*
			 * No, it must be a pattern.
			 * Delete the initial "^" (if present) and
			 * the final "$" from the pattern.
			 * Delete any backslash in the pattern.
			 */
			taglinenum = 0;
			search_char = *p++;
			if (*p == '^')
				p++;
			tagpattern = p;
			while (*p != search_char && *p != '\0') {
				if (*p == '\\')
					p++;
				p++;
			}
			tagendline = (p[-1] == '$');
			if (tagendline)
				p--;
			*p = '\0';
		}
		tp = maketagent(tagfile, taglinenum, tagpattern, tagendline);
		TAG_INS(tp);
		total++;
	}
	fclose(f);
	if (total == 0)
		return (TAG_NOTAG);
	curtag = taglist.tl_first;
	curseq = 1;
	return (TAG_FOUND);
}

/*
 * Edit current tagged file.
 */
int
edit_tagfile(void)
{
	if (curtag == NULL)
		return (1);
	return (edit(curtag->tag_file));
}

/*
 * Search for a tag.
 * This is a stripped-down version of search().
 * We don't use search() for several reasons:
 *   -	We don't want to blow away any search string we may have saved.
 *   -	The various regular-expression functions (from different systems:
 *	regcmp vs. re_comp) behave differently in the presence of
 *	parentheses (which are almost always found in a tag).
 */
static off_t
ctagsearch(void)
{
	off_t pos, linepos;
	off_t linenum;
	int len;
	char *line;

	pos = ch_zero();
	linenum = find_linenum(pos);

	for (;;) {
		/*
		 * Get lines until we find a matching one or
		 * until we hit end-of-file.
		 */
		if (abort_sigs())
			return (-1);

		/*
		 * Read the next line, and save the
		 * starting position of that line in linepos.
		 */
		linepos = pos;
		pos = forw_raw_line(pos, &line, (int *)NULL);
		if (linenum != 0)
			linenum++;

		if (pos == -1) {
			/*
			 * We hit EOF without a match.
			 */
			error("Tag not found", NULL);
			return (-1);
		}

		/*
		 * If we're using line numbers, we might as well
		 * remember the information we have now (the position
		 * and line number of the current line).
		 */
		if (linenums)
			add_lnum(linenum, pos);

		/*
		 * Test the line to see if we have a match.
		 * Use strncmp because the pattern may be
		 * truncated (in the tags file) if it is too long.
		 * If tagendline is set, make sure we match all
		 * the way to end of line (no extra chars after the match).
		 */
		len = strlen(curtag->tag_pattern);
		if (strncmp(curtag->tag_pattern, line, len) == 0 &&
		    (!curtag->tag_endline || line[len] == '\0' ||
		    line[len] == '\r')) {
			curtag->tag_linenum = find_linenum(linepos);
			break;
		}
	}

	return (linepos);
}

static int circular = 0;	/* 1: circular tag structure */

/*
 * Return the filename required for the next tag in the queue that was setup
 * by findctag().  The next call to ctagsearch() will try to position at the
 * appropriate tag.
 */
static char *
nextctag(void)
{
	struct tag *tp;

	if (curtag == NULL)
		/* No tag loaded */
		return (NULL);

	tp = curtag->next;
	if (tp == TAG_END) {
		if (!circular)
			return (NULL);
		/* Wrapped around to the head of the queue */
		curtag = taglist.tl_first;
		curseq = 1;
	} else {
		curtag = tp;
		curseq++;
	}
	return (curtag->tag_file);
}

/*
 * Return the filename required for the previous ctag in the queue that was
 * setup by findctag().  The next call to ctagsearch() will try to position
 * at the appropriate tag.
 */
static char *
prevctag(void)
{
	struct tag *tp;

	if (curtag == NULL)
		/* No tag loaded */
		return (NULL);

	tp = curtag->prev;
	if (tp == TAG_END) {
		if (!circular)
			return (NULL);
		/* Wrapped around to the tail of the queue */
		curtag = taglist.tl_last;
		curseq = total;
	} else {
		curtag = tp;
		curseq--;
	}
	return (curtag->tag_file);
}