941 lines · 20.0 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * calls - print a paragraphed list of who calls whom within a body of C source * * Author: M.M. Taylor, DCIEM, Toronto, Canada. * Modified by Alexis Kwan (HCR at DCIEM), *	Kevin Szabo (watmath!wateng!ksbszabo, Elec Eng, U of Waterloo), *	Tony Hansen, AT&T-IS, pegasus!hansen. */ #include <u.h>#include <libc.h>#include <ctype.h>#include <bio.h>#include <String.h> #define CPP		"cpp -+"#define RINSTERR	((Rinst *)-1)	/* ugly; error but keep going */ #define STREQ(a, b)	(*(a) == *(b) && strcmp(a, b) == 0)#define OTHER(rdwr)	((rdwr) == Rd? Wr: Rd)/* per 8c, all multibyte runes are considered alphabetic */#define ISIDENT(r) (isascii(r) && isalnum(r) || (r) == '_' || (r) >= Runeself) /* safe macros */#define checksys(atom)		strbsearch(atom, sysword, nelem(sysword)) enum {	Printstats =	0,		/* flag */	Maxseen =	4000,		/* # of instances w/in a function */	Maxdepth =	300,		/* max func call tree depth */	Hashsize =	2048, 	Maxid =		256 + UTFmax,	/* max size of name */	Tabwidth =	8,	Maxwidth =	132,		/* limits tabbing */	Defwidth =	80,		/* limits tabbing */ 	Backslash =	'\\',	Quote =		'\'', 	Rd =		0,		/* pipe indices */	Wr, 	Stdin =		0,	Stdout,	Stderr, 	Defn =		0,	Decl,	Call, 	Nomore =	-1,	Added,	Found,}; typedef struct Pushstate Pushstate;typedef struct Rinst Rinst;typedef struct Root Root;typedef struct Rname Rname;typedef struct Rnamehash Rnamehash; struct Pushstate {	int	kid;	int	fd;	/* original fd */	int	rfd;	/* replacement fd */	int	input;	int	open;}; struct Rname {	Rinst	*dlistp;	int	rnameout;	char	rnamecalled;	char	rnamedefined;	char	*namer;	Rname	*next;		/* next in hash chain */}; struct Rnamehash {	Rname	*head;}; /* list of calling instances of those names */struct Rinst {	Rname	*namep;	Rinst	*calls;	Rinst	*calledby;}; struct Root {	char	*func;	Root	*next;}; char *aseen[Maxseen];		/* names being gathered within a function */Rnamehash nameshash[Hashsize];	/* names being tracked */Rname *activelist[Maxdepth];	/* names being output */String *cppopt;Root *roots; static struct stats {	long	highestseen;	/* aseen high water mark */	long	highestname;	/* namelist high water mark */	long	highestact;	/* activelist high water mark */	long	highgetfree;	/* getfrees high water mark */} stats; static long getfrees = 0; int bracket = 0;			/* curly brace count in input */int linect = 0;				/* line number in output */int activep = 0;			/* current function being output */ char *infile;int lineno = 1;				/* line number of input */int prevc = '\n', thisc = '\n'; /* options */int terse = 1;				/* track functions only once */int ntabs = (Maxwidth - 20) / Tabwidth;	/* how wide to go */ char *dashes;				/* separators for deep nestings */ /* * These are C tokens after which a parenthesis is valid which would * otherwise be tagged as function names.  The reserved words which are not * listed are break, const, continue, default, goto and volatile. */char *sysword[] = {	"auto", "case", "char", "do", "double", "else", "enum",	"extern", "float", "for", "if", "int", "long", "register",	"return", "short", "sizeof", "static", "struct", "switch",	"typedef", "union", "unsigned", "void", "while",}; /* * warning - print best error message possible */voidwarning(char *s1, char *s2){	fprint(2, "%s: ", argv0);	fprint(2, s1, s2);	fprint(2, "\n");} /* *   safe malloc() code.  Does the checking for nil returns from malloc() */void *emalloc(int size){	void *f = mallocz(size, 1); 	if (f == nil)		sysfatal("cannot allocate memory");	return f;} unsignedhash(char *s){	unsigned h;	unsigned char *cp; 	h = 0;	for(cp = (unsigned char *)s; *cp; h += *cp++)		h *= 1119;	return h;} intnextc(Biobuf *in){	prevc = thisc;	thisc = Bgetrune(in);	return thisc;} intungetc(Biobuf *in){	prevc = thisc;	return Bungetrune(in);} intnewatom(Biobuf *in, char *atom){	atom[0] = '\0';	return nextc(in);} /* * lookup (name) accepts a pointer to a name and sees if the name is on the * namelist.  If so, it returns a pointer to the nameblock.  Otherwise it * returns nil. */Rname *lookfor(char *name){	int i;	unsigned buck;	Rname *np;	Rnamehash *hp; 	buck = hash(name) % Hashsize;	hp = &nameshash[buck];	i = 0;	for (np = hp->head; np != nil; np = np->next, i++)		if (STREQ(name, np->namer))			return np; 	if (i > stats.highestname)		stats.highestname = i;	return nil;} /* * place() returns a pointer to the name on the namelist.  If the name was * not in the namelist, place adds it. */Rname *place(char name[]){	unsigned buck;	Rname *np;	Rnamehash *hp; 	np = lookfor(name);	if (np != nil)		return np; 	buck = hash(name) % Hashsize;	hp = &nameshash[buck];	np = emalloc(sizeof *np);	np->namer = strdup(name);	np->next = hp->head;	hp->head = np;	return np;} /* * getfree returns a pointer to the next free instance block on the list */Rinst *getfree(void){	Rinst *ret, *new;	static Rinst *prev; 	++getfrees;	if (getfrees > stats.highgetfree)		stats.highgetfree = getfrees; 	if (prev == nil)		prev = emalloc(sizeof *prev);	new = emalloc(sizeof *new); 	prev->calls = new;		/* also serves as next pointer */	new->calledby = prev; 	ret = prev;	prev = new;	return ret;} /* * install(np, rp) puts a new instance of a function into the linked list. * It puts a pointer (np) to its own name (returned by place) into its * namepointer, a pointer to the calling routine (rp) into its called-by * pointer, and zero into the calls pointer.  It then puts a pointer to * itself into the last function in the chain. */Rinst *install(Rname *np, Rinst *rp){	Rinst *newp; 	if (np == nil)		return RINSTERR;	if ((newp = getfree()) == nil)		return nil;	newp->namep = np;	newp->calls = 0;	if (rp) {		while (rp->calls)			rp = rp->calls;		newp->calledby = rp->calledby;		rp->calls = newp;	} else {		newp->calledby = (Rinst *)np;		np->dlistp = newp;	}	return newp;} /* * When scanning the text, each function instance is inserted into a * linear list of names, using the Rname structure, when it is first * encountered.  It is also inserted into the linked list using the Rinst * structure.  The entry into the name list has a pointer to the defining * instance in the linked list, and each entry in the linked list has a * pointer back to the relevant name.  Newproc makes an entry in the * defining list, which is distinguished from the called list only * because it has no calledby link (value=0).  Add2proc enters into the * called list, by inserting a link to the new instance in the calls * pointer of the last entry (may be a defining instance, or a function * called by that defining instance), and points back to the defining * instance of the caller in its called-by pointer. */Rinst *newproc(char *name){	int i;	Rname *rp; 	for (i = 0; i < Maxseen; i++)		if (aseen[i] != nil) {			free(aseen[i]);			aseen[i] = nil;		}	rp = place(name);	if (rp == nil)		return RINSTERR;	/* declaration in a header file is enough to cause this. */	if (0 && rp->rnamedefined)		warning("function `%s' redeclared", name);	rp->rnamedefined = 1;	return install(rp, nil);} /* * add the function name to the calling stack of the current function. */intadd2call(char name[], Rinst *curp){	Rname *p = place(name);	Rinst *ip = install(p, curp); 	if (p != nil && curp != nil && curp->namep != nil &&	    !STREQ(p->namer, curp->namep->namer))		p->rnamecalled = 1;	return ip != nil;} /* * backup removes an item from the active stack */voidbackup(void){	if (activep > 0)		activelist[--activep] = nil;} /* * makeactive simply puts a pointer to the nameblock into a stack with * maximum depth Maxdepth.  the error return only happens for stack * overflow. */intmakeactive(Rname *func){	if (activep < Maxdepth) {		if (activep > stats.highestact)			stats.highestact = activep;		activelist[activep++] = func;		return 1;	}	return 0;} /* * active checks whether the pointer which is its argument has already * occurred on the active list, and returns 1 if so. */intactive(Rname *func){	int i; 	for (i = 0; i < activep - 1; i++)		if (func == activelist[i])			return 1;	return 0;} /* * output is a recursive routine to print one tab for each level of * recursion, then the name of the function called, followed by the next * function called by the same higher level routine.  In doing this, it * calls itself to output the name of the first function called by the * function whose name it is printing.  It maintains an active list of * functions currently being printed by the different levels of * recursion, and if it finds itself asked to print one which is already * active, it terminates, marking that call with a '*'. */voidoutput(Rname *func, int tabc){	int i, tabd, tabstar, tflag;	Rinst *nextp; 	++linect;	print("\n%d", linect);	if (!makeactive(func)) {		print("   * nesting is too deep");		return;	}	tabstar = 0;	tabd = tabc;	for (; tabd > ntabs; tabstar++)		tabd -= ntabs;	if (tabstar > 0) {		print("  ");		for (i = 0; i < tabstar; i++)			print("<");	}	if (tabd == 0)		print("   ");	else		for (i = 0; i < tabd; i++)			print("\t");	if (active(func))		print("<<< %s", func->namer);		/* recursive call */	else if (func->dlistp == nil)		print("%s [external]", func->namer);	else {		print("%s", func->namer);		nextp = func->dlistp->calls;		if (!terse || !func->rnameout) {			++tabc;			if (!func->rnameout)				func->rnameout = linect;			if (tabc > ntabs && tabc%ntabs == 1 && nextp) {				print("\n%s", dashes);				tflag = 1;			} else				tflag = 0;			for (; nextp; nextp = nextp->calls)				output(nextp->namep, tabc);			if (tflag) {				print("\n%s", dashes);				tflag = 0;				USED(tflag);			}		} else if (nextp != nil)		/* not a leaf */			print(" ... [see line %d]",  func->rnameout);	}	backup();} /* * Dumptree() lists out the calling stacks.  All names will be listed out * unless some function names are specified in -f options. */voiddumptree(void){	unsigned buck;	Root *rp;	Rname *np; 	if (roots != nil)		for (rp = roots; rp != nil; rp = rp->next)			if ((np = lookfor(rp->func)) != nil) {				output(np, 0);				print("\n\n");			} else				fprint(2, "%s: function '%s' not found\n",					argv0, rp->func);	else		/* print everything */		for (buck = 0; buck < Hashsize; buck++)			for (np = nameshash[buck].head; np != nil; np = np->next)				if (!np->rnamecalled) {					output(np, 0);					print("\n\n");				}} /* * Skipcomments() skips past any blanks and comments in the input stream. */intskipcomments(Biobuf *in, int firstc){	int c;

Lines 1–500 of 941.