home *** CD-ROM | disk | FTP | other *** search
- From: talcott!seismo!cbosgd!pegasus!hansen
- Subject: calls(1)
- Newsgroups: mod.sources
- Approved: jpn@panda.UUCP
-
- Mod.sources: Volume 3, Issue 47
- Submitted by: cbosgd!pegasus!hansen
-
-
- #
- # In 1983, the calls program was given to the net by
- # M. Taylor from DCIEM. Subsequent hacking by various
- # people produced a version posted in 1984. About that
- # time, I started using it heavily, found some things
- # lacking in it, and began fixing/reworking a number
- # of bugs/features. The current version has been in use
- # for over a year now with no further changes, so it is time
- # to give it back out to the net for further use and abuse.
- #
- # Tony Hansen
- # AT&T-IS
- # ihnp4!pegasus!hansen
-
- # Note that this version assumes that "cc -E" is available
- # to access the C preprocessor. It also assumes that getopt(3)
- # is available, which most people should have by now. (If not,
- # write.)
-
- #!/bin/sh
- # This is a shar archive.
- # The rest of this file is a shell script which will extract:
- # calls.1 calls.c Makefile
- # Archive created: Sat Nov 16 11:54:18 EST 1985
- echo x - calls.1
- sed 's/^X//' > calls.1 << '~FUNKY STUFF~'
- X.TH CALLS 1 PUBLIC
- X.SH NAME
- calls \- print out calling pattern of functions in a program
- X.SH SYNOPSIS
- calls [-tvs] [-w n] [-f function] [-D def] [-U def] [-I dir] [filenames]
- X.SH DESCRIPTION
- X.B Calls
- reads
- X.IR filenames ,
- which should be the source of C programmes, and outputs the analysed calling
- pattern to standard output.
- If no filenames are given, or a filename of \- is seen, standard input will
- be read.
- X.B Calls
- is intended to help analyse the flow of a programme by laying out the
- functions called in the heirarchical manner used in "Software Tools" by
- B. Kernighan and P. Plauger.
- X.P
- Functions called but not defined within the source file are shown as:
- X.sp
- X.RS
- function [external]
- X.RE
- X.P
- Recursive references are shown as:
- X.sp
- X.RS
- <<< function
- X.RE
- X.P
- For example, given the file
- X.B programme.c
- in which
- X.I main
- calls
- X.I abc
- and
- X.IR def ,
- X.I abc
- calls
- X.I ghi
- and
- X.IR jkl ,
- which are defined within the same source file, and
- X.I def
- calls
- X.IR mno ,
- defined in the same source, and
- X.IR pqr ,
- which is presumably a library function or defined in a different source file.
- The function
- X.I ghi
- calls
- X.I abc
- in a recursive loop.
- X.sp
- X.RS
- X.nf
- main() {
- abc();
- def();
- }
- abc() {
- ghi();
- jkl();
- }
- def() {
- mno();
- pqr();
- }
- ghi() {
- abc();
- }
- jkl() { }
- mno() { }
- X.fi
- X.RE
- X.sp
- Executing "calls programme.c" will produce:
- X.sp
- X.RS
- X.nf
- 1 main
- 2 abc
- 3 ghi
- 4 <<< abc
- 5 jkl
- 6 def
- 7 mno
- 8 pqr [external]
- X.fi
- X.RE
- X.SS FLAGS
- X.TP 20
- -t
- Provides a terse form of output, in which the calling pattern for any
- function is printed only once on the first occurrence of the function.
- Subsequent occurrences output the function name and a notation
- X.IP "" 30
- \|... [see line xx]
- X.IP "" 20
- This is the default case.
- X.TP 20
- -v
- Full output of function calling patterns on every occurrence.
- X.TP 20
- X.BI -w nn
- Set the output paper width to nn.
- The default is 132 columns.
- X.TP 20
- -s
- Normally all filenames given will have their calling sequences combined into
- one heirarchy.
- This option will force the calling heirarchies to be separated.
- The filename for each file will be printed before the calling pattern.
- X.TP 20
- X.BI -D name
- X.TP 20
- X.BI -D name=defn
- Define the
- X.I name
- for the preprocessor, as if by #define.
- If no definition is given, the name is defined as 1.
- X.TP 20
- X.BI -U name
- Remove any initial definition of
- X.IR name ,
- where
- X.I name
- is a reserved symbol that is predefined by the preprocessor.
- X.TP 20
- X.BI -I dir
- Change the algorithm for searching for #include files whose names do not
- begin with / to look in
- X.I dir
- before looking in the directories on the standard list.
- X.TP 20
- X.BI -f name
- Function names within the input programme may be selected as
- roots of the layout.
- For example, using the previous programme:
- X.sp
- X.RS
- calls -f def -f abc programme.c
- X.sp
- X.nf
- 1 def
- 2 mno
- 3 pqr [external]
-
-
- 4 abc
- 5 ghi
- 6 <<< abc
- 7 jkl
- X.fi
- X.RE
- X.SH AUTHOR
- M. M. Taylor (DCIEM)
- X.br
- Modified for V7 and stdio, Alexis Kwan (HCR for DCIEM)
- X.br
- Fixed bugs with '_' and variable names, names > ATOM_LENGTH chars.
- 12-Jun-84, Kevin Szabo,
- watmath!wateng!ksbszabo (Elec Eng, U of Waterloo).
- X.br
- Many other bug fixes and features, Tony Hansen (ihnp4!pegasus!hansen)
- X.SH BUGS
- Forward declared functions defined within a function body which are not
- subsequently used within that function body will be listed as having been
- called.
- X.ig
- Many intended features are not implemented:
- flags -g (list globals used), and -F and -P (Fortran and Pascal languages).
- X..
- ~FUNKY STUFF~
- ls -l calls.1
- echo x - calls.c
- sed 's/^X//' > calls.c << '~FUNKY STUFF~'
- /*
- * calls: calls prints a paragraphed list of who calls whom within
- * a body of source code.
- *
- * Author: M.M. Taylor, DCIEM, Toronto, Canada.
- * 22/Jan/81, Alexis Kwan (HCR at DCIEM).
- * Modified for V7 and stdio,
- * 12-Jun-84, Kevin Szabo
- * watmath!wateng!ksbszabo (Elec Eng, U of Waterloo)
- * Fixed bugs with '_' and variable names, names > ATOM_LENGTH chars.
- * 8/8/84, Tony Hansen, AT&T-IS, pegasus!hansen.
- * Modified to use getopt,
- * files are passed through CPP with "cc -E"
- * multiple filenames and '-' are allowed on the command line,
- * added -D, -U, -I and -f options,
- * (CPP prefers filenames rather than stdin. To make this
- * easier and faster, filenames needed to be allowed on the
- * command line. This conflicted with the old usage of
- * specifying function names on the command line, unfortunately.
- * The -f option was added to put that capability back. Passing
- * things through the CPP has the advantage of not picking up
- * macros as function calls and avoids all hassles with the
- * strangeness that can be done with the CPP. Also, it allows
- * the addition of the -D, -U and -I options.)
- * portable to unsigned char machines,
- * handle 31 chars in variable names
- * (chosen over flexnames because of the pending ANSI standards),
- * fixed bug with some keywords tagged as function names,
- * fixed bug with '_' at beginning of variable name,
- * fixed bug scanning file with CPP statement in the first line,
- * skips forward declarations external to a function body,
- * fixed bug to print out functions which are defined and
- * recursive to themselves, but aren't called elsewhere
- * within the same file,
- * added comments in many places,
- * did more de-linting
- * (I didn't cast functions to void because earlier
- * compilers didn't have it, nor declare exit() because
- * different versions of UNIX declare it differently),
- * rearranged structures to minimize padding,
- * dashes between deeply nested lists now vary with paper width,
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <errno.h>
-
- #define ATOMLENGTH 32 /* max size of name is 31 chars */
- #define MAXNAME 500 /* # of names to be followed */
- #define MAXINST 4000 /* # of instances of those names */
- #define MAXSEEN 100 /* # of instances w/in a function */
- #define MAXDEPTH 25 /* max output depth level */
- #define PAPERWIDTH 132 /* limits tabbing */
-
- int bracket = 0, /* curly brace count */
- linect = 0; /* line number */
- int activep = 0; /* current function being output */
-
- /* options */
- int terse = 1, /* track functions only once */
- ntabs = (PAPERWIDTH - 20)/8, /* how wide to go */
- functionlist = 0; /* restrict to functions listed */
-
- char *progname; /* argv[0] */
- FILE *input; /* open file */
- char *arglist = "tvw:f:D:U:I:"; /* valid options */
- char *dashes; /* separators for deep nestings */
-
- /*
- These are C tokens after which a parenthesis is valid
- which would otherwise be tagged as function names. The
- reserved words which are not listed are break, continue,
- default and goto.
- */
-
- char *sysword [] = {
- "auto", "case", "char", "do", "double", "else",
- "entry", "enum", "extern", "float", "for", "fortran",
- "if", "int", "long", "register", "return", "short",
- "sizeof", "static", "struct", "switch", "typedef",
- "union", "unsigned", "void", "while",
- 0
- };
-
- /* list of names being tracked */
- struct rname {
- struct rinst *dlistp;
- int rnamecalled;
- int rnameout;
- char namer[ATOMLENGTH];
- } namelist[MAXNAME];
-
- /* list of calling instances of those names */
- struct rinst {
- struct rname *namep;
- struct rinst *calls;
- struct rinst *calledby;
- } dlist[MAXINST];
-
- /* list of names currently being gathered within a function */
- char aseen [MAXSEEN][ATOMLENGTH];
-
- /* list of names currently being output */
- struct rname *activelist[MAXDEPTH];
-
- /* free list pointer */
- struct rinst *frp = dlist;
-
- extern int atoi();
- extern char *strcat(), *strcpy();
- extern int getopt();
- extern char *optarg;
- extern int optind;
- extern char *tmpnam();
- extern char *sys_errlist[];
- extern int sys_nerr;
-
- /* forward declarations */
- struct rname *lookfor(), *place();
- struct rinst *newproc(), *getfree(), *install();
- char *syserrlist(), *addtocpp();
-
- main(argc,argv)
- int argc;
- char *argv[];
- {
- char cppcommand[5120]; /* 5120 is the max # chars on command line */
- register char *cppptr = cppcommand;
- char _dashes[1024];
- register int c, i, width = PAPERWIDTH;
-
- progname = argv[0];
- cppptr = addtocpp(cppptr, &cppcommand[5120], "cc -E", ""); /* /lib/cpp */
- initfree();
-
- /*
- get arguments and flags:
- -t terse form (default case)
- -v verbose form
- -w nn paper width (default 132)
- -f name function to start printing from
- arguments to pass on to CPP
- -D def #define def
- -U def #undef def
- -I inc #include inc
- */
- while ((c = getopt (argc, argv, arglist)) != EOF)
- switch (c)
- {
- case 't': terse = 1; break;
- case 'v': terse = 0; break;
- case 'f': functionlist = 1; break;
- case 'w':
- width = atoi(optarg);
- if (width <= 0)
- width = PAPERWIDTH;
- break;
- case 'I':
- cppptr = addtocpp (cppptr, &cppcommand[5120], " -I", optarg);
- break;
- case 'D':
- cppptr = addtocpp (cppptr, &cppcommand[5120], " -D", optarg);
- break;
- case 'U':
- cppptr = addtocpp (cppptr, &cppcommand[5120], " -U", optarg);
- break;
- case '?':
- (void) fprintf (stderr,
- "usage: %s [-tv] [-f function] [-w width] [-D define] [-U undefine] [-I include-dir] [filenames]\n",
- progname);
- exit (1);
- }
-
- /* initialize the dashed separator list for deep nesting */
- ntabs = (width - 20) / 8;
- for (i = 0; (i < width) && (i < 1024); i += 2)
- {
- _dashes[i] = '-';
- _dashes[i+1] = ' ';
- }
- if (i < 1024)
- _dashes[i] = '\0';
- else
- _dashes[1023] = '\0';
- dashes = _dashes;
-
- scanfiles(argc, argv, cppcommand);
- exit(0);
- }
-
- /*
- Add the given string onto the end of the CPP command string.
- */
-
- char *
- addtocpp(cppptr, endptr, first, second)
- register char *cppptr, *endptr, *first, *second;
- {
- while ((cppptr < endptr) && *first)
- *cppptr++ = *first++;
- while ((cppptr < endptr) && *second)
- *cppptr++ = *second++;
- *cppptr = '\0';
- return cppptr;
- }
-
- /*
- Process() invokes the C preprocessor on the named file so that
- its output may be used as input for scanning.
- */
-
- process(cppcommand, filename)
- register char *cppcommand;
- register char *filename;
- {
- char command[5120];
- register int ret;
-
- if (access (filename, 04) != 0)
- {
- (void) fprintf (stderr, "%s: cannot open file '%s' (%s).\n",
- progname, filename, syserrlist());
- return;
- }
- sprintf (command, "%s %s", cppcommand, filename);
- input = popen (command, "r");
- if (input == NULL)
- {
- (void) fprintf (stderr,
- "%s: fork of CPP command '%s' failed on file '%s' (%s).\n",
- progname, command, filename, syserrlist());
- return;
- }
- addfuncs();
- ret = pclose(input);
- if (ret != 0)
- (void) fprintf (stderr,
- "%s: CPP command '%s' failed on file '%s' with return code %d (%s).\n",
- progname, command, filename, ret, syserrlist());
- }
-
- char *
- syserrlist()
- {
- register char *ret =
- errno == 0 ?
- "errno = 0" :
- errno < sys_nerr ?
- sys_errlist[errno] :
- "errno out of range";
- errno = 0;
- return ret;
- }
-
- /*
- addfuncs() scans the input file for function names and adds them to
- the calling list.
- */
-
- addfuncs()
- {
- register int ok = 1, internal;
- char atom[ATOMLENGTH];
- register struct rinst *curproc = 0;
-
- atom[0] = '\0';
- while ((internal = getfunc(atom)) != -1 && ok )
- if (internal) ok = add2call(atom,curproc);
- else ok = (int)(curproc = newproc(atom));
- }
-
- /*
- Since CPP can't be piped into, dostandardinput() takes the standard
- input and stuffs it into a file so that process() can work on it.
- */
-
- dostandardinput(cppcommand)
- char *cppcommand;
- {
- register int c;
- register char *filename = tmpnam ((char *) 0);
- register FILE *ofileptr = fopen (filename, "w");
-
- if (ofileptr == NULL)
- {
- (void) fprintf (stderr,
- "%s: cannot open tempfile '%s' for writing (%s).\n",
- progname, filename, syserrlist());
- return;
- }
- while ( (c = getchar()) != EOF)
- putc (c, ofileptr);
- fclose (ofileptr);
- process (cppcommand, filename);
- unlink(filename);
- }
-
- /* Scan the input files. */
- scanfiles(argc, argv, cppcommand)
- int argc;
- char **argv;
- char *cppcommand;
- {
- /* Dumptree modifies optind, so use a local version here. */
- register int loptind = optind;
-
- if (loptind >= argc)
- {
- dostandardinput(cppcommand);
- dumptree(argc,argv);
- }
- else
- {
- for ( ; loptind < argc ; loptind++)
- if (strcmp(argv[loptind], "-") == 0)
- dostandardinput(cppcommand);
- else
- process(cppcommand,argv[loptind]);
- dumptree(argc,argv);
- }
- }
-
- /*
- Dumptree() lists out the calling stacks. All names will be listed out
- unless some function names are specified in -f options.
- */
-
- dumptree(argc,argv)
- int argc;
- char **argv;
- {
- register int c;
- register struct rname *startp;
-
- if (functionlist)
- {
- /* restart argument list and only print functions listed */
- for (optind = 1 ; (c = getopt (argc, argv, arglist)) != EOF ; )
- if (c == 'f')
- if (startp = lookfor(optarg))
- {
- output (startp, 0);
- printf ("\n\n");
- }
- else
- (void) fprintf (stderr,
- "%s: *** error *** function '%s' not found\n",
- progname, optarg);
- }
- else
- /* output everything */
- for (startp = namelist ; startp->namer[0] ; startp++)
- if (!startp->rnamecalled)
- {
- output (startp, 0);
- printf ("\n\n");
- }
- }
-
- #define BACKSLASH '\\'
- #define QUOTE '\''
-
- /*
- getfunc() returns the name of a function in atom and
- 0 for a definition, 1 for an internal call
- */
-
- getfunc(atom)
- char atom[];
- {
- register int c, ss;
-
- for ( ; ; )
- if (isalpha(c = getc(input)) || (c == '_'))
- {
- ungetc(c,input);
- scan(atom);
- continue;
- }
- else
- switch(c)
- {
- case '\t': /* white space */
- case ' ':
- case '\n':
- case '\f':
- case '\r':
- continue;
- case '#': /* eat C compiler line control info */
- /* CPP output will not span lines */
- while ((c= getc(input)) != '\n')
- ;
- ungetc(c,input);
- continue;
- case QUOTE: /* character constant */
- atom[0]='\0';
- while ((c= getc(input)) != QUOTE)
- if (c == BACKSLASH)
- getc(input);
- continue;
- case '\"': /* string constant */
- while (( c = getc(input)) != '\"')
- if (c==BACKSLASH)
- getc(input);
- continue;
- case BACKSLASH: /* ? why is this here ? */
- atom[0] = '\0';
- getc(input);
- continue;
- case '{': /* start of a block */
- bracket++;
- atom[0]='\0';
- continue;
- case '}': /* end of a block */
- --bracket;
- if (bracket < 0)
- (void) fprintf (stderr, "%s: bracket underflow!\n",
- progname);
- atom[0]='\0';
- continue;
- case '(': /* parameter list for function? */
- if( ! atom[0] )
- continue;
- if (!checksys(atom)) {
- if (!bracket)
- if (checkinternal())
- return (0);
- else
- continue;
- if ((ss = seen(atom)) == -1)
- (void) fprintf(stderr, "%s: aseen overflow!\n",
- progname);
- if (bracket && !ss)
- return (1);
- }
- atom[0]='\0';
- continue;
- case EOF: /* end of file */
- return (-1);
- case '/': /* comment? */
- if (( c = getc(input))=='*')
- for (;;)
- {
- while (getc(input) != '*')
- ;
- if ((c = getc(input)) == '/')
- break;
- ungetc(c,input);
- }
- else
- ungetc(c,input);
- continue;
- case ')': /* end of parameter list */
- default:
- atom[0]='\0';
- continue;
- }
- }
-
- /*
- Skipblanksandcomments() skips past any blanks and comments
- in the input stream.
- */
-
- skipblanksandcomments()
- {
- register int c;
-
- for (c = getc(input);
- (c == ' ') || (c == '\t') ||
- (c == '\n') || (c == '\r') || (c == '\b') || (c == '\f') ||
- (c == '/');
- c = getc(input))
- if (c == '/')
- if ((c = getc(input)) == '*') /* start of comment? */
- for (;;)
- {
- while (getc(input) != '*')
- ;
- if ((c = getc(input)) == '/')
- break;
- ungetc(c,input);
- }
- else
- {
- ungetc(c,input);
- return;
- }
- ungetc(c,input);
- return;
- }
-
- /*
- checkinternal differentiates between an external declaration and
- a real function definition. For instance, between:
-
- extern char *getenv(), *strcmp();
-
- and
-
- char *getenv(name)
- char *name;
- {}
-
- It does it by making the two observations that nothing (except blanks and
- comments) can be between the parentheses of external calls nor between the
- right parenthesis and the semi-colon or comma following the definition.
- If the proposed ANSI standard is accepted, the first observation will no
- longer be valid. We can still use the second observation, however. The
- code will have to be changed at that point.
- */
-
- checkinternal()
- {
- register int c;
-
- skipblanksandcomments(); /* skip blanks between parens */
- c = getc(input);
- if (c != ')')
- {
- ungetc(c,input);
- return 1;
- }
- skipblanksandcomments(); /* skip blanks between paren and ; */
- c = getc(input);
- if (c == ';' || c == ',')
- return 0;
- ungetc(c,input);
- return 1;
- }
-
- /*
- scan text until a function name is found
- */
-
- scan (atom)
- char atom[];
- {
- register int c, i = 0;
-
- for (c = getc(input);
- (i < ATOMLENGTH) && isascii(c) &&
- ( isalpha(c) || isdigit(c) || (c == '_') );
- c = getc(input))
- atom [i++] = c;
- if (i == ATOMLENGTH)
- atom [i-1] = '\0';
- else
- atom [i] = '\0';
- while( isascii(c) && ( isalpha(c) || isdigit(c) || (c == '_') ))
- c = getc(input);
- ungetc(c,input);
- }
-
- /*
- checksys returns 1 if atom is a system keyword, else 0
- */
-
- checksys (atom)
- char atom[];
- {
- register int i;
-
- for (i=0; sysword[i] ; i++)
- if (strcmp(atom,sysword[i]) == 0)
- return (1);
- return (0);
- }
-
- /*
- see if we have seen this function within this process
- */
-
- seen (atom)
- char *atom;
- {
- register int i, j;
-
- for (i=0; aseen[i][0] && i < MAXSEEN ; i++)
- if (strcmp (atom, aseen[i]) == 0)
- return (1);
- if (i >= MAXSEEN)
- return (-1);
- for (j=0; (aseen[i][j] = atom[j]) != '\0' && j < ATOMLENGTH ; j++)
- ;
- aseen[i+1][0] = '\0';
- return (0);
- }
-
- /*
- When scanning the text each function instance is inserted into a
- linear list of names, using the rname structure, when it is first
- encountered. It is also inserted into the linked list using the rinst
- structure. The entry into the name list has a pointer to the defining
- instance in the linked list, and each entry in the linked list has
- a pointer back to the relevant name. Newproc makes an entry in the
- defining list, which is distinguished from the called list only because
- it has no calledby link (value=0). Add2proc enters into the called
- list, by inserting a link to the new instance in the calls pointer of
- the last entry (may be a defining instance, or a function called by
- that defining instance), and points back to the defining instance of
- the caller in its called-by pointer.
- */
-
- struct rinst *
- newproc (name)
- char name[];
- {
- aseen[0][0] = '\0';
- return (install(place(name),(struct rinst *)0));
- }
-
- /*
- add the function name to the calling stack of the current function.
- */
-
- add2call (name,curp)
- char name[];
- struct rinst *curp;
- {
- register struct rname *p;
- register struct rinst *ip;
-
- p = place (name);
- ip = install (p, curp);
- if (p && (strcmp(p->namer,curp->namep->namer) != 0))
- p->rnamecalled = 1;
- return (ip != (struct rinst *) 0);
- }
-
- /*
- place() returns a pointer to the name on the namelist.
- If the name was not there, it puts it at the end of the list.
- If there was no room, it returns -1.
- */
-
- struct rname *
- place (name)
- char name[];
- {
- register int i, j;
- register struct rname *npt;
-
- for (i = 0 ; (npt = &namelist[i])->namer[0] && i<MAXNAME ; i++)
- {
- if (strcmp(name,npt->namer) == 0)
- return (npt);
- if (i >= MAXNAME)
- {
- (void) fprintf (stderr, "%s: namelist overflown!\n", progname);
- return ( (struct rname *) -1);
- }
- }
-
- /* name was not on list, so put it on */
- for (j=0 ; name[j]; j++)
- npt->namer[j] = name[j];
- npt->namer[j] = '\0';
- (npt+1)->namer[0] = '\0';
- npt->rnamecalled = 0;
- npt->rnameout=0;
- return (npt);
- }
-
- /*
- install (np,rp) puts a new instance of a function into the linked list.
- It puts a pointer (np) to its own name (returned by place) into its
- namepointer, a pointer to the calling routine (rp) into
- its called-by pointer, and zero into the calls pointer. It then
- puts a pointer to itself into the last function in the chain.
- */
-
- struct rinst *
- install (np,rp)
- struct rname *np;
- struct rinst *rp;
- {
- register struct rinst *newp;
- register struct rinst *op;
-
- if (!np)
- return ( (struct rinst *) -1);
- if ( !(newp = getfree()))
- return ( (struct rinst *) 0);
- newp->namep = np;
- newp->calls = 0;
- if (rp)
- {
- op = rp;
- while (op->calls) op = op->calls;
- newp->calledby = op->calledby;
- op->calls = newp;
- }
- else
- {
- newp->calledby = (struct rinst *) np;
- np->dlistp = newp;
- }
- return (newp);
- }
-
- /*
- getfree returns a pointer to the next free instance block on the list
- */
-
- struct rinst *
- getfree()
- {
- register struct rinst *ret;
-
- ret = frp;
- if (!ret)
- (void) fprintf (stderr, "%s: out of instance blocks!\n", progname);
- frp=frp->calls;
- return (ret);
- }
-
- /*
- Initfree makes a linked list of instance blocks. It is called once,
- at the beginning of the programme, and between files if the -s option
- is specified.
- */
-
- initfree()
- {
- register int i;
- register struct rinst *rp = dlist;
-
- for (i = 0 ; i < MAXINST-2 ; i++)
- {
- rp->namep = 0;
- rp->calls = rp+1;
- (rp+1)->calledby = rp;
- rp++;
- }
- rp->namep=0;
- rp->calls = 0;
- }
-
- /*
- output is a recursive routine which is supposed to print one tab for each
- level of recursion, then the name of the function called, followed by the
- next function called by the same higher level routine. In doing this, it
- calls itself to output the name of the first function called by the
- function whose name it is outputting. It maintains an active list of
- functions currently being output by the different levels of recursion,
- and if it finds itself asked to output one which is already active,
- it terminates, marking that call with a '*'.
- */
-
- output (func,tabc)
- struct rname *func;
- int tabc;
- {
- register int i, tabd, tabstar, tflag;
- struct rinst *nextp;
-
- ++linect;
- printf ("\n%d", linect);
- if (!makeactive(func))
- printf (" * nesting is too deep"); /* calls nested too deep */
- else
- {
- tabstar= 0;
- tabd = tabc;
- for ( ; tabd > ntabs; tabstar++)
- tabd = tabd - ntabs;
- if (tabstar > 0)
- {
- printf (" ");
- for (i = 0 ; i < tabstar ; i++ )
- printf ("<");
- }
- if (tabd == 0)
- printf (" ");
- else
- for (i = 0 ; i < tabd ; i++ )
- printf ("\t");
- if (active(func))
- printf ("<<< %s",func->namer); /* recursive call */
- else
- {
- if (func->dlistp)
- {
- printf ("%s", func->namer);
- nextp = func->dlistp->calls;
- if (!terse || !func->rnameout)
- {
- ++tabc;
- if (!func->rnameout)
- func->rnameout = linect;
- if (tabc > ntabs && tabc%ntabs==1 && nextp)
- {
- printf("\n%s", dashes);
- tflag = 1;
- }
- else
- tflag = 0;
- for ( ; nextp; nextp = nextp->calls)
- output (nextp->namep, tabc);
- if (tflag)
- {
- printf("\n%s", dashes);
- tflag = 0;
- }
- }
- else if (nextp)
- printf (" ... [see line %d]", func->rnameout);
- }
- else
- printf ("%s [external]",func->namer); /* library or external call */
- }
- backup ();
- }
- return;
- }
-
- /*
- makeactive simply puts a pointer to the nameblock into a stack with
- maximum depth MAXDEPTH. the error return only happens for stack overflow.
- */
-
- makeactive (func)
- struct rname *func;
- {
- if (activep < MAXDEPTH)
- {
- activelist[activep] = func;
- activep++;
- return (1);
- }
- return (0);
- }
-
- /*
- backup removes an item from the active stack
- */
-
- backup()
- {
- if (activep)
- activelist [activep--] = 0;
- }
-
- /*
- active checks whether the pointer which is its argument has already
- occurred on the active list, and returns 1 if so.
- */
-
- active (func)
- register struct rname *func;
- {
- register int i;
-
- for (i = 0; i < activep-1 ; i++)
- if (func == activelist[i])
- return (1);
- return (0);
- }
-
- /*
- lookup (name) accepts a pointer to a name and sees if the name is on the
- namelist. If so, it returns a pointer to the nameblock. Otherwise it
- returns zero. If the name from argv is > ATOMLENGTH-1, then it is
- truncated.
- */
-
- struct rname *
- lookfor(name)
- register char *name;
- {
- register struct rname *np;
-
- if (strlen(name) >= ATOMLENGTH)
- name[ATOMLENGTH] = '\0';
-
- for (np = namelist; np->namer[0] ; np++)
- if (strcmp (name, np->namer) == 0)
- return (np);
- return (0);
- }
- ~FUNKY STUFF~
- ls -l calls.c
- echo x - Makefile
- sed 's/^X//' > Makefile << '~FUNKY STUFF~'
- # use 'make' to compile calls
- # use 'make install' to compile and install calls and install the man page
- # use 'make clean' to clean up the directory
-
- # CFLAGS = -g
- CFLAGS = -O -p
- DESTDIR = /u/ksbszabo/bin
- DOCDIR = /u/ksbszabo/man/man1
-
- calls: calls.c
- $(CC) $(CFLAGS) calls.c -o calls
-
- lint:
- lint calls.c
-
- ctrace:
- ctrace -p'fprintf(stderr,' calls.c > /tmp/calls.c
- cc /tmp/calls.c -o ctcalls
-
- everything: calls install clean
-
- $(DESTDIR)/calls: calls.c
- $(CC) $(CFLAGS) -s calls.c -o $(DESTDIR)/calls
-
- install: calls
- cp calls $(DESTDIR)/calls
- strip $(DESTDIR)/calls
- cp calls.1 $(DOCDIR)/calls.1
-
- clean:
- rm calls
- ~FUNKY STUFF~
- ls -l Makefile
- # The following exit is to ensure that extra garbage
- # after the end of the shar file will be ignored.
- exit 0
-
-
-
-