/*
 * Copyright (c) 1984,1985,1989,1994,1995,1996,1999  Mark Nudelman
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice in the documentation and/or other materials provided with 
 *    the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include "less.h"

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

#if TAGS

public char *tags = "tags";

static char *tagfile;
static char *tagpattern;
static int taglinenum;
static int tagendline;

extern int linenums;
extern int sigs;
extern int jump_sline;

/*
 * Find a tag in the "tags" file.
 * Sets "tagfile" to the name of the file containing the tag,
 * and "tagpattern" to the search pattern which should be used
 * to find the tag.
 */
	public void
findtag(tag)
	register char *tag;
{
	char *p;
	char *q;
	register FILE *f;
	register int taglen;
	int search_char;
	int err;
	char tline[TAGLINE_SIZE];

	p = unquote_file(tags);
	f = fopen(p, "r");
	free(p);
	if (f == NULL)
	{
		error("No tags file", NULL_PARG);
		tagfile = NULL;
		return;
	}

	taglen = strlen(tag);

	/*
	 * Search the tags file for the desired tag.
	 */
	while (fgets(tline, sizeof(tline), f) != NULL)
	{
		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.
		 */
		tagfile = tagpattern = NULL;
		taglinenum = 0;

		/*
		 * 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;
		tagfile = save(tagfile);

		/*
		 * First see if it is a line number. 
		 */
		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 = (char *) ecalloc(strlen(p)+1, sizeof(char));
			q = tagpattern;
			while (*p != search_char && *p != '\0')
			{
				if (*p == '\\')
					p++;
				*q++ = *p++;
			}
			tagendline = (q[-1] == '$');
			if (tagendline)
				q--;
			*q = '\0';
		}

		fclose(f);
		return;
	}
	fclose(f);
	error("No such tag in tags file", NULL_PARG);
	tagfile = NULL;
}

	public int
edit_tagfile()
{
	int r;

	if (tagfile == NULL)
		return (1);
	r = edit(tagfile);
	free(tagfile);
	tagfile = NULL;
	return (r);
}

/*
 * 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).
 */
	public POSITION
tagsearch()
{
	POSITION pos, linepos;
	int linenum;
	int len;
	char *line;

	/*
	 * If we have the line number of the tag instead of the pattern,
	 * just use find_pos.
	 */
	if (taglinenum)
		return (find_pos(taglinenum));

	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 (NULL_POSITION);

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

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

		/*
		 * 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(tagpattern);
		if (strncmp(tagpattern, line, len) == 0 &&
		    (!tagendline || line[len] == '\0' || line[len] == '\r'))
			break;
	}

	free(tagpattern);
	tagpattern = NULL;
	return (linepos);
}

#endif
