home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
c
/
getf.arc
/
BLDFUNCS.C
next >
Wrap
C/C++ Source or Header
|
1988-11-19
|
14KB
|
537 lines
/*
bldfuncs - contruct a table of the functions defined in C
source files.
Usage: bldfuncs file1.c file2.c ...
(wildcards are allowed also, e.g. *.c )
The output table is named funcs.txt
Copyright (c) 1988 Marvin Hymowech
Reference: DDJ, Aug 88
*/
#include <stdio.h>
#include <string.h>
#define LINT_ARGS
#define PRINTING_LINE_NUMBER
#define TRUE 1
#define FALSE 0
#define OK 0
#define ERROR 1
/*
return values for filter functions below.
EOF or any character is also possible.
*/
#define BRACES -2
#define PARENS -3
#define QUOTES -4
int line_num; /* line number in input file */
/* function prototypes for type checking */
char *get_fn_name( char * );
int get_names_one_file( char *, FILE * );
int filter_data( FILE * );
int filter_parens( FILE * );
int filter_curly_braces( FILE * );
int filter_ppdir( FILE * );
int filter_quotes( FILE * );
int filter_cmt( FILE * );
/* nonzero if new data is to be appended to existing file */
int appending = 0;
main(argc, argv) int argc; char **argv;
{
FILE *fp_out;
char *current_file, **av, *cp, c;
int num_files, i, ac;
quotearg(&argc, &argv); /* expand wildcards on command line */
/* parse option switches on command line & remove them */
ac = argc; argc = 1;
av = argv + 1;
while(--ac)
{if(**av == '-')
{cp = *av + 1;
while(c = *cp++)
{switch(c)
{case 'a': appending = 1; break;
default: help();
}
}
}
else
argv[argc++] = *av;
av++;
}
if(argc < 2) help();
if( (fp_out = fopen("funcs.txt", appending?"a":"w")) == NULL )
{
fprintf( stderr, "can't open %s\n", "funcs.txt" );
exit(1);
}
/*
build a function list for each file on the command line,
and write the list to the file funcs.txt.
*/
printf( "%s funcs.txt...\n", appending?"Appending to":"Creating" );
num_files = argc - 1;
for ( i=1; i<=num_files; i++)
{ /* tell the user where we're at */
printf( "%30s: %3d of %3d files\n",
current_file=strlwr(*++argv), i, num_files);
if( get_names_one_file( current_file, fp_out ) != OK )
{ /* use strlwr to lower_case the name - cosmetic only */
fprintf( stderr, "can't process %s", current_file );
exit(1);
}
}
fclose(fp_out);
exit(0);
}
/*
open the .c file source_file_name, scan it for function definitions,
and write a listing to fp_out in the form:
source_file_name:
function-1
function-2
...
function-n;
return values: OK or ERROR
*/
int
get_names_one_file( source_file_name, fp_out )
char *source_file_name;
FILE *fp_out;
{
int line_len, c, got_fn_defn=FALSE, definition_line_num;
#define LINE_LEN 8192
char line[LINE_LEN], *name_ptr, fn_name[64];
FILE *fp_source;
/* open the input source file */
if( (fp_source = fopen(source_file_name, "r")) == NULL )
return ERROR;
/* write "source file name:" */
sprintf( line, "\n%s:", source_file_name );
fprintf( fp_out, line );
line_num=1;
while( TRUE )
{
line_len = 0; /* using the filter_data() char stream */
/* collect chars until a semicolon or PARENS */
while( (c = filter_data(fp_source)) != EOF && c != ';' && c != PARENS )
line[line_len++] = c;
if( c == EOF )
break;
if( c == ';' ) /* bypass externals representing data items */
continue; /* now we know line ended with PARENS */
line[ line_len ] = 0; /* terminate line */
/*
At this point, line either contains a function definition
or a function type declaration or something else, perhaps
an occurrence of parentheses in a data item definition. We
only want function definitions.
*/
/* extract the function name from this possible */
/* function definition, and save it in fn_name. */
strcpy( fn_name, get_fn_name( line ) );
definition_line_num = line_num;
/* exclude the case of parenthetical expressions */
/* in a data definition, e.g. within array brackets */
if( !fn_name[0] )
continue;
/* skip white space */
while( (c = filter_data(fp_source)) != EOF && isspace(c) )
; /* EMPTY */
if( c == ';' || c == ',' ) /* functions type check declaration, */
continue; /* so bypass it */
if( c == PARENS ) /* something pointing to a function */
continue; /* bypass it (could be a mistake - if */
/* it's a function returning a pointer */
/* to a function we'll miss it) */
if( c == EOF )
break;
/* skip any parameter declarations - */
/* eat until braces or EOF */
while( c != BRACES && c != EOF )
c = filter_data(fp_source);
/* append this function definition */
/* to the output table */
#ifdef PRINTING_LINE_NUMBER
fprintf( fp_out, "\n\t%-25s", fn_name );
fprintf( fp_out, "%4d", definition_line_num );
#else
fprintf( fp_out, "\n\t%s", fn_name );
#endif
got_fn_defn = TRUE;
}
fclose(fp_source);
/* got_fn_defn FALSE if no functions in file */
/* write file terminator */
fputs( got_fn_defn ? ";\n" : "\n\t;\n", fp_out );
return OK;
}
/*
assuming that the input line ends with a function name,
extract and return this name (as a pointer into the input line).
*/
char *
get_fn_name(line)
char *line;
{
char *name_ptr;
int len;
if( !(len = strlen(line)) )
return line;
name_ptr = line + len - 1;
while( isspace(*name_ptr) ) /* skip trailing white space */
name_ptr--;
*(name_ptr + 1) = 0; /* terminate fn name */
/* function names consist entirely of */
/* alphanumeric chars and underscores */
while( (isalnum(*name_ptr) || *name_ptr == '_') && name_ptr >= line )
name_ptr--;
/* if this alleged function name begins */
/* with a digit, return an empty string */
if( isdigit(*++name_ptr) )
return( name_ptr + strlen(name_ptr) );
return name_ptr; /* else return the function name */
}
/*
Using the stream returned by filter_parens() as input, return a
character stream in which any data initialization expressions
between an equals sign and a semicolon have been replaced by a
single semicolon. This will filter out anything involving
parentheses in a data initialization expression, which might
otherwise be mistaken for a function definition.
*/
int filter_data(fp_source)
FILE *fp_source;
{
int c;
if( (c = filter_parens(fp_source)) != '=' )
return c;
while( (c = filter_parens(fp_source)) != ';' && c != EOF )
;
return c;
}
/*
Using the stream returned by filter_curly_braces() as input, return
a character stream in which all characters between '(' and the
matching ')' have been replaced by the single special value PARENS
(any nested parentheses within this top level pair will also have
been eaten). This will filter out anything within the parentheses
delimiting the arguments in a function definition.
*/
int
filter_parens(fp_source)
FILE *fp_source;
{
int paren_cnt, c;
if( (c = filter_curly_braces(fp_source)) != '(' )
return c;
paren_cnt = 1;
while( paren_cnt )
{switch( filter_curly_braces(fp_source) )
{
case ')':
paren_cnt--;
break;
case '(':
paren_cnt++;
break;
case EOF:
return EOF;
}
}
return PARENS;
}
/*
Using the stream returned by filter_ppdir() as input, return a
character stream in which all characters between '{' and the
matching '}' have been replaced by the single special value BRACES
(any nested braces within this top level pair will also have been
eaten). This will filter out anything internal to a function.
*/
int
filter_curly_braces(fp_source)
FILE *fp_source;
{
int brace_cnt, c;
if( (c = filter_ppdir(fp_source)) != '{' )
return c;
brace_cnt = 1;
while( brace_cnt ) /* wait for brace count to return to zero */
{switch( filter_ppdir(fp_source) )
{
case '}':
brace_cnt--; /* subtract right braces */
break;
case '{':
brace_cnt++; /* add left braces */
break;
case EOF:
return EOF;
}
}
return BRACES; /* brace count is now zero */
}
#define MAXLINE 1024
/*
Using the stream returned by filter_quotes() as input, return a
character stream in which all preprocessor directives have been
eaten.
*/
int
filter_ppdir(fp_source)
FILE *fp_source;
{
int c, i;
char line[MAXLINE + 1];
while(TRUE)
{ /* does this line begin a preprocessor directive? */
if( (c = filter_quotes(fp_source)) != '#' )
return c; /* no, return character */
/* yes, store until newline or EOF */
if( (c = get_ppdir_line( fp_source, line )) == EOF )
return EOF;
if( strncmp( line, "#define", 6) ) /* if not #define directive */
continue;
if( line[ strlen(line) - 1 ] != '\\' ) /* if #define ends with "\" */
continue;
else
while(TRUE) /* keep eating lines */
{
if( (c = get_ppdir_line( fp_source, line )) == EOF )
return EOF;
/* done with this #define directive if this
line is not also a continueation line */
if( line[ strlen(line) - 1 ] != '\\' )
break;
}
}
}
/*
Utility routine used by filter_ppdir() -
read the character stream using filter_quotes, storing characters
int he parameter "line", until EOF or '\n' is encountered. Return
EOF or '\n' accordingly.
*/
int
get_ppdir_line( fp_source, line )
FILE *fp_source;
char *line;
{
int i, c;
/* store until newline or EOF */
for( i=0; i<MAXLINE && (c = filter_quotes(fp_source)) != '\n'
&& c != EOF; i++)
line[i] = c;
line[i] = 0; /* terminate string */
if( c == EOF )
return EOF;
return '\n';
}
/*
Using the stream returned by filter_cmt() as input, return a
character stream in which any quoted character or quoted string has
been collapsed to the single special value QUOTES to avoid
considering special characters like '{', '}', '(', or ')' which may
occur within quotes.
*/
int
filter_quotes(fp_source)
FILE *fp_source;
{
int c1, c2;
if( (c1 = filter_cmt(fp_source)) != '\'' && c1 != '"' )
return c1; /* pass char through if not a single or double quote */
while( TRUE )
{
switch( c2 = filter_cmt(fp_source) )
{
case '\\': /* beginning of escape sequence */
filter_cmt(fp_source); /* so eat next char */
break;
case EOF:
return EOF;
default:
if( c2 == c1 ) /* found end of quoted char or string */
return QUOTES;
}
}
}
/*
return character stream, eating comments.
Returns EOF if end of file.
nested comments are allowed.
*/
int
filter_cmt(fp_source)
FILE *fp_source;
{
/* values for state */
#define STABLE 0 /* not in process of changing the comment
level: i.e., not in the middle of a
slash-star or star-slash combination */
#define IN_CMT_FS 1 /* got '/', looking for '*' */
#define OUT_CMT_STAR 2 /* got '*', looking for '/' */
int c, state=STABLE, cmt_level = 0;
while( TRUE )
{
c = fgetc(fp_source);
if( c == '\n') line_num++;
else if( c == EOF )
return EOF;
switch(state)
{
case STABLE:
if( c == '*' )
state = OUT_CMT_STAR;
else if( c == '/' )
state = IN_CMT_FS;
break;
case IN_CMT_FS:
if( c == '*' )
{
state = STABLE;
#ifdef NESTED_COMMENTS
cmt_level++; /* descend one comment level */
#else
cmt_level = 1; /* descend to comment level */
#endif
continue;
}
else if( !cmt_level ) /* if '/' not followed by '*'
and outside any comment */
{ungetc( c, fp_source ); /* push back this char
and return the '/' */
return '/';
}
else if( c != '/' ) /* stay in state IN_CMT_FS */
state = STABLE; /* if next char is '/' as well */
break;
case OUT_CMT_STAR:
if( c == '/' )
{
cmt_level--; /* ascend one comment level */
state = STABLE;
continue;
}
else if( ! cmt_level ) /* if '*' not followed by '/' */
{ /* and outside any comment */
ungetc( c, fp_source ); /* push back this char */
return '*'; /* and return the '*' */
}
else if( c != '*' ) /* stay in state IN_CMT_FS */
state = STABLE; /* if next char is '*' as well */
break;
}
if( state == STABLE && !cmt_level ) /* if outside any comment */
return c; /* return character */
}
}
/*
------------------------ debugging code --------------------------------
#define RR(x) if(debugging) printf x
int debugging=1;
#define filter_data(fp) copy(filter_data(fp))
#undef filter_data
FILE *dfile;
dfile=fopen( "bld.out", "w" );
fclose(dfile);
char *copy(s) char *s;
{ extern FILE *dfile;
fputs(s, dfile);
fputc('\n', dfile);
return s;
}
int copy(c) int c;
{ extern FILE *dfile;
if( c == EOF) fputs( "EOF", dfile );
else if( c == BRACES) fputs( "BRACES", dfile );
else if( c == PARENS) fputs( "PARENS", dfile );
else if( c == QUOTES) fputs( "QUOTES", dfile );
else fputc(c, dfile);
return c;
}
int stdcopy(c) int c;
{
if( c == EOF) fputs( "EOF", stdout );
else if( c == BRACES) fputs( "BRACES", stdout );
else if( c == PARENS) fputs( "PARENS", stdout );
else if( c == QUOTES) fputs( "QUOTES", stdout );
else if( c == '\n') fputs( "\\n", stdout );
else fputc(c, stdout);
return c;
}
*/
help()
{
printf( "usage: bldfuncs [options] foo.c [bar.c ...]\n" );
printf( " or: bldfuncs [options] *.c\n" );
printf( "options...\n" );
printf( " -a append to output file rather than replacing it\n");
exit(1);
}