522 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			522 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* ref2.c */
 | 
						|
 | 
						|
/* This is a totally rewritten version of ref.  This version looks for the
 | 
						|
 * desired function name in the "tags" file, and then reads the header out
 | 
						|
 * from the source file.  There is no longer any need for a "refs" file.
 | 
						|
 *
 | 
						|
 * Usage:	ref [-a] [-t] [-f file] [-c class] tag
 | 
						|
 * Options:	-t	   output tag info, not the description
 | 
						|
 *		-f file	   default filename for static functions
 | 
						|
 *		-c class   default class names for class functions
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include "config.h"
 | 
						|
extern char	*getenv();
 | 
						|
extern char	*fgets();
 | 
						|
 | 
						|
 | 
						|
/* 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
 | 
						|
#   define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib"
 | 
						|
#   define SEP ';'
 | 
						|
#  else
 | 
						|
#   if AMIGA
 | 
						|
#    define DEFTAGPATH ".;Include:;Include:sys"
 | 
						|
#    define SEP ';'
 | 
						|
#   else /* any other OS */
 | 
						|
#    define DEFTAGPATH "."
 | 
						|
#   endif
 | 
						|
#  endif
 | 
						|
# endif
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef SEP
 | 
						|
# define SEP ':'
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* These variables reflect the command-line options given by the user. */
 | 
						|
int	taginfo;	/* boolean: give only the tag info? (not header?) */
 | 
						|
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;
 | 
						|
	int	len;
 | 
						|
 | 
						|
#if AMIGA
 | 
						|
	if (dir[strlen(dir) - 1] == COLON)
 | 
						|
	    sprintf(buf, "%s%s", dir, TAGS);   /* no slash after colon. */
 | 
						|
	else
 | 
						|
#endif
 | 
						|
	/* construct the name of the "tags" file in this directory */
 | 
						|
	sprintf(buf, "%s%c%s", dir, SLASH, TAGS);
 | 
						|
 | 
						|
	/* Try to open the tags file.  Return NULL if can't open */
 | 
						|
#if AMIGA
 | 
						|
	if (buf[0] == '.' && buf[1] == SLASH)
 | 
						|
	    tfile = fopen(&buf[2], "r");
 | 
						|
	else
 | 
						|
#endif
 | 
						|
	tfile = fopen(buf, "r");
 | 
						|
	if (!tfile)
 | 
						|
	{
 | 
						|
		return (char *)0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* 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;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* 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
 | 
						|
		/* 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	buf[BLKSIZE];	/* pathname of sourcefile */
 | 
						|
	long	lnum;		/* 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 */
 | 
						|
	int	len;		/* length of string */
 | 
						|
	char	*ptr;
 | 
						|
 | 
						|
 | 
						|
	/* construct the pathname of the source file */
 | 
						|
	strcpy(buf, dir);
 | 
						|
	ptr = buf + strlen(buf);
 | 
						|
#if AMIGA
 | 
						|
	if (ptr[-1] != COLON)
 | 
						|
#endif
 | 
						|
	*ptr++ = SLASH;
 | 
						|
	while (*entry != '\t')
 | 
						|
	{
 | 
						|
		*ptr++ = *entry++;
 | 
						|
	}
 | 
						|
	*ptr = '\0';
 | 
						|
	entry++;
 | 
						|
 | 
						|
	/* searching for string or number? */
 | 
						|
	if (*entry >= '0' && *entry <= '9')
 | 
						|
	{
 | 
						|
		/* given a specific line number */
 | 
						|
		lnum = atol(entry);
 | 
						|
		entry = (char *)0;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* given a string -- strip off "/^" and "$/\n" */
 | 
						|
		entry += 2;
 | 
						|
		len = strlen(entry) - 2;
 | 
						|
		if (entry[len - 1] == '$')
 | 
						|
		{
 | 
						|
			entry[len - 1] = '\n';
 | 
						|
		}
 | 
						|
		lnum = 0L;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Open the file.  Note that we open the file in binary mode even
 | 
						|
	 * though we know it is a text file, because ftell() and fseek()
 | 
						|
	 * don't work on text files.
 | 
						|
	 */
 | 
						|
#if MSDOS || TOS
 | 
						|
	sfile = fopen(buf, "rb");
 | 
						|
#else
 | 
						|
# if AMIGA
 | 
						|
	if (buf[0] == '.' && buf[1] == SLASH)
 | 
						|
	    sfile = fopen(&buf[2], "r");
 | 
						|
	else
 | 
						|
# endif
 | 
						|
	sfile = fopen(buf, "r");
 | 
						|
#endif
 | 
						|
	if (!sfile)
 | 
						|
	{
 | 
						|
		/* can't open the real source file.  Try "refs" instead */
 | 
						|
#if AMIGA
 | 
						|
		if (dir[strlen(dir) - 1] == COLON)
 | 
						|
			sprintf(buf, "%srefs", dir);
 | 
						|
		else
 | 
						|
#endif
 | 
						|
		sprintf(buf, "%s%crefs", dir, SLASH);
 | 
						|
#if MSDOS || TOS
 | 
						|
		sfile = fopen(buf, "rb");
 | 
						|
#else
 | 
						|
# if AMIGA
 | 
						|
		if (buf[0] == '.' && buf[1] == SLASH)
 | 
						|
		    sfile = fopen(&buf[2], "r");
 | 
						|
		else
 | 
						|
# endif
 | 
						|
		sfile = fopen(buf, "r");
 | 
						|
#endif
 | 
						|
		if (!sfile)
 | 
						|
		{
 | 
						|
			/* failed! */
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* search the file */
 | 
						|
	for (comment = -1L; here = ftell(sfile), getline(buf, BLKSIZE, sfile) > 0; )
 | 
						|
	{
 | 
						|
		/* 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] == '-')
 | 
						|
			{
 | 
						|
				comment = here;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* ending a comment? */
 | 
						|
			if (buf[0] == '\n' || buf[0] == '#')
 | 
						|
			{
 | 
						|
				comment = -1L;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* is this the tag line? */
 | 
						|
		if (--lnum == 0L || (entry && !strncmp(buf, entry, len)))
 | 
						|
		{
 | 
						|
			/* 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);
 | 
						|
 | 
						|
			/* 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;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* 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;
 | 
						|
	int	len;
 | 
						|
 | 
						|
	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 != SEP; tagpath++)
 | 
						|
		{
 | 
						|
			*ptr++ = *tagpath;
 | 
						|
		}
 | 
						|
		if (*tagpath == SEP)
 | 
						|
		{
 | 
						|
			tagpath++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* if the entry ended with "/tags", then strip that off */
 | 
						|
		len = strlen(TAGS);
 | 
						|
		if (&dir[len] < ptr && ptr[-len - 1] == SLASH && !strncmp(&ptr[-len], TAGS, len))
 | 
						|
		{
 | 
						|
			ptr -= len + 1;
 | 
						|
		}
 | 
						|
 | 
						|
		/* 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! */
 | 
						|
				if (strcmp(dir, "."))
 | 
						|
				{
 | 
						|
					printf("%s%c%s", dir, SLASH, ptr);
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					/* avoid leading "./" if possible */
 | 
						|
					fputs(ptr, stdout);
 | 
						|
				}
 | 
						|
				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 [-a] [-t] [-c class] [-f file] tag\n", stderr);
 | 
						|
	fputs("   -a        function's args may be flush against left margin\n", stderr);
 | 
						|
	fputs("   -t        output tag info, instead of the function header\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);
 | 
						|
	exit(2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int countcolons(str)
 | 
						|
	char	*str;
 | 
						|
{
 | 
						|
	while (*str != ':' && *str)
 | 
						|
	{
 | 
						|
		str++;
 | 
						|
	}
 | 
						|
	if (str[0] != ':')
 | 
						|
	{
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	else if (str[1] != ':')
 | 
						|
	{
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	return 2;
 | 
						|
}
 | 
						|
 | 
						|
int main(argc, argv)
 | 
						|
	int	argc;
 | 
						|
	char	**argv;
 | 
						|
{
 | 
						|
	char	def_tag[100];	/* used to build tag name with default file/class */
 | 
						|
	int	i;
 | 
						|
 | 
						|
	/* parse flags */
 | 
						|
	for (i = 1; i < argc && argv[i][0] == '-'; i++)
 | 
						|
	{
 | 
						|
		switch (argv[i][1])
 | 
						|
		{
 | 
						|
		  case 't':
 | 
						|
			taginfo = 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;
 | 
						|
	}
 | 
						|
 | 
						|
	/* find the tag */
 | 
						|
	if (find(argv[i]))
 | 
						|
	{
 | 
						|
		exit(0);
 | 
						|
	}
 | 
						|
 | 
						|
	exit(1);
 | 
						|
	/*NOTREACHED*/
 | 
						|
}
 |