Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!uwm.edu!cs.utexas.edu!usc!rutgers!mcnc!decvax!zinn!mem From: mem@zinn.MV.COM (Mark E. Mallett) Newsgroups: comp.sources.wanted Subject: Re: C program source/comment counter wanted Message-ID: <664@zinn.MV.COM> Date: 8 Jan 90 19:07:56 GMT References: <256@dyndata.UUCP> Reply-To: mem@zinn.MV.COM (Mark E. Mallett) Organization: Zinn Computer Co., Litchfield NH Lines: 585 In article <256@dyndata.UUCP> dan@dyndata.UUCP (Dan Everhart) writes: >I'd like to find a program which will analyze a C source file, and >report the number of lines of executable code, and the number of lines >of comments. This would provide one piece of information for >productivity measurements. If you can help, please email. Thanks. Here's a program that does that, more or less. It's a wc-style program that knows how to remove comments and blank lines of a few different styles (C, C++, and a couple of assembly formats). For instance, xwc -sC files.. strips C-style comments. There's no document, but there is a usage message that tells arguments it takes. -mm- /* wc.c - yet another implementation of the traditional word counter. May 31 1988, Mark E. Mallett Dec 7 1988 mem add argument to -s to indicate commenting style; add commenting styles C_CPP, C_ASTAR, C_ASEMI. */ #include #include #ifdef OS_MSDOS #include #include #endif /* OS_MSDOS */ #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif /* FALSE */ #ifndef NUL #define NUL '\0' #endif /* NUL */ #ifdef OS_MSDOS #define OPENMODE "rb" #else #define OPENMODE "r" #define xgetc(a) getc(a) #endif /* OS_MSDOS */ extern FILE *fopen(); static char bolF = TRUE; /* beginning-of-line flag */ static char ccF; /* Print char count */ static char lcF; /* Print line count */ static char srcF = FALSE; /* Source mode */ static char wcF; /* Print word count */ static long Charttl; static int Filettl; static long Lignttl; /* Lines ignored */ static long Linettl; static long Wordttl; static enum { /* Commenting style */ C_C, /* C */ C_CPP, /* C++ */ C_ASTAR, /* Assembly with * comments */ C_ASEMI /* Assembly with ; comments */ } Cmtstyle = C_C; /* Default to C */ main( argc, argv ) int argc; /* # of command line arguments */ char **argv; /* command line args */ { int i; /* Scratch */ int n; /* Scratch again */ int c; /* A character */ char *aptr; /* Argument pointer */ /* Look at the command line arguments */ for( i = 1; i < argc; ++i ) { if (argv[i][0] != '-') /* If not option */ break; /* done with options */ else { /* Option char */ c = argv[i][1]; /* Get char */ if ( isupper(c) ) /* Be paranoid about tolower macro */ c = tolower( c ); /* don't care about arg case */ /* Find where arg is, if any */ n = i; aptr = NULL; if ( argv[i][2] != NUL ) { /* Arg attached to option */ aptr = &argv[i][2]; n = i; /* Where to set i if arg used */ } else if ( i < argc-1 ) { /* Use next */ n = i+1; aptr = argv[n]; } /* Process the option character */ switch( c ) { case 'c': /* Print char count */ ccF = TRUE; break; case 'l': /* Print line count */ lcF = TRUE; break; case 's': /* Enable source mode */ if ( aptr == NULL ) { usage(); exit(); } if ( nccmp( aptr, "c" ) == 0 ) Cmtstyle = C_C; else if ( nccmp( aptr, "c++" ) == 0 ) Cmtstyle = C_CPP; else if ( ( nccmp( aptr, "asemi" ) == 0 ) || ( strcmp( aptr, "34010" ) == 0 ) ) Cmtstyle = C_ASEMI; else if ( ( nccmp( aptr, "astar" ) == 0 ) || ( strcmp( aptr, "68020" ) == 0 ) ) Cmtstyle = C_ASTAR; else { usage(); exit(); } i = n; /* Skip over argument */ srcF = TRUE; /* Set source mode */ break; case 'w': /* Print word count */ wcF = TRUE; break; default: usage(); exit(); } } } /* Done with options -- do files */ if ( (!lcF) && (!wcF) && (!ccF) ) lcF = wcF = ccF = TRUE; if ( i == argc ) /* No filenames given? */ wc( stdin, "" ); else { for( ; i < argc; ++i ) { #ifdef OS_MSDOS msdowildfn( argv[i] ); /* Expand wildcards */ #else wcfn( argv[i] ); /* Do word count */ #endif /* OS_MSDOS */ } } /* Print summary if more than one file processed */ if ( Filettl > 1 ) { wcsep(); /* Separator bars */ wcprint( Lignttl, Linettl, Wordttl, Charttl, NULL ); } } /* *//* usage() Print program usage */ usage() { printf( "usage: wc [-c] [-l] [-w] [-s type] files...\n" ); printf( " -c to count characters\n" ); printf( " -l to count lines\n" ); printf( " -w to count words\n" ); printf( " -s type to strip blank lines & comments, type is:\n" ); printf( " C for C style commenting\n" ); printf( " C++ for C++ style\n" ); printf( " ASEMI for assembly with semicolon commenting\n" ); printf( " (also 34010)\n" ); printf( " ASTAR for assembly with asterisk commenting\n" ); printf( " (also 68020)\n" ); printf( "\n" ); printf( "If no options are given, default is -c -l -w\n" ); } /* *//* msdowildfn( ifnm ) Wildcard file search, for MS_DOS. Accepts : ifnm Name (possibly wildcarded) of file Returns : < wcfn called for each matching file > */ #ifdef OS_MSDOS msdowildfn( ifnm ) char *ifnm; { int i; int sts; struct find_t eachfile; char path[100]; char fullname[100]; /* Stupid MSDOS -- first find the drive-directory part of the filename. */ path[0] = NUL; for( i = strlen( ifnm )-1; i >= 0; --i ) if ( ( ifnm[i] == ':' ) || ( ifnm[i] == '\\' ) ) { strncpy( &path[0], ifnm, ++i ); path[i] = NUL; break; } if ( ( sts = _dos_findfirst( ifnm, _A_NORMAL, &eachfile ) ) != 0 ) { fprintf( stderr, "No such file: %s\n", ifnm ); return; } while( sts == 0 ) { sprintf( &fullname[0], "%s%s", &path[0], &eachfile.name[0] ); wcfn( &fullname[0] ); sts = _dos_findnext( &eachfile ); } } #endif /* OS_MSDOS */ /* *//* wcfn( fnP ) do wc for a named file Accepts : fnP Ptr to filename Returns : */ wcfn( fnP ) char *fnP; /* Name of file */ { FILE *fP; if ( ( fP = fopen( fnP, OPENMODE ) ) == NULL ) fprintf( stderr, "\"%s\" - can not open.\n", fnP ); else { wc( fP, fnP ); fclose( fP ); } } /* *//* wc( fP, fnP ) Do word-count for file Accepts : fP File ptr for open file fnP Name of the file Returns : */ wc( fP, fnP ) register FILE *fP; char *fnP; { register char inword; register char inline; char instr; char escaped; char nonblank; register int c; register long charC; long lignC; long lineC; long wordC; charC = lineC = wordC = lignC = 0; escaped = nonblank = FALSE; instr = 0; ++Filettl; inline = inword = FALSE; bolF = TRUE; while( ( c = xgetc( fP ) ) != EOF ) { if ( srcF ) { /* If filtering source */ if ( instr == 0 ) { /* Check for comment according to current style */ if ( Cmtstyle == C_ASTAR ) { if ( c == '*' ) { /* Comment to end of line */ skipeol( fP ); /* Skip to line end */ continue; /* Try next line */ } } else if ( Cmtstyle == C_ASEMI ) { if ( ( c == ';' ) || ( bolF && ( c == '*' ) ) ) { skipeol( fP ); continue; } } else if ( ( Cmtstyle == C_C ) || ( Cmtstyle == C_CPP ) ) { while ( c == '/' ) { /* Check for C++ style comment */ if ( ( c == '/' ) && ( Cmtstyle == C_CPP ) ) { skipeol( fP ); c = xgetc( fP ); continue; } if ( ( c = xgetc( fP ) ) == '*' ) { /* Skip to end of comment */ for( c = xgetc( fP ); c != EOF ; ) { if ( c == '\n' ) ++lignC; if ( c == '*' ) { if ( ( c = xgetc( fP ) ) == '/' ) break; } else c = xgetc( fP ); } if ( c == EOF ) { printf( " ^--- warning: unterminated comment.\n" ); break; } } c = xgetc( fP ); } } if ( c == EOF ) break; /* Check for string */ if ( ( c == '"' ) || ( c == '\'' ) ) instr = c; } else { /* In string -- check for termination */ if ( !escaped && ( c == instr ) ) instr = 0; } /* Check for escape character */ if ( c == '\\' ) escaped = !escaped; else escaped = FALSE; } ++charC; if ( c == '\n' ) { if ( !srcF || nonblank || instr ) ++lineC; else { ++lignC; --charC; } inword = inline = nonblank = FALSE; } else if ( ( c == ' ' ) || ( c == '\011' ) || ( c == '\r' ) || ( c == '\014' ) ) inword = FALSE; else { nonblank = TRUE; if ( !inword ) { ++wordC; inword = TRUE; } } } /* Check for unterminated last line */ if ( inline ) if ( !srcF || nonblank || instr ) ++lineC; else ++lignC; wcprint( lignC, lineC, wordC, charC, fnP ); Lignttl += lignC; Linettl += lineC; Wordttl += wordC; Charttl += charC; if ( instr ) printf( " ^--- warning: unterminated string.\n" ); } /* *//* skipeol( fP ) Skip to end of line, leaving EOL in input stream Accepts : fP File pointer Returns : */ skipeol( fP ) FILE *fP; /* Ptr to file var for input */ { int c; while( ( c = xgetc( fP ) ) != '\n' ) if ( c == EOF ) break; if ( c != EOF ) ungetc( c, fP ); } /* *//* wcprint( lignC, lineC, wordC, charC, fnP ) Print totals Accepts : lignC number of lines ignored lineC number of lines wordC number of words charC number of chars fnP Filename, if any (NULL if no name). Returns : */ wcprint( lignC, lineC, wordC, charC, fnP ) long lignC; /* Number of lines ignored */ long lineC; /* Number of lines */ long wordC; /* Number of words */ long charC; /* Number of characters */ char *fnP; /* Name of file, or NULL */ { if ( srcF ) printf( "%12ld", lignC ); if ( lcF ) printf( "%12ld", lineC ); if ( wcF ) printf( "%12ld", wordC ); if ( ccF ) printf( "%12ld", charC ); if ( fnP != NULL ) printf( " %s", fnP ); printf( "\n" ); } /* *//* wcsep() Print separator bars for totals Accepts : Returns : */ wcsep() { if ( srcF ) printf( " --------" ); if ( lcF ) printf( " --------" ); if ( wcF ) printf( " --------" ); if ( ccF ) printf( " --------" ); printf( "\n" ); } /* *//* nccmp( s1, s2 ) Compare two strings like strcmp, ignoring case */ int nccmp( s1, s2 ) register char *s1; /* First string */ register char *s2; /* Second string */ { register char c1; /* Char from first string */ register char c2; /* Char from second string */ for( ; ; ) { c1 = *s1++; if ( islower( c1 ) ) c1 = toupper( c1 ); c2 = *s2++; if ( islower( c2 ) ) c2 = toupper( c2 ); if ( c1 < c2 ) return( -1 ); if ( c1 > c2 ) return ( 1 ); if ( c1 == NUL ) return( 0 ); } } /* *//* xgetc( fP ) Implementation of getc for brain-damaged OS's such as MSDOS Accepts : fP File stream ptr Returns : < value > Character value, or EOF */ #ifndef xgetc /* May be a macro */ int xgetc( fP ) FILE *fP; /* Ptr to file stream */ { int c; c = getc( fP ); if ( c == '\032' ) /* if control-Z */ c = EOF; /* call it EOF */ return( c ); } #endif /* xgetc */ -- Mark E. Mallett Zinn Computer Co/ PO Box 4188/ Manchester NH/ 03103 Bus. Phone: 603 645 5069 Home: 603 424 8129 BIX: mmallett uucp: mem@zinn.MV.COM ( ...{decvax|elrond|harvard}!zinn!mem ) Northern MA and Southern NH consultants: Ask (in mail!) about MV.COM