Subject: v20i016: Tools for generating software metrics, Part09/14 Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: Brian Renaud 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