"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/tags.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  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 
   18 public char *tags = "tags";
   19 
   20 static int total;
   21 static int curseq;
   22 
   23 extern int linenums;
   24 extern int sigs;
   25 
   26 enum tag_result {
   27 	TAG_FOUND,
   28 	TAG_NOFILE,
   29 	TAG_NOTAG,
   30 	TAG_NOTYPE,
   31 	TAG_INTR
   32 };
   33 
   34 /*
   35  * Tag type
   36  */
   37 enum {
   38 	T_CTAGS,	/* 'tags': standard and extended format (ctags) */
   39 	T_CTAGS_X,	/* stdin: cross reference format (ctags) */
   40 	T_GTAGS,	/* 'GTAGS': function defenition (global) */
   41 	T_GRTAGS,	/* 'GRTAGS': function reference (global) */
   42 	T_GSYMS,	/* 'GSYMS': other symbols (global) */
   43 	T_GPATH		/* 'GPATH': path name (global) */
   44 };
   45 
   46 static enum tag_result findctag();
   47 static enum tag_result findgtag();
   48 static char *nextgtag();
   49 static char *prevgtag();
   50 static POSITION ctagsearch();
   51 static POSITION gtagsearch();
   52 static int getentry();
   53 
   54 /*
   55  * The list of tags generated by the last findgtag() call.
   56  *
   57  * Use either pattern or line number.
   58  * findgtag() always uses line number, so pattern is always NULL.
   59  * findctag() uses either pattern (in which case line number is 0),
   60  * or line number (in which case pattern is NULL).
   61  */
   62 struct taglist {
   63 	struct tag *tl_first;
   64 	struct tag *tl_last;
   65 };
   66 #define TAG_END  ((struct tag *) &taglist)
   67 static struct taglist taglist = { TAG_END, TAG_END };
   68 struct tag {
   69 	struct tag *next, *prev; /* List links */
   70 	char *tag_file;		/* Source file containing the tag */
   71 	LINENUM tag_linenum;	/* Appropriate line number in source file */
   72 	char *tag_pattern;	/* Pattern used to find the tag */
   73 	char tag_endline;	/* True if the pattern includes '$' */
   74 };
   75 static struct tag *curtag;
   76 
   77 #define TAG_INS(tp) \
   78 	(tp)->next = TAG_END; \
   79 	(tp)->prev = taglist.tl_last; \
   80 	taglist.tl_last->next = (tp); \
   81 	taglist.tl_last = (tp);
   82 
   83 #define TAG_RM(tp) \
   84 	(tp)->next->prev = (tp)->prev; \
   85 	(tp)->prev->next = (tp)->next;
   86 
   87 /*
   88  * Delete tag structures.
   89  */
   90 	public void
   91 cleantags()
   92 {
   93 	register struct tag *tp;
   94 
   95 	/*
   96 	 * Delete any existing tag list.
   97 	 * {{ Ideally, we wouldn't do this until after we know that we
   98 	 *    can load some other tag information. }}
   99 	 */
  100 	while ((tp = taglist.tl_first) != TAG_END)
  101 	{
  102 		TAG_RM(tp);
  103 		free(tp);
  104 	}
  105 	curtag = NULL;
  106 	total = curseq = 0;
  107 }
  108 
  109 /*
  110  * Create a new tag entry.
  111  */
  112 	static struct tag *
  113 maketagent(name, file, linenum, pattern, endline)
  114 	char *name;
  115 	char *file;
  116 	LINENUM linenum;
  117 	char *pattern;
  118 	int endline;
  119 {
  120 	register struct tag *tp;
  121 
  122 	tp = (struct tag *) ecalloc(sizeof(struct tag), 1);
  123 	tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char));
  124 	strcpy(tp->tag_file, file);
  125 	tp->tag_linenum = linenum;
  126 	tp->tag_endline = endline;
  127 	if (pattern == NULL)
  128 		tp->tag_pattern = NULL;
  129 	else
  130 	{
  131 		tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char));
  132 		strcpy(tp->tag_pattern, pattern);
  133 	}
  134 	return (tp);
  135 }
  136 
  137 /*
  138  * Get tag mode.
  139  */
  140 	public int
  141 gettagtype()
  142 {
  143 	int f;
  144 
  145 	if (strcmp(tags, "GTAGS") == 0)
  146 		return T_GTAGS;
  147 	if (strcmp(tags, "GRTAGS") == 0)
  148 		return T_GRTAGS;
  149 	if (strcmp(tags, "GSYMS") == 0)
  150 		return T_GSYMS;
  151 	if (strcmp(tags, "GPATH") == 0)
  152 		return T_GPATH;
  153 	if (strcmp(tags, "-") == 0)
  154 		return T_CTAGS_X;
  155 	f = open(tags, OPEN_READ);
  156 	if (f >= 0)
  157 	{
  158 		close(f);
  159 		return T_CTAGS;
  160 	}
  161 	return T_GTAGS;
  162 }
  163 
  164 /*
  165  * Find tags in tag file.
  166  * Find a tag in the "tags" file.
  167  * Sets "tag_file" to the name of the file containing the tag,
  168  * and "tagpattern" to the search pattern which should be used
  169  * to find the tag.
  170  */
  171 	public void
  172 findtag(tag)
  173 	register char *tag;
  174 {
  175 	int type = gettagtype();
  176 	enum tag_result result;
  177 
  178 	if (type == T_CTAGS)
  179 		result = findctag(tag);
  180 	else
  181 		result = findgtag(tag, type);
  182 	switch (result)
  183 	{
  184 	case TAG_FOUND:
  185 	case TAG_INTR:
  186 		break;
  187 	case TAG_NOFILE:
  188 		error("No tags file", NULL_PARG);
  189 		break;
  190 	case TAG_NOTAG:
  191 		error("No such tag in tags file", NULL_PARG);
  192 		break;
  193 	case TAG_NOTYPE:
  194 		error("unknown tag type", NULL_PARG);
  195 		break;
  196 	}
  197 }
  198 
  199 /*
  200  * Search for a tag.
  201  */
  202 	public POSITION
  203 tagsearch()
  204 {
  205 	if (curtag == NULL)
  206 		return (NULL_POSITION);  /* No gtags loaded! */
  207 	if (curtag->tag_linenum != 0)
  208 		return gtagsearch();
  209 	else
  210 		return ctagsearch();
  211 }
  212 
  213 /*
  214  * Go to the next tag.
  215  */
  216 	public char *
  217 nexttag(n)
  218 	int n;
  219 {
  220 	char *tagfile = (char *) NULL;
  221 
  222 	while (n-- > 0)
  223 		tagfile = nextgtag();
  224 	return tagfile;
  225 }
  226 
  227 /*
  228  * Go to the previous tag.
  229  */
  230 	public char *
  231 prevtag(n)
  232 	int n;
  233 {
  234 	char *tagfile = (char *) NULL;
  235 
  236 	while (n-- > 0)
  237 		tagfile = prevgtag();
  238 	return tagfile;
  239 }
  240 
  241 /*
  242  * Return the total number of tags.
  243  */
  244 	public int
  245 ntags()
  246 {
  247 	return total;
  248 }
  249 
  250 /*
  251  * Return the sequence number of current tag.
  252  */
  253 	public int
  254 curr_tag()
  255 {
  256 	return curseq;
  257 }
  258 
  259 /*****************************************************************************
  260  * ctags
  261  */
  262 
  263 /*
  264  * Find tags in the "tags" file.
  265  * Sets curtag to the first tag entry.
  266  */
  267 	static enum tag_result
  268 findctag(tag)
  269 	register char *tag;
  270 {
  271 	char *p;
  272 	register FILE *f;
  273 	register int taglen;
  274 	LINENUM taglinenum;
  275 	char *tagfile;
  276 	char *tagpattern;
  277 	int tagendline;
  278 	int search_char;
  279 	int err;
  280 	char tline[TAGLINE_SIZE];
  281 	struct tag *tp;
  282 
  283 	p = shell_unquote(tags);
  284 	f = fopen(p, "r");
  285 	free(p);
  286 	if (f == NULL)
  287 		return TAG_NOFILE;
  288 
  289 	cleantags();
  290 	total = 0;
  291 	taglen = strlen(tag);
  292 
  293 	/*
  294 	 * Search the tags file for the desired tag.
  295 	 */
  296 	while (fgets(tline, sizeof(tline), f) != NULL)
  297 	{
  298 		if (tline[0] == '!')
  299 			/* Skip header of extended format. */
  300 			continue;
  301 		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
  302 			continue;
  303 
  304 		/*
  305 		 * Found it.
  306 		 * The line contains the tag, the filename and the
  307 		 * location in the file, separated by white space.
  308 		 * The location is either a decimal line number,
  309 		 * or a search pattern surrounded by a pair of delimiters.
  310 		 * Parse the line and extract these parts.
  311 		 */
  312 		tagpattern = NULL;
  313 
  314 		/*
  315 		 * Skip over the whitespace after the tag name.
  316 		 */
  317 		p = skipsp(tline+taglen);
  318 		if (*p == '\0')
  319 			/* File name is missing! */
  320 			continue;
  321 
  322 		/*
  323 		 * Save the file name.
  324 		 * Skip over the whitespace after the file name.
  325 		 */
  326 		tagfile = p;
  327 		while (!WHITESP(*p) && *p != '\0')
  328 			p++;
  329 		*p++ = '\0';
  330 		p = skipsp(p);
  331 		if (*p == '\0')
  332 			/* Pattern is missing! */
  333 			continue;
  334 
  335 		/*
  336 		 * First see if it is a line number.
  337 		 */
  338 		tagendline = 0;
  339 		taglinenum = getnum(&p, 0, &err);
  340 		if (err)
  341 		{
  342 			/*
  343 			 * No, it must be a pattern.
  344 			 * Delete the initial "^" (if present) and
  345 			 * the final "$" from the pattern.
  346 			 * Delete any backslash in the pattern.
  347 			 */
  348 			taglinenum = 0;
  349 			search_char = *p++;
  350 			if (*p == '^')
  351 				p++;
  352 			tagpattern = p;
  353 			while (*p != search_char && *p != '\0')
  354 			{
  355 				if (*p == '\\')
  356 					p++;
  357 				p++;
  358 			}
  359 			tagendline = (p[-1] == '$');
  360 			if (tagendline)
  361 				p--;
  362 			*p = '\0';
  363 		}
  364 		tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
  365 		TAG_INS(tp);
  366 		total++;
  367 	}
  368 	fclose(f);
  369 	if (total == 0)
  370 		return TAG_NOTAG;
  371 	curtag = taglist.tl_first;
  372 	curseq = 1;
  373 	return TAG_FOUND;
  374 }
  375 
  376 /*
  377  * Edit current tagged file.
  378  */
  379 	public int
  380 edit_tagfile()
  381 {
  382 	if (curtag == NULL)
  383 		return (1);
  384 	return (edit(curtag->tag_file));
  385 }
  386 
  387 /*
  388  * Search for a tag.
  389  * This is a stripped-down version of search().
  390  * We don't use search() for several reasons:
  391  *   -	We don't want to blow away any search string we may have saved.
  392  *   -	The various regular-expression functions (from different systems:
  393  *	regcmp vs. re_comp) behave differently in the presence of
  394  *	parentheses (which are almost always found in a tag).
  395  */
  396 	static POSITION
  397 ctagsearch()
  398 {
  399 	POSITION pos, linepos;
  400 	LINENUM linenum;
  401 	int len;
  402 	char *line;
  403 
  404 	pos = ch_zero();
  405 	linenum = find_linenum(pos);
  406 
  407 	for (;;)
  408 	{
  409 		/*
  410 		 * Get lines until we find a matching one or
  411 		 * until we hit end-of-file.
  412 		 */
  413 		if (ABORT_SIGS())
  414 			return (NULL_POSITION);
  415 
  416 		/*
  417 		 * Read the next line, and save the
  418 		 * starting position of that line in linepos.
  419 		 */
  420 		linepos = pos;
  421 		pos = forw_raw_line(pos, &line, (int *)NULL);
  422 		if (linenum != 0)
  423 			linenum++;
  424 
  425 		if (pos == NULL_POSITION)
  426 		{
  427 			/*
  428 			 * We hit EOF without a match.
  429 			 */
  430 			error("Tag not found", NULL_PARG);
  431 			return (NULL_POSITION);
  432 		}
  433 
  434 		/*
  435 		 * If we're using line numbers, we might as well
  436 		 * remember the information we have now (the position
  437 		 * and line number of the current line).
  438 		 */
  439 		if (linenums)
  440 			add_lnum(linenum, pos);
  441 
  442 		/*
  443 		 * Test the line to see if we have a match.
  444 		 * Use strncmp because the pattern may be
  445 		 * truncated (in the tags file) if it is too long.
  446 		 * If tagendline is set, make sure we match all
  447 		 * the way to end of line (no extra chars after the match).
  448 		 */
  449 		len = strlen(curtag->tag_pattern);
  450 		if (strncmp(curtag->tag_pattern, line, len) == 0 &&
  451 		    (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
  452 		{
  453 			curtag->tag_linenum = find_linenum(linepos);
  454 			break;
  455 		}
  456 	}
  457 
  458 	return (linepos);
  459 }
  460 
  461 /*******************************************************************************
  462  * gtags
  463  */
  464 
  465 /*
  466  * Find tags in the GLOBAL's tag file.
  467  * The findgtag() will try and load information about the requested tag.
  468  * It does this by calling "global -x tag" and storing the parsed output
  469  * for future use by gtagsearch().
  470  * Sets curtag to the first tag entry.
  471  */
  472 	static enum tag_result
  473 findgtag(tag, type)
  474 	char *tag;		/* tag to load */
  475 	int type;		/* tags type */
  476 {
  477 	char buf[256];
  478 	FILE *fp;
  479 	struct tag *tp;
  480 
  481 	if (type != T_CTAGS_X && tag == NULL)
  482 		return TAG_NOFILE;
  483 
  484 	cleantags();
  485 	total = 0;
  486 
  487 	/*
  488 	 * If type == T_CTAGS_X then read ctags's -x format from stdin
  489 	 * else execute global(1) and read from it.
  490 	 */
  491 	if (type == T_CTAGS_X)
  492 	{
  493 		fp = stdin;
  494 		/* Set tag default because we cannot read stdin again. */
  495 		tags = "tags";
  496 	} else
  497 	{
  498 #if !HAVE_POPEN
  499 		return TAG_NOFILE;
  500 #else
  501 		char *command;
  502 		char *flag;
  503 		char *qtag;
  504 		char *cmd = lgetenv("LESSGLOBALTAGS");
  505 
  506 		if (cmd == NULL || *cmd == '\0')
  507 			return TAG_NOFILE;
  508 		/* Get suitable flag value for global(1). */
  509 		switch (type)
  510 		{
  511 		case T_GTAGS:
  512 			flag = "" ;
  513 			break;
  514 		case T_GRTAGS:
  515 			flag = "r";
  516 			break;
  517 		case T_GSYMS:
  518 			flag = "s";
  519 			break;
  520 		case T_GPATH:
  521 			flag = "P";
  522 			break;
  523 		default:
  524 			return TAG_NOTYPE;
  525 		}
  526 
  527 		/* Get our data from global(1). */
  528 		qtag = shell_quote(tag);
  529 		if (qtag == NULL)
  530 			qtag = tag;
  531 		command = (char *) ecalloc(strlen(cmd) + strlen(flag) +
  532 				strlen(qtag) + 5, sizeof(char));
  533 		sprintf(command, "%s -x%s %s", cmd, flag, qtag);
  534 		if (qtag != tag)
  535 			free(qtag);
  536 		fp = popen(command, "r");
  537 		free(command);
  538 #endif
  539 	}
  540 	if (fp != NULL)
  541 	{
  542 		while (fgets(buf, sizeof(buf), fp))
  543 		{
  544 			char *name, *file, *line;
  545 			int len;
  546 
  547 			if (sigs)
  548 			{
  549 #if HAVE_POPEN
  550 				if (fp != stdin)
  551 					pclose(fp);
  552 #endif
  553 				return TAG_INTR;
  554 			}
  555 			len = strlen(buf);
  556 			if (len > 0 && buf[len-1] == '\n')
  557 				buf[len-1] = '\0';
  558 			else
  559 			{
  560 				int c;
  561 				do {
  562 					c = fgetc(fp);
  563 				} while (c != '\n' && c != EOF);
  564 			}
  565 
  566  			if (getentry(buf, &name, &file, &line))
  567 			{
  568 				/*
  569 				 * Couldn't parse this line for some reason.
  570 				 * We'll just pretend it never happened.
  571 				 */
  572 				break;
  573 			}
  574 
  575 			/* Make new entry and add to list. */
  576 			tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
  577 			TAG_INS(tp);
  578 			total++;
  579 		}
  580 		if (fp != stdin)
  581 		{
  582 			if (pclose(fp))
  583 			{
  584 				curtag = NULL;
  585 				total = curseq = 0;
  586 				return TAG_NOFILE;
  587 			}
  588 		}
  589 	}
  590 
  591 	/* Check to see if we found anything. */
  592 	tp = taglist.tl_first;
  593 	if (tp == TAG_END)
  594 		return TAG_NOTAG;
  595 	curtag = tp;
  596 	curseq = 1;
  597 	return TAG_FOUND;
  598 }
  599 
  600 static int circular = 0;	/* 1: circular tag structure */
  601 
  602 /*
  603  * Return the filename required for the next gtag in the queue that was setup
  604  * by findgtag().  The next call to gtagsearch() will try to position at the
  605  * appropriate tag.
  606  */
  607 	static char *
  608 nextgtag()
  609 {
  610 	struct tag *tp;
  611 
  612 	if (curtag == NULL)
  613 		/* No tag loaded */
  614 		return NULL;
  615 
  616 	tp = curtag->next;
  617 	if (tp == TAG_END)
  618 	{
  619 		if (!circular)
  620 			return NULL;
  621 		/* Wrapped around to the head of the queue */
  622 		curtag = taglist.tl_first;
  623 		curseq = 1;
  624 	} else
  625 	{
  626 		curtag = tp;
  627 		curseq++;
  628 	}
  629 	return (curtag->tag_file);
  630 }
  631 
  632 /*
  633  * Return the filename required for the previous gtag in the queue that was
  634  * setup by findgtat().  The next call to gtagsearch() will try to position
  635  * at the appropriate tag.
  636  */
  637 	static char *
  638 prevgtag()
  639 {
  640 	struct tag *tp;
  641 
  642 	if (curtag == NULL)
  643 		/* No tag loaded */
  644 		return NULL;
  645 
  646 	tp = curtag->prev;
  647 	if (tp == TAG_END)
  648 	{
  649 		if (!circular)
  650 			return NULL;
  651 		/* Wrapped around to the tail of the queue */
  652 		curtag = taglist.tl_last;
  653 		curseq = total;
  654 	} else
  655 	{
  656 		curtag = tp;
  657 		curseq--;
  658 	}
  659 	return (curtag->tag_file);
  660 }
  661 
  662 /*
  663  * Position the current file at at what is hopefully the tag that was chosen
  664  * using either findtag() or one of nextgtag() and prevgtag().  Returns -1
  665  * if it was unable to position at the tag, 0 if successful.
  666  */
  667 	static POSITION
  668 gtagsearch()
  669 {
  670 	if (curtag == NULL)
  671 		return (NULL_POSITION);  /* No gtags loaded! */
  672 	return (find_pos(curtag->tag_linenum));
  673 }
  674 
  675 /*
  676  * The getentry() parses both standard and extended ctags -x format.
  677  *
  678  * [standard format]
  679  * <tag>   <lineno>  <file>         <image>
  680  * +------------------------------------------------
  681  * |main     30      main.c         main(argc, argv)
  682  * |func     21      subr.c         func(arg)
  683  *
  684  * The following commands write this format.
  685  *	o Traditinal Ctags with -x option
  686  *	o Global with -x option
  687  *		See <http://www.gnu.org/software/global/global.html>
  688  *
  689  * [extended format]
  690  * <tag>   <type>  <lineno>   <file>        <image>
  691  * +----------------------------------------------------------
  692  * |main     function 30      main.c         main(argc, argv)
  693  * |func     function 21      subr.c         func(arg)
  694  *
  695  * The following commands write this format.
  696  *	o Exuberant Ctags with -x option
  697  *		See <http://ctags.sourceforge.net>
  698  *
  699  * Returns 0 on success, -1 on error.
  700  * The tag, file, and line will each be NUL-terminated pointers
  701  * into buf.
  702  */
  703 	static int
  704 getentry(buf, tag, file, line)
  705 	char *buf;	/* standard or extended ctags -x format data */
  706 	char **tag;	/* name of the tag we actually found */
  707 	char **file;	/* file in which to find this tag */
  708 	char **line;	/* line number of file where this tag is found */
  709 {
  710 	char *p = buf;
  711 
  712 	for (*tag = p;  *p && !IS_SPACE(*p);  p++)	/* tag name */
  713 		;
  714 	if (*p == 0)
  715 		return (-1);
  716 	*p++ = 0;
  717 	for ( ;  *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
  718 		;
  719 	if (*p == 0)
  720 		return (-1);
  721 	/*
  722 	 * If the second part begin with other than digit,
  723 	 * it is assumed tag type. Skip it.
  724 	 */
  725 	if (!IS_DIGIT(*p))
  726 	{
  727 		for ( ;  *p && !IS_SPACE(*p);  p++)	/* (skip tag type) */
  728 			;
  729 		for (;  *p && IS_SPACE(*p);  p++)	/* (skip blanks) */
  730 			;
  731 	}
  732 	if (!IS_DIGIT(*p))
  733 		return (-1);
  734 	*line = p;					/* line number */
  735 	for (*line = p;  *p && !IS_SPACE(*p);  p++)
  736 		;
  737 	if (*p == 0)
  738 		return (-1);
  739 	*p++ = 0;
  740 	for ( ; *p && IS_SPACE(*p);  p++)		/* (skip blanks) */
  741 		;
  742 	if (*p == 0)
  743 		return (-1);
  744 	*file = p;					/* file name */
  745 	for (*file = p;  *p && !IS_SPACE(*p);  p++)
  746 		;
  747 	if (*p == 0)
  748 		return (-1);
  749 	*p = 0;
  750 
  751 	/* value check */
  752 	if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
  753 		return (0);
  754 	return (-1);
  755 }
  756 
  757 #endif