calls.c 20 KB

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