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 } 933