1236 lines
44 KiB
C
1236 lines
44 KiB
C
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
typedef unsigned int size_t;
|
|
|
|
extern char *strchr(char *, int);
|
|
extern char *strcpy(char *, char *);
|
|
extern char *strdup(char *);
|
|
extern int atoi(char *);
|
|
extern int strcmp(char *, char *);
|
|
extern void *malloc(size_t);
|
|
extern void exit(int);
|
|
extern void free(void *);
|
|
|
|
int lineno; /* The current line number being parsed */
|
|
int level; /* The level of quotes or comments being parsed */
|
|
int parens; /* current parenthesis level */
|
|
int braces; /* current curly brace level */
|
|
int totalfuncs; /* total number of functions seen */
|
|
|
|
#define PARENS(c) ( ( c == '(' ) - ( c == ')' ) )
|
|
#define BRACES(c) ( ( c == '{' ) - ( c == '}' ) )
|
|
|
|
#define INITIA 0 /* Initial state of input stream machine */
|
|
#define BEGCOM 1 /* Possible beginning of comment state */
|
|
#define COMMEN 2 /* In comment state */
|
|
#define AFTCOM 3 /* Possible end of comment state */
|
|
#define DOUBQT 4 /* In double quote state */
|
|
#define DBQTBS 5 /* Backslash in double quote state */
|
|
#define SINGQT 6 /* In single quote state */
|
|
#define SGQTBS 7 /* Backslash in single quote state */
|
|
#define POUND 8 /* Preprocessor directive */
|
|
#define BKSLHPND 9 /* Backslash in preprocessor directive */
|
|
|
|
#define TYPE 16 /* This character might preceed a possible return type */
|
|
#define NAME 32 /* This character might preceed a possible function name */
|
|
|
|
#define HASHSIZE ( 2 << 10 ) /* Must be a power of two */
|
|
#if 0
|
|
#define MYBUFSIZ 15872 /* Size of buffer used on input stream */
|
|
#endif
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: a structure capable of keeping info on the **/
|
|
/** particular state of a file stream **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: SAVEPLACE saves the position in the buffer, **/
|
|
/** the line number, the current count of braces, and parens **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: lineno, braces, parens. **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
#define SAVEPLACE(x) (x).lineno = lineno; (x).braces = braces; \
|
|
(x).parens = parens; (x).place = ftell( fp )
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: a structure capable of keeping info on the **/
|
|
/** particular state of a file stream. **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: RECOVERPLACE macro restores the position, **/
|
|
/** the line number, the current count of braces, and parens **/
|
|
/** of the last time a SAVEPLACE was done to the argument. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: lineno, braces, parens. **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: lineno, braces, parens, fp **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
#define RECOVERPLACE(x) lineno = (x).lineno; braces = \
|
|
(x).braces; parens = (x).parens; fseek( fp, (x).place, 0 )
|
|
|
|
/***************************************************************************/
|
|
/** **/
|
|
/** Global struct/union/enums **/
|
|
/** **/
|
|
/***************************************************************************/
|
|
|
|
enum relationchoice {
|
|
CALLER,
|
|
CALLEE
|
|
};
|
|
|
|
struct placeptr {
|
|
long place; /* position as reported/used by ftell/fseek */
|
|
int parens; /* parenthesis level to save/recover */
|
|
int braces; /* curly brace level to save/recover */
|
|
int lineno; /* The current line number being parsed */
|
|
};
|
|
|
|
struct func_ref_type {
|
|
char *name; /* name of function referenced */
|
|
char *type; /* type fo functions return */
|
|
char *pnext_func_ref; /* pointer to next function in hashtable */
|
|
char *pcallerlist; /* singly linked list of pointers to functions
|
|
callee's */
|
|
char *pcalleelist; /* singly linked list of pointers to functions
|
|
caller's */
|
|
char *file_name; /* name of file containing function reference */
|
|
int lineno; /* line number of reference to function */
|
|
int xref; /* cross reference of last pruning */
|
|
};
|
|
|
|
/* hash table */
|
|
struct func_ref_type *hashtbl[ HASHSIZE ] = { (struct func_ref_type *)NULL };
|
|
|
|
struct listtype {
|
|
struct func_ref_type *pelist;
|
|
char *pnext_func_ref; /* pointer to next element of list */
|
|
char *file_name; /* name of file where reference occoured */
|
|
int lineno; /* line number where reference occoured */
|
|
};
|
|
|
|
struct listtype ellipses = { /* A null list element for pruning purposes */
|
|
(struct func_ref_type *)NULL,
|
|
(char *)NULL,
|
|
(char *)NULL,
|
|
0
|
|
};
|
|
|
|
struct placeptr last_func_name;
|
|
struct placeptr last_func_ret_type;
|
|
|
|
|
|
/***************************************************************************/
|
|
/** **/
|
|
/** Function prototypes **/
|
|
/** **/
|
|
/***************************************************************************/
|
|
|
|
int mycompare( char **, char ** );
|
|
int getsucc();
|
|
int hashfunc( char * );
|
|
int ismember( char *, char *[] );
|
|
int main( int, char *[] );
|
|
int nsuccmemb( char * );
|
|
int succpos( int );
|
|
|
|
static void quicksort( char *, char * );
|
|
static void swap ( char *, register char *, unsigned int );
|
|
struct func_ref_type *findname( char * );
|
|
struct func_ref_type *makename( char * );
|
|
|
|
void addlist( struct func_ref_type *, struct func_ref_type *, char *,
|
|
int, enum relationchoice );
|
|
void dumptree( struct func_ref_type *, int, enum relationchoice );
|
|
void findproc( void );
|
|
void initstatemachine();
|
|
void qsort( char *, unsigned int, unsigned int, int (*)( char **, char ** ) );
|
|
void readtoken( char *, char * );
|
|
void sortdump( enum relationchoice );
|
|
|
|
|
|
|
|
/***************************************************************************/
|
|
/** **/
|
|
/** Global declarations **/
|
|
/** **/
|
|
/***************************************************************************/
|
|
|
|
char *defaulttype = "int"; /* Pointer to the default return type for C */
|
|
char *endofsp = "Out of space"; /* Pointer to error message */
|
|
char *reswords[] = { /* reserved words that could look like
|
|
functions */
|
|
"if",
|
|
"while",
|
|
"for",
|
|
"return",
|
|
"sizeof",
|
|
"switch",
|
|
NULL
|
|
};
|
|
|
|
char alphanum[] = /* array of alpha numeric characters */
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
|
|
/* delimiters used by cflow */
|
|
char delimits[] = " !@#$%^&*()-+=<>,.?/:;~`'|\n\\\t";
|
|
char filename[ 30 ]; /* Contains the name of the current file
|
|
being scanned */
|
|
/* array of state transitions for input
|
|
state machine */
|
|
char gotonextstate[ 10 ][ 128 ] = { 0 };
|
|
#if 0
|
|
char mybuffer[ MYBUFSIZ ]; /* Buffer for the input file */
|
|
#endif
|
|
char parentproc[ 2048 ]; /* Name of current parent procedure */
|
|
char procname[ 2048 ]; /* Contains the name of the procedure
|
|
reference being scanned */
|
|
char spceq[] = " \t\n\r"; /* whitespace */
|
|
char *typename; /* Contains the name of the return type of the
|
|
reference being scanned */
|
|
|
|
FILE *fp; /* File pointer used for input */
|
|
int cutoff = 35; /* Maximum depth of output ( default 35 ) */
|
|
int verbose = 0; /* Verbose flag ( 0 off, 1 on ) */
|
|
enum relationchoice relationship = CALLEE;
|
|
/* relationship wanted on output
|
|
( default CALLEE ) */
|
|
int xref = 0; /* Starting xref number */
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: count of command line arguments **/
|
|
/** a pointer to the array of command line args. **/
|
|
/** **/
|
|
/** RETURN: 0 if no errors, -1 if out of memory. **/
|
|
/** **/
|
|
/** DESCRIPTION: This program scanns the flags, and sets **/
|
|
/** appropriate flags. After this all of the remaining args **/
|
|
/** that can be opened as files are opened and the contents **/
|
|
/** scanned for function references. These are put in lists **/
|
|
/** such that each function has a linked list of caller to **/
|
|
/** that function, and of callees of that function. On **/
|
|
/** output either the callee tree(s) is/are printed, or the **/
|
|
/** callers trees are printed under control of the -r flag. **/
|
|
/** If there is more than one tree to be printed, then the **/
|
|
/** function names, which are the roots of the trees, are **/
|
|
/** sorted lexicographically before output. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: verbose, cutoff, relationship **/
|
|
/** parens, braces, lineno, level, **/
|
|
/** totalfuncs **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: verbose, cutoff, relationship **/
|
|
/** parens, braces, lineno, level, **/
|
|
/** totalfuncs **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int main( argc, argv )
|
|
|
|
int argc;
|
|
char *argv[];
|
|
|
|
{
|
|
int c;
|
|
if ( argc < 2 )
|
|
{
|
|
printf( "usage: cflow [-v][-r][-d] filelist" );
|
|
}
|
|
else
|
|
{
|
|
initstatemachine();
|
|
while ( --argc > 0 )
|
|
{ /* for all flags/files on the command line */
|
|
++argv;
|
|
if ( ( c = **argv ) == '-' || c == '/' )
|
|
{ /* we are processing a flag */
|
|
while ( c = *(++(*argv)) )
|
|
{
|
|
switch( c )
|
|
{
|
|
|
|
case 'v': /* be verbose on self references */
|
|
case 'V':
|
|
verbose++;
|
|
break;
|
|
|
|
case 'r': /* reverse the caller:callee relationship */
|
|
case 'R':
|
|
relationship = CALLER;
|
|
break;
|
|
|
|
case 'd': /* set the flow graph cutoff depth */
|
|
case 'D':
|
|
if ( isdigit(*(++(*argv))) )
|
|
{
|
|
cutoff = atoi( *argv );
|
|
}
|
|
break;
|
|
}
|
|
} /* End of while */
|
|
}
|
|
else
|
|
{ /* we are processing a file, and should be done
|
|
with flags */
|
|
if ( ( fp = fopen( *argv, "rt" ) ) == NULL )
|
|
{
|
|
printf( "cflow: can't open %s\n", *argv );
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
setvbuf( fp, mybuffer, _IOFBF, (unsigned int)sizeof( mybuffer ) );
|
|
#endif
|
|
puts( *argv );
|
|
strcpy( filename, *argv );
|
|
totalfuncs = parens = braces = level = 0;
|
|
lineno = 1;
|
|
SAVEPLACE( last_func_name );
|
|
SAVEPLACE( last_func_ret_type );
|
|
while ( succpos( '(' ) )
|
|
{ /* we have found a begin paren */
|
|
findproc();
|
|
} /* End of while */
|
|
fclose( fp );
|
|
}
|
|
}
|
|
} /* End of while */
|
|
}
|
|
if ( relationship == CALLEE )
|
|
{ /* "normal" caller:callee relationship */
|
|
if ( findname( "main" ) )
|
|
{ /* Found main */
|
|
dumptree( findname( "main" ), 0, relationship );
|
|
}
|
|
else
|
|
{ /* No main found */
|
|
sortdump( relationship );
|
|
}
|
|
}
|
|
else
|
|
{ /* reverse caller:callee relationship */
|
|
sortdump( relationship );
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: none **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: findproc locates the name of the function **/
|
|
/** preceeding the parenthesis, and figures out whether we **/
|
|
/** are looking at a function definition, or a function call **/
|
|
/** also adds to the caller, and callee list of the function **/
|
|
/** as appropriate. ( see addlist() ) **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: last_func_ret_type, **/
|
|
/** last_func_name, alphanum, **/
|
|
/** procname, braces, parentproc, **/
|
|
/** lineno, parens, spceq, **/
|
|
/** defaulttype **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void findproc()
|
|
|
|
{
|
|
char *thisplace;
|
|
long sizetype;
|
|
int oldlineno, oldbraces, oldparens;
|
|
struct placeptr place, typeplace, tplace;
|
|
struct func_ref_type *alist, *blist;
|
|
SAVEPLACE( place );
|
|
typeplace = last_func_ret_type;
|
|
place.place++;
|
|
RECOVERPLACE( last_func_name );
|
|
sizetype = last_func_name.place;
|
|
readtoken( alphanum, procname );
|
|
if ( ! braces )
|
|
{
|
|
strcpy( parentproc, procname );
|
|
}
|
|
if ( *procname && strchr( alphanum, *procname ) &&
|
|
( ismember( procname, reswords ) == -1 ) )
|
|
{ /* This is a function call, prototype, or definition */
|
|
oldlineno = lineno;
|
|
oldbraces = braces;
|
|
oldparens = parens + 1;
|
|
while ( succpos( ')' ) )
|
|
{
|
|
getsucc();
|
|
if ( parens <= oldparens )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
nsuccmemb( spceq );
|
|
if ( getsucc() != ';' || oldbraces != 0 )
|
|
{ /* This is a call or a definition */
|
|
alist = makename( parentproc );
|
|
blist = makename( procname );
|
|
|
|
if ( oldbraces )
|
|
{ /* This is scanning a call */
|
|
addlist( alist, blist, filename, oldlineno, CALLEE );
|
|
addlist( blist, alist, filename, oldlineno, CALLER );
|
|
}
|
|
else
|
|
{ /* This is scanning a definition */
|
|
if ( alist->file_name )
|
|
{
|
|
free( alist->file_name );
|
|
}
|
|
alist->file_name = strdup( filename );
|
|
alist->lineno = oldlineno;
|
|
}
|
|
}
|
|
else
|
|
{ /* This is scanning a prototype */
|
|
RECOVERPLACE( typeplace );
|
|
/* remove starting whitespace */
|
|
nsuccmemb( spceq );
|
|
SAVEPLACE( typeplace );
|
|
sizetype -= typeplace.place;
|
|
if ( ( thisplace = typename = malloc( sizetype+ 1 ) ) == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
for ( thisplace = typename; --sizetype >= 0L; )
|
|
{
|
|
*thisplace++ = (char)getsucc();
|
|
} /* End of for */
|
|
*thisplace = '\0';
|
|
alist = makename( procname );
|
|
if ( alist->type != defaulttype )
|
|
{
|
|
free( alist->type );
|
|
}
|
|
alist->type = typename;
|
|
}
|
|
}
|
|
RECOVERPLACE( place );
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: a token name. **/
|
|
/** **/
|
|
/** RETURN: a pointer to a struct func_ref_type **/
|
|
/** **/
|
|
/** DESCRIPTION: makename sets up an function reference **/
|
|
/** structure for hash lookup later. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: hashtbl, hashval, totalfuncs, **/
|
|
/** endofsp **/
|
|
/** **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: hashtbl, hashval, totalfuncs **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
struct func_ref_type *makename( nameptr )
|
|
|
|
char *nameptr;
|
|
|
|
{
|
|
struct func_ref_type *pelist;
|
|
int hashval;
|
|
int newel = 1;
|
|
pelist = hashtbl[ hashval = hashfunc( nameptr ) ];
|
|
if ( pelist )
|
|
{
|
|
while ( ( newel = strcmp( nameptr, pelist->name ) ) &&
|
|
pelist->pnext_func_ref )
|
|
{
|
|
pelist = (struct func_ref_type *)pelist->pnext_func_ref;
|
|
} /* End of while */
|
|
if ( newel )
|
|
{
|
|
if ( ( pelist->pnext_func_ref =
|
|
malloc( sizeof( struct func_ref_type ) ) ) == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
pelist = (struct func_ref_type *)(pelist->pnext_func_ref);
|
|
totalfuncs++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( pelist = ( hashtbl[ hashval ]
|
|
= (struct func_ref_type *)malloc(
|
|
sizeof( struct func_ref_type ) ) ) ) == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
totalfuncs++;
|
|
}
|
|
if ( newel )
|
|
{ /* Initialize function name node to undefined state. */
|
|
pelist->pcallerlist = NULL;
|
|
pelist->pnext_func_ref = NULL;
|
|
pelist->type = defaulttype;
|
|
pelist->pcalleelist = NULL;
|
|
pelist->file_name = NULL;
|
|
pelist->name = strdup( nameptr );
|
|
}
|
|
if ( pelist == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
return( pelist );
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: a token name. **/
|
|
/** **/
|
|
/** RETURN: a pointer to a struct func_ref_type **/
|
|
/** **/
|
|
/** DESCRIPTION: findname returns a function reference to **/
|
|
/** the structure whose name field matches the token name, **/
|
|
/** or NULL in the case of no match. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: hashval, hashfunc **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
struct func_ref_type *findname( nameptr )
|
|
|
|
char *nameptr;
|
|
|
|
{
|
|
struct func_ref_type *pelist;
|
|
int hashval;
|
|
pelist = hashtbl[ hashval = hashfunc( nameptr ) ];
|
|
if ( pelist )
|
|
{
|
|
while ( strcmp( nameptr, pelist->name ) &&
|
|
pelist->pnext_func_ref )
|
|
{
|
|
pelist = (struct func_ref_type *)pelist->pnext_func_ref;
|
|
} /* End of while */
|
|
}
|
|
return( pelist );
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: token to get hashed **/
|
|
/** **/
|
|
/** RETURN: hash value. **/
|
|
/** **/
|
|
/** DESCRIPTION: hashfunc hashes tokens based on the sum **/
|
|
/** of all the characters in the token. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: hashval, hashfunc **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int hashfunc( token )
|
|
|
|
char *token;
|
|
|
|
{
|
|
int retval = 0;
|
|
for ( ; *token ; retval += *token++ )
|
|
;
|
|
return( retval & ( HASHSIZE - 1 ) );
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: Two function reference pointers, the **/
|
|
/** filename where the reference took place. **/
|
|
/** The line number where the reference occoured. **/
|
|
/** And the relation between the first two **/
|
|
/** references. **/
|
|
/** **/
|
|
/** RETURN: void **/
|
|
/** **/
|
|
/** DESCRIPTION: addlist adds one function to the others **/
|
|
/** CALLEE/CALLER list the list being chosen by the relation **/
|
|
/** being passed in. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: endofsp **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void addlist( pafunc, pbfunc, filename, lineno, relation )
|
|
|
|
struct func_ref_type *pafunc;
|
|
struct func_ref_type *pbfunc;
|
|
char *filename;
|
|
int lineno;
|
|
enum relationchoice relation;
|
|
|
|
{
|
|
struct listtype *listptr;
|
|
char *parentptr;
|
|
if ( ( ( relation == CALLEE ) ?
|
|
pafunc->pcalleelist : pafunc->pcallerlist ) == NULL )
|
|
{
|
|
if ( ( listptr = (struct listtype *)malloc(
|
|
sizeof( struct listtype ) ) ) == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
if ( relation == CALLEE )
|
|
{
|
|
pafunc->pcalleelist = (char *)listptr;
|
|
}
|
|
else
|
|
{
|
|
pafunc->pcallerlist = (char *)listptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
listptr = (struct listtype *)( relation == CALLEE ?
|
|
pafunc->pcalleelist : pafunc->pcallerlist );
|
|
while ( listptr->pnext_func_ref )
|
|
{ /* walk the list, and insert at the end */
|
|
listptr = (struct listtype *)listptr->pnext_func_ref;
|
|
} /* End of while */
|
|
if ( ( listptr->pnext_func_ref = (char *)malloc(
|
|
sizeof( struct listtype ) ) ) == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
listptr = (struct listtype *)listptr->pnext_func_ref;
|
|
}
|
|
|
|
listptr->pelist = pbfunc;
|
|
listptr->pnext_func_ref = NULL;
|
|
listptr->file_name = strdup( filename );
|
|
listptr->lineno = lineno;
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: pointer to a function reference. **/
|
|
/** level of nesting so far. **/
|
|
/** relation to use. **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: dumptree prints out the current nesting **/
|
|
/** level of relation and calls itself recursively for lower **/
|
|
/** nesting levels. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: cutoff, verbose, ellipses **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void dumptree( pafunc, level, relation )
|
|
|
|
struct func_ref_type *pafunc;
|
|
int level;
|
|
enum relationchoice relation;
|
|
|
|
{
|
|
struct listtype *roverptr;
|
|
struct listtype *beginlist;
|
|
struct func_ref_type *old_pafunc;
|
|
char *old_nextptr;
|
|
int i;
|
|
if ( pafunc && level < cutoff )
|
|
{ /* There is a branch below this level */
|
|
if ( !level )
|
|
{
|
|
printf( "%4d", level );
|
|
for ( i = 0; i <= level; i++ )
|
|
{
|
|
putchar( '\t' );
|
|
} /* End of for */
|
|
|
|
if ( pafunc->file_name )
|
|
{ /* a definition has been found */
|
|
printf( "%s:%s() <%s,%d>\n", pafunc->name, pafunc->type,
|
|
pafunc->file_name, pafunc->lineno );
|
|
}
|
|
else
|
|
{
|
|
printf( "%s:%s() <,>\n", pafunc->name, pafunc->type );
|
|
}
|
|
level++;
|
|
}
|
|
if ( relation == CALLEE )
|
|
{
|
|
beginlist = roverptr = (struct listtype *)pafunc->pcalleelist;
|
|
}
|
|
else
|
|
{
|
|
beginlist = roverptr = (struct listtype *)pafunc->pcallerlist;
|
|
}
|
|
|
|
while ( roverptr )
|
|
{ /* Walk the list of CALLER/CALLEE's */
|
|
if ( !verbose && beginlist != &ellipses && beginlist == roverptr )
|
|
{
|
|
printf( "(%3d)", xref );
|
|
}
|
|
else
|
|
{
|
|
printf( " ", xref );
|
|
}
|
|
printf( "%4d", level );
|
|
|
|
for ( i = 0; i <= level; i++ )
|
|
{
|
|
putchar( '\t' );
|
|
} /* End of for */
|
|
|
|
if ( roverptr->pelist != (struct func_ref_type *)NULL )
|
|
{
|
|
printf( "%s:%s() <%s,%d>", roverptr->pelist->name,
|
|
roverptr->pelist->type, roverptr->file_name,
|
|
roverptr->lineno );
|
|
}
|
|
else
|
|
{
|
|
printf( "...relations shown at (%1d)", pafunc->xref );
|
|
}
|
|
old_pafunc = roverptr->pelist;
|
|
old_nextptr = roverptr->pnext_func_ref;
|
|
if ( !verbose && beginlist != &ellipses && beginlist == roverptr )
|
|
{
|
|
free( beginlist->file_name );
|
|
free( beginlist );
|
|
pafunc->xref = xref++;
|
|
if ( relation == CALLEE )
|
|
{
|
|
pafunc->pcalleelist = (char *)&ellipses;
|
|
}
|
|
else
|
|
{
|
|
pafunc->pcallerlist = (char *)&ellipses;
|
|
}
|
|
}
|
|
putchar( '\n' );
|
|
dumptree( old_pafunc, level + 1, relation );
|
|
roverptr = (struct listtype *)old_nextptr;
|
|
} /* End of while */
|
|
}
|
|
}
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: none **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: initstatemachine sets up which state to go **/
|
|
/** to given which state is currently active and which **/
|
|
/** character has been read in. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: gotonextstate **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: gotonextstate **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void initstatemachine()
|
|
|
|
{
|
|
int character;
|
|
for ( character = 0; character < 128; character++ )
|
|
{ /* set up the majority of state jumps */
|
|
if ( !isalnum( character ) && character != '_' && character != '(' )
|
|
{
|
|
gotonextstate[ INITIA ][ character ] = NAME;
|
|
}
|
|
else
|
|
{
|
|
gotonextstate[ INITIA ][ character ] = INITIA;
|
|
}
|
|
gotonextstate[ BEGCOM ][ character ] = INITIA;
|
|
gotonextstate[ COMMEN ][ character ] = COMMEN;
|
|
gotonextstate[ AFTCOM ][ character ] = COMMEN;
|
|
gotonextstate[ DOUBQT ][ character ] = DOUBQT;
|
|
gotonextstate[ DBQTBS ][ character ] = DOUBQT;
|
|
gotonextstate[ SINGQT ][ character ] = SINGQT;
|
|
gotonextstate[ SGQTBS ][ character ] = SINGQT;
|
|
gotonextstate[ POUND ][ character ] = POUND;
|
|
gotonextstate[ BKSLHPND ][ character ] = POUND;
|
|
} /* End of for */
|
|
/* Now set all of the exceptions */
|
|
|
|
gotonextstate[ INITIA ][ ';' ] = TYPE; /* preceeding beginning of a type */
|
|
gotonextstate[ INITIA ][ ',' ] = TYPE;
|
|
gotonextstate[ INITIA ][ '{' ] = TYPE;
|
|
gotonextstate[ INITIA ][ '}' ] = TYPE;
|
|
|
|
gotonextstate[ AFTCOM ][ '*' ] = AFTCOM; /* possible end of comment */
|
|
gotonextstate[ AFTCOM ][ '/' ] = INITIA; /* end comment is confirmed */
|
|
gotonextstate[ BEGCOM ][ '*' ] = COMMEN; /* possible end of comment */
|
|
gotonextstate[ COMMEN ][ '*' ] = AFTCOM; /* possible end of comment */
|
|
gotonextstate[ DOUBQT ][ '"' ] = INITIA; /* end of double quoted stuff */
|
|
gotonextstate[ DOUBQT ][ '\\' ] = DBQTBS; /* protect the next character */
|
|
gotonextstate[ INITIA ][ '"' ] = DOUBQT; /* double quoted stuff */
|
|
gotonextstate[ INITIA ][ '#' ] = POUND; /* beginning of preprcssr */
|
|
gotonextstate[ INITIA ][ '/' ] = BEGCOM; /* beginning of comment*/
|
|
gotonextstate[ INITIA ][ '\'' ] = SINGQT; /* single quoted stuff */
|
|
gotonextstate[ POUND ][ '\\' ] = BKSLHPND; /* backslash in preprocessor */
|
|
gotonextstate[ POUND ][ '\n' ] = TYPE; /* end of preprocessor */
|
|
gotonextstate[ SINGQT ][ '\'' ] = INITIA; /* end of single quoted stuff */
|
|
gotonextstate[ SINGQT ][ '\\' ] = SGQTBS; /* protect the next character */
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: relationship to use for outputting trees. **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: sortdump looks at all of the function names **/
|
|
/** and sorts them using qsort. Then the functions are **/
|
|
/** dumped out using dumptree with the relation wanted being **/
|
|
/** passed on to dumptree. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: totalfuncs, endofsp, hashtbl **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void sortdump( relationship )
|
|
|
|
enum relationchoice relationship;
|
|
|
|
{
|
|
int hashlook;
|
|
struct func_ref_type **ppelist;
|
|
struct func_ref_type *pcollisionwalker;
|
|
struct func_ref_type **ppsorttable;
|
|
if ( ( ppsorttable = (struct func_ref_type **)malloc(
|
|
sizeof(struct func_ref_type *) * ( totalfuncs + 1 ) ) ) == NULL )
|
|
{
|
|
fprintf( stderr, endofsp );
|
|
exit( -1 );
|
|
}
|
|
ppelist = ppsorttable;
|
|
for ( hashlook = 0; hashlook < HASHSIZE; hashlook++ )
|
|
{ /* for each element in the hash table */
|
|
if ( hashtbl[ hashlook ] )
|
|
{
|
|
pcollisionwalker = *ppelist = hashtbl[ hashlook ];
|
|
ppelist++;
|
|
while ( pcollisionwalker->pnext_func_ref )
|
|
{ /* for each collision element */
|
|
pcollisionwalker =
|
|
(struct func_ref_type *)pcollisionwalker->pnext_func_ref;
|
|
*ppelist = pcollisionwalker;
|
|
ppelist++;
|
|
} /* End of while */
|
|
}
|
|
} /* End of for */
|
|
qsort( (char *)ppsorttable, totalfuncs, sizeof(struct func_ref_type *),
|
|
mycompare );
|
|
ppelist = ppsorttable;
|
|
while ( ppelist < (ppsorttable + totalfuncs) )
|
|
{
|
|
putchar( '\n' );
|
|
dumptree( *ppelist, 0, relationship );
|
|
ppelist++;
|
|
} /* End of while */
|
|
}
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: pointers to two function reference nodes **/
|
|
/** **/
|
|
/** RETURN: 1, 0, or -1 **/
|
|
/** **/
|
|
/** DESCRIPTION: compare compares the two function names in **/
|
|
/** the structures pointed to by ppa, and ppb. The return **/
|
|
/** value is the result of the strcmp of these two names. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: none **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int mycompare( ppa, ppb )
|
|
|
|
char **ppa;
|
|
char **ppb;
|
|
|
|
{
|
|
return( strcmp( ((struct func_ref_type *)(*ppa))->name,
|
|
((struct func_ref_type *)(*ppb))->name ) );
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: none **/
|
|
/** **/
|
|
/** RETURN: an integer **/
|
|
/** **/
|
|
/** DESCRIPTION: This state machine reads in characters and **/
|
|
/** keeps reading in characters until an initial state is **/
|
|
/** reached, this means the character being read is not in a **/
|
|
/** comment, or quoted. Also tags are marked to show the **/
|
|
/** possible start of function names, and type returns. In **/
|
|
/** addition a count of parens and braces is made for use **/
|
|
/** elsewhere. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: lineno, parens, gotonextstate, **/
|
|
/** braces, last_func_ret_type, **/
|
|
/** last_func_name **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: lineno, parens, braces, **/
|
|
/** last_func_ret_type, **/
|
|
/** last_func_name **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int getsucc()
|
|
|
|
|
|
{
|
|
static int near c;
|
|
static int near state = INITIA; /* start things out in the initial state */
|
|
static int near previousstate;
|
|
do
|
|
{
|
|
if ( ( c = getc( fp ) ) == '\n' )
|
|
{
|
|
lineno++;
|
|
}
|
|
if ( c == EOF )
|
|
{
|
|
return( EOF );
|
|
}
|
|
previousstate = state & 15;
|
|
state = gotonextstate[ state & 15 ][ c ];
|
|
} while ( ( state & 15 ) != INITIA
|
|
|| ( previousstate != INITIA && previousstate != BEGCOM ) );
|
|
/* Keep eating characters until we are out of comments and quotes */
|
|
|
|
if ( previousstate == BEGCOM )
|
|
{ /* This is the only context sensitive area, if a '/' is seen
|
|
followed by anything but a '*' then we must back up */
|
|
ungetc( c, fp );
|
|
return( '/' );
|
|
}
|
|
if ( state == TYPE )
|
|
{ /* Save the location of the last function return value */
|
|
SAVEPLACE( last_func_ret_type );
|
|
braces += BRACES( c );
|
|
}
|
|
else
|
|
{
|
|
parens += PARENS( c );
|
|
if ( state == NAME )
|
|
{
|
|
SAVEPLACE( last_func_name );
|
|
}
|
|
}
|
|
return( c );
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: character **/
|
|
/** **/
|
|
/** RETURN: character **/
|
|
/** **/
|
|
/** DESCRIPTION: succpos finds the next succesive position **/
|
|
/** in a file that matches the character passed into it. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: none **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int succpos( c ) /* Sets pointer to next position of character [c] */
|
|
int c;
|
|
{
|
|
static int near tmpc;
|
|
while ( ( c != ( tmpc = getsucc() ) ) && ( tmpc != EOF ) )
|
|
;
|
|
if( tmpc != EOF )
|
|
{
|
|
ungetc( c, fp );
|
|
return( 1 );
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: character pointer **/
|
|
/** **/
|
|
/** RETURN: character **/
|
|
/** **/
|
|
/** DESCRIPTION: nsuccmemb finds the next succesive character**/
|
|
/** using getsucc that is not a member of the characters **/
|
|
/** pointed to by the variable passed in. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: none **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int nsuccmemb( lstr )
|
|
|
|
char *lstr;
|
|
{
|
|
int c;
|
|
if( ( c = getsucc() ) != EOF )
|
|
{
|
|
while ( ( strchr( lstr, c ) != NULL ) && ( c != EOF ) )
|
|
{
|
|
c = getsucc();
|
|
} /* End of while */
|
|
ungetc( c, fp );
|
|
}
|
|
return( c != EOF );
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: character pointers **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: readtoken reads in characters using getsucc **/
|
|
/** as long as the characters are members of characters **/
|
|
/** pointed to by plegalstr, these characters are put into **/
|
|
/** the place pointed to by ptokenstr, and null terminated. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: none **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void readtoken( plegalstr, ptokenstr ) /* Reads token composed of
|
|
*plegalstr into *ptokenstr */
|
|
char *plegalstr;
|
|
char *ptokenstr;
|
|
|
|
{
|
|
int c;
|
|
struct placeptr place;
|
|
SAVEPLACE( place );
|
|
RECOVERPLACE( place );
|
|
while ( ( c = getsucc() ) && strchr( plegalstr, c ) && c != EOF )
|
|
{
|
|
*ptokenstr++ = (char )c;
|
|
} /* End of while */
|
|
*ptokenstr = 0;
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: character pointer, and an array of character **/
|
|
/** pointers. **/
|
|
/** **/
|
|
/** RETURN: integer **/
|
|
/** **/
|
|
/** DESCRIPTION: ismember returns -1 if ptoken is not a **/
|
|
/** member of any of the characters pointed to in the array **/
|
|
/** plistwords, if the ptoken is a member, the position in **/
|
|
/** that array is returned. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: none **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: none **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
int ismember( ptoken, plistwords )
|
|
char *ptoken, *plistwords[];
|
|
{
|
|
int i = 0;
|
|
while ( *plistwords && strcmp( ptoken, *plistwords ) )
|
|
{
|
|
*++plistwords;
|
|
++i;
|
|
} /* End of while */
|
|
return( ( *plistwords == NULL || strcmp( ptoken, *plistwords ) ) ? -1 : i );
|
|
}
|
|
|
|
|
|
|
|
static unsigned int width;
|
|
static int (*compare)( char **, char ** );
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/** **/
|
|
/** ARGUMENTS: base = (char *) pointer to base of array **/
|
|
/** num = (unsigned) number of elements in the array **/
|
|
/** wid = (unsigned) width in bytes of each array element **/
|
|
/** comp = (int (*) ()) pointer to function returning **/
|
|
/** analog of strncmp for strings, but supplied for **/
|
|
/** comparing the array elements. It accepts 2 pointers **/
|
|
/** to elements and returns neg if 1<2, 0 if 1=2, **/
|
|
/** pos if 1>2. **/
|
|
/** **/
|
|
/** RETURN: none **/
|
|
/** **/
|
|
/** DESCRIPTION: qsort quick sorts an array of elements in **/
|
|
/** place. **/
|
|
/** **/
|
|
/** GLOBAL VARS REFERENCED: array starting at base **/
|
|
/** **/
|
|
/** GLOBAL VARS MODIFIED: array starting at base **/
|
|
/** **/
|
|
/****************************************************************/
|
|
|
|
void qsort( base, num, wid, comp )
|
|
|
|
char *base;
|
|
unsigned int num;
|
|
unsigned int wid;
|
|
int (*comp)( char **, char ** );
|
|
|
|
{
|
|
register char *q = base;
|
|
register char *p = base + wid;
|
|
int i = num - 1;
|
|
int sort = 0; /* set to non-zero if sort must be done */
|
|
/* (some element is out of order) */
|
|
|
|
if ( num )
|
|
{
|
|
while ( i-- )
|
|
{
|
|
if ( (*comp)( (char **)q, (char **)p ) > 0 )
|
|
{
|
|
sort++;
|
|
break;
|
|
}
|
|
|
|
q = p;
|
|
p += wid;
|
|
}
|
|
}
|
|
|
|
if (sort)
|
|
{
|
|
width = wid;
|
|
compare = comp;
|
|
quicksort( base, base + (num - 1) * width );
|
|
}
|
|
}
|
|
|
|
|
|
static void quicksort( lo, hi )
|
|
|
|
char *lo;
|
|
char *hi;
|
|
|
|
{
|
|
register char *higuy = hi + width;
|
|
register char *loguy = lo;
|
|
|
|
while ( lo < hi )
|
|
{
|
|
for ( ; ; )
|
|
{
|
|
do {
|
|
loguy += width;
|
|
} while (loguy < hi && (*compare)( (char **)loguy, (char **)lo ) <= 0);
|
|
|
|
do {
|
|
higuy -= width;
|
|
} while (higuy > lo && (*compare)( (char **)higuy, (char **)lo ) >= 0);
|
|
|
|
if (higuy <= loguy)
|
|
break;
|
|
|
|
swap( loguy, higuy, width );
|
|
} /* End of for */
|
|
|
|
swap( lo, higuy, width );
|
|
if ( higuy - lo >= hi - higuy )
|
|
{
|
|
quicksort( higuy + width, hi );
|
|
hi = higuy - width;
|
|
loguy = lo;
|
|
}
|
|
else
|
|
{
|
|
quicksort( lo, higuy - width );
|
|
loguy = lo = higuy + width;
|
|
higuy = hi + width;
|
|
}
|
|
} /* End of while */
|
|
}
|
|
|
|
|
|
|
|
static void swap ( one, two, w )
|
|
|
|
char *one;
|
|
register char *two;
|
|
unsigned int w;
|
|
|
|
{
|
|
char temp;
|
|
|
|
while ( w-- )
|
|
{
|
|
temp = *one;
|
|
*one++ = *two;
|
|
*two++ = temp;
|
|
}
|
|
}
|