xref: /plan9/sys/src/cmd/calls.c (revision 0b04b1f88ffa4f022ff2064ee0454e267f28f072)
1 /*
2  * calls - print a paragraphed list of who calls whom within a body of C source
3  *
4  * Author: M.M. Taylor, DCIEM, Toronto, Canada.
5  * Modified by Alexis Kwan (HCR at DCIEM),
6  *	Kevin Szabo (watmath!wateng!ksbszabo, Elec Eng, U of Waterloo),
7  *	Tony Hansen, AT&T-IS, pegasus!hansen.
8  */
9 
10 #include <u.h>
11 #include <libc.h>
12 #include <ctype.h>
13 #include <bio.h>
14 #include <String.h>
15 
16 #define CPP		"cpp -+"
17 #define RINSTERR	((Rinst *)-1)	/* ugly; error but keep going */
18 
19 #define STREQ(a, b)	(*(a) == *(b) && strcmp(a, b) == 0)
20 #define OTHER(rdwr)	((rdwr) == Rd? Wr: Rd)
21 /* per 8c, all multibyte runes are considered alphabetic */
22 #define ISIDENT(r) (isascii(r) && isalnum(r) || (r) == '_' || (r) >= Runeself)
23 
24 /* safe macros */
25 #define checksys(atom)		strbsearch(atom, sysword, nelem(sysword))
26 
27 enum {
28 	Printstats =	0,		/* flag */
29 	Maxseen =	4000,		/* # of instances w/in a function */
30 	Maxdepth =	300,		/* max func call tree depth */
31 	Hashsize =	2048,
32 
33 	Maxid =		256 + UTFmax,	/* max size of name */
34 	Tabwidth =	8,
35 	Maxwidth =	132,		/* limits tabbing */
36 	Defwidth =	80,		/* limits tabbing */
37 
38 	Backslash =	'\\',
39 	Quote =		'\'',
40 
41 	Rd =		0,		/* pipe indices */
42 	Wr,
43 
44 	Stdin =		0,
45 	Stdout,
46 	Stderr,
47 
48 	Defn =		0,
49 	Decl,
50 	Call,
51 
52 	Nomore =	-1,
53 	Added,
54 	Found,
55 };
56 
57 typedef struct Pushstate Pushstate;
58 typedef struct Rinst Rinst;
59 typedef struct Root Root;
60 typedef struct Rname Rname;
61 typedef struct Rnamehash Rnamehash;
62 
63 struct Pushstate {
64 	int	kid;
65 	int	fd;	/* original fd */
66 	int	rfd;	/* replacement fd */
67 	int	input;
68 	int	open;
69 };
70 
71 struct Rname {
72 	Rinst	*dlistp;
73 	int	rnameout;
74 	char	rnamecalled;
75 	char	rnamedefined;
76 	char	*namer;
77 	Rname	*next;		/* next in hash chain */
78 };
79 
80 struct Rnamehash {
81 	Rname	*head;
82 };
83 
84 /* list of calling instances of those names */
85 struct Rinst {
86 	Rname	*namep;
87 	Rinst	*calls;
88 	Rinst	*calledby;
89 };
90 
91 struct Root {
92 	char	*func;
93 	Root	*next;
94 };
95 
96 char *aseen[Maxseen];		/* names being gathered within a function */
97 Rnamehash nameshash[Hashsize];	/* names being tracked */
98 Rname *activelist[Maxdepth];	/* names being output */
99 String *cppopt;
100 Root *roots;
101 
102 static struct stats {
103 	long	highestseen;	/* aseen high water mark */
104 	long	highestname;	/* namelist high water mark */
105 	long	highestact;	/* activelist high water mark */
106 	long	highgetfree;	/* getfrees high water mark */
107 } stats;
108 
109 static long getfrees = 0;
110 
111 int bracket = 0;			/* curly brace count in input */
112 int linect = 0;				/* line number in output */
113 int activep = 0;			/* current function being output */
114 
115 char *infile;
116 int lineno = 1;				/* line number of input */
117 int prevc = '\n', thisc = '\n';
118 
119 /* options */
120 int terse = 1;				/* track functions only once */
121 int ntabs = (Maxwidth - 20) / Tabwidth;	/* how wide to go */
122 
123 char *dashes;				/* separators for deep nestings */
124 
125 /*
126  * These are C tokens after which a parenthesis is valid which would
127  * otherwise be tagged as function names.  The reserved words which are not
128  * listed are break, const, continue, default, goto and volatile.
129  */
130 char *sysword[] = {
131 	"auto", "case", "char", "do", "double", "else", "enum",
132 	"extern", "float", "for", "if", "int", "long", "register",
133 	"return", "short", "sizeof", "static", "struct", "switch",
134 	"typedef", "union", "unsigned", "void", "while",
135 };
136 
137 /*
138  * warning - print best error message possible
139  */
140 void
warning(char * s1,char * s2)141 warning(char *s1, char *s2)
142 {
143 	fprint(2, "%s: ", argv0);
144 	fprint(2, s1, s2);
145 	fprint(2, "\n");
146 }
147 
148 /*
149  *   safe malloc() code.  Does the checking for nil returns from malloc()
150  */
151 void *
emalloc(int size)152 emalloc(int size)
153 {
154 	void *f = mallocz(size, 1);
155 
156 	if (f == nil)
157 		sysfatal("cannot allocate memory");
158 	return f;
159 }
160 
161 unsigned
hash(char * s)162 hash(char *s)
163 {
164 	unsigned h;
165 	unsigned char *cp;
166 
167 	h = 0;
168 	for(cp = (unsigned char *)s; *cp; h += *cp++)
169 		h *= 1119;
170 	return h;
171 }
172 
173 int
nextc(Biobuf * in)174 nextc(Biobuf *in)
175 {
176 	prevc = thisc;
177 	thisc = Bgetrune(in);
178 	return thisc;
179 }
180 
181 int
ungetc(Biobuf * in)182 ungetc(Biobuf *in)
183 {
184 	prevc = thisc;
185 	return Bungetrune(in);
186 }
187 
188 int
newatom(Biobuf * in,char * atom)189 newatom(Biobuf *in, char *atom)
190 {
191 	atom[0] = '\0';
192 	return nextc(in);
193 }
194 
195 /*
196  * lookup (name) accepts a pointer to a name and sees if the name is on the
197  * namelist.  If so, it returns a pointer to the nameblock.  Otherwise it
198  * returns nil.
199  */
200 Rname *
lookfor(char * name)201 lookfor(char *name)
202 {
203 	int i;
204 	unsigned buck;
205 	Rname *np;
206 	Rnamehash *hp;
207 
208 	buck = hash(name) % Hashsize;
209 	hp = &nameshash[buck];
210 	i = 0;
211 	for (np = hp->head; np != nil; np = np->next, i++)
212 		if (STREQ(name, np->namer))
213 			return np;
214 
215 	if (i > stats.highestname)
216 		stats.highestname = i;
217 	return nil;
218 }
219 
220 /*
221  * place() returns a pointer to the name on the namelist.  If the name was
222  * not in the namelist, place adds it.
223  */
224 Rname *
place(char name[])225 place(char name[])
226 {
227 	unsigned buck;
228 	Rname *np;
229 	Rnamehash *hp;
230 
231 	np = lookfor(name);
232 	if (np != nil)
233 		return np;
234 
235 	buck = hash(name) % Hashsize;
236 	hp = &nameshash[buck];
237 	np = emalloc(sizeof *np);
238 	np->namer = strdup(name);
239 	np->next = hp->head;
240 	hp->head = np;
241 	return np;
242 }
243 
244 /*
245  * getfree returns a pointer to the next free instance block on the list
246  */
247 Rinst *
getfree(void)248 getfree(void)
249 {
250 	Rinst *ret, *new;
251 	static Rinst *prev;
252 
253 	++getfrees;
254 	if (getfrees > stats.highgetfree)
255 		stats.highgetfree = getfrees;
256 
257 	if (prev == nil)
258 		prev = emalloc(sizeof *prev);
259 	new = emalloc(sizeof *new);
260 
261 	prev->calls = new;		/* also serves as next pointer */
262 	new->calledby = prev;
263 
264 	ret = prev;
265 	prev = new;
266 	return ret;
267 }
268 
269 /*
270  * install(np, rp) puts a new instance of a function into the linked list.
271  * It puts a pointer (np) to its own name (returned by place) into its
272  * namepointer, a pointer to the calling routine (rp) into its called-by
273  * pointer, and zero into the calls pointer.  It then puts a pointer to
274  * itself into the last function in the chain.
275  */
276 Rinst *
install(Rname * np,Rinst * rp)277 install(Rname *np, Rinst *rp)
278 {
279 	Rinst *newp;
280 
281 	if (np == nil)
282 		return RINSTERR;
283 	if ((newp = getfree()) == nil)
284 		return nil;
285 	newp->namep = np;
286 	newp->calls = 0;
287 	if (rp) {
288 		while (rp->calls)
289 			rp = rp->calls;
290 		newp->calledby = rp->calledby;
291 		rp->calls = newp;
292 	} else {
293 		newp->calledby = (Rinst *)np;
294 		np->dlistp = newp;
295 	}
296 	return newp;
297 }
298 
299 /*
300  * When scanning the text, each function instance is inserted into a
301  * linear list of names, using the Rname structure, when it is first
302  * encountered.  It is also inserted into the linked list using the Rinst
303  * structure.  The entry into the name list has a pointer to the defining
304  * instance in the linked list, and each entry in the linked list has a
305  * pointer back to the relevant name.  Newproc makes an entry in the
306  * defining list, which is distinguished from the called list only
307  * because it has no calledby link (value=0).  Add2proc enters into the
308  * called list, by inserting a link to the new instance in the calls
309  * pointer of the last entry (may be a defining instance, or a function
310  * called by that defining instance), and points back to the defining
311  * instance of the caller in its called-by pointer.
312  */
313 Rinst *
newproc(char * name)314 newproc(char *name)
315 {
316 	int i;
317 	Rname *rp;
318 
319 	for (i = 0; i < Maxseen; i++)
320 		if (aseen[i] != nil) {
321 			free(aseen[i]);
322 			aseen[i] = nil;
323 		}
324 	rp = place(name);
325 	if (rp == nil)
326 		return RINSTERR;
327 	/* declaration in a header file is enough to cause this. */
328 	if (0 && rp->rnamedefined)
329 		warning("function `%s' redeclared", name);
330 	rp->rnamedefined = 1;
331 	return install(rp, nil);
332 }
333 
334 /*
335  * add the function name to the calling stack of the current function.
336  */
337 int
add2call(char name[],Rinst * curp)338 add2call(char name[], Rinst *curp)
339 {
340 	Rname *p = place(name);
341 	Rinst *ip = install(p, curp);
342 
343 	if (p != nil && curp != nil && curp->namep != nil &&
344 	    !STREQ(p->namer, curp->namep->namer))
345 		p->rnamecalled = 1;
346 	return ip != nil;
347 }
348 
349 /*
350  * backup removes an item from the active stack
351  */
352 void
backup(void)353 backup(void)
354 {
355 	if (activep > 0)
356 		activelist[--activep] = nil;
357 }
358 
359 /*
360  * makeactive simply puts a pointer to the nameblock into a stack with
361  * maximum depth Maxdepth.  the error return only happens for stack
362  * overflow.
363  */
364 int
makeactive(Rname * func)365 makeactive(Rname *func)
366 {
367 	if (activep < Maxdepth) {
368 		if (activep > stats.highestact)
369 			stats.highestact = activep;
370 		activelist[activep++] = func;
371 		return 1;
372 	}
373 	return 0;
374 }
375 
376 /*
377  * active checks whether the pointer which is its argument has already
378  * occurred on the active list, and returns 1 if so.
379  */
380 int
active(Rname * func)381 active(Rname *func)
382 {
383 	int i;
384 
385 	for (i = 0; i < activep - 1; i++)
386 		if (func == activelist[i])
387 			return 1;
388 	return 0;
389 }
390 
391 /*
392  * output is a recursive routine to print one tab for each level of
393  * recursion, then the name of the function called, followed by the next
394  * function called by the same higher level routine.  In doing this, it
395  * calls itself to output the name of the first function called by the
396  * function whose name it is printing.  It maintains an active list of
397  * functions currently being printed by the different levels of
398  * recursion, and if it finds itself asked to print one which is already
399  * active, it terminates, marking that call with a '*'.
400  */
401 void
output(Rname * func,int tabc)402 output(Rname *func, int tabc)
403 {
404 	int i, tabd, tabstar, tflag;
405 	Rinst *nextp;
406 
407 	++linect;
408 	print("\n%d", linect);
409 	if (!makeactive(func)) {
410 		print("   * nesting is too deep");
411 		return;
412 	}
413 	tabstar = 0;
414 	tabd = tabc;
415 	for (; tabd > ntabs; tabstar++)
416 		tabd -= ntabs;
417 	if (tabstar > 0) {
418 		print("  ");
419 		for (i = 0; i < tabstar; i++)
420 			print("<");
421 	}
422 	if (tabd == 0)
423 		print("   ");
424 	else
425 		for (i = 0; i < tabd; i++)
426 			print("\t");
427 	if (active(func))
428 		print("<<< %s", func->namer);		/* recursive call */
429 	else if (func->dlistp == nil)
430 		print("%s [external]", func->namer);
431 	else {
432 		print("%s", func->namer);
433 		nextp = func->dlistp->calls;
434 		if (!terse || !func->rnameout) {
435 			++tabc;
436 			if (!func->rnameout)
437 				func->rnameout = linect;
438 			if (tabc > ntabs && tabc%ntabs == 1 && nextp) {
439 				print("\n%s", dashes);
440 				tflag = 1;
441 			} else
442 				tflag = 0;
443 			for (; nextp; nextp = nextp->calls)
444 				output(nextp->namep, tabc);
445 			if (tflag) {
446 				print("\n%s", dashes);
447 				tflag = 0;
448 				USED(tflag);
449 			}
450 		} else if (nextp != nil)		/* not a leaf */
451 			print(" ... [see line %d]",  func->rnameout);
452 	}
453 	backup();
454 }
455 
456 /*
457  * Dumptree() lists out the calling stacks.  All names will be listed out
458  * unless some function names are specified in -f options.
459  */
460 void
dumptree(void)461 dumptree(void)
462 {
463 	unsigned buck;
464 	Root *rp;
465 	Rname *np;
466 
467 	if (roots != nil)
468 		for (rp = roots; rp != nil; rp = rp->next)
469 			if ((np = lookfor(rp->func)) != nil) {
470 				output(np, 0);
471 				print("\n\n");
472 			} else
473 				fprint(2, "%s: function '%s' not found\n",
474 					argv0, rp->func);
475 	else
476 		/* print everything */
477 		for (buck = 0; buck < Hashsize; buck++)
478 			for (np = nameshash[buck].head; np != nil; np = np->next)
479 				if (!np->rnamecalled) {
480 					output(np, 0);
481 					print("\n\n");
482 				}
483 }
484 
485 /*
486  * Skipcomments() skips past any blanks and comments in the input stream.
487  */
488 int
skipcomments(Biobuf * in,int firstc)489 skipcomments(Biobuf *in, int firstc)
490 {
491 	int c;
492 
493 	for (c = firstc; isascii(c) && isspace(c) || c == '/'; c = nextc(in)) {
494 		if (c == '\n')
495 			lineno++;
496 		if (c != '/')
497 			continue;
498 		c = nextc(in);			/* read ahead */
499 		if (c == Beof)
500 			break;
501 		if (c != '*' && c != '/') {	/* not comment start? */
502 			ungetc(in);		/* push back readahead */
503 			return '/';
504 		}
505 		if (c == '/') {			/* c++ style */
506 			while ((c = nextc(in)) != '\n' && c != Beof)
507 				;
508 			if (c == '\n')
509 				lineno++;
510 			continue;
511 		}
512 		for (;;) {
513 			/* skip to possible closing delimiter */
514 			while ((c = nextc(in)) != '*' && c != Beof)
515 				if (c == '\n')
516 					lineno++;
517 			if (c == Beof)
518 				break;
519 			/* else c == '*' */
520 			c = nextc(in);		 /* read ahead */
521 			if (c == Beof || c == '/') /* comment end? */
522 				break;
523 			ungetc(in);		/* push back readahead */
524 		}
525 	}
526 	return c;
527 }
528 
529 /*
530  * isfndefn differentiates between an external declaration and a real
531  * function definition.  For instance, between:
532  *
533  *	extern char *getenv(char *), *strcmp(char *, char *);
534  *  and
535  *	char *getenv(char *name)
536  *	{}
537  *
538  * It does so by making the observation that nothing (except blanks and
539  * comments) can be between the right parenthesis and the semi-colon or
540  * comma following the extern declaration.
541  */
542 int
isfndefn(Biobuf * in)543 isfndefn(Biobuf *in)
544 {
545 	int c;
546 
547 	c = skipcomments(in, nextc(in));
548 	while (c != ')' && c != Beof)	/* consume arg. decl.s */
549 		c = nextc(in);
550 	if (c == Beof)
551 		return 1;		/* definition at Beof */
552 	c = skipcomments(in, nextc(in)); /* skip blanks between ) and ; */
553 
554 	if (c == ';' || c == ',')
555 		return 0;		/* an extern declaration */
556 	if (c != Beof)
557 		ungetc(in);
558 	return 1;			/* a definition */
559 }
560 
561 /*
562  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
563  * is WAY faster than the generic bsearch().
564  */
565 int
strbsearch(char * key,char ** base,unsigned nel)566 strbsearch(char *key, char **base, unsigned nel)
567 {
568 	int cmp;
569 	char **last = base + nel - 1, **pos;
570 
571 	while (last >= base) {
572 		pos = base + ((last - base) >> 1);
573 		cmp = key[0] - (*pos)[0];
574 		if (cmp == 0) {
575 			/* there are no empty strings in the table */
576 			cmp = strcmp(key, *pos);
577 			if (cmp == 0)
578 				return 1;
579 		}
580 		if (cmp < 0)
581 			last = pos - 1;
582 		else
583 			base = pos + 1;
584 	}
585 	return 0;
586 }
587 
588 /*
589  * see if we have seen this function within this process
590  */
591 int
seen(char * atom)592 seen(char *atom)
593 {
594 	int i;
595 
596 	for (i = 0; aseen[i] != nil && i < Maxseen-1; i++)
597 		if (STREQ(atom, aseen[i]))
598 			return Found;
599 	if (i >= Maxseen-1)
600 		return Nomore;
601 	aseen[i] = strdup(atom);
602 	if (i > stats.highestseen)
603 		stats.highestseen = i;
604 	return Added;
605 }
606 
607 /*
608  * getfunc returns the name of a function in atom and Defn for a definition,
609  * Call for an internal call, or Beof.
610  */
611 int
getfunc(Biobuf * in,char * atom)612 getfunc(Biobuf *in, char *atom)
613 {
614 	int c, nf, last, ss, quote;
615 	char *ln, *nm, *ap, *ep = &atom[Maxid-1-UTFmax];
616 	char *flds[4];
617 	Rune r;
618 
619 	c = nextc(in);
620 	while (c != Beof) {
621 		if (ISIDENT(c)) {
622 			ap = atom;
623 			do {
624 				if (isascii(c))
625 					*ap++ = c;
626 				else {
627 					r = c;
628 					ap += runetochar(ap, &r);
629 				}
630 				c = nextc(in);
631 			} while(ap < ep && ISIDENT(c));
632 			*ap = '\0';
633 			if (ap >= ep) {	/* uncommon case: id won't fit */
634 				/* consume remainder of too-long id */
635 				while (ISIDENT(c))
636 					c = nextc(in);
637 			}
638 		}
639 
640 		switch (c) {
641 		case Beof:
642 			return Beof;
643 		case '\n':
644 			lineno++;
645 			/* fall through */
646 		case '\t':		/* ignore white space */
647 		case ' ':
648 		case '\f':
649 		case '\r':
650 		case '/':		/* potential comment? */
651 			c = skipcomments(in, nextc(in));
652 			break;
653 		case Backslash:		/* consume a newline or something */
654 		case ')':		/* end of parameter list */
655 		default:
656 			c = newatom(in, atom);
657 			break;
658 		case '#':
659 			if (prevc != '\n') {	/* cpp # or ## operator? */
660 				c = nextc(in);	/* read ahead */
661 				break;
662 			}
663 			/* it's a cpp directive */
664 			ln = Brdline(in, '\n');
665 			if (ln == nil)
666 				thisc = c = Beof;
667 			else {
668 				nf = tokenize(ln, flds, nelem(flds));
669 				if (nf >= 3 && strcmp(flds[0], "line") == 0) {
670 					lineno = atoi(flds[1]);
671 					free(infile);
672 					nm = flds[2];
673 					if (nm[0] == '"')
674 						nm++;
675 					last = strlen(nm) - 1;
676 					if (nm[last] == '"')
677 						nm[last] = '\0';
678 					infile = strdup(nm);
679 				} else
680 					lineno++;
681 				c = nextc(in);	/* read ahead */
682 			}
683 			break;
684 		case Quote:		/* character constant */
685 		case '\"':		/* string constant */
686 			quote = c;
687 			atom[0] = '\0';
688 			while ((c = nextc(in)) != quote && c != Beof)
689 				if (c == Backslash)
690 					nextc(in);
691 			if (c == quote)
692 				c = nextc(in);
693 			break;
694 		case '{':		/* start of a block */
695 			bracket++;
696 			c = newatom(in, atom);
697 			break;
698 		case '}':		/* end of a block */
699 			if (bracket < 1)
700 				fprint(2, "%s: %s:%d: too many closing braces; "
701 					"previous open brace missing\n",
702 					argv0, infile, lineno);
703 			else
704 				--bracket;
705 			c = newatom(in, atom);
706 			break;
707 		case '(':		/* parameter list for function? */
708 			if (atom[0] != '\0' && !checksys(atom)) {
709 				if (bracket == 0)
710 					if (isfndefn(in))
711 						return Defn;
712 					else {
713 						c = nextc(in);
714 						break;		/* ext. decl. */
715 					}
716 				ss = seen(atom);
717 				if (ss == Nomore)
718 					fprint(2, "%s: %s:%d: more than %d "
719 						"identifiers in a function\n",
720 						argv0, infile, lineno, Maxseen);
721 				if (bracket > 0 && ss == Added)
722 					return Call;
723 			}
724 			c = newatom(in, atom);
725 			break;
726 		}
727 	}
728 	return Beof;
729 }
730 
731 /*
732  * addfuncs() scans the input file for function names and adds them to the
733  * calling list.
734  */
735 void
addfuncs(int infd)736 addfuncs(int infd)
737 {
738 	int intern;
739 	uintptr ok = 1;
740 	char atom[Maxid];
741 	Biobuf inbb;
742 	Biobuf *in;
743 	Rinst *curproc = nil;
744 
745 	in = &inbb;
746 	Binit(in, infd, OREAD);
747 	atom[0] = '\0';
748 	while ((intern = getfunc(in, atom)) != Beof && ok)
749 		if (intern == Call)
750 			ok = add2call(atom, curproc);	/* function call */
751 		else
752 			ok = (uintptr)(curproc = newproc(atom)); /* fn def'n */
753 	Bterm(in);
754 }
755 
756 /*
757  * push a filter, cmd, onto fd.  if input, it's an input descriptor.
758  * returns a descriptor to replace fd, or -1 on error.
759  */
760 static int
push(int fd,char * cmd,int input,Pushstate * ps)761 push(int fd, char *cmd, int input, Pushstate *ps)
762 {
763 	int nfd, pifds[2];
764 	String *s;
765 
766 	ps->open = 0;
767 	ps->fd = fd;
768 	ps->input = input;
769 	if (fd < 0 || pipe(pifds) < 0)
770 		return -1;
771 	ps->kid = fork();
772 	switch (ps->kid) {
773 	case -1:
774 		return -1;
775 	case 0:
776 		if (input)
777 			dup(pifds[Wr], Stdout);
778 		else
779 			dup(pifds[Rd], Stdin);
780 		close(pifds[input? Rd: Wr]);
781 		dup(fd, (input? Stdin: Stdout));
782 
783 		s = s_new();
784 		if (cmd[0] != '/')
785 			s_append(s, "/bin/");
786 		s_append(s, cmd);
787 		execl(s_to_c(s), cmd, nil);
788 		execl("/bin/rc", "rc", "-c", cmd, nil);
789 		sysfatal("can't exec %s: %r", cmd);
790 	default:
791 		nfd = pifds[input? Rd: Wr];
792 		close(pifds[input? Wr: Rd]);
793 		break;
794 	}
795 	ps->rfd = nfd;
796 	ps->open = 1;
797 	return nfd;
798 }
799 
800 static char *
pushclose(Pushstate * ps)801 pushclose(Pushstate *ps)
802 {
803 	Waitmsg *wm;
804 
805 	if (ps->fd < 0 || ps->rfd < 0 || !ps->open)
806 		return "not open";
807 	close(ps->rfd);
808 	ps->rfd = -1;
809 	ps->open = 0;
810 	while ((wm = wait()) != nil && wm->pid != ps->kid)
811 		continue;
812 	return wm? wm->msg: nil;
813 }
814 
815 /*
816  * invoke the C preprocessor on the named files so that its
817  * output can be read.
818  *
819  * must fork/exec cpp for each input file.
820  * otherwise we get macro redefinitions and other problems.
821  * also plan 9's cpp can only process one input file per invocation.
822  */
823 void
scanfiles(int argc,char ** argv)824 scanfiles(int argc, char **argv)
825 {
826 	int i, infd;
827 	char *sts;
828 	Pushstate ps;
829 	String *cmd;
830 
831 	cmd = s_new();
832 	for (i = 0; i < argc; i++) {
833 		s_reset(cmd);
834 		s_append(cmd, s_to_c(cppopt));
835 		s_append(cmd, " ");
836 		s_append(cmd, argv[i]);
837 
838 		infd = push(Stdin, s_to_c(cmd), Rd, &ps);
839 		if (infd < 0) {
840 			warning("can't execute cmd `%s'", s_to_c(cmd));
841 			return;
842 		}
843 
844 		free(infile);
845 		infile = strdup(argv[i]);
846 		lineno = 1;
847 		addfuncs(infd);
848 
849 		sts = pushclose(&ps);
850 		if (sts != nil && sts[0] != '\0') {
851 			warning("cmd `%s' failed", s_to_c(cmd));
852 			fprint(2, "exit status %s\n", sts);
853 		}
854 	}
855 	s_free(cmd);
856 }
857 
858 static void
usage(void)859 usage(void)
860 {
861 	fprint(2, "usage: %s [-ptv] [-f func] [-w width] [-D define] [-U undef]"
862 		" [-I dir] [file...]\n", argv0);
863 	exits("usage");
864 }
865 
866 void
main(int argc,char ** argv)867 main(int argc, char **argv)
868 {
869 	int i, width = Defwidth;
870 	char _dashes[1024];
871 	Root *rp;
872 
873 	cppopt = s_copy(CPP);
874 	ARGBEGIN{
875 	case 'f':			/* start from function arg. */
876 		rp = emalloc(sizeof *rp);
877 		rp->func = EARGF(usage());
878 		rp->next = roots;
879 		roots = rp;
880 		break;
881 	case 'p':			/* ape includes */
882 		s_append(cppopt, " -I /sys/include/ape");
883 		s_append(cppopt, " -I /");
884 		s_append(cppopt, getenv("objtype"));
885 		s_append(cppopt, "/include/ape");
886 		break;
887 	case 't':			/* terse (default) */
888 		terse = 1;
889 		break;
890 	case 'v':
891 		terse = 0;
892 		break;
893 	case 'w':			/* output width */
894 		width = atoi(EARGF(usage()));
895 		if (width <= 0)
896 			width = Defwidth;
897 		break;
898 	case 'D':
899 	case 'I':
900 	case 'U':
901 		s_append(cppopt, " -");
902 		s_putc(cppopt, ARGC());
903 		s_append(cppopt, EARGF(usage()));
904 		break;
905 	default:
906 		usage();
907 	}ARGEND
908 
909 	/* initialize the dashed separator list for deep nesting */
910 	ntabs = (width - 20) / Tabwidth;
911 	for (i = 0; i < width && i+1 < sizeof dashes; i += 2) {
912 		_dashes[i] = '-';
913 		_dashes[i+1] = ' ';
914 	}
915 	if (i < sizeof dashes)
916 		_dashes[i] = '\0';
917 	else
918 		_dashes[sizeof dashes - 1] = '\0';
919 	dashes = _dashes;
920 
921 	scanfiles(argc, argv);
922 	dumptree();
923 
924 	if (Printstats) {
925 		fprint(2, "%ld/%d aseen entries\n", stats.highestseen, Maxseen);
926 		fprint(2, "%ld longest namelist hash chain\n", stats.highestname);
927 		fprint(2, "%ld/%d activelist high water mark\n",
928 			stats.highestact, Maxdepth);
929 		fprint(2, "%ld dlist high water mark\n", stats.highgetfree);
930 	}
931 	exits(0);
932 }
933