[comp.sources.unix] v20i016: Tools for generating software metrics, Part09/14

rsalz@uunet.uu.net (Rich Salz) (09/20/89)

Submitted-by: Brian Renaud <!huron.ann-arbor.mi.us!bdr>
Posting-number: Volume 20, Issue 16
Archive-name: metrics/part09

---- Cut Here and unpack ----
#!/bin/sh
# this is part 9 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file src/kdsi/kdsi.c continued
#
CurArch=9
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file src/kdsi/kdsi.c"
sed 's/^X//' << 'SHAR_EOF' >> src/kdsi/kdsi.c
X	tot_cdline = tot_cmline = tot_bkline = tot_comment = 0;
X	while ( (fp = nextfp(argc, argv, &filename)) != FNULL )
X	{
X		cod_linect = com_linect = blnk_linect = comment_ct = 0;
X		filecount++;
X
X		while ( (input = GetChar(fp)) != STOP_INPUT )
X		{
X			switch ( input )
X			{
X			case NEWLINE:
X				if ( statevar == Code )
X					cod_linect++;
X				else if ( statevar == Comment )
X					com_linect++;
X				/* state is quiescent */
X				else if ( laststate == Comment )
X				{
X					/* if is supposed to catch cases where a comment
X					 * follows a line of code
X					 */
X					if ( following_com )
X						cod_linect++;
X					else
X						com_linect++;
X				}
X				else
X					blnk_linect++;
X				if ( statevar != Comment )
X				{
X					laststate = Quiescent;
X					statevar = Quiescent;
X				}
X				following_com = False;
X				break;
X			case START_COMMENT:
X				laststate = statevar;
X				statevar = Comment;
X				break;
X			case END_COMMENT:
X				comment_ct++;
X					/* if true, is a comment on same line as code */
X				if ( laststate == Code )
X					following_com = True;
X
X				laststate = Comment;
X				statevar = Quiescent;
X				break;
X			case MISC_CHARACTER:
X				if ( statevar == Quiescent )
X				{
X					laststate = statevar;
X					statevar = Code;
X				}
X				break;
X			default:
X				fprintf(stderr, "kdsi: illegal token (%d) returned from GetChar\n", input);
X				exit(1);
X				break;
X
X			}
X		}
X		if ( !only_stdin )
X			printf("%8ld %8ld %8ld %7ld  %s\n",
X				cod_linect, blnk_linect, com_linect, comment_ct,
X				filename);
X		else
X			printf("%8ld %8ld %8ld %7ld\n",
X				cod_linect, blnk_linect, com_linect, comment_ct);
X		tot_cdline += cod_linect;
X		tot_cmline += com_linect;
X		tot_bkline += blnk_linect;
X		tot_comment += comment_ct;
X	}
X	if ( !only_stdin && filecount > 1 )
X		printf("%8ld %8ld %8ld %7ld  total\n",
X			tot_cdline, tot_bkline, tot_cmline, tot_comment);
X	exit(0);
X}
X
XToken
XGetChar( file )
X	FILE	*file;
X{
X	/* return token for char type, taking into account comment delims */
X	/* ignores spaces and tabs */
X
X	register int	c;
X	register Token	retval;
X	static int	buf;
X	static Bool	inbuf = False;
X
X	do
X	{
X		if ( inbuf )
X		{
X			c = buf;
X			inbuf = False;
X		}
X		else
X			c = getc(file);
X		
X		switch ( c )
X		{
X		case EOF:
X			retval = STOP_INPUT;
X			break;
X		case '\n':
X			retval = NEWLINE;
X			break;
X		case '/':
X			buf = getc( file );
X			if ( buf == '*' )
X				retval = START_COMMENT;
X			else
X			{
X				inbuf = True;
X				retval = MISC_CHARACTER;
X			}
X			break;
X		case '*':
X			buf = getc( file );
X			if ( buf == '/' )
X				retval = END_COMMENT;
X			else
X			{
X				inbuf = True;
X				retval = MISC_CHARACTER;
X			}
X			break;
X		case ' ':
X		case '\t':
X			retval = WHITE_SPACE;
X			break;
X		default:
X			retval = MISC_CHARACTER;
X		}
X	}
X	while ( retval == WHITE_SPACE );
X
X	return (retval);
X}
X
XFILE *
Xnextfp( argc, argv, p_filename)
X	int	argc;
X	char	*argv[];
X	char	**p_filename;
X{
X	/* looks through parameters trying to return next FILE * to next
X	 * specified file
X	 * passes back a pointer to the filename as a side effect
X	 */
X	
X	static Bool	first = True;
X	static int	index = 1;
X	static FILE	*result = FNULL;
X
X	*p_filename = CNULL;
X
X	if ( result != FNULL )
X	{
X		fclose( result );
X		result = FNULL;
X	}
X	while ( index < argc && *argv[index] == '-' )
X		index++;
X
X	if ( index < argc )
X	{
X		if ( (result = fopen( argv[index], "r")) == NULL )
X		{
X			fprintf(stderr, "%s: unable to open %s for read\n",
X				argv[0], argv[index]);
X			exit(1);
X		}
X		else
X			*p_filename = argv[index];
X		index++;
X	}
X	if ( first )
X	{
X		/* if no files specified, read from stdin */
X		/* filename remains null */
X		if ( result == FNULL )
X		{
X			result = stdin;
X			only_stdin = True;
X		}
X		first = False;
X	}
X	return ( result );
X}
SHAR_EOF
echo "File src/kdsi/kdsi.c is complete"
chmod 0644 src/kdsi/kdsi.c || echo "restore of src/kdsi/kdsi.c fails"
echo "x - extracting src/kdsi/test.result (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/kdsi/test.result
X     153       25       20      11  test1.c
X     233       22        4       4  test2.y
X     311       44        8      22  test3.c
X     697       91       32      37  total
SHAR_EOF
chmod 0644 src/kdsi/test.result || echo "restore of src/kdsi/test.result fails"
echo "x - extracting src/mccabe/Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/mccabe/Makefile
X# makefile for mccabe utilities
X
XBIN=../bin
XTEST=../testfiles
X
X
Xall:	mccabe
X
Xmccabe:	mccabe.sh
X	cp mccabe.sh mccabe
X	chmod u+x mccabe
X
Xinstall:	all
X	mv mccabe $(BIN)/mccabe
X	chmod 755 $(BIN)/mccabe
X
Xclean:
X	-rm -f mccabe _test
X	
Xtest:
X	@echo results of this command should be the same as test.result
X	@cp $(TEST)/test1.c $(TEST)/test2.y $(TEST)/test3.c .
X	mccabe test1.c test2.y test3.c > _test
X	diff _test test.result
X	@/bin/rm -f test1.c test2.y test3.c
SHAR_EOF
chmod 0644 src/mccabe/Makefile || echo "restore of src/mccabe/Makefile fails"
echo "x - extracting src/mccabe/mccabe.sh (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/mccabe/mccabe.sh
X:
X# determine function complexity based on Mccabe model of program complexity
X# anything greater than 10 is usually considered bad, or at least questionable
X#
X# originally written by Rick Cobb, modified by Brian Renaud to add out
X# of function complexity detection and to fix some minor bugs
X
X# NOTE the beginning and ending braces "{}" in a function must be at the
X# same indent level, or this tools will NOT work!!
X
X# heuristics for function declaration detection:
X#	Handles three conventions:
X#
X#	|int foo()
X#	|	char *
X#	|	{
X
X#	|int foo()
X#	|char *
X#	|[	]*{
X
X#	or the preferred
X#	int
X#	foo()
X#	[]*{
X
X
Xif [ $# = 0 ]
Xthen
X	echo "usage: mccabe [-n] file [file]" > /dev/tty
X	exit 1
Xfi
X
X# the -n flag (No Header) is useful if you are using this to produce data for
X# other tools
X
XHEADER=1
Xif [ $1 = "-n" ]
Xthen
X	HEADER=0
X	shift
Xfi
X
Xif [ $HEADER = "1" ]
Xthen
X	echo "File          	Name           	Complexity	No. of returns"
X	echo "--------------	---------------	----------	--------------"
Xfi
X
Xfor file in $*
Xdo
X	stripcom ${file} |\
X	awk 'BEGIN	{
X	File = "'"${file}"'";
X	Header = '${HEADER}';
X	gotfunction = "FALSE";
X	infunction = "FALSE";
X	nofunc = "***"
X	complexity[nofunc] = 1;
X	returns[nofunc] = 0;
X	casecount[nofunc] = 0;
X	}
X
X# Should recognize the actual function:
X/^[_a-zA-Z][_a-zA-Z]*.*\(.*\)[ 	]*$/ && $1 !~ /extern/ && infunction == "FALSE"{
X
X	gotfunction="TRUE";
X
X	# strip off parens (so we can make PARMS statement)
X	endpos = index($0,"(");
X	funcname = substr($0,1,endpos-1);
X
X	# strip off beginning declaratory stuff (if any)
X	parts = split(funcname,funky," 	");
X	funcname = funky[parts];
X	complexity[funcname] = 1;	# default complexity is 1
X	casecount[funcname] = 0;
X	switchcount[funcname] = 0;
X
X	next;
X	}
X
X#do not count preprocessor lines
X/^#/	{ next; }
X
X# find end of formal parameters
X
Xgotfunction == "TRUE" && /[ 	]*{/	{
X	gotfunction = "FALSE";
X	infunction = "TRUE";
X
X	depth = index($0,"{");
X	next;
X	}
X
Xinfunction == "TRUE" && /(^|[ \t;])(if|else|while|for)($|[ \t(])/ {
X	complexity[funcname]++;
X	}
X
Xinfunction == "TRUE" && /(^|[ \t;])(switch)($|[ \t(])/ {
X	switchcount[funcname]++;
X	}
X
Xinfunction == "TRUE" && /(^|[ \t;])(case|default[ \t]*:)($|[ \t])/ {
X	casecount[funcname]++;
X	}
X
Xinfunction == "TRUE" && /(^|[ \t;])return([ \t(]|$)/	{
X	returns[funcname]++;
X	}
X
Xinfunction == "TRUE" && /(^|[ \t;])exit($|[ \t(])/	{
X	returns[funcname]++;
X	}
X
Xinfunction == "TRUE" && /}/	{ 
X	if (index($0,"}") == depth)
X		{
X		infunction = "FALSE";
X		gotfunction = "FALSE";
X		}
X	next;
X	}
X
Xinfunction == "FALSE" && /(^|[ \t;])(if|else|while|for)($|[ \t(])/ {
X	complexity[nofunc]++;
X	}
X
Xinfunction == "FALSE" && /(^|[ \t;])(case|default[ \t]*:)($|[ \t])/ {
X	casecount[nofunc]++;
X	}
X
Xinfunction == "FALSE" && /(^|[ \t;])return([ \t(]|$)/	{
X	returns[nofunc]++;
X	}
X
Xinfunction == "FALSE" && /(^|[ \t;])exit($|[ \t(])/	{
X	returns[nofunc]++;
X	}
X
XEND	{
X	count = 0;
X	for (func in complexity)
X		{
X		if ( func == nofunc &&\
X		     complexity[ func ]	== 1 &&\
X		     casecount[ func ]	== 0 &&\
X		     returns[ func ]	== 0)
X			continue;
X		count++;
X		complex=complexity[func];
X		cases=casecount[func];
X
X		if ( Header )
X			printf("%-14s\t%-15s\t%10d\t%10d\n",\
X				File, func, complex + cases, returns[func]);
X		else
X			printf("%s\t%s\t%d\t%d\n",\
X				File, func, complex + cases, returns[func]);
X		}
X	if ( count == 0 )
X		{
X		# this means that no functions were found in the file
X		if ( Header )
X			printf("%-14s\t%-15s\t%10d\t%10d\n",\
SHAR_EOF
echo "End of part 9"
echo "File src/mccabe/mccabe.sh is continued in part 10"
echo "10" > s2_seq_.tmp
exit 0


-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.