/* File : metrics.c Author : Richard A. O'Keefe Updated: 03/23/09 Purpose: Compute some source metrics for C and Java. */ #include #include #include /* Options: metrics [-j] [-q] [-lsdrbfmc] [-h] [--] file... -q don't complain about unreadable files -j Java, means to report NMETH NCLSS instead of NFUNC -h print header line -l report lines -s report SLOC (;) -d report #directives (turned off by -j) -r report remarks -b report blank lines -f report functions (for -j means the same as -m) -m report methods (for C means the same as -f) -c report classes -- end of options */ static int be_quiet = 0, process_java = 0, want_header = 1; static char const c_default[] = "lsfdrb", java_default[] = "lsfcrb", *opts; static char opts_buff[16]; static void process(FILE *f, char const *n) { int n_directives = 0; int n_blanks = 0; int n_comments = 0; int n_lines = 0; int n_sloc = 0; int n_methods = 0; int n_classes = 0; int depth = 0; char buff[128*1024]; char const *p; char c; enum { Normal, In_Comment, In_Char, In_String } state = Normal; while (fgets(buff, sizeof buff, f) != 0) { n_lines++; p = buff; while ((c = *p) == ' ' || c == '\t') p++; if (c == '\r' || c == '\n') { n_blanks++; } else if (c == '#' && state == Normal) { n_directives++; } else for (p = buff; (c = *p) != '\0'; p++) { switch (state) { case Normal: switch (c) { case '{': depth++; break; case '}': depth--; if (p[1] == ',' || p[1] == ';') break; if (depth == 0) { if (process_java) n_classes++; else n_methods++; } else if (depth == 1 && process_java) { n_methods++; } break; case '"': state = In_String; break; case '\'': state = In_Char; break; case '/': if (p[1] =='/') { p = "x"; n_comments++; } else if (p[1] == '*') { p++; state = In_Comment; n_comments++; } break; case ';': n_sloc++; break; default: break; } break; case In_Comment: if (c == '*' && p[1] == '/') { p++; state = Normal; } break; case In_String: if (c == '"') { state = Normal; } else if (c == '\\' && p[1] != '\0' && p[1] != '\n') { p++; } break; case In_Char: if (c == '\'') { state = Normal; } else if (c == '\\' && p[1] != '\0' && p[1] != '\n') { p++; } break; } } } p = opts; for (;;) switch (*p++) { case 'l': printf("%5d ", n_lines); break; case 's': printf("%5d ", n_sloc); break; case 'd': printf("%5d ", n_directives); break; case 'r': printf("%5d ", n_comments); break; case 'b': printf("%5d ", n_blanks); break; case 'f': printf("%5d ", n_methods); break; case 'c': printf("%5d ", n_classes); break; case '\0': printf("%s\n", n); return; default: break; } } static void usage(void) { fprintf(stderr, "Usage: cmet [-q] [-h] [-j] [-lsdrbfmc] file...\n"); exit(EXIT_FAILURE); } static void print_header(void) { char const *o = opts; for (;;) switch (*o++) { case 'l': printf("LINES "); break; case 's': printf(" SLOC "); break; case 'd': printf("DIRCT "); break; case 'r': printf("RMRKS "); break; case 'b': printf("BLNKS "); break; case 'f': printf(process_java ? "METHS " : "FUNCS "); break; case 'c': printf("CLASS "); break; case '\0': printf("FILE\n"); return; default : break; } } int main(int argc, char **argv) { int i; FILE *f; int r = EXIT_SUCCESS; int opt; char *opst = opts_buff; while ((opt = getopt(argc, argv, "jJqQhHlLsSdDrRbBfFmMcC")) != -1) { switch (opt) { case 'j': case 'J': process_java = 1; break; case 'q': case 'Q': be_quiet = 1; break; case 'h': case 'H': want_header = 0; break; case 'l': case 'L': *opst++ = 'l'; break; case 's': case 'S': *opst++ = 's'; break; case 'd': case 'D': *opst++ = 'd'; break; case 'r': case 'R': *opst++ = 'r'; break; case 'b': case 'B': *opst++ = 'b'; break; case 'f': case 'F': case 'm': case 'M': *opst++ = 'f'; break; case 'c': case 'C': *opst++ = 'c'; break; case '?': default: usage(); } } if (opst != opts_buff) { *opst = '\0'; opts = (char const *)opts_buff; } else { opts = process_java ? java_default : c_default; } if (want_header) print_header(); if (argv[optind] == 0) { process(stdin, "/dev/stdin"); } else for (i = optind; argv[i] != 0; i++) { f = fopen(argv[i], "r"); if (f == 0) { if (!be_quiet) perror(argv[i]); r = EXIT_FAILURE; } else { process(f, argv[i]); fclose(f); } } return r; }