ftp.nice.ch/pub/next/unix/editor/elvis-2.0.N.bs.tar.gz#/elvis-2.0.N.bs/ref.c

This is ref.c in view mode; [Download] [Up]

/* ref.c */

char id_ref[] = "$Id: ref.c,v 2.8 1996/09/21 02:12:31 steve Exp $";

/* This program looks for a givne tag in the "tags" file, and then tries to
 * locate the definition in the original source file, and display it.  If the
 * original source file is unreadable then it looks for it in a file named
 * "refs".
 *
 * Usage:	ref [-t] [-x] [-f file] [-c class] tag
 * Options:	-t	   output tag info, not the definition
 *		-x         require exact match
 *		-f file	   default filename for static functions
 *		-c class   default class names for class functions
 */
#ifdef __STDC__
# include <string.h>
# include <stdlib.h>
#endif

#include <stdio.h>
#include "elvis.h"
#define JUST_DIRPATH
#include "osdir.c"

#ifndef BLKSIZE
# define BLKSIZE 512
#endif
#ifndef TAGS
# define TAGS	"tags"
#endif
#ifndef REFS
# define REFS	"refs"
#endif

extern char	*cktagdir P_((char *, char *));
extern int	getline P_((char *, int, FILE *));
extern int	lookup P_((char *, char *));
extern int	find P_((char *));
extern void	usage P_((void));
extern int	countcolons P_((char *));
extern void	main P_((int, char **));


/* This is the default path that is searched for tags */
#if OSK
# define DEFTAGPATH ".:/dd/defs:/dd/defs/sys:/dd/usr/src/lib:../lib:/dd/usr/lib"
#else
# if ANY_UNIX
#  define DEFTAGPATH ".:/usr/include:/usr/include/sys:/usr/src/lib:../lib:/usr/local/lib"
# else
#  if MSDOS || TOS || OS2
#   define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib"
#   define OSPATHDELIM ';'
#  else
#   if AMIGA
#    define DEFTAGPATH ".;Include:;Include:sys"
#    define OSPATHDELIM ';'
#   else /* any other OS */
#    define DEFTAGPATH "."
#   endif
#  endif
# endif
#endif

#ifndef OSPATHDELIM
# define OSPATHDELIM ':'
#endif


/* These variables reflect the command-line options given by the user. */
int	taginfo;	/* boolean: give only the tag info? (not header?) */
int	exact;		/* boolean: require exact match? */
char	*def_file;	/* default filename for static functions */
char	*def_class;	/* default classname for class members */
int	colons;		/* #colons in tag: 0=normal, 1=static, 2=member */

/* This function checks for a tag in the "tags" file of given directory.
 * If the tag is found, then it returns a pointer to a static buffer which
 * contains the filename, a tab character, and a linespec for finding the
 * the tag.  If the tag is not found in the "tags" file, or if the "tags"
 * file cannot be opened or doesn't exist, then this function returns NULL.
 */
char *cktagdir(tag, dir)
	char	*tag;	/* name of the tag to look for */
	char	*dir;	/* name of the directory to check */
{
	char	buf[BLKSIZE];
	static char found[BLKSIZE];
	FILE	*tfile;
	unsigned len;
	char	*scan;

	/* first try to open a "tags" file in the directory.  If that fails,
	 * then try to open dir as a regular file itself.
	 */
	tfile = fopen(dirpath(dir, TAGS), "r");
	if (!tfile)
	{
		tfile = fopen(dir, "r");
		if (!tfile)
		{
			return (char *)0;
		}

		/* If we get here, the "dir" variable must contain the name of
		 * a file, not a directory.  Get the directory name from it.
		 */
		strcpy(dir, dirdir(dir));
	}

	/* compute the length of the tagname once */
	len = strlen(tag);

	/* read lines until we get the one for this tag */
	found[0] = '\0';
	while (fgets(buf, sizeof buf, tfile))
	{
		/* is this the one we want? */
		if (!strncmp(buf, tag, len) && buf[len] == '\t')
		{
			/* we've found a match -- remember it */
			strcpy(found, buf);

			/* if there is no default file, or this match is in
			 * the default file, then we've definitely found the
			 * one we want.  Break out of the loop now.
			 */
			if (!def_file || !strncmp(&buf[len + 1], def_file, strlen(def_file)))
			{
				break;
			}
		}

		/* does it maybe match the stuff after the colons? */
		if (!exact)
		{
			scan = strchr(buf, '\t');
			if (scan
			 && (unsigned int)(scan - buf) > len
			 && scan[-(int)len - 1] == ':'
			 && !strncmp(tag, scan - len, len))
			{
				strcpy(found, buf);
				len = (int)(scan - buf);
				break;
			}
		}
	}

	/* we're through reading */
	fclose(tfile);

	/* if there's anything in found[], use it */
	if (found[0])
	{
		return &found[len + 1];
	}

	/* else we didn't find it */
	return (char *)0;
}

/* This function reads a single textline from a binary file.  It returns
 * the number of bytes read, or 0 at EOF.
 */
int getline(buf, limit, fp)
	char	*buf;	/* buffer to read into */
	int	limit;	/* maximum characters to read */
	FILE	*fp;	/* binary stream to read from */
{
	int	bytes;	/* number of bytes read so far */
	int	ch;	/* single character from file */

	for (bytes = 0, ch = 0; ch != '\n' && --limit > 0 && (ch = getc(fp)) != EOF; bytes++)
	{
#if MSDOS || TOS || OS2
		/* since this is a binary file, we'll need to manually strip CR's */
		if (ch == '\r')
		{
			continue;
		}
#endif
		*buf++ = ch;
	}
	*buf = '\0';

	return bytes;
}


/* This function reads a source file, looking for a given tag.  If it finds
 * the tag, then it displays it and returns TRUE.  Otherwise it returns FALSE.
 * To display the tag, it attempts to output any introductory comment, the
 * tag line itself, and any arguments.  Arguments are assumed to immediately
 * follow the tag line, and start with whitespace.  Comments are assumed to
 * start with lines that begin with "/ *", "//", "(*", or "--", and end at the
 * tag line or at a blank line.
 */
int lookup(dir, entry)
	char	*dir;	/* name of the directory that contains the source */
	char	*entry;	/* source filename, <Tab>, linespec */
{
	char	*name;		/* basename of source file */
	char	buf[BLKSIZE];	/* pathname of source file */
	long	lnum;		/* desired line number */
	long	thislnum;	/* current line number */
	long	here;		/* seek position where current line began */
	long	comment;	/* seek position of introductory comment, or -1L */
	FILE	*sfile;		/* used for reading the source file */
	unsigned len = 0;	/* length of string */
	int	noargs = 0;	/* boolean: don't show lines after tag line? */
	unsigned i, j;


	/* skip past the name, to find the linespec */
	name = entry;
	do
	{
		if (!*entry)
		{
			printf("malformed tag line: \"%s\"\n", name);
		}
	} while (*entry++ != '\t');
	entry[-1] = '\0';

	/* construct the pathname of the source file */
	strcpy(buf, dirpath(dir, name));

	/* searching for regexp or number? */
	if (*entry >= '0' && *entry <= '9')
	{
		/* given a specific line number */
		lnum = atol(entry);
		entry = (char *)0;
		noargs = 1;
	}
	else
	{
		/* given a string -- strip off "/^" and "$/\n" */
		entry += 2;
		len = strlen(entry) - 2;
		if (entry[len - 1] == '$')
		{
			entry[len - 1] = '\n';
		}

		/* delete backslashes used for quoting characters */
		for (i = j = 0; i < len; )
		{
			if (entry[i] == '\\' && i + 1 < len && strchr("\\?/", entry[i + 1]))
			{
				i++;
			}
			entry[j++] = entry[i++];
		}
		len = j;

		/* decide whether we'll need to show following lines */
		if (!strchr(entry, '('))
		{
			noargs = 1;
		}
		lnum = 0L;
	}

	/* Open the file. */
	sfile = fopen(buf, "rb");
	if (!sfile)
	{
		/* can't open the real source file.  Try "refs" instead */
		strcpy(buf, dirpath(dir, REFS));
		sfile = fopen(buf, "rb");
		if (!sfile)
		{
			/* failed! */
			return 0;
		}
		name = "refs";
	}

	/* search the file */
	for (comment = -1L, thislnum = 0; here = ftell(sfile), thislnum++, getline(buf, BLKSIZE, sfile) > 0; )
	{
		/* is this the tag line? */
		if (lnum == thislnum || (entry && !strncmp(buf, entry, len)))
		{
			/* display the filename & line number where found */
			printf("%s, line %ld:\n", dirpath(dir, name), thislnum);

			/* if there were introductory comments, show them */
			if (comment != -1L)
			{
				fseek(sfile, comment, 0);
				while (comment != here)
				{
					getline(buf, BLKSIZE, sfile);
					fputs(buf, stdout);
					comment = ftell(sfile);
				}

				/* re-fetch the tag line */
				fgets(buf, BLKSIZE, sfile);
			}

			/* show the tag line */
			fputs(buf, stdout);

			/* are we expected to show argument lines? */
			if (!noargs)
			{
				/* show any argument lines */
				while (getline(buf, BLKSIZE, sfile) > 0
				    && buf[0] != '#'
				    && strchr(buf, '{') == (char *)0)
				{
					fputs(buf, stdout);
				}
			}

			/* Done!  Close the file, and return TRUE */
			fclose(sfile);
			return 1;
		}

		/* Is this the start/end of a comment? */
		if (comment == -1L)
		{
			/* starting a comment? */
			if ((buf[0] == '/' && buf[1] == '*')
			 || (buf[0] == '/' && buf[1] == '/')
			 || (buf[0] == '(' && buf[1] == '*')
			 || (buf[0] == '-' && buf[1] == '-')
			 || (strlen(buf) >= 2 && buf[strlen(buf) - 2] == '{'))
			{
				comment = here;
			}
		}
		else
		{
			/* ending a comment? */
			if (buf[0] == '\n' || buf[0] == '#' 
				|| buf[0] == '}' || (unsigned)buf[0] >= 'A')
			{
				comment = -1L;
			}
		}

	}

	/* not found -- return FALSE */
	return 0;
}

/* This function searches through the entire search path for a given tag.
 * If it finds the tag, then it displays the info and returns TRUE;
 * otherwise it returns FALSE.
 */
int find(tag)
	char	*tag;	/* the tag to look up */
{
	char	*tagpath;
	char	dir[80];
	char	*ptr;

	if (colons == 1)
	{
		/* looking for static function -- only look in current dir */
		tagpath = ".";
	}
	else
	{
		/* get the tagpath from the environment.  Default to DEFTAGPATH */
		tagpath = getenv("TAGPATH");
		if (!tagpath)
		{
			tagpath = DEFTAGPATH;
		}
	}

	/* for each entry in the path... */
	while (*tagpath)
	{
		/* Copy the entry into the dir[] buffer */
		for (ptr = dir; *tagpath && *tagpath != OSPATHDELIM; tagpath++)
		{
			*ptr++ = *tagpath;
		}
		if (*tagpath == OSPATHDELIM)
		{
			tagpath++;
		}

		/* if the entry is now an empty string, then assume "." */
		if (ptr == dir)
		{
			*ptr++ = '.';
		}
		*ptr = '\0';

		/* look for the tag in this path.  If found, then display it
		 * and exit.
		 */
		ptr = cktagdir(tag, dir);
		if (ptr) {
			/* just supposed to display tag info? */
			if (taginfo)
			{
				/* then do only that! */
				printf("%s\n", dirpath(dir, ptr));
				return 1;
			}
			else
			{
				/* else look up the declaration of the thing */
				return lookup(dir, ptr);
			}
		}
	}

	/* if we get here, then the tag wasn't found anywhere */
	return 0;
}

void usage()
{
	fputs("usage: ref [-t] [-x] [-c class] [-f file] tag\n", stderr);
	fputs("   -t        output tag info, instead of the function header\n", stderr);
	fputs("   -x        require exact match, including colons\n", stderr);
	fputs("   -f File   tag might be a static function in File\n", stderr);
	fputs("   -c Class  tag might be a member of class Class\n", stderr);
	fputs("Report bugs to kirkenda@cs.pdx.edu\n", stderr);
	exit(2);
}


int countcolons(str)
	char	*str;
{
	while (*str != ':' && *str)
	{
		str++;
	}
	if (str[0] != ':')
	{
		return 0;
	}
	else if (str[1] != ':')
	{
		return 1;
	}
	return 2;
}

void main(argc, argv)
	int	argc;
	char	**argv;
{
	char	def_tag[100];	/* used to build tag name with default file/class */
	int	i;

	/* detect special GNU flags */
	if (argc >= 2)
	{
		if (!strcmp(argv[1], "-v")
		 || !strcmp(argv[1], "-version")
		 || !strcmp(argv[1], "--version"))
		{
			printf("ref (elvis) %s\n", VERSION);
#ifdef COPY1
			puts(COPY1);
#endif
#ifdef COPY2
			puts(COPY2);
#endif
#ifdef COPY3
			puts(COPY3);
#endif
#ifdef COPY4
			puts(COPY4);
#endif
			exit(0);
		}
	}

	/* parse flags */
	for (i = 1; i < argc && argv[i][0] == '-'; i++)
	{
		switch (argv[i][1])
		{
		  case 't':
			taginfo = 1;
			break;

		  case 'x':
			exact = 1;
			break;

		  case 'f':
			if (argv[i][2])
			{
				def_file = &argv[i][2];
			}
			else if (++i < argc)
			{
				def_file = argv[i];
			}
			else
			{
				usage();
			}
			break;

		  case 'c':
			if (argv[i][2])
			{
				def_class = &argv[i][2];
			}
			else if (++i < argc)
			{
				def_class = argv[i];
			}
			else
			{
				usage();
			}
			break;

		  default:
			usage();
		}
	}

	/* if no tag was given, complain */
	if (i + 1 != argc)
	{
		usage();
	}

	/* does the tag have an explicit class or file? */
	colons = countcolons(argv[i]);

	/* if not, then maybe try some defaults */
	if (colons == 0)
	{
		/* try a static function in the file first */
		if (def_file)
		{
			sprintf(def_tag, "%s:%s", def_file, argv[i]);
			colons = 1;
			if (find(def_tag))
			{
				exit(0);
			}
		}

		/* try a member function for a class */
		if (def_class)
		{
			sprintf(def_tag, "%s::%s", def_class, argv[i]);
			colons = 2;
			if (find(def_tag))
			{
				exit(0);
			}
		}

		/* oh, well */
		colons = 0;
	}
	else /* at least one colon was given */
	{
		/* this implies -x */
		exact = 1;
	}

	/* find the tag */
	if (find(argv[i]))
	{
		exit(0);
	}

	/* Give up.  If doing tag lookup then exit(0), else exit(1) */
	exit(!taginfo);
	/*NOTREACHED*/
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.