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