Category : C Source Code
Archive   : CCOUNT12.ZIP
Filename : CCOUNT.C

 
Output of file : CCOUNT.C contained in archive : CCOUNT12.ZIP
/* CCOUNT - A 'C' program written to gather statistics from 'C'
* source programs. The main number to count is non-comment logical
* statements (NCLS). We also report the number of PROCEDUREs + FUNCTIONs,
* comments, blank lines, and total source lines and lines of code (LOC).
* NCLS is estimated by counting the number of semicolons, not counting
* those in string constants or comments. A line that contains a semicolon
* and a comment will be counted in both categories. Preprocessor
* directives (lines starting with '#') are also included in the NCLS count.
*
* The program was originally compiled in Turbo C. If it is linked with
* "WILDARGS.OBJ", wildcards can be used for the file name. If no input
* parameter is supplied, it reads the standard input stream. Output is
* both to the console and to a file "CCOUNT.LST".
* Usage: ccount file1.c file2.c ...
* (wildcards are allowed also, e.g.
* ccount *.c *.h ...
*
* Hacked by Ted Shapin, 2/9/89 from the code for
* "bldfuncs" in Dr Dobbs Journal, Aug. 1988 by Marvin Hymowech.
*
* Version 1.1. 3/15/89.
* Removed nested comments and added unexpected EOF error msg.
* Fixed bug in counting comment lines ending in '*'.
* Added LOC count (lines of code except for comment and blank lines).
* Added preprocessor directives to NCLS count.
*
* Version 1.2 4/6/89. Add preprocessor statements to LOC. Improved
* blank line detection
*/

#include
#include
#include
#define LINT_ARGS

#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

/* function declarations for type checking */
char *get_fn_name(char *);
int get_names_one_file(char *, FILE *);
int filter_data(FILE *);
int get_ppdir_line(FILE *, char *);
int filter_quotes(FILE *);

long tc = 0; /* total comments */
long tl = 0; /* total lines */
long ts = 0; /* total non-comment semicolons */
long tf = 0; /* total functions */
long tb = 0; /* total blank lines */
long tloc = 0; /* total lines of code */
/* (following are for one file) */
long fc = 0; /* file comments */
long fl = 0; /* file lines */
long fs = 0; /* file non-comment semicolons */
long ff = 0; /* file functions */
long fb = 0; /* file blank lines */
long floc = 0; /* file lines of code */
int lastc = 0; /* save last char read */
int nlf = 0; /* flag indicating we saw a new line */
void
main(argc, argv)
int argc;
char **argv;
{
FILE *fp_out;
char *current_file;
int num_files, i;

if( argc < 2 )
{
fprintf( stderr, "Usage: CCOUNT file(s)" );
exit(1);
}

if( (fp_out = fopen("ccount.lst", "w")) == NULL )
{
fprintf( stderr, "can't open %s\n", "ccount.lst" );
exit(1);
}

/* Count each file on the command line,
* and write the list to the file funcs.txt.
*/
printf( "%13s:\t NCLS Comnts Funcs Blanks Lines LOC\n"
,"(ccount 1.2)");
fprintf( fp_out, "%13s:\t NCLS Comnts Funcs Blanks Lines LOC\n",
"(ccount 1.2)");
num_files = argc - 1;
for ( i = 1; i <= num_files; i++ )
{
current_file = strlwr(*++argv);
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);
}
}
if(num_files>1){
printf("%13s:\t%6ld %6ld %6ld %6ld %6ld %6ld\n","Totals",
ts,tc,tf,tb,tl,tloc);
fprintf( fp_out, "%13s:\t%6ld %6ld %6ld %6ld %6ld %6ld\n","Totals",
ts,tc,tf,tb,tl,tloc);}
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: number of NCLS, number of comments,
* number of functions
* 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;
#define LINE_LEN 8192
char line[LINE_LEN], 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, "%13s:", source_file_name );
printf( line );
fprintf( fp_out, line );
fc = 0; /* file comments */
fl = 0; /* file lines */
fs = 0; /* file non-comment semicolons */
ff = 0; /* file functions */
fb = 0; /* file blank lines */
floc = 0; /* file lines of code */

for(;;)
{
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++] = (char) c;
if( c == EOF )
{fprintf( fp_out, "\t%6ld %6ld %6ld %6ld %6ld %6ld\n",
fs,fc,ff,fb,fl,floc);
printf( "\t%6ld %6ld %6ld %6ld %6ld %6ld\n", fs,fc,ff,fb,fl,floc);
tc += fc; /* accumulate totals for all files */
tl += fl;
ts += fs;
tf += ff;
tb += fb;
tloc += floc;
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) );
/* Exclude the case of parenthetical expressions */
/* in a data defintion, e.g. within array brackets. */
if( !fn_name[0] )
continue;
/* skip white space */
while( (c = filter_data(fp_source)) != EOF && isspace(c) )
;
if( c == ';') /* functions type check declaration */
continue; /* so bypass it */
if( c == ',' ) /* functions type check declaration */
continue; /* so bypass it */
if( c == EOF )
{fprintf( fp_out, "\tUnexpected end-of-file\n");
printf( "\tUnexpected end-of-file\n");
break; }
/* skip any parameter declarations - */
while( c != BRACES && c != EOF ) /* eat until braces or EOF */
c = filter_data(fp_source);

++ff; /* count a function */
}
fclose(fp_source);
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;
unsigned int len;

if( (len = strlen(line)) == 0 )
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 characters and underscores */
while( (isalnum(*name_ptr) || *name_ptr == '_') && name_ptr >= line )
name_ptr--;
/* if this alleged function name begins */
if( isdigit(*++name_ptr) ) /* with a digit, return an empty string */
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 defintion.
*/
int filter_data(fp_source)
FILE *fp_source;
{
/* prototype for type checking */
int filter_parens(FILE *);

int c;

if( (c = filter_parens(fp_source)) != '=' )
return (int) c;
while( (c = filter_parens(fp_source)) != ';' && c != EOF )
;
return (int) 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;
{
/* prototype for type checking */
int filter_curly_braces(FILE *);

int paren_cnt, c;

if( (c = filter_curly_braces(fp_source)) != '(' )
return (int) c;
paren_cnt = 1;
while( paren_cnt )
switch( filter_curly_braces(fp_source) )
{
case ')':
paren_cnt--;
break;
case '(':
paren_cnt++;
break;
case EOF:
return (int) EOF;
}
return (int) 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;
{
/* prototype for type checking */
int filter_ppdir(FILE *);

int brace_cnt, c;

if( (c = filter_ppdir(fp_source)) != '{' )
return (int) 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 (int) EOF;
}
return (int) 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;
char line[MAXLINE + 1];

for(;;)
{ /* does this line begin a preprocessor directive? */
if( (c = filter_quotes(fp_source)) != '#' )
return (int) c; /* no, return character */
/* yes, store until newline or EOF */
if( (c = get_ppdir_line( fp_source, line )) == EOF )
return (int) EOF;
if( strncmp( line, "define", 6 ) ) /* if not #define directive */
continue; /* eat this line */
if( line[ strlen(line) - 1 ] != '\\' ) /* if #define line ends */
continue; /* with "\" */
else
for(;;) /* keep eating lines */
{ /* which also end with "\" */
/* store until newline or EOF */
if( (c = get_ppdir_line( fp_source, line )) == EOF )
return (int) EOF;
/* done with this #define directive if this */
/* line is not also a continuation line */
if( line[ strlen(line) - 1 ] != '\\' )
break;
}
}
}

/* Utility routine used by filter_ppdir() -
* read the character stream using filter_quotes, storing characters
* in the 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] = (char) c;
line[i] = 0; /* terminate string */
if( c == EOF )
return (int) EOF;
return (int) '\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;
{
/* prototype for type checking */
int filter_cmt(FILE *);

int c1, c2;

if( (c1 = filter_cmt(fp_source)) != '\'' && c1 != '"' )
{ if(c1==';') ++fs;
return (int) c1; } /* pass char through if not single or double quote */
for(;;)
switch( c2 = filter_cmt(fp_source) )
{
case '\\': /* beginning of an escape sequence */
filter_cmt(fp_source); /* so eat next char */
break;
case EOF:
return (int) EOF;
default:
if( c2 == c1 ) /* found end of quoted char or string */
return (int) QUOTES;
}
}

/* Returns character stream, eating comments. */
/* Returns EOF if end of file. */
/* Nested comments are not 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, in_cmt = 0;

for(;;)
{
c = fgetc(fp_source);
if (c=='\n') ++fl ; /* count a line */
if (c=='\n'&& !in_cmt) nlf = 1; /* set new line flag */
if ((c=='\n') && (lastc =='\n')) ++fb; /* count a blank line */
if (!(c==' ') && nlf)
lastc = c; /* save last */
if( c == EOF )
return (int) EOF;

switch(state)
{
case STABLE:
if( c == '*' )
state = OUT_CMT_STAR;
else if( in_cmt && c=='\n')
++fc;
else if( c == '/' )
state = IN_CMT_FS;
break;

case IN_CMT_FS:
if( c == '*' )
{
state = STABLE;
if (in_cmt==0){
in_cmt=1;
++fc; } /* count a comment */
continue;
}
else if( !in_cmt ) /* if '/' not followed by '*' */
{ /* and outside any comment */
ungetc( c, fp_source ); /* push back this char */
return (int) '/'; /* and return the '/' */
}
else if( c != '/' ) /* stay in state IN_CMT_FS */
state = STABLE; /* if next char is '/' as well */
break;

case OUT_CMT_STAR:
if( c == '/' )
{
in_cmt=0; /* leave comment */
state = STABLE;
continue;
}
else if( !in_cmt ) /* if '*' not followed by '/' */
{ /* and outside any comment */
ungetc( c, fp_source ); /* push back this char */
return (int) '*'; /* and return the '*' */
}
else if( c != '*' ) /* stay in state IN_CMT_FS */
{state = STABLE; /* if next char is '*' as well */
if (c=='\n' && in_cmt) ++fc;} /* count a comment */
break;
}
if( state == STABLE && !in_cmt ) /* if outside any comment */
{if ((c=='#') && nlf)
{++fs; ++floc; nlf=0;} /* count a preprocessor statement */
if (!(c==' ') && !(c=='\n') && nlf)
{++floc; nlf=0;} /* count a line of code */
return (int) c; } /* return character */
}
}



  3 Responses to “Category : C Source Code
Archive   : CCOUNT12.ZIP
Filename : CCOUNT.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/