-
Notifications
You must be signed in to change notification settings - Fork 0
/
calls
1138 lines (1029 loc) · 25.9 KB
/
calls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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;