universe@10: /* universe@34: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@57: * Copyright 2018 Mike Becker. All rights reserved. universe@34: * universe@34: * Redistribution and use in source and binary forms, with or without universe@34: * modification, are permitted provided that the following conditions are met: universe@34: * universe@34: * 1. Redistributions of source code must retain the above copyright universe@34: * notice, this list of conditions and the following disclaimer. universe@34: * universe@34: * 2. Redistributions in binary form must reproduce the above copyright universe@34: * notice, this list of conditions and the following disclaimer in the universe@34: * documentation and/or other materials provided with the distribution. universe@34: * universe@34: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@34: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@34: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE universe@34: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE universe@34: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL universe@34: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR universe@34: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER universe@34: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, universe@34: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE universe@57: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. universe@10: */ universe@10: universe@3: #include "cline.h" universe@10: #include "scanner.h" universe@10: #include "settings.h" universe@12: #include "arguments.h" universe@27: #include "regex_parser.h" universe@0: universe@12: void printHelpText() { universe@34: printf( universe@1: "\nUsage:" universe@33: "\n cline [Options] [Directories...]" universe@0: "\n\nCounts the line terminator characters (\\n) within all" universe@33: " files in the specified\ndirectories." universe@0: "\n\nOptions:" universe@21: "\n -b - binary file heuristics level (default medium)" universe@21: "\n One of: ignore low medium high" universe@28: "\n -E - Excludes any line matching the " universe@27: "\n -e - Excludes lines between and " universe@28: "\n You may use these options multiple times" universe@0: "\n -h, --help - this help text" universe@61: "\n -i - print out individual sums per file extension" universe@61: "\n (cannot be used together with -V)" universe@1: "\n -m - print information about matching files only" universe@1: "\n -s - only count files with these suffixes (separated" universe@0: "\n by commas)" universe@1: "\n -S - count any file except those with these suffixes" universe@0: "\n (separated by commas)" universe@1: "\n -r, -R - includes subdirectories" universe@14: "\n -v, --version - print out version information" universe@16: "\n -V - turn verbose output off, print the result only" universe@31: "\n\nShortcuts:" universe@57: "\n --exclude-cstyle-comments : -E '\\s*//' -e '\\s*/\\*' '\\*/\\s*'" universe@57: "\n --exclude-blank-lines : -E '^\\s*$'" universe@0: "\n\n" universe@1: "The default call without any options is:" universe@28: "\n cline ./\n\n" universe@7: "So each file in the working directory is counted. If you want to count C" universe@7: "\nsource code in your working directory and its subdirectories, type:" universe@27: "\n cline -rs .c\n" universe@28: "\nIf you want to exclude comment lines, you may use the -e/-E option." universe@27: "\nAfter a line matches the regex pattern any following line is" universe@28: "\nnot counted unless a line matches the pattern. A line is still " universe@28: "\ncounted when it does not start or end with the respective patterns." universe@28: "\nPlease note, that cline does not remove whitespace characters as this" universe@28: "\nmight not be reasonable in some cases." universe@31: "\n\nExample (C without comments):" universe@36: "\n cline -s .c,.h --exclude-cstyle-comments" universe@36: "\n"); universe@1: } universe@1: universe@14: int exit_with_version(settings_t* settings) { universe@48: printf("cline - Version: " VERSION "\n"); universe@14: destroy_settings_t(settings); universe@14: return 0; universe@14: } universe@14: universe@12: int exit_with_help(settings_t* settings, int code) { universe@50: printf("cline - Version: " VERSION "\n"); universe@12: printHelpText(); universe@8: destroy_settings_t(settings); universe@8: return code; universe@8: } universe@8: universe@1: int main(int argc, char** argv) { universe@0: universe@22: /* Settings */ universe@3: settings_t *settings = new_settings_t(); universe@5: if (settings == NULL) { universe@5: fprintf(stderr, "Memory allocation failed.\n"); universe@5: return 1; universe@5: } universe@3: universe@22: /* Get arguments */ universe@33: string_list_t *directories = new_string_list_t(); universe@33: if (directories == NULL) { universe@33: fprintf(stderr, "Memory allocation failed.\n"); universe@33: return 1; universe@33: } universe@30: char* includeSuffix = NULL; universe@30: char* excludeSuffix = NULL; universe@8: int checked = 0; universe@0: universe@1: for (int t = 1 ; t < argc ; t++) { universe@1: universe@60: int argflags = checkArgument(argv[t], "hsSrRmvVbeEi"); universe@30: int paropt = 0; universe@1: universe@59: /* h */ universe@59: if ((argflags & 1) > 0 || strcmp(argv[t], "--help") == 0) { universe@59: return exit_with_help(settings, 0); universe@59: } universe@30: /* s */ universe@30: if ((argflags & 2) > 0) { universe@30: if (!checkParamOpt(&paropt) || registerArgument(&checked, 2)) { universe@12: return exit_with_help(settings, 1); universe@0: } universe@1: t++; universe@1: if (t >= argc) { universe@12: return exit_with_help(settings, 1); universe@1: } universe@30: includeSuffix = argv[t]; universe@30: } universe@30: /* S */ universe@30: if ((argflags & 4) > 0) { universe@30: if (!checkParamOpt(&paropt) || registerArgument(&checked, 4)) { universe@30: return exit_with_help(settings, 1); universe@30: } universe@30: t++; universe@30: if (t >= argc) { universe@30: return exit_with_help(settings, 1); universe@30: } universe@30: excludeSuffix = argv[t]; universe@0: } universe@22: /* r, R */ universe@1: if ((argflags & 24) > 0) { universe@8: if (registerArgument(&checked, 24)) { universe@12: return exit_with_help(settings, 1); universe@0: } universe@3: settings->recursive = true; universe@0: } universe@22: /* m */ universe@1: if ((argflags & 32) > 0) { universe@8: if (registerArgument(&checked, 32)) { universe@12: return exit_with_help(settings, 1); universe@0: } universe@3: settings->matchesOnly = true; universe@0: } universe@22: /* v */ universe@14: if ((argflags & 64) > 0 || strcmp(argv[t], "--version") == 0) { universe@14: return exit_with_version(settings); universe@14: } universe@22: /* V */ universe@16: if ((argflags & 128) > 0) { universe@16: if (registerArgument(&checked, 128)) { universe@16: return exit_with_help(settings, 1); universe@16: } universe@16: settings->verbose = false; universe@16: } universe@22: /* b */ universe@21: if ((argflags & 256) > 0) { universe@30: if (!checkParamOpt(&paropt) || registerArgument(&checked, 256)) { universe@21: return exit_with_help(settings, 1); universe@21: } universe@21: t++; universe@21: if (t >= argc) { universe@21: return exit_with_help(settings, 1); universe@21: } universe@24: if (strcasecmp(argv[t], "ignore") == 0) { universe@21: settings->bfileHeuristics->level = BFILE_IGNORE; universe@24: } else if (strcasecmp(argv[t], "low") == 0) { universe@21: settings->bfileHeuristics->level = BFILE_LOW_ACCURACY; universe@24: } else if (strcasecmp(argv[t], "medium") == 0) { universe@21: settings->bfileHeuristics->level = BFILE_MEDIUM_ACCURACY; universe@24: } else if (strcasecmp(argv[t], "high") == 0) { universe@21: settings->bfileHeuristics->level = BFILE_HIGH_ACCURACY; universe@21: } else { universe@21: return exit_with_help(settings, 1); universe@21: } universe@21: } universe@28: /* e */ universe@27: if ((argflags & 512) > 0) { universe@30: if (!checkParamOpt(&paropt) || t + 2 >= argc) { universe@27: return exit_with_help(settings, 1); universe@27: } universe@27: t++; add_string(settings->regex->pattern_list, argv[t]); universe@27: t++; add_string(settings->regex->pattern_list, argv[t]); universe@27: } universe@28: /* E */ universe@28: if ((argflags & 1024) > 0) { universe@28: t++; universe@30: if (!checkParamOpt(&paropt) || t >= argc) { universe@28: return exit_with_help(settings, 1); universe@28: } universe@28: add_string(settings->regex->pattern_list, argv[t]); universe@28: add_string(settings->regex->pattern_list, "$"); universe@28: } universe@61: /* i */ universe@60: if ((argflags & 2048) > 0) { universe@61: // cannot be used together with -V universe@61: if (registerArgument(&checked, 128)) { universe@61: return exit_with_help(settings, 1); universe@61: } universe@61: settings->individual_sums = true; universe@60: } universe@1: if (argflags == 0) { universe@31: /* SHORTCUTS */ universe@31: if (strcmp(argv[t], "--exclude-cstyle-comments") == 0) { universe@31: add_string(settings->regex->pattern_list, "\\s*//"); universe@31: add_string(settings->regex->pattern_list, "$"); universe@31: add_string(settings->regex->pattern_list, "\\s*/\\*"); universe@31: add_string(settings->regex->pattern_list, "\\*/\\s*"); universe@57: } else if (strcmp(argv[t], "--exclude-blank-lines") == 0) { universe@57: add_string(settings->regex->pattern_list, "^\\s*$"); universe@57: add_string(settings->regex->pattern_list, "$"); universe@31: } universe@31: /* Path */ universe@33: else { universe@33: add_string(directories, argv[t]); universe@0: } universe@0: } universe@0: } universe@0: universe@22: /* Find tokens */ universe@30: parseCSL(includeSuffix, settings->includeSuffixes); universe@30: parseCSL(excludeSuffix, settings->excludeSuffixes); universe@0: universe@33: /* Scan directories */ universe@28: if (regex_compile_all(settings->regex)) { universe@61: scanresult_t* result = new_scanresult_t(settings); universe@44: /* Don't waste memory when only the total sum is needed */ universe@44: string_list_t *output = settings->verbose ? new_string_list_t() : NULL; universe@44: char *outbuf; universe@44: universe@60: int total = 0; universe@33: if (directories->count == 0) { universe@33: add_string(directories, "."); universe@33: } universe@33: for (int t = 0 ; t < directories->count ; t++) { universe@60: scanDirectory((scanner_t){directories->items[t], 0}, settings, universe@61: output, result); universe@61: total += result->lines; universe@44: if (directories->count > 1 ) { universe@44: outbuf = (char*) malloc(81); universe@44: memset(outbuf, '-', 79); universe@44: outbuf[79] = '\n'; universe@44: outbuf[80] = 0; universe@44: add_string(output, outbuf); universe@44: outbuf = (char*) malloc(81); universe@60: snprintf(outbuf, 81, "%-63s%10d lines\n", directories->items[t], universe@61: result->lines); universe@44: add_string(output, outbuf); universe@44: outbuf = (char*) malloc(81); universe@44: memset(outbuf, '-', 79); universe@44: outbuf[79] = '\n'; universe@44: outbuf[80] = 0; universe@44: add_string(output, outbuf); universe@33: } universe@33: } universe@33: destroy_string_list_t(directories); universe@0: universe@44: /* Print result */ universe@44: if (settings->verbose) { universe@44: for (int i = 0 ; i < output->count ; i++) { universe@44: printf("%s", output->items[i]); universe@44: free(output->items[i]); universe@44: } universe@44: universe@61: if (result->ext) { universe@61: if (result->ext->count > 0) { universe@61: for (int t = 0 ; t < 79 ; t++) { universe@61: printf("="); universe@61: } universe@61: printf("\nIndividual sums:\n"); universe@61: for (int t = 0 ; t < result->ext->count ; t++) { universe@61: printf(" %-62s%10d lines\n", universe@61: result->ext->extensions[t], universe@61: result->ext->lines[t]); universe@61: } universe@61: } universe@61: } universe@61: universe@44: for (int t = 0 ; t < 79 ; t++) { universe@44: printf("="); universe@44: } universe@60: printf("\n%73d lines\n", total); universe@44: universe@44: if (settings->confusing_lnlen && universe@44: settings->regex->pattern_list->count > 0) { universe@44: universe@44: printf("\nSome files contain too long lines.\n" universe@44: "The regex parser currently supports a maximum line length of %d." universe@44: "\nThe result might be wrong.\n", REGEX_MAX_LINELENGTH); universe@44: } universe@44: } else { universe@60: printf("%d", total); universe@28: } universe@61: destroy_scanresult_t(result); universe@44: destroy_string_list_t(output); universe@33: destroy_settings_t(settings); universe@16: } universe@16: universe@16: fflush(stdout); universe@28: fflush(stderr); universe@0: return 0; universe@0: }