home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / calls < prev    next >
Internet Message Format  |  1986-11-30  |  27KB

  1. From: talcott!seismo!cbosgd!pegasus!hansen
  2. Subject: calls(1)
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 47
  7. Submitted by: cbosgd!pegasus!hansen
  8.  
  9.  
  10. #
  11. # In 1983, the calls program was given to the net by
  12. # M. Taylor from DCIEM. Subsequent hacking by various 
  13. # people produced a version posted in 1984. About that 
  14. # time, I started using it heavily, found some things
  15. # lacking in it, and began fixing/reworking a number
  16. # of bugs/features. The current version has been in use
  17. # for over a year now with no further changes, so it is time
  18. # to give it back out to the net for further use and abuse.
  19. #
  20. #                Tony Hansen
  21. #                AT&T-IS
  22. #                ihnp4!pegasus!hansen
  23.  
  24. # Note that this version assumes that "cc -E" is available
  25. # to access the C preprocessor. It also assumes that getopt(3)
  26. # is available, which most people should have by now. (If not,
  27. # write.)
  28.  
  29. #!/bin/sh
  30. # This is a shar archive.
  31. # The rest of this file is a shell script which will extract:
  32. # calls.1 calls.c Makefile
  33. # Archive created: Sat Nov 16 11:54:18 EST 1985
  34. echo x - calls.1
  35. sed 's/^X//' > calls.1 << '~FUNKY STUFF~'
  36. X.TH CALLS 1 PUBLIC
  37. X.SH NAME
  38. calls \- print out calling pattern of functions in a program
  39. X.SH SYNOPSIS
  40. calls [-tvs] [-w n] [-f function] [-D def] [-U def] [-I dir] [filenames]
  41. X.SH DESCRIPTION
  42. X.B Calls
  43. reads
  44. X.IR filenames ,
  45. which should be the source of C programmes, and outputs the analysed calling
  46. pattern to standard output.
  47. If no filenames are given, or a filename of \- is seen, standard input will
  48. be read.
  49. X.B Calls
  50. is intended to help analyse the flow of a programme by laying out the
  51. functions called in the heirarchical manner used in "Software Tools" by
  52. B. Kernighan and P. Plauger.
  53. X.P
  54. Functions called but not defined within the source file are shown as:
  55. X.sp
  56. X.RS
  57. function [external]
  58. X.RE
  59. X.P
  60. Recursive references are shown as:
  61. X.sp
  62. X.RS
  63. <<< function
  64. X.RE
  65. X.P
  66. For example, given the file
  67. X.B programme.c
  68. in which
  69. X.I main
  70. calls
  71. X.I abc
  72. and
  73. X.IR def ,
  74. X.I abc
  75. calls
  76. X.I ghi
  77. and
  78. X.IR jkl ,
  79. which are defined within the same source file, and
  80. X.I def
  81. calls
  82. X.IR mno ,
  83. defined in the same source, and
  84. X.IR pqr ,
  85. which is presumably a library function or defined in a different source file.
  86. The function
  87. X.I ghi
  88. calls
  89. X.I abc
  90. in a recursive loop.
  91. X.sp
  92. X.RS
  93. X.nf
  94. main() {
  95.     abc();
  96.     def();
  97. }
  98. abc() {
  99.     ghi();
  100.     jkl();
  101. }
  102. def() {
  103.     mno();
  104.     pqr();
  105. }
  106. ghi() {
  107.     abc();
  108. }
  109. jkl() { }
  110. mno() { }
  111. X.fi
  112. X.RE
  113. X.sp
  114. Executing "calls programme.c" will produce:
  115. X.sp
  116. X.RS
  117. X.nf
  118. 1   main
  119. 2    abc
  120. 3        ghi
  121. 4            <<< abc
  122. 5        jkl
  123. 6    def
  124. 7        mno
  125. 8        pqr [external]
  126. X.fi
  127. X.RE
  128. X.SS FLAGS
  129. X.TP 20
  130. -t
  131. Provides a terse form of output, in which the calling pattern for any
  132. function is printed only once on the first occurrence of the function.
  133. Subsequent occurrences output the function name and a notation
  134. X.IP "" 30
  135. \|... [see line xx]
  136. X.IP "" 20
  137. This is the default case.
  138. X.TP 20
  139. -v
  140. Full output of function calling patterns on every occurrence.
  141. X.TP 20
  142. X.BI -w nn
  143. Set the output paper width to nn.
  144. The default is 132 columns.
  145. X.TP 20
  146. -s
  147. Normally all filenames given will have their calling sequences combined into
  148. one heirarchy.
  149. This option will force the calling heirarchies to be separated.
  150. The filename for each file will be printed before the calling pattern.
  151. X.TP 20
  152. X.BI -D name
  153. X.TP 20
  154. X.BI -D name=defn
  155. Define the
  156. X.I name
  157. for the preprocessor, as if by #define.
  158. If no definition is given, the name is defined as 1.
  159. X.TP 20
  160. X.BI -U name
  161. Remove any initial definition of
  162. X.IR name ,
  163. where
  164. X.I name
  165. is a reserved symbol that is predefined by the preprocessor.
  166. X.TP 20
  167. X.BI -I dir
  168. Change the algorithm for searching for #include files whose names do not
  169. begin with / to look in
  170. X.I dir
  171. before looking in the directories on the standard list.
  172. X.TP 20
  173. X.BI -f name
  174. Function names within the input programme may be selected as
  175. roots of the layout.
  176. For example, using the previous programme:
  177. X.sp
  178. X.RS
  179. calls -f def -f abc programme.c
  180. X.sp
  181. X.nf
  182. 1   def
  183. 2    mno
  184. 3    pqr [external]
  185.  
  186.  
  187. 4   abc
  188. 5    ghi
  189. 6        <<< abc
  190. 7    jkl
  191. X.fi
  192. X.RE
  193. X.SH AUTHOR
  194. M. M. Taylor (DCIEM)
  195. X.br
  196. Modified for V7 and stdio, Alexis Kwan (HCR for DCIEM)
  197. X.br
  198. Fixed bugs with '_' and variable names, names > ATOM_LENGTH chars.
  199. 12-Jun-84, Kevin Szabo,
  200. watmath!wateng!ksbszabo (Elec Eng, U of Waterloo).
  201. X.br
  202. Many other bug fixes and features, Tony Hansen (ihnp4!pegasus!hansen)
  203. X.SH BUGS
  204. Forward declared functions defined within a function body which are not
  205. subsequently used within that function body will be listed as having been
  206. called.
  207. X.ig
  208. Many intended features are not implemented:
  209. flags -g (list globals used), and -F and -P (Fortran and Pascal languages).
  210. X..
  211. ~FUNKY STUFF~
  212. ls -l calls.1
  213. echo x - calls.c
  214. sed 's/^X//' > calls.c << '~FUNKY STUFF~'
  215. /*
  216.  * calls: calls prints a paragraphed list of who calls whom within
  217.  *    a body of source code.
  218.  *
  219.  * Author: M.M. Taylor, DCIEM, Toronto, Canada.
  220.  * 22/Jan/81, Alexis Kwan (HCR at DCIEM).
  221.  *    Modified for V7 and stdio,
  222.  * 12-Jun-84, Kevin Szabo
  223.  * watmath!wateng!ksbszabo (Elec Eng, U of Waterloo)
  224.  *    Fixed bugs with '_' and variable names, names > ATOM_LENGTH chars.
  225.  * 8/8/84, Tony Hansen, AT&T-IS, pegasus!hansen.
  226.  *    Modified to use getopt,
  227.  *    files are passed through CPP with "cc -E"
  228.  *    multiple filenames and '-' are allowed on the command line,
  229.  *    added -D, -U, -I and -f options,
  230.  *        (CPP prefers filenames rather than stdin. To make this
  231.  *        easier and faster, filenames needed to be allowed on the
  232.  *        command line. This conflicted with the old usage of
  233.  *        specifying function names on the command line, unfortunately.
  234.  *        The -f option was added to put that capability back. Passing
  235.  *        things through the CPP has the advantage of not picking up
  236.  *        macros as function calls and avoids all hassles with the
  237.  *        strangeness that can be done with the CPP. Also, it allows
  238.  *        the addition of the -D, -U and -I options.)
  239.  *    portable to unsigned char machines,
  240.  *    handle 31 chars in variable names
  241.  *        (chosen over flexnames because of the pending ANSI standards),
  242.  *    fixed bug with some keywords tagged as function names,
  243.  *    fixed bug with '_' at beginning of variable name,
  244.  *    fixed bug scanning file with CPP statement in the first line,
  245.  *    skips forward declarations external to a function body,
  246.  *    fixed bug to print out functions which are defined and
  247.  *        recursive to themselves, but aren't called elsewhere
  248.  *        within the same file,
  249.  *    added comments in many places,
  250.  *    did more de-linting
  251.  *        (I didn't cast functions to void because earlier
  252.  *        compilers didn't have it, nor declare exit() because
  253.  *        different versions of UNIX declare it differently),
  254.  *    rearranged structures to minimize padding,
  255.  *    dashes between deeply nested lists now vary with paper width,
  256.  */
  257.  
  258. #include <stdio.h>
  259. #include <ctype.h>
  260. #include <errno.h>
  261.  
  262. #define ATOMLENGTH 32            /* max size of name is 31 chars */
  263. #define MAXNAME 500            /* # of names to be followed */
  264. #define MAXINST 4000            /* # of instances of those names */
  265. #define MAXSEEN 100            /* # of instances w/in a function */
  266. #define MAXDEPTH 25            /* max output depth level */
  267. #define PAPERWIDTH 132            /* limits tabbing */
  268.  
  269. int bracket = 0,            /* curly brace count */
  270.     linect = 0;                /* line number */
  271. int activep = 0;            /* current function being output */
  272.  
  273. /* options */
  274. int terse = 1,                /* track functions only once */
  275.     ntabs = (PAPERWIDTH - 20)/8,    /* how wide to go */
  276.     functionlist = 0;            /* restrict to functions listed */
  277.  
  278. char *progname;                /* argv[0] */
  279. FILE *input;                /* open file */
  280. char *arglist = "tvw:f:D:U:I:";        /* valid options */
  281. char *dashes;                /* separators for deep nestings */
  282.  
  283. /*
  284.     These are C tokens after which a parenthesis is valid
  285.     which would otherwise be tagged as function names. The
  286.     reserved words which are not listed are break, continue,
  287.     default and goto.
  288. */
  289.  
  290. char *sysword [] = {
  291.     "auto", "case", "char", "do", "double", "else",
  292.     "entry", "enum", "extern", "float", "for", "fortran",
  293.     "if", "int", "long", "register", "return", "short",
  294.     "sizeof", "static", "struct", "switch", "typedef",
  295.     "union", "unsigned", "void", "while",
  296.     0
  297.     };
  298.  
  299. /* list of names being tracked */
  300. struct rname {
  301.     struct rinst *dlistp;
  302.     int rnamecalled;
  303.     int rnameout;
  304.     char namer[ATOMLENGTH];
  305.     } namelist[MAXNAME];
  306.  
  307. /* list of calling instances of those names */
  308. struct rinst {
  309.     struct rname *namep;
  310.     struct rinst *calls;
  311.     struct rinst *calledby;
  312.     } dlist[MAXINST];
  313.  
  314. /* list of names currently being gathered within a function */
  315. char aseen [MAXSEEN][ATOMLENGTH];
  316.  
  317. /* list of names currently being output */
  318. struct rname *activelist[MAXDEPTH];
  319.  
  320. /* free list pointer */
  321. struct rinst *frp = dlist;
  322.  
  323. extern int atoi();
  324. extern char *strcat(), *strcpy();
  325. extern int getopt();
  326. extern char *optarg;
  327. extern int optind;
  328. extern char *tmpnam();
  329. extern char *sys_errlist[];
  330. extern int sys_nerr;
  331.  
  332. /* forward declarations */
  333. struct rname *lookfor(), *place();
  334. struct rinst *newproc(), *getfree(), *install();
  335. char *syserrlist(), *addtocpp();
  336.  
  337. main(argc,argv)
  338. int argc;
  339. char *argv[];
  340. {
  341.     char cppcommand[5120];    /* 5120 is the max # chars on command line */
  342.     register char *cppptr = cppcommand;
  343.     char _dashes[1024];
  344.     register int c, i, width = PAPERWIDTH;
  345.  
  346.     progname = argv[0];
  347.     cppptr = addtocpp(cppptr, &cppcommand[5120], "cc -E", "");    /* /lib/cpp */
  348.     initfree();
  349.  
  350. /*
  351.     get arguments and flags:
  352.     -t    terse form  (default case)
  353.     -v    verbose form
  354.     -w nn    paper width  (default 132)
  355.     -f name    function to start printing from
  356.     arguments to pass on to CPP
  357.     -D def    #define def
  358.     -U def    #undef def
  359.     -I inc    #include inc
  360. */
  361.     while ((c = getopt (argc, argv, arglist)) != EOF)
  362.     switch (c)
  363.         {
  364.         case 't':    terse = 1;        break;
  365.         case 'v':    terse = 0;        break;
  366.         case 'f':    functionlist = 1;    break;
  367.         case 'w':
  368.         width = atoi(optarg);
  369.         if (width <= 0)
  370.             width = PAPERWIDTH;
  371.         break;
  372.         case 'I':
  373.         cppptr = addtocpp (cppptr, &cppcommand[5120], " -I", optarg);
  374.         break;
  375.         case 'D':
  376.         cppptr = addtocpp (cppptr, &cppcommand[5120], " -D", optarg);
  377.         break;
  378.         case 'U':
  379.         cppptr = addtocpp (cppptr, &cppcommand[5120], " -U", optarg);
  380.         break;
  381.         case '?':
  382.         (void) fprintf (stderr,
  383.             "usage: %s [-tv] [-f function] [-w width] [-D define] [-U undefine] [-I include-dir] [filenames]\n",
  384.             progname);
  385.         exit (1);
  386.         }
  387.  
  388.     /* initialize the dashed separator list for deep nesting */
  389.     ntabs = (width - 20) / 8;
  390.     for (i = 0; (i < width) && (i < 1024); i += 2)
  391.     {
  392.     _dashes[i] = '-';
  393.     _dashes[i+1] = ' ';
  394.     }
  395.     if (i < 1024)
  396.     _dashes[i] = '\0';
  397.     else
  398.     _dashes[1023] = '\0';
  399.     dashes = _dashes;
  400.  
  401.     scanfiles(argc, argv, cppcommand);
  402.     exit(0);
  403. }
  404.  
  405. /*
  406.     Add the given string onto the end of the CPP command string.
  407. */
  408.  
  409. char *
  410. addtocpp(cppptr, endptr, first, second)
  411. register char *cppptr, *endptr, *first, *second;
  412. {
  413.     while ((cppptr < endptr) && *first)
  414.     *cppptr++ = *first++;
  415.     while ((cppptr < endptr) && *second)
  416.     *cppptr++ = *second++;
  417.     *cppptr = '\0';
  418.     return cppptr;
  419. }
  420.  
  421. /*
  422.     Process() invokes the C preprocessor on the named file so that
  423.     its output may be used as input for scanning.
  424. */
  425.  
  426. process(cppcommand, filename)
  427. register char *cppcommand;
  428. register char *filename;
  429. {
  430.     char command[5120];
  431.     register int ret;
  432.  
  433.     if (access (filename, 04) != 0)
  434.     {
  435.     (void) fprintf (stderr, "%s: cannot open file '%s' (%s).\n",
  436.         progname, filename, syserrlist());
  437.     return;
  438.     }
  439.     sprintf (command, "%s %s", cppcommand, filename);
  440.     input = popen (command, "r");
  441.     if (input == NULL)
  442.     {
  443.     (void) fprintf (stderr,
  444.         "%s: fork of CPP command '%s' failed on file '%s' (%s).\n",
  445.         progname, command, filename, syserrlist());
  446.     return;
  447.     }
  448.     addfuncs();
  449.     ret = pclose(input);
  450.     if (ret != 0)
  451.     (void) fprintf (stderr,
  452.         "%s: CPP command '%s' failed on file '%s' with return code %d (%s).\n",
  453.         progname, command, filename, ret, syserrlist());
  454. }
  455.  
  456. char *
  457. syserrlist()
  458. {
  459.     register char *ret =
  460.     errno == 0 ?
  461.         "errno = 0" :
  462.     errno < sys_nerr ?
  463.         sys_errlist[errno] :
  464.         "errno out of range";
  465.     errno = 0;
  466.     return ret;
  467. }
  468.  
  469. /*
  470.     addfuncs() scans the input file for function names and adds them to
  471.     the calling list.
  472. */
  473.  
  474. addfuncs()
  475. {
  476.     register int ok = 1, internal;
  477.     char atom[ATOMLENGTH];
  478.     register struct rinst *curproc = 0;
  479.  
  480.     atom[0] = '\0';
  481.     while ((internal = getfunc(atom)) != -1 && ok )
  482.     if (internal)    ok = add2call(atom,curproc);
  483.     else        ok = (int)(curproc = newproc(atom));
  484. }
  485.  
  486. /*
  487.     Since CPP can't be piped into, dostandardinput() takes the standard
  488.     input and stuffs it into a file so that process() can work on it.
  489. */
  490.  
  491. dostandardinput(cppcommand)
  492. char *cppcommand;
  493. {
  494.     register int c;
  495.     register char *filename = tmpnam ((char *) 0);
  496.     register FILE *ofileptr = fopen (filename, "w");
  497.  
  498.     if (ofileptr == NULL)
  499.     {
  500.     (void) fprintf (stderr,
  501.         "%s: cannot open tempfile '%s' for writing (%s).\n",
  502.         progname, filename, syserrlist());
  503.     return;
  504.     }
  505.     while ( (c = getchar()) != EOF)
  506.     putc (c, ofileptr);
  507.     fclose (ofileptr);
  508.     process (cppcommand, filename);
  509.     unlink(filename);
  510. }
  511.  
  512. /* Scan the input files. */
  513. scanfiles(argc, argv, cppcommand)
  514. int argc;
  515. char **argv;
  516. char *cppcommand;
  517. {
  518.     /* Dumptree modifies optind, so use a local version here. */
  519.     register int loptind = optind;
  520.  
  521.     if (loptind >= argc)
  522.     {
  523.     dostandardinput(cppcommand);
  524.     dumptree(argc,argv);
  525.     }
  526.     else
  527.     {
  528.     for ( ; loptind < argc ; loptind++)
  529.         if (strcmp(argv[loptind], "-") == 0)
  530.         dostandardinput(cppcommand);
  531.         else
  532.         process(cppcommand,argv[loptind]);
  533.     dumptree(argc,argv);
  534.     }
  535. }
  536.  
  537. /*
  538.     Dumptree() lists out the calling stacks. All names will be listed out
  539.     unless some function names are specified in -f options.
  540. */
  541.  
  542. dumptree(argc,argv)
  543. int argc;
  544. char **argv;
  545. {
  546.     register int c;
  547.     register struct rname *startp;
  548.  
  549.     if (functionlist)
  550.     {
  551.     /* restart argument list and only print functions listed */
  552.     for (optind = 1 ; (c = getopt (argc, argv, arglist)) != EOF ; )
  553.         if (c == 'f')
  554.         if (startp = lookfor(optarg))
  555.             {
  556.             output (startp, 0);
  557.             printf ("\n\n");
  558.             }
  559.         else
  560.             (void) fprintf (stderr,
  561.             "%s: *** error *** function '%s' not found\n",
  562.             progname, optarg);
  563.     }
  564.     else
  565.     /* output everything */
  566.     for (startp = namelist ; startp->namer[0] ; startp++)
  567.         if (!startp->rnamecalled)
  568.         {
  569.         output (startp, 0);
  570.         printf ("\n\n");
  571.         }
  572. }
  573.  
  574. #define BACKSLASH '\\'
  575. #define QUOTE '\''
  576.  
  577. /*
  578.     getfunc() returns the name of a function in atom and
  579.     0 for a definition, 1 for an internal call
  580. */
  581.  
  582. getfunc(atom)
  583. char atom[];
  584. {
  585.     register int c, ss;
  586.  
  587.     for ( ; ; )
  588.     if (isalpha(c = getc(input)) || (c == '_'))
  589.         {
  590.         ungetc(c,input);
  591.         scan(atom);
  592.         continue;
  593.         }
  594.     else
  595.         switch(c)
  596.         {
  597.         case '\t':        /* white space */
  598.         case ' ':
  599.         case '\n':
  600.         case '\f':
  601.         case '\r':
  602.             continue;
  603.         case '#':        /* eat C compiler line control info */
  604.                     /* CPP output will not span lines */
  605.             while ((c= getc(input)) != '\n')
  606.             ;
  607.             ungetc(c,input);
  608.             continue;
  609.         case QUOTE:        /* character constant */
  610.             atom[0]='\0';
  611.             while ((c= getc(input)) != QUOTE)
  612.             if (c == BACKSLASH)
  613.                 getc(input);
  614.             continue;
  615.         case '\"':        /* string constant */
  616.             while (( c = getc(input)) != '\"')
  617.             if (c==BACKSLASH)
  618.                 getc(input);
  619.             continue;
  620.         case BACKSLASH:        /* ? why is this here ? */
  621.             atom[0] = '\0';
  622.             getc(input);
  623.             continue;
  624.         case '{':        /* start of a block */
  625.             bracket++;
  626.             atom[0]='\0';
  627.             continue;
  628.         case '}':        /* end of a block */
  629.             --bracket;
  630.             if (bracket < 0)
  631.             (void) fprintf (stderr, "%s: bracket underflow!\n",
  632.                 progname);
  633.             atom[0]='\0';
  634.             continue;
  635.         case '(':        /* parameter list for function? */
  636.             if( ! atom[0] )
  637.             continue;
  638.             if (!checksys(atom)) {
  639.             if (!bracket)
  640.                 if (checkinternal())
  641.                 return (0);
  642.                 else
  643.                 continue;
  644.             if ((ss = seen(atom)) == -1)
  645.                 (void) fprintf(stderr, "%s: aseen overflow!\n",
  646.                 progname);
  647.             if (bracket && !ss)
  648.                 return (1);
  649.             }
  650.             atom[0]='\0';
  651.             continue;
  652.         case EOF:        /* end of file */
  653.             return (-1);
  654.         case '/':        /* comment? */
  655.             if (( c = getc(input))=='*')
  656.             for (;;)
  657.                 {
  658.                 while (getc(input) != '*')
  659.                 ;
  660.                 if ((c = getc(input)) == '/')
  661.                 break;
  662.                 ungetc(c,input);
  663.                 }
  664.             else
  665.             ungetc(c,input);
  666.             continue;
  667.         case ')':        /* end of parameter list */
  668.         default:
  669.             atom[0]='\0';
  670.             continue;
  671.         }
  672. }
  673.  
  674. /*
  675.     Skipblanksandcomments() skips past any blanks and comments
  676.     in the input stream.
  677. */
  678.  
  679. skipblanksandcomments()
  680. {
  681.     register int c;
  682.  
  683.     for (c = getc(input);
  684.      (c == ' ') || (c == '\t') ||
  685.      (c == '\n') || (c == '\r') || (c == '\b') || (c == '\f') ||
  686.      (c == '/');
  687.      c = getc(input))
  688.     if (c == '/')
  689.         if ((c = getc(input)) == '*')    /* start of comment? */
  690.         for (;;)
  691.             {
  692.             while (getc(input) != '*')
  693.             ;
  694.             if ((c = getc(input)) == '/')
  695.             break;
  696.             ungetc(c,input);
  697.             }
  698.         else
  699.         {
  700.         ungetc(c,input);
  701.         return;
  702.         }
  703.     ungetc(c,input);
  704.     return;
  705. }
  706.  
  707. /*
  708.     checkinternal differentiates between an external declaration and
  709.     a real function definition. For instance, between:
  710.  
  711.     extern char *getenv(), *strcmp();
  712.  
  713.     and
  714.  
  715.     char *getenv(name)
  716.     char *name;
  717.     {}
  718.  
  719.     It does it by making the two observations that nothing (except blanks and
  720.     comments) can be between the parentheses of external calls nor between the
  721.     right parenthesis and the semi-colon or comma following the definition.
  722.     If the proposed ANSI standard is accepted, the first observation will no
  723.     longer be valid. We can still use the second observation, however. The 
  724.     code will have to be changed at that point.
  725. */
  726.  
  727. checkinternal()
  728. {
  729.     register int c;
  730.  
  731.     skipblanksandcomments();        /* skip blanks between parens */
  732.     c = getc(input);
  733.     if (c != ')')
  734.     {
  735.     ungetc(c,input);
  736.     return 1;
  737.     }
  738.     skipblanksandcomments();        /* skip blanks between paren and ; */
  739.     c = getc(input);
  740.     if (c == ';' || c == ',')
  741.     return 0;
  742.     ungetc(c,input);
  743.     return 1;
  744. }
  745.  
  746. /*
  747.     scan text until a function name is found
  748. */
  749.  
  750. scan (atom)
  751. char atom[];
  752. {
  753.     register int c, i = 0;
  754.  
  755.     for (c = getc(input);
  756.      (i < ATOMLENGTH) && isascii(c) &&
  757.      ( isalpha(c) || isdigit(c) || (c == '_') );
  758.      c = getc(input))
  759.     atom [i++] = c;
  760.     if (i == ATOMLENGTH)
  761.     atom [i-1] = '\0';
  762.     else
  763.     atom [i] = '\0';
  764.     while( isascii(c) && ( isalpha(c) || isdigit(c) || (c == '_') ))
  765.         c = getc(input);
  766.     ungetc(c,input);
  767. }
  768.  
  769. /*
  770.     checksys returns 1 if atom is a system keyword, else 0
  771. */
  772.  
  773. checksys (atom)
  774. char atom[];
  775. {
  776.     register int i;
  777.  
  778.     for (i=0; sysword[i] ; i++)
  779.     if (strcmp(atom,sysword[i]) == 0)
  780.         return (1);
  781.     return (0);
  782. }
  783.  
  784. /*
  785.     see if we have seen this function within this process
  786. */
  787.  
  788. seen (atom)
  789. char *atom;
  790. {
  791.     register int i, j;
  792.  
  793.     for (i=0; aseen[i][0] && i < MAXSEEN ; i++)
  794.     if (strcmp (atom, aseen[i]) == 0)
  795.         return (1);
  796.     if (i >= MAXSEEN)
  797.     return (-1);
  798.     for (j=0; (aseen[i][j] = atom[j]) != '\0' && j < ATOMLENGTH ; j++)
  799.     ;
  800.     aseen[i+1][0] = '\0';
  801.     return (0);
  802. }
  803.  
  804. /*
  805.     When scanning the text each function instance is inserted into a
  806.     linear list of names, using the rname structure, when it is first
  807.     encountered. It is also inserted into the linked list using the rinst
  808.     structure. The entry into the name list has a pointer to the defining
  809.     instance in the linked list, and each entry in the linked list has
  810.     a pointer back to the relevant name. Newproc makes an entry in the
  811.     defining list, which is distinguished from the called list only because
  812.     it has no calledby link (value=0). Add2proc enters into the called
  813.     list, by inserting a link to the new instance in the calls pointer of
  814.     the last entry (may be a defining instance, or a function called by
  815.     that defining instance), and points back to the defining instance of
  816.     the caller in its called-by pointer.
  817. */
  818.  
  819. struct rinst *
  820. newproc (name)
  821. char name[];
  822. {
  823.     aseen[0][0] = '\0';
  824.     return (install(place(name),(struct rinst *)0));
  825. }
  826.  
  827. /*
  828.     add the function name to the calling stack of the current function.
  829. */
  830.  
  831. add2call (name,curp)
  832. char name[];
  833. struct rinst *curp;
  834. {
  835.     register struct rname *p;
  836.     register struct rinst *ip;
  837.  
  838.     p = place (name);
  839.     ip = install (p, curp);
  840.     if (p && (strcmp(p->namer,curp->namep->namer) != 0))
  841.     p->rnamecalled = 1;
  842.     return (ip != (struct rinst *) 0);
  843. }
  844.  
  845. /*
  846.     place() returns a pointer to the name on the namelist.
  847.     If the name was not there, it puts it at the end of the list.
  848.     If there was no room, it returns -1.
  849. */
  850.  
  851. struct rname *
  852. place (name)
  853. char name[];
  854. {
  855.     register int i, j;
  856.     register struct rname *npt;
  857.  
  858.     for (i = 0 ; (npt = &namelist[i])->namer[0] && i<MAXNAME ; i++)
  859.     {
  860.     if (strcmp(name,npt->namer) == 0)
  861.         return (npt);
  862.     if (i >= MAXNAME)
  863.         {
  864.         (void) fprintf (stderr, "%s: namelist overflown!\n", progname);
  865.         return ( (struct rname *) -1);
  866.         }
  867.     }
  868.  
  869.     /* name was not on list, so put it on */
  870.     for (j=0 ; name[j]; j++)
  871.     npt->namer[j] = name[j];
  872.     npt->namer[j] = '\0';
  873.     (npt+1)->namer[0] = '\0';
  874.     npt->rnamecalled = 0;
  875.     npt->rnameout=0;
  876.     return (npt);
  877. }
  878.  
  879. /*
  880.     install (np,rp) puts a new instance of a function into the linked list.
  881.     It puts a pointer (np) to its own name (returned by place) into its
  882.     namepointer, a pointer to the calling routine (rp) into
  883.     its called-by pointer, and zero into the calls pointer. It then
  884.     puts a pointer to itself into the last function in the chain.
  885. */
  886.  
  887. struct rinst *
  888. install (np,rp)
  889. struct rname *np;
  890. struct rinst *rp;
  891. {
  892.     register struct rinst *newp;
  893.     register struct rinst *op;
  894.  
  895.     if (!np)
  896.     return ( (struct rinst *) -1);
  897.     if ( !(newp = getfree()))
  898.     return ( (struct rinst *) 0);
  899.     newp->namep = np;
  900.     newp->calls = 0;
  901.     if (rp)
  902.     {
  903.     op = rp;
  904.     while (op->calls) op = op->calls;
  905.     newp->calledby = op->calledby;
  906.     op->calls = newp;
  907.     }
  908.     else
  909.     {
  910.     newp->calledby = (struct rinst *) np;
  911.     np->dlistp = newp;
  912.     }
  913.     return (newp);
  914. }
  915.  
  916. /*
  917.     getfree returns a pointer to the next free instance block on the list
  918. */
  919.  
  920. struct rinst *
  921. getfree()
  922. {
  923.     register struct rinst *ret;
  924.  
  925.     ret = frp;
  926.     if (!ret)
  927.     (void) fprintf (stderr, "%s: out of instance blocks!\n", progname);
  928.     frp=frp->calls;
  929.     return (ret);
  930. }
  931.  
  932. /*
  933.     Initfree makes a linked list of instance blocks. It is called once,
  934.     at the beginning of the programme, and between files if the -s option
  935.     is specified.
  936. */
  937.  
  938. initfree()
  939. {
  940.     register int i;
  941.     register struct rinst *rp = dlist;
  942.  
  943.     for (i = 0 ; i < MAXINST-2 ; i++)
  944.     {
  945.     rp->namep = 0;
  946.     rp->calls = rp+1;
  947.     (rp+1)->calledby = rp;
  948.     rp++;
  949.     }
  950.     rp->namep=0;
  951.     rp->calls = 0;
  952. }
  953.  
  954. /*
  955.     output is a recursive routine which is supposed to print one tab for each
  956.     level of recursion, then the name of the function called, followed by the
  957.     next function called by the same higher level routine. In doing this, it
  958.     calls itself to output the name of the first function called by the
  959.     function whose name it is outputting. It maintains an active list of
  960.     functions currently being output by the different levels of recursion,
  961.     and if it finds itself asked to output one which is already active,
  962.     it terminates, marking that call with a '*'.
  963. */
  964.  
  965. output (func,tabc)
  966. struct rname *func;
  967. int tabc;
  968. {
  969.     register int i, tabd, tabstar, tflag;
  970.     struct rinst *nextp;
  971.  
  972.     ++linect;
  973.     printf ("\n%d", linect);
  974.     if (!makeactive(func))
  975.     printf ("   * nesting is too deep"); /* calls nested too deep */
  976.     else
  977.     {
  978.     tabstar= 0;
  979.     tabd = tabc;
  980.     for ( ; tabd > ntabs; tabstar++)
  981.         tabd = tabd - ntabs;
  982.     if (tabstar > 0)
  983.         {
  984.         printf ("  ");
  985.         for (i = 0 ; i < tabstar ; i++ )
  986.         printf ("<");
  987.         }
  988.     if (tabd == 0)
  989.         printf ("   ");
  990.     else
  991.         for (i = 0 ; i < tabd ; i++ )
  992.         printf ("\t");
  993.     if (active(func))
  994.         printf ("<<< %s",func->namer); /* recursive call */
  995.     else
  996.         {
  997.         if (func->dlistp)
  998.         {
  999.         printf ("%s", func->namer);
  1000.         nextp = func->dlistp->calls;
  1001.         if (!terse || !func->rnameout)
  1002.             {
  1003.             ++tabc;
  1004.             if (!func->rnameout)
  1005.             func->rnameout = linect;
  1006.             if (tabc > ntabs && tabc%ntabs==1 && nextp)
  1007.             {
  1008.             printf("\n%s", dashes);
  1009.             tflag = 1;
  1010.             }
  1011.             else
  1012.             tflag = 0;
  1013.             for ( ; nextp; nextp = nextp->calls)
  1014.             output (nextp->namep, tabc);
  1015.             if (tflag)
  1016.             {
  1017.             printf("\n%s", dashes);
  1018.             tflag = 0;
  1019.             }
  1020.             }
  1021.         else if (nextp)
  1022.             printf (" ... [see line %d]", func->rnameout);
  1023.         }
  1024.         else
  1025.         printf ("%s [external]",func->namer); /* library or external call */
  1026.         }
  1027.     backup ();
  1028.     }
  1029.     return;
  1030. }
  1031.  
  1032. /*
  1033.     makeactive simply puts a pointer to the nameblock into a stack with
  1034.     maximum depth MAXDEPTH. the error return only happens for stack overflow.
  1035. */
  1036.  
  1037. makeactive (func)
  1038. struct rname *func;
  1039. {
  1040.     if (activep < MAXDEPTH)
  1041.     {
  1042.     activelist[activep] = func;
  1043.     activep++;
  1044.     return (1);
  1045.     }
  1046.     return (0);
  1047. }
  1048.  
  1049. /*
  1050.     backup removes an item from the active stack
  1051. */
  1052.  
  1053. backup()
  1054. {
  1055.     if (activep)
  1056.     activelist [activep--] = 0;
  1057. }
  1058.  
  1059. /*
  1060.     active checks whether the pointer which is its argument has already
  1061.     occurred on the active list, and returns 1 if so.
  1062. */
  1063.  
  1064. active (func)
  1065. register struct rname *func;
  1066. {
  1067.     register int i;
  1068.  
  1069.     for (i = 0; i < activep-1 ; i++)
  1070.     if (func == activelist[i])
  1071.         return (1);
  1072.     return (0);
  1073. }
  1074.  
  1075. /*
  1076.     lookup (name) accepts a pointer to a name and sees if the name is on the
  1077.     namelist. If so, it returns a pointer to the nameblock. Otherwise it
  1078.     returns zero. If the name from argv is > ATOMLENGTH-1, then it is
  1079.     truncated.
  1080. */
  1081.  
  1082. struct rname *
  1083. lookfor(name)
  1084. register char *name;
  1085. {
  1086.     register struct rname *np;
  1087.  
  1088.     if (strlen(name) >= ATOMLENGTH)
  1089.     name[ATOMLENGTH] = '\0';
  1090.  
  1091.     for (np = namelist; np->namer[0] ; np++)
  1092.     if (strcmp (name, np->namer) == 0)
  1093.         return (np);
  1094.     return (0);
  1095. }
  1096. ~FUNKY STUFF~
  1097. ls -l calls.c
  1098. echo x - Makefile
  1099. sed 's/^X//' > Makefile << '~FUNKY STUFF~'
  1100. #    use 'make' to compile calls
  1101. #    use 'make install' to compile and install calls and install the man page
  1102. #    use 'make clean' to clean up the directory
  1103.  
  1104. # CFLAGS = -g
  1105. CFLAGS = -O -p
  1106. DESTDIR = /u/ksbszabo/bin
  1107. DOCDIR = /u/ksbszabo/man/man1
  1108.  
  1109. calls:    calls.c
  1110.     $(CC) $(CFLAGS) calls.c -o calls
  1111.  
  1112. lint:
  1113.     lint calls.c
  1114.  
  1115. ctrace:
  1116.     ctrace -p'fprintf(stderr,' calls.c > /tmp/calls.c
  1117.     cc /tmp/calls.c -o ctcalls
  1118.  
  1119. everything: calls install clean
  1120.  
  1121. $(DESTDIR)/calls: calls.c
  1122.     $(CC) $(CFLAGS) -s calls.c -o $(DESTDIR)/calls
  1123.  
  1124. install:    calls
  1125.     cp calls $(DESTDIR)/calls
  1126.     strip $(DESTDIR)/calls
  1127.     cp calls.1 $(DOCDIR)/calls.1
  1128.  
  1129. clean:
  1130.     rm calls
  1131. ~FUNKY STUFF~
  1132. ls -l Makefile
  1133. # The following exit is to ensure that extra garbage 
  1134. # after the end of the shar file will be ignored.
  1135. exit 0
  1136.  
  1137.  
  1138.  
  1139.