src/cline.c

Fri, 03 Jun 2022 20:05:15 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 03 Jun 2022 20:05:15 +0200
changeset 66
be2084398c37
parent 62
7f5f9f43d0c0
permissions
-rw-r--r--

new feature: count non-whitespace characters

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 
     3  * Copyright 2018 Mike Becker. All rights reserved.
     4  * 
     5  * Redistribution and use in source and binary forms, with or without
     6  * modification, are permitted provided that the following conditions are met:
     7  * 
     8  * 1. Redistributions of source code must retain the above copyright
     9  * notice, this list of conditions and the following disclaimer.
    10  * 
    11  * 2. Redistributions in binary form must reproduce the above copyright
    12  * notice, this list of conditions and the following disclaimer in the
    13  * documentation and/or other materials provided with the distribution.
    14  * 
    15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    25  */
    27 #include "cline.h"
    28 #include "scanner.h"
    29 #include "settings.h"
    30 #include "arguments.h"
    31 #include "regex_parser.h"
    33 void printHelpText() {
    34   printf(
    35     "\nUsage:"
    36     "\n      cline [Options] [Directories...]"
    37     "\n\nCounts the line terminator characters (\\n) within all"
    38     " files in the specified\ndirectories."
    39     "\n\nOptions:"
    40     "\n  -b <level>          - binary file heuristics level (default medium)"
    41     "\n                        One of: ignore low medium high"
    42     "\n  -c                  - Count non-whitespace characters instead of lines"
    43     "\n  -E <pattern>        - Excludes any line matching the <pattern>"
    44     "\n  -e <start> <end>    - Excludes lines between <start> and <end>"
    45     "\n                        You may use these options multiple times"
    46     "\n  -h, --help          - this help text"
    47     "\n  -i                  - print out individual sums per file extension"
    48     "\n                        (cannot be used together with -V)"
    49     "\n  -m                  - print information about matching files only"
    50     "\n  -s <suffixes>       - only count files with these suffixes (separated"
    51     "\n                        by commas)"
    52     "\n  -S <suffixes>       - count any file except those with these suffixes"
    53     "\n                        (separated by commas)"
    54     "\n  -r, -R              - includes subdirectories"
    55     "\n  -v, --version       - print out version information"
    56     "\n  -V                  - turn verbose output off, print the result only"
    57     "\n\nShortcuts:"
    58     "\n  --exclude-cstyle-comments : -E '\\s*//' -e '\\s*/\\*' '\\*/\\s*'"
    59     "\n  --exclude-blank-lines     : -E '^\\s*$'"
    60     "\n\n"
    61     "The default call without any options is:"    
    62     "\n  cline ./\n\n"
    63     "So each file in the working directory is counted. If you want to count C"
    64     "\nsource code in your working directory and its subdirectories, type:"
    65     "\n  cline -rs .c\n"
    66     "\nIf you want to exclude comment lines, you may use the -e/-E option."
    67     "\nAfter a line matches the regex pattern <start>, this and any following"
    68     "\nline is not counted unless a line matches the <end> pattern. A line is"
    69     "\nstill counted when it does not start or end with the respective pattern."
    70     "\nPlease note, that cline does not trim the lines before matching against"
    71     "\nthe pattern."
    72     "\n\nExample (C without comments):"
    73     "\n  cline -s .c,.h --exclude-cstyle-comments"
    74     "\n");
    75 }
    77 int exit_with_version(settings_t* settings) {
    78   printf("cline - Version: " VERSION "\n");
    79   destroy_settings_t(settings);
    80   return 0;
    81 }
    83 int exit_with_help(settings_t* settings, int code) {
    84   printf("cline - Version: " VERSION "\n");
    85   printHelpText();
    86   destroy_settings_t(settings);
    87   return code;
    88 }
    90 int main(int argc, char** argv) {
    92   /* Settings */
    93   settings_t *settings = new_settings_t();
    94   if (settings == NULL) {
    95     fprintf(stderr, "Memory allocation failed.\n");
    96     return 1;
    97   }
    99   /* Get arguments */
   100   string_list_t *directories = new_string_list_t();
   101   if (directories == NULL) {
   102     fprintf(stderr, "Memory allocation failed.\n");
   103     return 1;
   104   }
   105   char* includeSuffix = NULL;
   106   char* excludeSuffix = NULL;
   107   int checked = 0;
   109   for (int t = 1 ; t < argc ; t++) {
   111     int argflags = checkArgument(argv[t], "hsSrRmvVbeEic");
   112     int paropt = 0;
   114     /* h */
   115     if ((argflags & 1) > 0 || strcmp(argv[t], "--help") == 0) {
   116       return exit_with_help(settings, 0);
   117     }
   118     /* s */
   119     if ((argflags & 2) > 0) {
   120       if (!checkParamOpt(&paropt) || registerArgument(&checked, 2)) {
   121         return exit_with_help(settings, 1);
   122       }
   123       t++;
   124       if (t >= argc) {
   125         return exit_with_help(settings, 1);
   126       }
   127       includeSuffix = argv[t];
   128     }
   129     /* S */
   130     if ((argflags & 4) > 0) {
   131       if (!checkParamOpt(&paropt) || registerArgument(&checked, 4)) {
   132         return exit_with_help(settings, 1);
   133       }
   134       t++;
   135       if (t >= argc) {
   136         return exit_with_help(settings, 1);
   137       }
   138       excludeSuffix = argv[t];
   139     }
   140     /* r, R */
   141     if ((argflags & 24) > 0) {
   142       if (registerArgument(&checked, 24)) {
   143         return exit_with_help(settings, 1);
   144       }
   145       settings->recursive = true;
   146     }
   147     /* m */
   148     if ((argflags & 32) > 0) {
   149       if (registerArgument(&checked, 32)) {
   150         return exit_with_help(settings, 1);
   151       }
   152       settings->matchesOnly = true;
   153     }
   154     /* v */
   155     if ((argflags & 64) > 0 || strcmp(argv[t], "--version") == 0) {
   156       return exit_with_version(settings);
   157     }
   158     /* V */
   159     if ((argflags & 128) > 0) {
   160       if (registerArgument(&checked, 128)) {
   161         return exit_with_help(settings, 1);
   162       }
   163       settings->verbose = false;
   164     }
   165     /* b */
   166     if ((argflags & 256) > 0) {
   167       if (!checkParamOpt(&paropt) || registerArgument(&checked, 256)) {
   168         return exit_with_help(settings, 1);
   169       }
   170       t++;
   171       if (t >= argc) {
   172         return exit_with_help(settings, 1);
   173       }
   174       if (strcasecmp(argv[t], "ignore") == 0) {
   175         settings->bfileHeuristics->level = BFILE_IGNORE;
   176       } else if (strcasecmp(argv[t], "low") == 0) {
   177         settings->bfileHeuristics->level = BFILE_LOW_ACCURACY;
   178       } else if (strcasecmp(argv[t], "medium") == 0) {
   179         settings->bfileHeuristics->level = BFILE_MEDIUM_ACCURACY;
   180       } else if (strcasecmp(argv[t], "high") == 0) {
   181         settings->bfileHeuristics->level = BFILE_HIGH_ACCURACY;
   182       } else {
   183         return exit_with_help(settings, 1);
   184       }
   185     }
   186     /* e */
   187     if ((argflags & 512) > 0) {
   188       if (!checkParamOpt(&paropt) || t + 2 >= argc) {
   189         return exit_with_help(settings, 1);
   190       }
   191       t++; add_string(settings->regex->pattern_list, argv[t]);
   192       t++; add_string(settings->regex->pattern_list, argv[t]);
   193     }
   194     /* E */
   195     if ((argflags & 1024) > 0) {
   196       t++;
   197       if (!checkParamOpt(&paropt) || t >= argc) {
   198         return exit_with_help(settings, 1);
   199       }
   200       add_string(settings->regex->pattern_list, argv[t]);
   201       add_string(settings->regex->pattern_list, "$");
   202     }
   203     /* i */
   204     if ((argflags & 2048) > 0) {
   205       /* cannot be used together with -V */
   206       if (registerArgument(&checked, 128)) {
   207         return exit_with_help(settings, 1);
   208       }
   209       settings->individual_sums = true;
   210     }
   211     if ((argflags & 4096) > 0) {
   212         if (registerArgument(&checked, 4096)) {
   213             return exit_with_help(settings, 1);
   214         }
   215         settings->count_chars = true;
   216         settings->regex->count_chars = true;
   217     }
   218     if (argflags == 0) {
   219       /* SHORTCUTS */
   220       if (strcmp(argv[t], "--exclude-cstyle-comments") == 0) {
   221         add_string(settings->regex->pattern_list, "\\s*//");
   222         add_string(settings->regex->pattern_list, "$");
   223         add_string(settings->regex->pattern_list, "\\s*/\\*");
   224         add_string(settings->regex->pattern_list, "\\*/\\s*");
   225       } else if (strcmp(argv[t], "--exclude-blank-lines") == 0) {
   226         add_string(settings->regex->pattern_list, "^\\s*$");
   227         add_string(settings->regex->pattern_list, "$");
   228       }
   229       /* Path */
   230       else {
   231         add_string(directories, argv[t]);
   232       }
   233     }
   234   }
   236   /* Find tokens */
   237   parseCSL(includeSuffix, settings->includeSuffixes);
   238   parseCSL(excludeSuffix, settings->excludeSuffixes);
   240   /* Scan directories */
   241   if (regex_compile_all(settings->regex)) {
   242     scanresult_t* result = new_scanresult_t(settings);
   243     /* Don't waste memory when only the total sum is needed */
   244     string_list_t *output = settings->verbose ? new_string_list_t() : NULL;
   245     char *outbuf;
   246     const char* result_type = settings->count_chars ? "chars" : "lines";
   248     unsigned total = 0;
   249     if (directories->count == 0) {
   250         add_string(directories, ".");
   251     }
   252     for (unsigned t = 0 ; t < directories->count ; t++) {
   253       scanDirectory((scanner_t){directories->items[t], 0}, settings,
   254           output, result);
   255       total += result->result;
   256       if (directories->count > 1 ) {
   257         outbuf = (char*) malloc(81);
   258         memset(outbuf, '-', 79);
   259         outbuf[79] = '\n';
   260         outbuf[80] = 0;
   261         add_string(output, outbuf);
   262         outbuf = (char*) malloc(81);
   263         snprintf(outbuf, 81, "%-63s%10u %s\n", directories->items[t],
   264                 result->result, result_type);
   265         add_string(output, outbuf);
   266         outbuf = (char*) malloc(81);
   267         memset(outbuf, '-', 79);
   268         outbuf[79] = '\n';
   269         outbuf[80] = 0;
   270         add_string(output, outbuf);
   271       }
   272     }
   273     destroy_string_list_t(directories);
   275     /* Print result */
   276     if (settings->verbose) {
   277       for (int i = 0 ; i < output->count ; i++) {
   278         printf("%s", output->items[i]);
   279         free(output->items[i]);
   280       }
   282       if (result->ext) {
   283         if (result->ext->count > 0) {
   284           for (unsigned t = 0 ; t < 79 ; t++) {
   285             printf("=");
   286           }
   287           printf("\nIndividual sums:\n");
   288           for (unsigned t = 0 ; t < result->ext->count ; t++) {
   289             printf(" %-62s%10u %s\n",
   290                    result->ext->extensions[t],
   291                    result->ext->result[t],
   292                    result_type);
   293           }
   294         }
   295       }
   297       for (unsigned t = 0 ; t < 79 ; t++) {
   298         printf("=");
   299       }
   300       printf("\n%73d %s\n", total, result_type);
   302       if (settings->confusing_lnlen &&
   303           settings->regex->pattern_list->count > 0) {
   305         printf("\nSome files contain too long lines.\n"
   306           "The parser currently supports a maximum line length of %u."
   307           "\nThe result might be wrong.\n", MAX_LINELENGTH);
   308       }
   309     } else {
   310       printf("%u", total);
   311     }
   312     destroy_scanresult_t(result);
   313     destroy_string_list_t(output);
   314     destroy_settings_t(settings);
   315   }
   317   fflush(stdout);
   318   fflush(stderr);
   319   return 0;
   320 }

mercurial