new feature: count non-whitespace characters v1.4

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 65
49fa681f3a7e
child 67
767fe7a007f7

new feature: count non-whitespace characters

prebuild.sh file | annotate | diff | comparison | revisions
src/arguments.c file | annotate | diff | comparison | revisions
src/cline.c file | annotate | diff | comparison | revisions
src/regex_parser.c file | annotate | diff | comparison | revisions
src/regex_parser.h file | annotate | diff | comparison | revisions
src/scanner.c file | annotate | diff | comparison | revisions
src/scanner.h file | annotate | diff | comparison | revisions
src/settings.c file | annotate | diff | comparison | revisions
src/settings.h file | annotate | diff | comparison | revisions
src/string_list.h file | annotate | diff | comparison | revisions
test/Makefile.am file | annotate | diff | comparison | revisions
test/codeonly.sh file | annotate | diff | comparison | revisions
test/codeonly_chars.sh file | annotate | diff | comparison | revisions
test/nocomments.sh file | annotate | diff | comparison | revisions
test/testfile.c file | annotate | diff | comparison | revisions
test/total.sh file | annotate | diff | comparison | revisions
test/total_chars.sh file | annotate | diff | comparison | revisions
--- a/src/arguments.c	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/arguments.c	Fri Jun 03 20:05:15 2022 +0200
@@ -27,12 +27,12 @@
 #include "arguments.h"
 
 int checkArgument(const char* arg, const char* expected) {
-  int len = strlen(expected);
+  size_t len = strlen(expected);
   int ret = 0;
 
   if (arg[0] == '-') {
     if (arg[1] != '-') {
-      for (int t = 0 ; t < len ; t++) {
+      for (size_t t = 0 ; t < len ; t++) {
         ret |= (strchr(arg, expected[t])?1:0) << t;
       }
     }
--- a/src/cline.c	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/cline.c	Fri Jun 03 20:05:15 2022 +0200
@@ -39,6 +39,7 @@
     "\n\nOptions:"
     "\n  -b <level>          - binary file heuristics level (default medium)"
     "\n                        One of: ignore low medium high"
+    "\n  -c                  - Count non-whitespace characters instead of lines"
     "\n  -E <pattern>        - Excludes any line matching the <pattern>"
     "\n  -e <start> <end>    - Excludes lines between <start> and <end>"
     "\n                        You may use these options multiple times"
@@ -63,11 +64,11 @@
     "\nsource code in your working directory and its subdirectories, type:"
     "\n  cline -rs .c\n"
     "\nIf you want to exclude comment lines, you may use the -e/-E option."
-    "\nAfter a line matches the regex pattern <start> any following line is"
-    "\nnot counted unless a line matches the <end> pattern. A line is still "
-    "\ncounted when it does not start or end with the respective patterns."
-    "\nPlease note, that cline does not remove whitespace characters as this"
-    "\nmight not be reasonable in some cases."
+    "\nAfter a line matches the regex pattern <start>, this and any following"
+    "\nline is not counted unless a line matches the <end> pattern. A line is"
+    "\nstill counted when it does not start or end with the respective pattern."
+    "\nPlease note, that cline does not trim the lines before matching against"
+    "\nthe pattern."
     "\n\nExample (C without comments):"
     "\n  cline -s .c,.h --exclude-cstyle-comments"
     "\n");
@@ -107,7 +108,7 @@
 
   for (int t = 1 ; t < argc ; t++) {
 
-    int argflags = checkArgument(argv[t], "hsSrRmvVbeEi");
+    int argflags = checkArgument(argv[t], "hsSrRmvVbeEic");
     int paropt = 0;
 
     /* h */
@@ -201,12 +202,19 @@
     }
     /* i */
     if ((argflags & 2048) > 0) {
-      // cannot be used together with -V
+      /* cannot be used together with -V */
       if (registerArgument(&checked, 128)) {
         return exit_with_help(settings, 1);
       }
       settings->individual_sums = true;
     }
+    if ((argflags & 4096) > 0) {
+        if (registerArgument(&checked, 4096)) {
+            return exit_with_help(settings, 1);
+        }
+        settings->count_chars = true;
+        settings->regex->count_chars = true;
+    }
     if (argflags == 0) {
       /* SHORTCUTS */
       if (strcmp(argv[t], "--exclude-cstyle-comments") == 0) {
@@ -235,15 +243,16 @@
     /* Don't waste memory when only the total sum is needed */
     string_list_t *output = settings->verbose ? new_string_list_t() : NULL;
     char *outbuf;
+    const char* result_type = settings->count_chars ? "chars" : "lines";
     
-    int total = 0;
+    unsigned total = 0;
     if (directories->count == 0) {
         add_string(directories, ".");
     }
-    for (int t = 0 ; t < directories->count ; t++) {
+    for (unsigned t = 0 ; t < directories->count ; t++) {
       scanDirectory((scanner_t){directories->items[t], 0}, settings,
           output, result);
-      total += result->lines;
+      total += result->result;
       if (directories->count > 1 ) {
         outbuf = (char*) malloc(81);
         memset(outbuf, '-', 79);
@@ -251,8 +260,8 @@
         outbuf[80] = 0;
         add_string(output, outbuf);
         outbuf = (char*) malloc(81);
-        snprintf(outbuf, 81, "%-63s%10d lines\n", directories->items[t],
-                result->lines);
+        snprintf(outbuf, 81, "%-63s%10u %s\n", directories->items[t],
+                result->result, result_type);
         add_string(output, outbuf);
         outbuf = (char*) malloc(81);
         memset(outbuf, '-', 79);
@@ -272,32 +281,33 @@
       
       if (result->ext) {
         if (result->ext->count > 0) {
-          for (int t = 0 ; t < 79 ; t++) {
+          for (unsigned t = 0 ; t < 79 ; t++) {
             printf("=");
           }
           printf("\nIndividual sums:\n");
-          for (int t = 0 ; t < result->ext->count ; t++) {
-            printf(" %-62s%10d lines\n",
-                    result->ext->extensions[t],
-                    result->ext->lines[t]);
+          for (unsigned t = 0 ; t < result->ext->count ; t++) {
+            printf(" %-62s%10u %s\n",
+                   result->ext->extensions[t],
+                   result->ext->result[t],
+                   result_type);
           }
         }
       }
       
-      for (int t = 0 ; t < 79 ; t++) {
+      for (unsigned t = 0 ; t < 79 ; t++) {
         printf("=");
       }
-      printf("\n%73d lines\n", total);
+      printf("\n%73d %s\n", total, result_type);
 
       if (settings->confusing_lnlen &&
           settings->regex->pattern_list->count > 0) {
 
         printf("\nSome files contain too long lines.\n"
-          "The regex parser currently supports a maximum line length of %d."
-          "\nThe result might be wrong.\n", REGEX_MAX_LINELENGTH);
+          "The parser currently supports a maximum line length of %u."
+          "\nThe result might be wrong.\n", MAX_LINELENGTH);
       }
     } else {
-      printf("%d", total);
+      printf("%u", total);
     }
     destroy_scanresult_t(result);
     destroy_string_list_t(output);
--- a/src/regex_parser.c	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/regex_parser.c	Fri Jun 03 20:05:15 2022 +0200
@@ -25,26 +25,28 @@
  */
 
 #include "regex_parser.h"
+#include <ctype.h>
 
 regex_parser_t* new_regex_parser_t() {
   regex_parser_t* ret = malloc(sizeof(regex_parser_t));
   if (ret != NULL) {
     ret->pattern_list = new_string_list_t();
-    ret->matched_lines = 0;
+    ret->matched_counted = 0;
     ret->pattern_match = 0;
     ret->compiled_patterns = NULL;
     ret->compiled_pattern_count = 0;
+    ret->count_chars = false;
   }
   return ret;
 }
 
 void regex_parser_reset(regex_parser_t* parser) {
-  parser->pattern_match = parser->matched_lines = 0;
+  parser->pattern_match = parser->matched_counted = 0;
 }
 
 void regex_destcomppats(regex_parser_t* parser) {
   if (parser->compiled_patterns != NULL) {
-    for (int i = 0 ; i < parser->compiled_pattern_count ; i++) {
+    for (unsigned i = 0 ; i < parser->compiled_pattern_count ; i++) {
       if (parser->compiled_patterns[i] != NULL) {
         free(parser->compiled_patterns[i]);
       }
@@ -65,13 +67,27 @@
   return parser->pattern_match > 0;
 }
 
+static unsigned regex_parser_count_chars(const char* input,
+                                         unsigned start, unsigned end) {
+  unsigned ret = 0;
+  for (unsigned i = start ; i < end ; i++) {
+    ret += isspace(input[i]) ? 0 : 1;
+  }
+  return ret;
+}
+
 int regex_parser_do(regex_parser_t* parser, char* input) {
   int err = REG_NOMATCH;
   if (parser->compiled_pattern_count > 0) {
     regmatch_t match;
 
     if (regex_parser_matching(parser)) {
-      parser->matched_lines++;
+      if (parser->count_chars) {
+        parser->matched_counted +=
+            regex_parser_count_chars(input, 0, strlen(input));
+      } else {
+        parser->matched_counted++;
+      }
 
       err = regexec(parser->compiled_patterns[parser->pattern_match],
           input, 1, &match, 0);
@@ -80,25 +96,42 @@
       }
       if (err == 0) {
         parser->pattern_match = 0;
-        /* do not match line, if it does not end with the pattern */
-        if (match.rm_eo < strlen(input)) {
-          parser->matched_lines--;
+        size_t input_len = strlen(input);
+        if (match.rm_eo < input_len) {
+          if (parser->count_chars) {
+            /* do not exclude chars that occur after pattern end */
+            parser->matched_counted -=
+                regex_parser_count_chars(input, match.rm_eo, input_len);
+          } else {
+            /* do not exclude line, if it does not end with the pattern */
+            parser->matched_counted--;
+          }
         }
       }
     } else {
-      for (int i = 0 ; i < parser->compiled_pattern_count - 1 ; i += 2) {
+      for (unsigned i = 0 ; i < parser->compiled_pattern_count - 1 ; i += 2) {
         err = regexec(parser->compiled_patterns[i], input, 1, &match, 0);
         if (err > 0 && err != REG_NOMATCH) {
           fprintf(stderr, "Regex-Error: 0x%08x", err);
         }
         if (err == 0) {
+          /* a start pattern matches, start counting */
+          parser->matched_counted = 0;
+          /* Check, if end pattern is also in this line */
           parser->pattern_match = i+1;
-          parser->matched_lines = 0;
-          /* Check, if end pattern is also in this line */
           regex_parser_do(parser, input);
-          /* do not match line, if it does not start with the pattern */
-          if (match.rm_so > 0 && parser->matched_lines > 0) {
-            parser->matched_lines--;
+          /* If something was found, determine what exactly to exclude */
+          if (parser->matched_counted > 0) {
+            if (parser->count_chars) {
+              /* do not exclude the characters before the pattern */
+              parser->matched_counted -=
+                  regex_parser_count_chars(input, 0, match.rm_so);
+            } else {
+              /* do not match line, if it does not start with the pattern */
+              if (match.rm_so > 0) {
+                parser->matched_counted--;
+              }
+            }
           }
           break;
         }
@@ -117,7 +150,7 @@
     parser->compiled_pattern_count = pcount;
 
     regex_t* re;
-    for (int i = 0 ; i < pcount ; i++) {
+    for (unsigned i = 0 ; i < pcount ; i++) {
       re = malloc(sizeof(regex_t));
       if (regcomp(re, parser->pattern_list->items[i], REG_EXTENDED) == 0) {
         parser->compiled_patterns[i] = re;
--- a/src/regex_parser.h	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/regex_parser.h	Fri Jun 03 20:05:15 2022 +0200
@@ -27,7 +27,7 @@
 #ifndef REGEX_PARSER_H_
 #define REGEX_PARSER_H_
 
-#define REGEX_MAX_LINELENGTH           2048
+#define MAX_LINELENGTH           4096u
 
 #include <sys/types.h>
 #include <stdbool.h>
@@ -40,7 +40,8 @@
   size_t compiled_pattern_count;
   unsigned int pattern_match; /* save position of end pattern to match -
                                  NULL when a start pattern shall match first */
-  unsigned int matched_lines;
+  unsigned int matched_counted;
+  bool count_chars;
 } regex_parser_t;
 
 #ifdef _cplusplus
--- a/src/scanner.c	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/scanner.c	Fri Jun 03 20:05:15 2022 +0200
@@ -29,22 +29,23 @@
 #include "bfile_heuristics.h"
 #include "regex_parser.h"
 #include <sys/stat.h>
+#include <ctype.h>
 
 typedef struct filelist filelist_t;
 
 struct filelist {
   char *displayname;
-  int displayname_len;
+  unsigned displayname_len;
   char *filename;
   char *ext;
-  int st_mode;
+  unsigned st_mode;
   filelist_t *next;
 };
 
 static bool testSuffix(char* filename, string_list_t* list) {
   bool ret = false;
-  int tokenlen, fnamelen = strlen(filename);
-  for (int t = 0 ; t < list->count ; t++) {
+  size_t tokenlen, fnamelen = strlen(filename);
+  for (size_t t = 0 ; t < list->count ; t++) {
     tokenlen = strlen(list->items[t]);
     if (fnamelen >= tokenlen && tokenlen > 0) {
       if (strncmp(filename+fnamelen-tokenlen,
@@ -57,34 +58,34 @@
   return ret;
 }
 
-static void addLinesPerExtension(scanresult_ext_t* result,
-        char* ext, int lines) {
+static void addResultPerExtension(scanresult_ext_t* result,
+                                  char* ext, unsigned value) {
   if (!result) return;
   
   if (!ext) ext = "w/o";
   
-  for (int i = 0 ; i < result->count ; i++) {
+  for (unsigned i = 0 ; i < result->count ; i++) {
     if (strcasecmp(result->extensions[i], ext) == 0) {
-      result->lines[i] += lines;
+      result->result[i] += value;
       return;
     }
   }
   
   if (result->count == result->capacity) {
-    int newcap = result->capacity+8;
+    unsigned newcap = result->capacity+8;
     char** extarr = realloc(result->extensions, newcap*sizeof(char*));
-    int* linesarr = realloc(result->lines, newcap*sizeof(int));
-    if (!extarr || !linesarr) {
+    unsigned* resultarr = realloc(result->result, newcap*sizeof(unsigned));
+    if (!extarr || !resultarr) {
       fprintf(stderr, "Memory allocation error.\n");
       abort();
     }
     result->extensions = extarr;
-    result->lines = linesarr;
+    result->result = resultarr;
     result->capacity = newcap;
   }
   
   result->extensions[result->count] = strdup(ext);
-  result->lines[result->count] = lines;
+  result->result[result->count] = value;
   result->count++;
 }
 
@@ -99,21 +100,20 @@
 void destroy_scanresult_t(scanresult_t* result) {
   if (result->ext) {
     if (result->ext->count > 0) {
-      for (int i = 0 ; i < result->ext->count ; i++) {
+      for (unsigned i = 0 ; i < result->ext->count ; i++) {
         free(result->ext->extensions[i]);
       }
       free(result->ext->extensions);
-      free(result->ext->lines);
+      free(result->ext->result);
     }
     free(result->ext);
   }
   free(result);
 }
 
+static filelist_t *buildFileList(scanner_t scanner, settings_t* settings) {
 
-static filelist_t *buildFileList(scanner_t scanner, settings_t* settings,
-    filelist_t* list) {
-  
+  filelist_t* list = NULL;
   DIR *dirf;
   struct dirent *entry;
   struct stat statbuf;
@@ -159,7 +159,7 @@
       }
       
       if (list) {
-        // create fake root to have a pointer on the true root
+        /* create fake root to have a pointer on the true root */
         filelist_t root;
         root.next = list;
         filelist_t *parent = &root;
@@ -188,12 +188,12 @@
 void scanDirectory(scanner_t scanner, settings_t* settings,
     string_list_t* output, scanresult_t* result) {
 
-  result->lines = 0;
-  int a;
+  result->result = 0;
   bool bfile;
   char *outbuf;
+  const char *result_type = settings->count_chars ? "chars" : "lines";
 
-  filelist_t *filelist = buildFileList(scanner, settings, NULL);
+  filelist_t *filelist = buildFileList(scanner, settings);
 
   while (filelist != NULL) {
 
@@ -206,23 +206,24 @@
         scanDirectory(
             (scanner_t) {filelist->filename, scanner.spaces+1},
             settings, recoutput, &recresult);
-        result->lines += recresult.lines;
+        result->result += recresult.result;
         if (!settings->matchesOnly || recoutput->count > 0) {
           outbuf = (char*) malloc(81);
-          snprintf(outbuf, 81, "%*s/%*s%13d lines\n",
+          snprintf(outbuf, 81, "%*s/%*s%13u %s\n",
               filelist->displayname_len+scanner.spaces, filelist->displayname,
               60-filelist->displayname_len-scanner.spaces-1, "",
-              recresult.lines);
+              recresult.result, result_type);
           add_string(output, outbuf);
-          for (int i = 0 ; i < recoutput->count ; i++) {
+          for (unsigned i = 0 ; i < recoutput->count ; i++) {
             add_string(output, recoutput->items[i]);
           }
         }
         destroy_string_list_t(recoutput);
       } else {
         outbuf = (char*) malloc(81);
-        snprintf(outbuf, 81, "%*s\n", filelist->displayname_len+scanner.spaces,
-          filelist->displayname);
+        snprintf(outbuf, 81, "%*s\n",
+                 filelist->displayname_len+scanner.spaces,
+                 filelist->displayname);
         add_string(output, outbuf);
       }
     } else {
@@ -230,44 +231,55 @@
         || testSuffix(filelist->displayname, settings->includeSuffixes))
         && !testSuffix(filelist->displayname, settings->excludeSuffixes)) {
 
-        /* Count lines */
-        int lines = 0;
+        /* Count */
+        unsigned res_value = 0;
         bfile = false;
         bfile_reset(settings->bfileHeuristics);
         regex_parser_reset(settings->regex);
-        char line_buffer[REGEX_MAX_LINELENGTH];
-        int line_buffer_offset = 0;
+        char line_buffer[MAX_LINELENGTH];
+        unsigned line_buffer_pos = 0;
 
         FILE *file = fopen(filelist->filename, "r");
         if (file == NULL) {
           outbuf = (char*) malloc(81);
-          snprintf(outbuf, 81, "%*s", filelist->displayname_len+scanner.spaces,
-              filelist->displayname);
+          snprintf(outbuf, 81, "%*s",
+                   filelist->displayname_len+scanner.spaces,
+                   filelist->displayname);
           add_string(output, outbuf);
           perror("  File acces failed");
         } else {
+          int a;
           do {
             a = fgetc(file);
 
             bfile = bfile_check(settings->bfileHeuristics, a);
 
+            /* ignore carriage return completely */
+            if (a == 13) continue;
+
             if (a == 10 || a == EOF) {
-              line_buffer[line_buffer_offset] = 0;
+              line_buffer[line_buffer_pos] = 0;
               if (regex_parser_do(settings->regex, line_buffer) == 0) {
-                /* Only subtract lines when matching has finished */
+                /* Subtract excluded lines/chars when matching has finished */
                 if (!regex_parser_matching(settings->regex)) {
-                  lines -= settings->regex->matched_lines;
+                  res_value -= settings->regex->matched_counted;
                 }
               }
 
-              line_buffer_offset = 0;
-              lines++;
+              if (settings->count_chars) {
+                for (size_t i = 0 ; i < line_buffer_pos ; i++) {
+                  if (!isspace(line_buffer[i])) res_value++;
+                }
+              } else {
+                res_value++;
+              }
+              line_buffer_pos = 0;
             } else {
-              if (line_buffer_offset < REGEX_MAX_LINELENGTH) {
-                line_buffer[line_buffer_offset] = a;
-                line_buffer_offset++;
+              if (line_buffer_pos < MAX_LINELENGTH) {
+                line_buffer[line_buffer_pos] = (char) a;
+                line_buffer_pos++;
               } else {
-                line_buffer[line_buffer_offset-1] = 0;
+                line_buffer[line_buffer_pos - 1] = 0;
                 settings->confusing_lnlen = true;
               }
             }
@@ -285,12 +297,17 @@
               add_string(output, outbuf);
             }
           } else {
-            addLinesPerExtension(result->ext, filelist->ext, lines);
-            result->lines += lines;
+            addResultPerExtension(result->ext, filelist->ext, res_value);
+            result->result += res_value;
             outbuf = (char*) malloc(81);
-            snprintf(outbuf, 81, "%*s%*s%13d lines\n",
-                filelist->displayname_len+scanner.spaces, filelist->displayname,
-                60-filelist->displayname_len-scanner.spaces, "", lines);
+            snprintf(outbuf, 81, "%*s%*s%13u %s\n",
+                     filelist->displayname_len+scanner.spaces,
+                     filelist->displayname,
+                     60-filelist->displayname_len-scanner.spaces,
+                     "",
+                     res_value,
+                     result_type
+            );
             add_string(output, outbuf);
           }
         }
--- a/src/scanner.h	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/scanner.h	Fri Jun 03 20:05:15 2022 +0200
@@ -33,18 +33,18 @@
 
 typedef struct {
   char *dir;
-  int spaces;
+  unsigned spaces;
 } scanner_t;
 
 typedef struct {
-  int count;
-  int capacity;
+  unsigned count;
+  unsigned capacity;
   char** extensions;
-  int* lines; 
+  unsigned* result;
 } scanresult_ext_t;
 
 typedef struct {
-  int lines;
+  unsigned result;
   scanresult_ext_t* ext;
 } scanresult_t;
 
--- a/src/settings.c	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/settings.c	Fri Jun 03 20:05:15 2022 +0200
@@ -42,7 +42,8 @@
     settings->bfileHeuristics    = new_bfile_heuristics_t();
     settings->confusing_lnlen    = false;
     settings->regex              = new_regex_parser_t();
-    settings->individual_sums           = false;
+    settings->individual_sums    = false;
+    settings->count_chars        = false;
   }
 
   return settings;
--- a/src/settings.h	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/settings.h	Fri Jun 03 20:05:15 2022 +0200
@@ -32,7 +32,7 @@
 #include "bfile_heuristics.h"
 #include "regex_parser.h"
 
-typedef struct _settings {
+typedef struct settings_s {
   string_list_t* includeSuffixes;
   string_list_t* excludeSuffixes;
   regex_parser_t* regex;
@@ -43,6 +43,7 @@
   bool verbose;
   bool confusing_lnlen; /* this flag is set by the scanner */
   bool individual_sums;
+  bool count_chars;
 } settings_t;
 
 #ifdef _cplusplus
--- a/src/string_list.h	Fri Jun 03 18:13:46 2022 +0200
+++ b/src/string_list.h	Fri Jun 03 20:05:15 2022 +0200
@@ -29,7 +29,7 @@
 
 #include "stdinc.h"
 
-typedef struct _string_list {
+typedef struct string_list_s {
   size_t count;
   char** items;
 } string_list_t;
--- a/test/Makefile.am	Fri Jun 03 18:13:46 2022 +0200
+++ b/test/Makefile.am	Fri Jun 03 20:05:15 2022 +0200
@@ -22,5 +22,5 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 
 TESTS = $(check_SCRIPTS)
-check_SCRIPTS = total.sh nocomments.sh codeonly.sh
+check_SCRIPTS = total.sh total_chars.sh nocomments.sh codeonly.sh codeonly_chars.sh
 EXTRA_DIST = $(check_SCRIPTS) testfile.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/codeonly_chars.sh	Fri Jun 03 20:05:15 2022 +0200
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2018 Mike Becker. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above copyright
+#      notice, this list of conditions and the following disclaimer in the
+#      documentation and/or other materials provided with the distribution.
+#
+# 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.
+#
+
+echo -n "Verifying correctness of code only character count: "
+
+clineprg="$(pwd)/../src/cline"
+
+cd $srcdir
+count=`$clineprg -Vcs testfile.c --exclude-cstyle-comments`
+expected=50
+
+if [ "$count" -eq "$expected" ]; then
+    echo "OK."
+    exit 0;
+else
+    echo "FAIL! $count is not $expected"
+    exit 1;
+fi
--- a/test/testfile.c	Fri Jun 03 18:13:46 2022 +0200
+++ b/test/testfile.c	Fri Jun 03 20:05:15 2022 +0200
@@ -4,7 +4,7 @@
 // And add other stuff below.
 
 
-int main(int argc, char* argv) { // comments behind code don't matter
+int main(int argc, char** argv) { // comments behind code don't matter
     
     int zero /* in block comments we are genius enough to
               * detect, if there is still some code in the line
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/total_chars.sh	Fri Jun 03 20:05:15 2022 +0200
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2018 Mike Becker. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above copyright
+#      notice, this list of conditions and the following disclaimer in the
+#      documentation and/or other materials provided with the distribution.
+#
+# 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.
+#
+
+echo -n "Verifying correctness of total character count: "
+
+clineprg="$(pwd)/../src/cline"
+
+cd $srcdir
+count=`$clineprg -Vcs testfile.c`
+expected=334
+
+if [ "$count" -eq "$expected" ]; then
+    echo "OK."
+    exit 0;
+else
+    echo "FAIL! $count is not $expected"
+    exit 1;
+fi

mercurial