xref: /plan9/sys/src/cmd/calls.c (revision 0b04b1f88ffa4f022ff2064ee0454e267f28f072)
181bcec06SDavid du Colombier /*
281bcec06SDavid du Colombier  * calls - print a paragraphed list of who calls whom within a body of C source
381bcec06SDavid du Colombier  *
481bcec06SDavid du Colombier  * Author: M.M. Taylor, DCIEM, Toronto, Canada.
581bcec06SDavid du Colombier  * Modified by Alexis Kwan (HCR at DCIEM),
681bcec06SDavid du Colombier  *	Kevin Szabo (watmath!wateng!ksbszabo, Elec Eng, U of Waterloo),
781bcec06SDavid du Colombier  *	Tony Hansen, AT&T-IS, pegasus!hansen.
881bcec06SDavid du Colombier  */
981bcec06SDavid du Colombier 
1081bcec06SDavid du Colombier #include <u.h>
1181bcec06SDavid du Colombier #include <libc.h>
1281bcec06SDavid du Colombier #include <ctype.h>
1381bcec06SDavid du Colombier #include <bio.h>
1481bcec06SDavid du Colombier #include <String.h>
1581bcec06SDavid du Colombier 
1681bcec06SDavid du Colombier #define CPP		"cpp -+"
1781bcec06SDavid du Colombier #define RINSTERR	((Rinst *)-1)	/* ugly; error but keep going */
1881bcec06SDavid du Colombier 
1981bcec06SDavid du Colombier #define STREQ(a, b)	(*(a) == *(b) && strcmp(a, b) == 0)
2081bcec06SDavid du Colombier #define OTHER(rdwr)	((rdwr) == Rd? Wr: Rd)
2181bcec06SDavid du Colombier /* per 8c, all multibyte runes are considered alphabetic */
2281bcec06SDavid du Colombier #define ISIDENT(r) (isascii(r) && isalnum(r) || (r) == '_' || (r) >= Runeself)
2381bcec06SDavid du Colombier 
2481bcec06SDavid du Colombier /* safe macros */
2581bcec06SDavid du Colombier #define checksys(atom)		strbsearch(atom, sysword, nelem(sysword))
2681bcec06SDavid du Colombier 
2781bcec06SDavid du Colombier enum {
2881bcec06SDavid du Colombier 	Printstats =	0,		/* flag */
29*0b04b1f8SDavid du Colombier 	Maxseen =	4000,		/* # of instances w/in a function */
3081bcec06SDavid du Colombier 	Maxdepth =	300,		/* max func call tree depth */
3181bcec06SDavid du Colombier 	Hashsize =	2048,
3281bcec06SDavid du Colombier 
3381bcec06SDavid du Colombier 	Maxid =		256 + UTFmax,	/* max size of name */
3481bcec06SDavid du Colombier 	Tabwidth =	8,
3581bcec06SDavid du Colombier 	Maxwidth =	132,		/* limits tabbing */
3681bcec06SDavid du Colombier 	Defwidth =	80,		/* limits tabbing */
3781bcec06SDavid du Colombier 
3881bcec06SDavid du Colombier 	Backslash =	'\\',
3981bcec06SDavid du Colombier 	Quote =		'\'',
4081bcec06SDavid du Colombier 
4181bcec06SDavid du Colombier 	Rd =		0,		/* pipe indices */
4281bcec06SDavid du Colombier 	Wr,
4381bcec06SDavid du Colombier 
4481bcec06SDavid du Colombier 	Stdin =		0,
4581bcec06SDavid du Colombier 	Stdout,
4681bcec06SDavid du Colombier 	Stderr,
4781bcec06SDavid du Colombier 
4881bcec06SDavid du Colombier 	Defn =		0,
4981bcec06SDavid du Colombier 	Decl,
5081bcec06SDavid du Colombier 	Call,
5181bcec06SDavid du Colombier 
5281bcec06SDavid du Colombier 	Nomore =	-1,
5381bcec06SDavid du Colombier 	Added,
5481bcec06SDavid du Colombier 	Found,
5581bcec06SDavid du Colombier };
5681bcec06SDavid du Colombier 
5781bcec06SDavid du Colombier typedef struct Pushstate Pushstate;
5881bcec06SDavid du Colombier typedef struct Rinst Rinst;
5981bcec06SDavid du Colombier typedef struct Root Root;
6081bcec06SDavid du Colombier typedef struct Rname Rname;
6181bcec06SDavid du Colombier typedef struct Rnamehash Rnamehash;
6281bcec06SDavid du Colombier 
6381bcec06SDavid du Colombier struct Pushstate {
6481bcec06SDavid du Colombier 	int	kid;
6581bcec06SDavid du Colombier 	int	fd;	/* original fd */
6681bcec06SDavid du Colombier 	int	rfd;	/* replacement fd */
6781bcec06SDavid du Colombier 	int	input;
6881bcec06SDavid du Colombier 	int	open;
6981bcec06SDavid du Colombier };
7081bcec06SDavid du Colombier 
7181bcec06SDavid du Colombier struct Rname {
7281bcec06SDavid du Colombier 	Rinst	*dlistp;
7381bcec06SDavid du Colombier 	int	rnameout;
7481bcec06SDavid du Colombier 	char	rnamecalled;
7581bcec06SDavid du Colombier 	char	rnamedefined;
7681bcec06SDavid du Colombier 	char	*namer;
7781bcec06SDavid du Colombier 	Rname	*next;		/* next in hash chain */
7881bcec06SDavid du Colombier };
7981bcec06SDavid du Colombier 
8081bcec06SDavid du Colombier struct Rnamehash {
8181bcec06SDavid du Colombier 	Rname	*head;
8281bcec06SDavid du Colombier };
8381bcec06SDavid du Colombier 
8481bcec06SDavid du Colombier /* list of calling instances of those names */
8581bcec06SDavid du Colombier struct Rinst {
8681bcec06SDavid du Colombier 	Rname	*namep;
8781bcec06SDavid du Colombier 	Rinst	*calls;
8881bcec06SDavid du Colombier 	Rinst	*calledby;
8981bcec06SDavid du Colombier };
9081bcec06SDavid du Colombier 
9181bcec06SDavid du Colombier struct Root {
9281bcec06SDavid du Colombier 	char	*func;
9381bcec06SDavid du Colombier 	Root	*next;
9481bcec06SDavid du Colombier };
9581bcec06SDavid du Colombier 
9681bcec06SDavid du Colombier char *aseen[Maxseen];		/* names being gathered within a function */
97*0b04b1f8SDavid du Colombier Rnamehash nameshash[Hashsize];	/* names being tracked */
9881bcec06SDavid du Colombier Rname *activelist[Maxdepth];	/* names being output */
9981bcec06SDavid du Colombier String *cppopt;
10081bcec06SDavid du Colombier Root *roots;
10181bcec06SDavid du Colombier 
10281bcec06SDavid du Colombier static struct stats {
10381bcec06SDavid du Colombier 	long	highestseen;	/* aseen high water mark */
10481bcec06SDavid du Colombier 	long	highestname;	/* namelist high water mark */
10581bcec06SDavid du Colombier 	long	highestact;	/* activelist high water mark */
10681bcec06SDavid du Colombier 	long	highgetfree;	/* getfrees high water mark */
10781bcec06SDavid du Colombier } stats;
10881bcec06SDavid du Colombier 
10981bcec06SDavid du Colombier static long getfrees = 0;
11081bcec06SDavid du Colombier 
111c3617180SDavid du Colombier int bracket = 0;			/* curly brace count in input */
112c3617180SDavid du Colombier int linect = 0;				/* line number in output */
11381bcec06SDavid du Colombier int activep = 0;			/* current function being output */
11481bcec06SDavid du Colombier 
115c3617180SDavid du Colombier char *infile;
116c3617180SDavid du Colombier int lineno = 1;				/* line number of input */
117500d234bSDavid du Colombier int prevc = '\n', thisc = '\n';
118c3617180SDavid du Colombier 
11981bcec06SDavid du Colombier /* options */
12081bcec06SDavid du Colombier int terse = 1;				/* track functions only once */
12181bcec06SDavid du Colombier int ntabs = (Maxwidth - 20) / Tabwidth;	/* how wide to go */
12281bcec06SDavid du Colombier 
12381bcec06SDavid du Colombier char *dashes;				/* separators for deep nestings */
12481bcec06SDavid du Colombier 
12581bcec06SDavid du Colombier /*
12681bcec06SDavid du Colombier  * These are C tokens after which a parenthesis is valid which would
12781bcec06SDavid du Colombier  * otherwise be tagged as function names.  The reserved words which are not
12881bcec06SDavid du Colombier  * listed are break, const, continue, default, goto and volatile.
12981bcec06SDavid du Colombier  */
13081bcec06SDavid du Colombier char *sysword[] = {
13181bcec06SDavid du Colombier 	"auto", "case", "char", "do", "double", "else", "enum",
13281bcec06SDavid du Colombier 	"extern", "float", "for", "if", "int", "long", "register",
13381bcec06SDavid du Colombier 	"return", "short", "sizeof", "static", "struct", "switch",
13481bcec06SDavid du Colombier 	"typedef", "union", "unsigned", "void", "while",
13581bcec06SDavid du Colombier };
13681bcec06SDavid du Colombier 
13781bcec06SDavid du Colombier /*
13881bcec06SDavid du Colombier  * warning - print best error message possible
13981bcec06SDavid du Colombier  */
14081bcec06SDavid du Colombier void
warning(char * s1,char * s2)14181bcec06SDavid du Colombier warning(char *s1, char *s2)
14281bcec06SDavid du Colombier {
14381bcec06SDavid du Colombier 	fprint(2, "%s: ", argv0);
14481bcec06SDavid du Colombier 	fprint(2, s1, s2);
14581bcec06SDavid du Colombier 	fprint(2, "\n");
14681bcec06SDavid du Colombier }
14781bcec06SDavid du Colombier 
14881bcec06SDavid du Colombier /*
14981bcec06SDavid du Colombier  *   safe malloc() code.  Does the checking for nil returns from malloc()
15081bcec06SDavid du Colombier  */
15181bcec06SDavid du Colombier void *
emalloc(int size)15281bcec06SDavid du Colombier emalloc(int size)
15381bcec06SDavid du Colombier {
15481bcec06SDavid du Colombier 	void *f = mallocz(size, 1);
15581bcec06SDavid du Colombier 
15681bcec06SDavid du Colombier 	if (f == nil)
15781bcec06SDavid du Colombier 		sysfatal("cannot allocate memory");
15881bcec06SDavid du Colombier 	return f;
15981bcec06SDavid du Colombier }
16081bcec06SDavid du Colombier 
16181bcec06SDavid du Colombier unsigned
hash(char * s)16281bcec06SDavid du Colombier hash(char *s)
16381bcec06SDavid du Colombier {
16481bcec06SDavid du Colombier 	unsigned h;
16581bcec06SDavid du Colombier 	unsigned char *cp;
16681bcec06SDavid du Colombier 
16781bcec06SDavid du Colombier 	h = 0;
16881bcec06SDavid du Colombier 	for(cp = (unsigned char *)s; *cp; h += *cp++)
16981bcec06SDavid du Colombier 		h *= 1119;
17081bcec06SDavid du Colombier 	return h;
17181bcec06SDavid du Colombier }
17281bcec06SDavid du Colombier 
173500d234bSDavid du Colombier int
nextc(Biobuf * in)174500d234bSDavid du Colombier nextc(Biobuf *in)
175500d234bSDavid du Colombier {
176500d234bSDavid du Colombier 	prevc = thisc;
177500d234bSDavid du Colombier 	thisc = Bgetrune(in);
178500d234bSDavid du Colombier 	return thisc;
179500d234bSDavid du Colombier }
180500d234bSDavid du Colombier 
181500d234bSDavid du Colombier int
ungetc(Biobuf * in)182500d234bSDavid du Colombier ungetc(Biobuf *in)
183500d234bSDavid du Colombier {
184500d234bSDavid du Colombier 	prevc = thisc;
185500d234bSDavid du Colombier 	return Bungetrune(in);
186500d234bSDavid du Colombier }
187500d234bSDavid du Colombier 
188500d234bSDavid du Colombier int
newatom(Biobuf * in,char * atom)189500d234bSDavid du Colombier newatom(Biobuf *in, char *atom)
190500d234bSDavid du Colombier {
191500d234bSDavid du Colombier 	atom[0] = '\0';
192500d234bSDavid du Colombier 	return nextc(in);
193500d234bSDavid du Colombier }
194500d234bSDavid du Colombier 
19581bcec06SDavid du Colombier /*
19681bcec06SDavid du Colombier  * lookup (name) accepts a pointer to a name and sees if the name is on the
19781bcec06SDavid du Colombier  * namelist.  If so, it returns a pointer to the nameblock.  Otherwise it
19881bcec06SDavid du Colombier  * returns nil.
19981bcec06SDavid du Colombier  */
20081bcec06SDavid du Colombier Rname *
lookfor(char * name)20181bcec06SDavid du Colombier lookfor(char *name)
20281bcec06SDavid du Colombier {
20381bcec06SDavid du Colombier 	int i;
20481bcec06SDavid du Colombier 	unsigned buck;
20581bcec06SDavid du Colombier 	Rname *np;
20681bcec06SDavid du Colombier 	Rnamehash *hp;
20781bcec06SDavid du Colombier 
20881bcec06SDavid du Colombier 	buck = hash(name) % Hashsize;
20981bcec06SDavid du Colombier 	hp = &nameshash[buck];
21081bcec06SDavid du Colombier 	i = 0;
21181bcec06SDavid du Colombier 	for (np = hp->head; np != nil; np = np->next, i++)
21281bcec06SDavid du Colombier 		if (STREQ(name, np->namer))
21381bcec06SDavid du Colombier 			return np;
21481bcec06SDavid du Colombier 
21581bcec06SDavid du Colombier 	if (i > stats.highestname)
21681bcec06SDavid du Colombier 		stats.highestname = i;
21781bcec06SDavid du Colombier 	return nil;
21881bcec06SDavid du Colombier }
21981bcec06SDavid du Colombier 
22081bcec06SDavid du Colombier /*
22181bcec06SDavid du Colombier  * place() returns a pointer to the name on the namelist.  If the name was
22281bcec06SDavid du Colombier  * not in the namelist, place adds it.
22381bcec06SDavid du Colombier  */
22481bcec06SDavid du Colombier Rname *
place(char name[])22581bcec06SDavid du Colombier place(char name[])
22681bcec06SDavid du Colombier {
22781bcec06SDavid du Colombier 	unsigned buck;
22881bcec06SDavid du Colombier 	Rname *np;
22981bcec06SDavid du Colombier 	Rnamehash *hp;
23081bcec06SDavid du Colombier 
23181bcec06SDavid du Colombier 	np = lookfor(name);
23281bcec06SDavid du Colombier 	if (np != nil)
23381bcec06SDavid du Colombier 		return np;
23481bcec06SDavid du Colombier 
23581bcec06SDavid du Colombier 	buck = hash(name) % Hashsize;
23681bcec06SDavid du Colombier 	hp = &nameshash[buck];
23781bcec06SDavid du Colombier 	np = emalloc(sizeof *np);
23881bcec06SDavid du Colombier 	np->namer = strdup(name);
23981bcec06SDavid du Colombier 	np->next = hp->head;
24081bcec06SDavid du Colombier 	hp->head = np;
24181bcec06SDavid du Colombier 	return np;
24281bcec06SDavid du Colombier }
24381bcec06SDavid du Colombier 
24481bcec06SDavid du Colombier /*
24581bcec06SDavid du Colombier  * getfree returns a pointer to the next free instance block on the list
24681bcec06SDavid du Colombier  */
24781bcec06SDavid du Colombier Rinst *
getfree(void)24881bcec06SDavid du Colombier getfree(void)
24981bcec06SDavid du Colombier {
25081bcec06SDavid du Colombier 	Rinst *ret, *new;
25181bcec06SDavid du Colombier 	static Rinst *prev;
25281bcec06SDavid du Colombier 
25381bcec06SDavid du Colombier 	++getfrees;
25481bcec06SDavid du Colombier 	if (getfrees > stats.highgetfree)
25581bcec06SDavid du Colombier 		stats.highgetfree = getfrees;
25681bcec06SDavid du Colombier 
25781bcec06SDavid du Colombier 	if (prev == nil)
25881bcec06SDavid du Colombier 		prev = emalloc(sizeof *prev);
25981bcec06SDavid du Colombier 	new = emalloc(sizeof *new);
26081bcec06SDavid du Colombier 
26181bcec06SDavid du Colombier 	prev->calls = new;		/* also serves as next pointer */
26281bcec06SDavid du Colombier 	new->calledby = prev;
26381bcec06SDavid du Colombier 
26481bcec06SDavid du Colombier 	ret = prev;
26581bcec06SDavid du Colombier 	prev = new;
26681bcec06SDavid du Colombier 	return ret;
26781bcec06SDavid du Colombier }
26881bcec06SDavid du Colombier 
26981bcec06SDavid du Colombier /*
27081bcec06SDavid du Colombier  * install(np, rp) puts a new instance of a function into the linked list.
27181bcec06SDavid du Colombier  * It puts a pointer (np) to its own name (returned by place) into its
27281bcec06SDavid du Colombier  * namepointer, a pointer to the calling routine (rp) into its called-by
27381bcec06SDavid du Colombier  * pointer, and zero into the calls pointer.  It then puts a pointer to
27481bcec06SDavid du Colombier  * itself into the last function in the chain.
27581bcec06SDavid du Colombier  */
27681bcec06SDavid du Colombier Rinst *
install(Rname * np,Rinst * rp)27781bcec06SDavid du Colombier install(Rname *np, Rinst *rp)
27881bcec06SDavid du Colombier {
27981bcec06SDavid du Colombier 	Rinst *newp;
28081bcec06SDavid du Colombier 
28181bcec06SDavid du Colombier 	if (np == nil)
28281bcec06SDavid du Colombier 		return RINSTERR;
28381bcec06SDavid du Colombier 	if ((newp = getfree()) == nil)
28481bcec06SDavid du Colombier 		return nil;
28581bcec06SDavid du Colombier 	newp->namep = np;
28681bcec06SDavid du Colombier 	newp->calls = 0;
28781bcec06SDavid du Colombier 	if (rp) {
28881bcec06SDavid du Colombier 		while (rp->calls)
28981bcec06SDavid du Colombier 			rp = rp->calls;
29081bcec06SDavid du Colombier 		newp->calledby = rp->calledby;
29181bcec06SDavid du Colombier 		rp->calls = newp;
29281bcec06SDavid du Colombier 	} else {
29381bcec06SDavid du Colombier 		newp->calledby = (Rinst *)np;
29481bcec06SDavid du Colombier 		np->dlistp = newp;
29581bcec06SDavid du Colombier 	}
29681bcec06SDavid du Colombier 	return newp;
29781bcec06SDavid du Colombier }
29881bcec06SDavid du Colombier 
29981bcec06SDavid du Colombier /*
30081bcec06SDavid du Colombier  * When scanning the text, each function instance is inserted into a
30181bcec06SDavid du Colombier  * linear list of names, using the Rname structure, when it is first
30281bcec06SDavid du Colombier  * encountered.  It is also inserted into the linked list using the Rinst
30381bcec06SDavid du Colombier  * structure.  The entry into the name list has a pointer to the defining
30481bcec06SDavid du Colombier  * instance in the linked list, and each entry in the linked list has a
30581bcec06SDavid du Colombier  * pointer back to the relevant name.  Newproc makes an entry in the
30681bcec06SDavid du Colombier  * defining list, which is distinguished from the called list only
30781bcec06SDavid du Colombier  * because it has no calledby link (value=0).  Add2proc enters into the
30881bcec06SDavid du Colombier  * called list, by inserting a link to the new instance in the calls
30981bcec06SDavid du Colombier  * pointer of the last entry (may be a defining instance, or a function
31081bcec06SDavid du Colombier  * called by that defining instance), and points back to the defining
31181bcec06SDavid du Colombier  * instance of the caller in its called-by pointer.
31281bcec06SDavid du Colombier  */
31381bcec06SDavid du Colombier Rinst *
newproc(char * name)31481bcec06SDavid du Colombier newproc(char *name)
31581bcec06SDavid du Colombier {
31681bcec06SDavid du Colombier 	int i;
31781bcec06SDavid du Colombier 	Rname *rp;
31881bcec06SDavid du Colombier 
31981bcec06SDavid du Colombier 	for (i = 0; i < Maxseen; i++)
32081bcec06SDavid du Colombier 		if (aseen[i] != nil) {
32181bcec06SDavid du Colombier 			free(aseen[i]);
32281bcec06SDavid du Colombier 			aseen[i] = nil;
32381bcec06SDavid du Colombier 		}
32481bcec06SDavid du Colombier 	rp = place(name);
32581bcec06SDavid du Colombier 	if (rp == nil)
32681bcec06SDavid du Colombier 		return RINSTERR;
327*0b04b1f8SDavid du Colombier 	/* declaration in a header file is enough to cause this. */
32881bcec06SDavid du Colombier 	if (0 && rp->rnamedefined)
329*0b04b1f8SDavid du Colombier 		warning("function `%s' redeclared", name);
33081bcec06SDavid du Colombier 	rp->rnamedefined = 1;
33181bcec06SDavid du Colombier 	return install(rp, nil);
33281bcec06SDavid du Colombier }
33381bcec06SDavid du Colombier 
33481bcec06SDavid du Colombier /*
33581bcec06SDavid du Colombier  * add the function name to the calling stack of the current function.
33681bcec06SDavid du Colombier  */
33781bcec06SDavid du Colombier int
add2call(char name[],Rinst * curp)33881bcec06SDavid du Colombier add2call(char name[], Rinst *curp)
33981bcec06SDavid du Colombier {
34081bcec06SDavid du Colombier 	Rname *p = place(name);
34181bcec06SDavid du Colombier 	Rinst *ip = install(p, curp);
34281bcec06SDavid du Colombier 
34381bcec06SDavid du Colombier 	if (p != nil && curp != nil && curp->namep != nil &&
34481bcec06SDavid du Colombier 	    !STREQ(p->namer, curp->namep->namer))
34581bcec06SDavid du Colombier 		p->rnamecalled = 1;
34681bcec06SDavid du Colombier 	return ip != nil;
34781bcec06SDavid du Colombier }
34881bcec06SDavid du Colombier 
34981bcec06SDavid du Colombier /*
35081bcec06SDavid du Colombier  * backup removes an item from the active stack
35181bcec06SDavid du Colombier  */
35281bcec06SDavid du Colombier void
backup(void)35381bcec06SDavid du Colombier backup(void)
35481bcec06SDavid du Colombier {
35581bcec06SDavid du Colombier 	if (activep > 0)
35681bcec06SDavid du Colombier 		activelist[--activep] = nil;
35781bcec06SDavid du Colombier }
35881bcec06SDavid du Colombier 
35981bcec06SDavid du Colombier /*
36081bcec06SDavid du Colombier  * makeactive simply puts a pointer to the nameblock into a stack with
36181bcec06SDavid du Colombier  * maximum depth Maxdepth.  the error return only happens for stack
36281bcec06SDavid du Colombier  * overflow.
36381bcec06SDavid du Colombier  */
36481bcec06SDavid du Colombier int
makeactive(Rname * func)36581bcec06SDavid du Colombier makeactive(Rname *func)
36681bcec06SDavid du Colombier {
36781bcec06SDavid du Colombier 	if (activep < Maxdepth) {
36881bcec06SDavid du Colombier 		if (activep > stats.highestact)
36981bcec06SDavid du Colombier 			stats.highestact = activep;
37081bcec06SDavid du Colombier 		activelist[activep++] = func;
37181bcec06SDavid du Colombier 		return 1;
37281bcec06SDavid du Colombier 	}
37381bcec06SDavid du Colombier 	return 0;
37481bcec06SDavid du Colombier }
37581bcec06SDavid du Colombier 
37681bcec06SDavid du Colombier /*
37781bcec06SDavid du Colombier  * active checks whether the pointer which is its argument has already
37881bcec06SDavid du Colombier  * occurred on the active list, and returns 1 if so.
37981bcec06SDavid du Colombier  */
38081bcec06SDavid du Colombier int
active(Rname * func)38181bcec06SDavid du Colombier active(Rname *func)
38281bcec06SDavid du Colombier {
38381bcec06SDavid du Colombier 	int i;
38481bcec06SDavid du Colombier 
38581bcec06SDavid du Colombier 	for (i = 0; i < activep - 1; i++)
38681bcec06SDavid du Colombier 		if (func == activelist[i])
38781bcec06SDavid du Colombier 			return 1;
38881bcec06SDavid du Colombier 	return 0;
38981bcec06SDavid du Colombier }
39081bcec06SDavid du Colombier 
39181bcec06SDavid du Colombier /*
39281bcec06SDavid du Colombier  * output is a recursive routine to print one tab for each level of
39381bcec06SDavid du Colombier  * recursion, then the name of the function called, followed by the next
39481bcec06SDavid du Colombier  * function called by the same higher level routine.  In doing this, it
39581bcec06SDavid du Colombier  * calls itself to output the name of the first function called by the
39681bcec06SDavid du Colombier  * function whose name it is printing.  It maintains an active list of
39781bcec06SDavid du Colombier  * functions currently being printed by the different levels of
39881bcec06SDavid du Colombier  * recursion, and if it finds itself asked to print one which is already
39981bcec06SDavid du Colombier  * active, it terminates, marking that call with a '*'.
40081bcec06SDavid du Colombier  */
40181bcec06SDavid du Colombier void
output(Rname * func,int tabc)40281bcec06SDavid du Colombier output(Rname *func, int tabc)
40381bcec06SDavid du Colombier {
40481bcec06SDavid du Colombier 	int i, tabd, tabstar, tflag;
40581bcec06SDavid du Colombier 	Rinst *nextp;
40681bcec06SDavid du Colombier 
40781bcec06SDavid du Colombier 	++linect;
40881bcec06SDavid du Colombier 	print("\n%d", linect);
40981bcec06SDavid du Colombier 	if (!makeactive(func)) {
41081bcec06SDavid du Colombier 		print("   * nesting is too deep");
41181bcec06SDavid du Colombier 		return;
41281bcec06SDavid du Colombier 	}
41381bcec06SDavid du Colombier 	tabstar = 0;
41481bcec06SDavid du Colombier 	tabd = tabc;
41581bcec06SDavid du Colombier 	for (; tabd > ntabs; tabstar++)
41681bcec06SDavid du Colombier 		tabd -= ntabs;
41781bcec06SDavid du Colombier 	if (tabstar > 0) {
41881bcec06SDavid du Colombier 		print("  ");
41981bcec06SDavid du Colombier 		for (i = 0; i < tabstar; i++)
42081bcec06SDavid du Colombier 			print("<");
42181bcec06SDavid du Colombier 	}
42281bcec06SDavid du Colombier 	if (tabd == 0)
42381bcec06SDavid du Colombier 		print("   ");
42481bcec06SDavid du Colombier 	else
42581bcec06SDavid du Colombier 		for (i = 0; i < tabd; i++)
42681bcec06SDavid du Colombier 			print("\t");
42781bcec06SDavid du Colombier 	if (active(func))
42881bcec06SDavid du Colombier 		print("<<< %s", func->namer);		/* recursive call */
42981bcec06SDavid du Colombier 	else if (func->dlistp == nil)
43081bcec06SDavid du Colombier 		print("%s [external]", func->namer);
43181bcec06SDavid du Colombier 	else {
43281bcec06SDavid du Colombier 		print("%s", func->namer);
43381bcec06SDavid du Colombier 		nextp = func->dlistp->calls;
43481bcec06SDavid du Colombier 		if (!terse || !func->rnameout) {
43581bcec06SDavid du Colombier 			++tabc;
43681bcec06SDavid du Colombier 			if (!func->rnameout)
43781bcec06SDavid du Colombier 				func->rnameout = linect;
43881bcec06SDavid du Colombier 			if (tabc > ntabs && tabc%ntabs == 1 && nextp) {
43981bcec06SDavid du Colombier 				print("\n%s", dashes);
44081bcec06SDavid du Colombier 				tflag = 1;
44181bcec06SDavid du Colombier 			} else
44281bcec06SDavid du Colombier 				tflag = 0;
44381bcec06SDavid du Colombier 			for (; nextp; nextp = nextp->calls)
44481bcec06SDavid du Colombier 				output(nextp->namep, tabc);
44581bcec06SDavid du Colombier 			if (tflag) {
44681bcec06SDavid du Colombier 				print("\n%s", dashes);
44781bcec06SDavid du Colombier 				tflag = 0;
44881bcec06SDavid du Colombier 				USED(tflag);
44981bcec06SDavid du Colombier 			}
45081bcec06SDavid du Colombier 		} else if (nextp != nil)		/* not a leaf */
45181bcec06SDavid du Colombier 			print(" ... [see line %d]",  func->rnameout);
45281bcec06SDavid du Colombier 	}
45381bcec06SDavid du Colombier 	backup();
45481bcec06SDavid du Colombier }
45581bcec06SDavid du Colombier 
45681bcec06SDavid du Colombier /*
45781bcec06SDavid du Colombier  * Dumptree() lists out the calling stacks.  All names will be listed out
45881bcec06SDavid du Colombier  * unless some function names are specified in -f options.
45981bcec06SDavid du Colombier  */
46081bcec06SDavid du Colombier void
dumptree(void)46181bcec06SDavid du Colombier dumptree(void)
46281bcec06SDavid du Colombier {
46381bcec06SDavid du Colombier 	unsigned buck;
46481bcec06SDavid du Colombier 	Root *rp;
46581bcec06SDavid du Colombier 	Rname *np;
46681bcec06SDavid du Colombier 
46781bcec06SDavid du Colombier 	if (roots != nil)
46881bcec06SDavid du Colombier 		for (rp = roots; rp != nil; rp = rp->next)
46981bcec06SDavid du Colombier 			if ((np = lookfor(rp->func)) != nil) {
47081bcec06SDavid du Colombier 				output(np, 0);
47181bcec06SDavid du Colombier 				print("\n\n");
47281bcec06SDavid du Colombier 			} else
47381bcec06SDavid du Colombier 				fprint(2, "%s: function '%s' not found\n",
47481bcec06SDavid du Colombier 					argv0, rp->func);
47581bcec06SDavid du Colombier 	else
47681bcec06SDavid du Colombier 		/* print everything */
47781bcec06SDavid du Colombier 		for (buck = 0; buck < Hashsize; buck++)
47881bcec06SDavid du Colombier 			for (np = nameshash[buck].head; np != nil; np = np->next)
47981bcec06SDavid du Colombier 				if (!np->rnamecalled) {
48081bcec06SDavid du Colombier 					output(np, 0);
48181bcec06SDavid du Colombier 					print("\n\n");
48281bcec06SDavid du Colombier 				}
48381bcec06SDavid du Colombier }
48481bcec06SDavid du Colombier 
48581bcec06SDavid du Colombier /*
48681bcec06SDavid du Colombier  * Skipcomments() skips past any blanks and comments in the input stream.
48781bcec06SDavid du Colombier  */
48881bcec06SDavid du Colombier int
skipcomments(Biobuf * in,int firstc)48981bcec06SDavid du Colombier skipcomments(Biobuf *in, int firstc)
49081bcec06SDavid du Colombier {
49181bcec06SDavid du Colombier 	int c;
49281bcec06SDavid du Colombier 
493500d234bSDavid du Colombier 	for (c = firstc; isascii(c) && isspace(c) || c == '/'; c = nextc(in)) {
494c3617180SDavid du Colombier 		if (c == '\n')
495c3617180SDavid du Colombier 			lineno++;
49681bcec06SDavid du Colombier 		if (c != '/')
49781bcec06SDavid du Colombier 			continue;
498500d234bSDavid du Colombier 		c = nextc(in);			/* read ahead */
49981bcec06SDavid du Colombier 		if (c == Beof)
50081bcec06SDavid du Colombier 			break;
50181bcec06SDavid du Colombier 		if (c != '*' && c != '/') {	/* not comment start? */
502500d234bSDavid du Colombier 			ungetc(in);		/* push back readahead */
50381bcec06SDavid du Colombier 			return '/';
50481bcec06SDavid du Colombier 		}
50581bcec06SDavid du Colombier 		if (c == '/') {			/* c++ style */
506500d234bSDavid du Colombier 			while ((c = nextc(in)) != '\n' && c != Beof)
50781bcec06SDavid du Colombier 				;
508c3617180SDavid du Colombier 			if (c == '\n')
509c3617180SDavid du Colombier 				lineno++;
51081bcec06SDavid du Colombier 			continue;
51181bcec06SDavid du Colombier 		}
51281bcec06SDavid du Colombier 		for (;;) {
51381bcec06SDavid du Colombier 			/* skip to possible closing delimiter */
514500d234bSDavid du Colombier 			while ((c = nextc(in)) != '*' && c != Beof)
515c3617180SDavid du Colombier 				if (c == '\n')
516c3617180SDavid du Colombier 					lineno++;
51781bcec06SDavid du Colombier 			if (c == Beof)
51881bcec06SDavid du Colombier 				break;
51981bcec06SDavid du Colombier 			/* else c == '*' */
520500d234bSDavid du Colombier 			c = nextc(in);		 /* read ahead */
52181bcec06SDavid du Colombier 			if (c == Beof || c == '/') /* comment end? */
52281bcec06SDavid du Colombier 				break;
523500d234bSDavid du Colombier 			ungetc(in);		/* push back readahead */
52481bcec06SDavid du Colombier 		}
52581bcec06SDavid du Colombier 	}
52681bcec06SDavid du Colombier 	return c;
52781bcec06SDavid du Colombier }
52881bcec06SDavid du Colombier 
52981bcec06SDavid du Colombier /*
53081bcec06SDavid du Colombier  * isfndefn differentiates between an external declaration and a real
53181bcec06SDavid du Colombier  * function definition.  For instance, between:
53281bcec06SDavid du Colombier  *
53381bcec06SDavid du Colombier  *	extern char *getenv(char *), *strcmp(char *, char *);
53481bcec06SDavid du Colombier  *  and
53581bcec06SDavid du Colombier  *	char *getenv(char *name)
53681bcec06SDavid du Colombier  *	{}
53781bcec06SDavid du Colombier  *
53881bcec06SDavid du Colombier  * It does so by making the observation that nothing (except blanks and
53981bcec06SDavid du Colombier  * comments) can be between the right parenthesis and the semi-colon or
54081bcec06SDavid du Colombier  * comma following the extern declaration.
54181bcec06SDavid du Colombier  */
54281bcec06SDavid du Colombier int
isfndefn(Biobuf * in)54381bcec06SDavid du Colombier isfndefn(Biobuf *in)
54481bcec06SDavid du Colombier {
54581bcec06SDavid du Colombier 	int c;
54681bcec06SDavid du Colombier 
547500d234bSDavid du Colombier 	c = skipcomments(in, nextc(in));
54881bcec06SDavid du Colombier 	while (c != ')' && c != Beof)	/* consume arg. decl.s */
549500d234bSDavid du Colombier 		c = nextc(in);
55081bcec06SDavid du Colombier 	if (c == Beof)
55181bcec06SDavid du Colombier 		return 1;		/* definition at Beof */
552500d234bSDavid du Colombier 	c = skipcomments(in, nextc(in)); /* skip blanks between ) and ; */
55381bcec06SDavid du Colombier 
55481bcec06SDavid du Colombier 	if (c == ';' || c == ',')
55581bcec06SDavid du Colombier 		return 0;		/* an extern declaration */
55681bcec06SDavid du Colombier 	if (c != Beof)
557500d234bSDavid du Colombier 		ungetc(in);
55881bcec06SDavid du Colombier 	return 1;			/* a definition */
55981bcec06SDavid du Colombier }
56081bcec06SDavid du Colombier 
56181bcec06SDavid du Colombier /*
56281bcec06SDavid du Colombier  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
56381bcec06SDavid du Colombier  * is WAY faster than the generic bsearch().
56481bcec06SDavid du Colombier  */
56581bcec06SDavid du Colombier int
strbsearch(char * key,char ** base,unsigned nel)56681bcec06SDavid du Colombier strbsearch(char *key, char **base, unsigned nel)
56781bcec06SDavid du Colombier {
56881bcec06SDavid du Colombier 	int cmp;
56981bcec06SDavid du Colombier 	char **last = base + nel - 1, **pos;
57081bcec06SDavid du Colombier 
57181bcec06SDavid du Colombier 	while (last >= base) {
57281bcec06SDavid du Colombier 		pos = base + ((last - base) >> 1);
57381bcec06SDavid du Colombier 		cmp = key[0] - (*pos)[0];
57481bcec06SDavid du Colombier 		if (cmp == 0) {
57581bcec06SDavid du Colombier 			/* there are no empty strings in the table */
57681bcec06SDavid du Colombier 			cmp = strcmp(key, *pos);
57781bcec06SDavid du Colombier 			if (cmp == 0)
57881bcec06SDavid du Colombier 				return 1;
57981bcec06SDavid du Colombier 		}
58081bcec06SDavid du Colombier 		if (cmp < 0)
58181bcec06SDavid du Colombier 			last = pos - 1;
58281bcec06SDavid du Colombier 		else
58381bcec06SDavid du Colombier 			base = pos + 1;
58481bcec06SDavid du Colombier 	}
58581bcec06SDavid du Colombier 	return 0;
58681bcec06SDavid du Colombier }
58781bcec06SDavid du Colombier 
58881bcec06SDavid du Colombier /*
58981bcec06SDavid du Colombier  * see if we have seen this function within this process
59081bcec06SDavid du Colombier  */
59181bcec06SDavid du Colombier int
seen(char * atom)59281bcec06SDavid du Colombier seen(char *atom)
59381bcec06SDavid du Colombier {
59481bcec06SDavid du Colombier 	int i;
59581bcec06SDavid du Colombier 
59681bcec06SDavid du Colombier 	for (i = 0; aseen[i] != nil && i < Maxseen-1; i++)
59781bcec06SDavid du Colombier 		if (STREQ(atom, aseen[i]))
59881bcec06SDavid du Colombier 			return Found;
59981bcec06SDavid du Colombier 	if (i >= Maxseen-1)
60081bcec06SDavid du Colombier 		return Nomore;
60181bcec06SDavid du Colombier 	aseen[i] = strdup(atom);
60281bcec06SDavid du Colombier 	if (i > stats.highestseen)
60381bcec06SDavid du Colombier 		stats.highestseen = i;
60481bcec06SDavid du Colombier 	return Added;
60581bcec06SDavid du Colombier }
60681bcec06SDavid du Colombier 
60781bcec06SDavid du Colombier /*
60881bcec06SDavid du Colombier  * getfunc returns the name of a function in atom and Defn for a definition,
60981bcec06SDavid du Colombier  * Call for an internal call, or Beof.
61081bcec06SDavid du Colombier  */
61181bcec06SDavid du Colombier int
getfunc(Biobuf * in,char * atom)61281bcec06SDavid du Colombier getfunc(Biobuf *in, char *atom)
61381bcec06SDavid du Colombier {
614500d234bSDavid du Colombier 	int c, nf, last, ss, quote;
615500d234bSDavid du Colombier 	char *ln, *nm, *ap, *ep = &atom[Maxid-1-UTFmax];
616500d234bSDavid du Colombier 	char *flds[4];
61781bcec06SDavid du Colombier 	Rune r;
61881bcec06SDavid du Colombier 
619500d234bSDavid du Colombier 	c = nextc(in);
62081bcec06SDavid du Colombier 	while (c != Beof) {
62181bcec06SDavid du Colombier 		if (ISIDENT(c)) {
62281bcec06SDavid du Colombier 			ap = atom;
62381bcec06SDavid du Colombier 			do {
62481bcec06SDavid du Colombier 				if (isascii(c))
62581bcec06SDavid du Colombier 					*ap++ = c;
62681bcec06SDavid du Colombier 				else {
62781bcec06SDavid du Colombier 					r = c;
62881bcec06SDavid du Colombier 					ap += runetochar(ap, &r);
62981bcec06SDavid du Colombier 				}
630500d234bSDavid du Colombier 				c = nextc(in);
63181bcec06SDavid du Colombier 			} while(ap < ep && ISIDENT(c));
63281bcec06SDavid du Colombier 			*ap = '\0';
63381bcec06SDavid du Colombier 			if (ap >= ep) {	/* uncommon case: id won't fit */
63481bcec06SDavid du Colombier 				/* consume remainder of too-long id */
63581bcec06SDavid du Colombier 				while (ISIDENT(c))
636500d234bSDavid du Colombier 					c = nextc(in);
63781bcec06SDavid du Colombier 			}
63881bcec06SDavid du Colombier 		}
63981bcec06SDavid du Colombier 
64081bcec06SDavid du Colombier 		switch (c) {
64181bcec06SDavid du Colombier 		case Beof:
64281bcec06SDavid du Colombier 			return Beof;
643c3617180SDavid du Colombier 		case '\n':
644c3617180SDavid du Colombier 			lineno++;
645c3617180SDavid du Colombier 			/* fall through */
64681bcec06SDavid du Colombier 		case '\t':		/* ignore white space */
64781bcec06SDavid du Colombier 		case ' ':
64881bcec06SDavid du Colombier 		case '\f':
64981bcec06SDavid du Colombier 		case '\r':
65081bcec06SDavid du Colombier 		case '/':		/* potential comment? */
651500d234bSDavid du Colombier 			c = skipcomments(in, nextc(in));
65281bcec06SDavid du Colombier 			break;
65381bcec06SDavid du Colombier 		case Backslash:		/* consume a newline or something */
65481bcec06SDavid du Colombier 		case ')':		/* end of parameter list */
65581bcec06SDavid du Colombier 		default:
65681bcec06SDavid du Colombier 			c = newatom(in, atom);
65781bcec06SDavid du Colombier 			break;
658500d234bSDavid du Colombier 		case '#':
659500d234bSDavid du Colombier 			if (prevc != '\n') {	/* cpp # or ## operator? */
660500d234bSDavid du Colombier 				c = nextc(in);	/* read ahead */
661500d234bSDavid du Colombier 				break;
662500d234bSDavid du Colombier 			}
663500d234bSDavid du Colombier 			/* it's a cpp directive */
664500d234bSDavid du Colombier 			ln = Brdline(in, '\n');
665500d234bSDavid du Colombier 			if (ln == nil)
666500d234bSDavid du Colombier 				thisc = c = Beof;
667500d234bSDavid du Colombier 			else {
668500d234bSDavid du Colombier 				nf = tokenize(ln, flds, nelem(flds));
669500d234bSDavid du Colombier 				if (nf >= 3 && strcmp(flds[0], "line") == 0) {
670500d234bSDavid du Colombier 					lineno = atoi(flds[1]);
671500d234bSDavid du Colombier 					free(infile);
672500d234bSDavid du Colombier 					nm = flds[2];
673500d234bSDavid du Colombier 					if (nm[0] == '"')
674500d234bSDavid du Colombier 						nm++;
675500d234bSDavid du Colombier 					last = strlen(nm) - 1;
676500d234bSDavid du Colombier 					if (nm[last] == '"')
677500d234bSDavid du Colombier 						nm[last] = '\0';
678500d234bSDavid du Colombier 					infile = strdup(nm);
679500d234bSDavid du Colombier 				} else
680c3617180SDavid du Colombier 					lineno++;
681500d234bSDavid du Colombier 				c = nextc(in);	/* read ahead */
682c3617180SDavid du Colombier 			}
68381bcec06SDavid du Colombier 			break;
68481bcec06SDavid du Colombier 		case Quote:		/* character constant */
68581bcec06SDavid du Colombier 		case '\"':		/* string constant */
68681bcec06SDavid du Colombier 			quote = c;
68781bcec06SDavid du Colombier 			atom[0] = '\0';
688500d234bSDavid du Colombier 			while ((c = nextc(in)) != quote && c != Beof)
68981bcec06SDavid du Colombier 				if (c == Backslash)
690500d234bSDavid du Colombier 					nextc(in);
69181bcec06SDavid du Colombier 			if (c == quote)
692500d234bSDavid du Colombier 				c = nextc(in);
69381bcec06SDavid du Colombier 			break;
69481bcec06SDavid du Colombier 		case '{':		/* start of a block */
69581bcec06SDavid du Colombier 			bracket++;
69681bcec06SDavid du Colombier 			c = newatom(in, atom);
69781bcec06SDavid du Colombier 			break;
69881bcec06SDavid du Colombier 		case '}':		/* end of a block */
699c3617180SDavid du Colombier 			if (bracket < 1)
700c3617180SDavid du Colombier 				fprint(2, "%s: %s:%d: too many closing braces; "
701c3617180SDavid du Colombier 					"previous open brace missing\n",
702c3617180SDavid du Colombier 					argv0, infile, lineno);
703c3617180SDavid du Colombier 			else
70481bcec06SDavid du Colombier 				--bracket;
70581bcec06SDavid du Colombier 			c = newatom(in, atom);
70681bcec06SDavid du Colombier 			break;
70781bcec06SDavid du Colombier 		case '(':		/* parameter list for function? */
70881bcec06SDavid du Colombier 			if (atom[0] != '\0' && !checksys(atom)) {
70981bcec06SDavid du Colombier 				if (bracket == 0)
71081bcec06SDavid du Colombier 					if (isfndefn(in))
71181bcec06SDavid du Colombier 						return Defn;
71281bcec06SDavid du Colombier 					else {
713500d234bSDavid du Colombier 						c = nextc(in);
71481bcec06SDavid du Colombier 						break;		/* ext. decl. */
71581bcec06SDavid du Colombier 					}
71681bcec06SDavid du Colombier 				ss = seen(atom);
71781bcec06SDavid du Colombier 				if (ss == Nomore)
718c3617180SDavid du Colombier 					fprint(2, "%s: %s:%d: more than %d "
719c3617180SDavid du Colombier 						"identifiers in a function\n",
720c3617180SDavid du Colombier 						argv0, infile, lineno, Maxseen);
72181bcec06SDavid du Colombier 				if (bracket > 0 && ss == Added)
72281bcec06SDavid du Colombier 					return Call;
72381bcec06SDavid du Colombier 			}
72481bcec06SDavid du Colombier 			c = newatom(in, atom);
72581bcec06SDavid du Colombier 			break;
72681bcec06SDavid du Colombier 		}
72781bcec06SDavid du Colombier 	}
72881bcec06SDavid du Colombier 	return Beof;
72981bcec06SDavid du Colombier }
73081bcec06SDavid du Colombier 
73181bcec06SDavid du Colombier /*
73281bcec06SDavid du Colombier  * addfuncs() scans the input file for function names and adds them to the
73381bcec06SDavid du Colombier  * calling list.
73481bcec06SDavid du Colombier  */
73581bcec06SDavid du Colombier void
addfuncs(int infd)73681bcec06SDavid du Colombier addfuncs(int infd)
73781bcec06SDavid du Colombier {
73881bcec06SDavid du Colombier 	int intern;
73981bcec06SDavid du Colombier 	uintptr ok = 1;
74081bcec06SDavid du Colombier 	char atom[Maxid];
74181bcec06SDavid du Colombier 	Biobuf inbb;
74281bcec06SDavid du Colombier 	Biobuf *in;
74381bcec06SDavid du Colombier 	Rinst *curproc = nil;
74481bcec06SDavid du Colombier 
74581bcec06SDavid du Colombier 	in = &inbb;
74681bcec06SDavid du Colombier 	Binit(in, infd, OREAD);
74781bcec06SDavid du Colombier 	atom[0] = '\0';
74881bcec06SDavid du Colombier 	while ((intern = getfunc(in, atom)) != Beof && ok)
74981bcec06SDavid du Colombier 		if (intern == Call)
75081bcec06SDavid du Colombier 			ok = add2call(atom, curproc);	/* function call */
75181bcec06SDavid du Colombier 		else
75281bcec06SDavid du Colombier 			ok = (uintptr)(curproc = newproc(atom)); /* fn def'n */
75381bcec06SDavid du Colombier 	Bterm(in);
75481bcec06SDavid du Colombier }
75581bcec06SDavid du Colombier 
75681bcec06SDavid du Colombier /*
75781bcec06SDavid du Colombier  * push a filter, cmd, onto fd.  if input, it's an input descriptor.
75881bcec06SDavid du Colombier  * returns a descriptor to replace fd, or -1 on error.
75981bcec06SDavid du Colombier  */
76081bcec06SDavid du Colombier static int
push(int fd,char * cmd,int input,Pushstate * ps)76181bcec06SDavid du Colombier push(int fd, char *cmd, int input, Pushstate *ps)
76281bcec06SDavid du Colombier {
76381bcec06SDavid du Colombier 	int nfd, pifds[2];
76481bcec06SDavid du Colombier 	String *s;
76581bcec06SDavid du Colombier 
76681bcec06SDavid du Colombier 	ps->open = 0;
76781bcec06SDavid du Colombier 	ps->fd = fd;
76881bcec06SDavid du Colombier 	ps->input = input;
76981bcec06SDavid du Colombier 	if (fd < 0 || pipe(pifds) < 0)
77081bcec06SDavid du Colombier 		return -1;
77181bcec06SDavid du Colombier 	ps->kid = fork();
77281bcec06SDavid du Colombier 	switch (ps->kid) {
77381bcec06SDavid du Colombier 	case -1:
77481bcec06SDavid du Colombier 		return -1;
77581bcec06SDavid du Colombier 	case 0:
77681bcec06SDavid du Colombier 		if (input)
77781bcec06SDavid du Colombier 			dup(pifds[Wr], Stdout);
77881bcec06SDavid du Colombier 		else
77981bcec06SDavid du Colombier 			dup(pifds[Rd], Stdin);
78081bcec06SDavid du Colombier 		close(pifds[input? Rd: Wr]);
78181bcec06SDavid du Colombier 		dup(fd, (input? Stdin: Stdout));
78281bcec06SDavid du Colombier 
78381bcec06SDavid du Colombier 		s = s_new();
78481bcec06SDavid du Colombier 		if (cmd[0] != '/')
78581bcec06SDavid du Colombier 			s_append(s, "/bin/");
78681bcec06SDavid du Colombier 		s_append(s, cmd);
78781bcec06SDavid du Colombier 		execl(s_to_c(s), cmd, nil);
78881bcec06SDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
78981bcec06SDavid du Colombier 		sysfatal("can't exec %s: %r", cmd);
79081bcec06SDavid du Colombier 	default:
79181bcec06SDavid du Colombier 		nfd = pifds[input? Rd: Wr];
79281bcec06SDavid du Colombier 		close(pifds[input? Wr: Rd]);
79381bcec06SDavid du Colombier 		break;
79481bcec06SDavid du Colombier 	}
79581bcec06SDavid du Colombier 	ps->rfd = nfd;
79681bcec06SDavid du Colombier 	ps->open = 1;
79781bcec06SDavid du Colombier 	return nfd;
79881bcec06SDavid du Colombier }
79981bcec06SDavid du Colombier 
80081bcec06SDavid du Colombier static char *
pushclose(Pushstate * ps)80181bcec06SDavid du Colombier pushclose(Pushstate *ps)
80281bcec06SDavid du Colombier {
80381bcec06SDavid du Colombier 	Waitmsg *wm;
80481bcec06SDavid du Colombier 
80581bcec06SDavid du Colombier 	if (ps->fd < 0 || ps->rfd < 0 || !ps->open)
80681bcec06SDavid du Colombier 		return "not open";
80781bcec06SDavid du Colombier 	close(ps->rfd);
80881bcec06SDavid du Colombier 	ps->rfd = -1;
80981bcec06SDavid du Colombier 	ps->open = 0;
81081bcec06SDavid du Colombier 	while ((wm = wait()) != nil && wm->pid != ps->kid)
81181bcec06SDavid du Colombier 		continue;
81281bcec06SDavid du Colombier 	return wm? wm->msg: nil;
81381bcec06SDavid du Colombier }
81481bcec06SDavid du Colombier 
81581bcec06SDavid du Colombier /*
81681bcec06SDavid du Colombier  * invoke the C preprocessor on the named files so that its
81781bcec06SDavid du Colombier  * output can be read.
81881bcec06SDavid du Colombier  *
81981bcec06SDavid du Colombier  * must fork/exec cpp for each input file.
82081bcec06SDavid du Colombier  * otherwise we get macro redefinitions and other problems.
821*0b04b1f8SDavid du Colombier  * also plan 9's cpp can only process one input file per invocation.
82281bcec06SDavid du Colombier  */
82381bcec06SDavid du Colombier void
scanfiles(int argc,char ** argv)82481bcec06SDavid du Colombier scanfiles(int argc, char **argv)
82581bcec06SDavid du Colombier {
82681bcec06SDavid du Colombier 	int i, infd;
82781bcec06SDavid du Colombier 	char *sts;
82881bcec06SDavid du Colombier 	Pushstate ps;
82981bcec06SDavid du Colombier 	String *cmd;
83081bcec06SDavid du Colombier 
83181bcec06SDavid du Colombier 	cmd = s_new();
83281bcec06SDavid du Colombier 	for (i = 0; i < argc; i++) {
83381bcec06SDavid du Colombier 		s_reset(cmd);
83481bcec06SDavid du Colombier 		s_append(cmd, s_to_c(cppopt));
83581bcec06SDavid du Colombier 		s_append(cmd, " ");
83681bcec06SDavid du Colombier 		s_append(cmd, argv[i]);
83781bcec06SDavid du Colombier 
83881bcec06SDavid du Colombier 		infd = push(Stdin, s_to_c(cmd), Rd, &ps);
83981bcec06SDavid du Colombier 		if (infd < 0) {
84081bcec06SDavid du Colombier 			warning("can't execute cmd `%s'", s_to_c(cmd));
84181bcec06SDavid du Colombier 			return;
84281bcec06SDavid du Colombier 		}
84381bcec06SDavid du Colombier 
844500d234bSDavid du Colombier 		free(infile);
845500d234bSDavid du Colombier 		infile = strdup(argv[i]);
846c3617180SDavid du Colombier 		lineno = 1;
84781bcec06SDavid du Colombier 		addfuncs(infd);
84881bcec06SDavid du Colombier 
84981bcec06SDavid du Colombier 		sts = pushclose(&ps);
85081bcec06SDavid du Colombier 		if (sts != nil && sts[0] != '\0') {
85181bcec06SDavid du Colombier 			warning("cmd `%s' failed", s_to_c(cmd));
85281bcec06SDavid du Colombier 			fprint(2, "exit status %s\n", sts);
85381bcec06SDavid du Colombier 		}
85481bcec06SDavid du Colombier 	}
85581bcec06SDavid du Colombier 	s_free(cmd);
85681bcec06SDavid du Colombier }
85781bcec06SDavid du Colombier 
85881bcec06SDavid du Colombier static void
usage(void)85981bcec06SDavid du Colombier usage(void)
86081bcec06SDavid du Colombier {
86181bcec06SDavid du Colombier 	fprint(2, "usage: %s [-ptv] [-f func] [-w width] [-D define] [-U undef]"
86281bcec06SDavid du Colombier 		" [-I dir] [file...]\n", argv0);
86381bcec06SDavid du Colombier 	exits("usage");
86481bcec06SDavid du Colombier }
86581bcec06SDavid du Colombier 
86681bcec06SDavid du Colombier void
main(int argc,char ** argv)86781bcec06SDavid du Colombier main(int argc, char **argv)
86881bcec06SDavid du Colombier {
86981bcec06SDavid du Colombier 	int i, width = Defwidth;
87081bcec06SDavid du Colombier 	char _dashes[1024];
87181bcec06SDavid du Colombier 	Root *rp;
87281bcec06SDavid du Colombier 
87381bcec06SDavid du Colombier 	cppopt = s_copy(CPP);
87481bcec06SDavid du Colombier 	ARGBEGIN{
87581bcec06SDavid du Colombier 	case 'f':			/* start from function arg. */
87681bcec06SDavid du Colombier 		rp = emalloc(sizeof *rp);
87781bcec06SDavid du Colombier 		rp->func = EARGF(usage());
87881bcec06SDavid du Colombier 		rp->next = roots;
87981bcec06SDavid du Colombier 		roots = rp;
88081bcec06SDavid du Colombier 		break;
88181bcec06SDavid du Colombier 	case 'p':			/* ape includes */
88281bcec06SDavid du Colombier 		s_append(cppopt, " -I /sys/include/ape");
88381bcec06SDavid du Colombier 		s_append(cppopt, " -I /");
88481bcec06SDavid du Colombier 		s_append(cppopt, getenv("objtype"));
88581bcec06SDavid du Colombier 		s_append(cppopt, "/include/ape");
88681bcec06SDavid du Colombier 		break;
88781bcec06SDavid du Colombier 	case 't':			/* terse (default) */
88881bcec06SDavid du Colombier 		terse = 1;
88981bcec06SDavid du Colombier 		break;
89081bcec06SDavid du Colombier 	case 'v':
89181bcec06SDavid du Colombier 		terse = 0;
89281bcec06SDavid du Colombier 		break;
89381bcec06SDavid du Colombier 	case 'w':			/* output width */
89481bcec06SDavid du Colombier 		width = atoi(EARGF(usage()));
89581bcec06SDavid du Colombier 		if (width <= 0)
89681bcec06SDavid du Colombier 			width = Defwidth;
89781bcec06SDavid du Colombier 		break;
89881bcec06SDavid du Colombier 	case 'D':
89981bcec06SDavid du Colombier 	case 'I':
90081bcec06SDavid du Colombier 	case 'U':
90181bcec06SDavid du Colombier 		s_append(cppopt, " -");
90281bcec06SDavid du Colombier 		s_putc(cppopt, ARGC());
90381bcec06SDavid du Colombier 		s_append(cppopt, EARGF(usage()));
90481bcec06SDavid du Colombier 		break;
90581bcec06SDavid du Colombier 	default:
90681bcec06SDavid du Colombier 		usage();
90781bcec06SDavid du Colombier 	}ARGEND
90881bcec06SDavid du Colombier 
90981bcec06SDavid du Colombier 	/* initialize the dashed separator list for deep nesting */
91081bcec06SDavid du Colombier 	ntabs = (width - 20) / Tabwidth;
91181bcec06SDavid du Colombier 	for (i = 0; i < width && i+1 < sizeof dashes; i += 2) {
91281bcec06SDavid du Colombier 		_dashes[i] = '-';
91381bcec06SDavid du Colombier 		_dashes[i+1] = ' ';
91481bcec06SDavid du Colombier 	}
91581bcec06SDavid du Colombier 	if (i < sizeof dashes)
91681bcec06SDavid du Colombier 		_dashes[i] = '\0';
91781bcec06SDavid du Colombier 	else
91881bcec06SDavid du Colombier 		_dashes[sizeof dashes - 1] = '\0';
91981bcec06SDavid du Colombier 	dashes = _dashes;
92081bcec06SDavid du Colombier 
92181bcec06SDavid du Colombier 	scanfiles(argc, argv);
92281bcec06SDavid du Colombier 	dumptree();
92381bcec06SDavid du Colombier 
92481bcec06SDavid du Colombier 	if (Printstats) {
92581bcec06SDavid du Colombier 		fprint(2, "%ld/%d aseen entries\n", stats.highestseen, Maxseen);
92681bcec06SDavid du Colombier 		fprint(2, "%ld longest namelist hash chain\n", stats.highestname);
92781bcec06SDavid du Colombier 		fprint(2, "%ld/%d activelist high water mark\n",
92881bcec06SDavid du Colombier 			stats.highestact, Maxdepth);
92981bcec06SDavid du Colombier 		fprint(2, "%ld dlist high water mark\n", stats.highgetfree);
93081bcec06SDavid du Colombier 	}
93181bcec06SDavid du Colombier 	exits(0);
93281bcec06SDavid du Colombier }
933