Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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 *
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
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
174
nextc(Biobuf *in)
175
{
176
	prevc = thisc;
177
	thisc = Bgetrune(in);
178
	return thisc;
179
}
180
 
181
int
182
ungetc(Biobuf *in)
183
{
184
	prevc = thisc;
185
	return Bungetrune(in);
186
}
187
 
188
int
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 *
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 *
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 *
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
}