calls.c 20 KB

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