/* Utility to count lines of code and comments in C source files * * count_lines_of_code.c * * Copyright (C) 2008, Sunrise Telephone Systems KK. All rights reserved. * * Use and redistribution in source and binary forms, with or without * modification, is permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the no-warranty disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the no-warranty disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of Sunrise Telephone Systems KK nor the names of its * employees or contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * o The licensee irrevocably accepts the no-warranty disclaimer below and * is legally empowered to do so, that is to say, persons and * organisations which fall under the jurisdiction of countries and * territories in which the no-warranty disclaimer is invalidated by * applicable law are not granted license to use this software. * * NO-WARRANTY DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ // --------------------------------------------------------------------------- // imports // --------------------------------------------------------------------------- #include #include #include #include "ASCII.h" //http://www.sunrisetel.net/software/devtools/ObjM2/ASCII.h #define NOT ! // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- FILE *sourcefile = NULL; unsigned int current_line; unsigned int current_col; // --------------------------------------------------------------------------- // forward declarations // --------------------------------------------------------------------------- unsigned char readchar(); unsigned char nextchar(); // --------------------------------------------------------------------------- // function: main(argc, argv) // --------------------------------------------------------------------------- // // Reads one or more arguments from the command line, interprets each argument // as a pathname to a C source file, opens the source file, counts all empty // lines, lines with C comments, lines with C++ comment and lines with code, // then prints the results for each file and grand totals to stdout. int main (int argc, const char **argv) { // counters unsigned int code_lines; unsigned int c_comment_lines; unsigned int cpp_comment_lines; unsigned int empty_lines; unsigned int last_code_line_counted; unsigned int last_c_comment_line_counted; // grand totals unsigned int grand_total_code_lines = 0; unsigned int grand_total_lines = 0; unsigned int grand_total_c_comment_lines = 0; unsigned int grand_total_cpp_comment_lines = 0; unsigned int grand_total_empty_lines = 0; // temps unsigned char ch; unsigned int index; // args const char *pathname; if (argc <= 1) { printf("usage: count_lines_of_code filename { filename }\n"); exit(1); } // end if index = 1; while (index < argc) { pathname = argv[index]; sourcefile = fopen(pathname, "r"); // file check if (sourcefile == NULL) { // file couldn't be opened if (index > 1) printf("\n"); printf("unable to find/open file %s\n", pathname); } else /* file is open for reading */ { // initialise counters current_line = 1; current_col = 1; code_lines = 0; c_comment_lines = 0; cpp_comment_lines = 0; empty_lines = 0; last_code_line_counted = 0; last_c_comment_line_counted = 0; if (index > 1) printf("\n"); ch = readchar(); while (NOT feof(sourcefile)) { // empty line check if ((current_col == 0) && (nextchar() == EOL)) { // is empty line empty_lines++; // skip end of line marker ch = readchar(); } // end empty line check // blank line check if ((current_col == 1) && ((ch == WHITESPACE) || (ch == TAB))) { while ((ch == WHITESPACE) || (ch == TAB)) ch = readchar(); if (ch == EOL) { // is empty line empty_lines++; // skip end of line marker ch = readchar(); } // end if } // end blank line check // C comment check else if ((ch == SLASH) && (nextchar() == ASTERISK)) { // is a C comment if (current_line != last_c_comment_line_counted) { c_comment_lines++; last_c_comment_line_counted = current_line; } // end if // skip slash and asterisk ch = readchar(); ch = readchar(); // skip until end of comment while (NOT ((ch == ASTERISK) && (nextchar() == SLASH))) { if (ch == EOL) { c_comment_lines++; last_c_comment_line_counted = current_line; } // end if ch = readchar(); } // end while // end of comment } // end C comment check // C++ comment check else if ((ch == SLASH) && (nextchar() == SLASH)) { // is a C++ comment cpp_comment_lines++; // skip both slashes ch = readchar(); ch = readchar(); // skip until end of line while ((NOT feof(sourcefile)) && (ch != EOL)) ch = readchar(); // end of comment } // end C++ comment check // code check else if ((IS_NOT_CONTROL(ch)) && (ch != SLASH)) { // not a comment, count as code unless already counted if (current_line != last_code_line_counted) { code_lines++; last_code_line_counted = current_line; } // end if while ((ch != EOL) && (ch != SLASH)) ch = readchar(); } // end code check else /* is control char */ { // skip it ch = readchar(); } // end all checks } // end while // close this file fclose(sourcefile); // report printf("file %s has\n", pathname); // total lines printf(" %8i (100%%) total lines\n", current_line); // code lines printf(" %8i (%3i%%) lines with code\n", code_lines, 100 * code_lines / current_line); // C comment lines printf(" %8i (%3i%%) lines with C comments\n", c_comment_lines, 100 * c_comment_lines / current_line); // C++ commet lines printf(" %8i (%3i%%) lines with C++ comments\n", cpp_comment_lines, 100 * cpp_comment_lines / current_line); // empty lines printf(" %8i (%3i%%) empty lines\n", empty_lines, 100 * empty_lines / current_line); // accumulate grand totals grand_total_lines = grand_total_lines + current_line; grand_total_code_lines = grand_total_code_lines + code_lines; grand_total_c_comment_lines = grand_total_c_comment_lines + c_comment_lines; grand_total_cpp_comment_lines = grand_total_cpp_comment_lines + cpp_comment_lines; grand_total_empty_lines = grand_total_empty_lines + empty_lines; } // end file check index++; } // end while // report grand totals if (argc > 2) { printf("\nGrand totals\n"); // total lines printf(" %8i (100%%) total lines\n", grand_total_lines); // code lines printf(" %8i (%3i%%) lines with code\n", grand_total_code_lines, 100 * grand_total_code_lines / grand_total_lines); // C comment lines printf(" %8i (%3i%%) lines with C comments\n", grand_total_c_comment_lines, 100 * grand_total_c_comment_lines / grand_total_lines); // C++ commet lines printf(" %8i (%3i%%) lines with C++ comments\n", grand_total_cpp_comment_lines, 100 * grand_total_cpp_comment_lines / grand_total_lines); // empty lines printf(" %8i (%3i%%) empty lines\n", grand_total_empty_lines, 100 * grand_total_empty_lines / grand_total_lines); } // end if return 0; } // end main // --------------------------------------------------------------------------- // function: readchar() // --------------------------------------------------------------------------- // // Reads one character from sourcefile and returns it. The coloumn counter // is incremented. Returns the linefeed character (LF) if LF or carriage // return (CR) or CRLF is read. If LF is returned, the coloumn counter // counter will be reset to 0 and the line counter will be incremented. // // pre-conditions: // // o global variable is a file handle open for reading // o global variables and hold the position // // post-conditions: // // o new current character is the character read (consumed) // o new lookahead character is the character following the character read // o global variable s and are updated // // return-value: // // o read (consumed) character is returned unsigned char readchar() { register int c; // read one character from source file c = getc(sourcefile); // handle LF style end-of-line if (c == ASCII_LF) { current_col = 0; current_line++; } // handle CRLF and CR style end-of-line else if (c == ASCII_CR) { current_col = 0; current_line++; c = getc(sourcefile); if (c != NEWLINE) { ungetc(c, sourcefile); } // end if c = NEWLINE; } // handle end-of-file else if (c == EOF) { c = 0; } else /* any other characters */ { // increment row counter current_col++; } // end if if (((unsigned char) c == 255) || (c == 0)) { printf(""); } // end if // return character return (unsigned char) c; } // end _readchar // --------------------------------------------------------------------------- // function: nextchar() // --------------------------------------------------------------------------- // // Returns the next character in sourcefile without incrementing the file // pointer and without changing the coloumn and line counters. // // pre-conditions: // // o global variable is a file handle open for reading // // post-conditions: // // o global variables and remain unchanged // // return-value: // // o lookahead character is returned unsigned char nextchar() { register int status; register int c; c = getc(sourcefile); status = ungetc(c, sourcefile); if (status == EOF) { c = 0; } // end if return (unsigned char) c; } // end _nextchar // END OF FILE