Mon, 27 Jul 2020 17:19:56 +0200
adds option to compute individual sums
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 cline [Options] [Directories...]"
38 "\n\nCounts the line terminator characters (\\n) within all"
39 " files in the specified\ndirectories."
40 "\n\nOptions:"
41 "\n -b <level> - binary file heuristics level (default medium)"
42 "\n One of: ignore low medium high"
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> any following line is"
68 "\nnot counted unless a line matches the <end> pattern. A line is still "
69 "\ncounted when it does not start or end with the respective patterns."
70 "\nPlease note, that cline does not remove whitespace characters as this"
71 "\nmight not be reasonable in some cases."
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], "hsSrRmvVbeEi");
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 == 0) {
212 /* SHORTCUTS */
213 if (strcmp(argv[t], "--exclude-cstyle-comments") == 0) {
214 add_string(settings->regex->pattern_list, "\\s*//");
215 add_string(settings->regex->pattern_list, "$");
216 add_string(settings->regex->pattern_list, "\\s*/\\*");
217 add_string(settings->regex->pattern_list, "\\*/\\s*");
218 } else if (strcmp(argv[t], "--exclude-blank-lines") == 0) {
219 add_string(settings->regex->pattern_list, "^\\s*$");
220 add_string(settings->regex->pattern_list, "$");
221 }
222 /* Path */
223 else {
224 add_string(directories, argv[t]);
225 }
226 }
227 }
229 /* Find tokens */
230 parseCSL(includeSuffix, settings->includeSuffixes);
231 parseCSL(excludeSuffix, settings->excludeSuffixes);
233 /* Scan directories */
234 if (regex_compile_all(settings->regex)) {
235 scanresult_t* result = new_scanresult_t(settings);
236 /* Don't waste memory when only the total sum is needed */
237 string_list_t *output = settings->verbose ? new_string_list_t() : NULL;
238 char *outbuf;
240 int total = 0;
241 if (directories->count == 0) {
242 add_string(directories, ".");
243 }
244 for (int t = 0 ; t < directories->count ; t++) {
245 scanDirectory((scanner_t){directories->items[t], 0}, settings,
246 output, result);
247 total += result->lines;
248 if (directories->count > 1 ) {
249 outbuf = (char*) malloc(81);
250 memset(outbuf, '-', 79);
251 outbuf[79] = '\n';
252 outbuf[80] = 0;
253 add_string(output, outbuf);
254 outbuf = (char*) malloc(81);
255 snprintf(outbuf, 81, "%-63s%10d lines\n", directories->items[t],
256 result->lines);
257 add_string(output, outbuf);
258 outbuf = (char*) malloc(81);
259 memset(outbuf, '-', 79);
260 outbuf[79] = '\n';
261 outbuf[80] = 0;
262 add_string(output, outbuf);
263 }
264 }
265 destroy_string_list_t(directories);
267 /* Print result */
268 if (settings->verbose) {
269 for (int i = 0 ; i < output->count ; i++) {
270 printf("%s", output->items[i]);
271 free(output->items[i]);
272 }
274 if (result->ext) {
275 if (result->ext->count > 0) {
276 for (int t = 0 ; t < 79 ; t++) {
277 printf("=");
278 }
279 printf("\nIndividual sums:\n");
280 for (int t = 0 ; t < result->ext->count ; t++) {
281 printf(" %-62s%10d lines\n",
282 result->ext->extensions[t],
283 result->ext->lines[t]);
284 }
285 }
286 }
288 for (int t = 0 ; t < 79 ; t++) {
289 printf("=");
290 }
291 printf("\n%73d lines\n", total);
293 if (settings->confusing_lnlen &&
294 settings->regex->pattern_list->count > 0) {
296 printf("\nSome files contain too long lines.\n"
297 "The regex parser currently supports a maximum line length of %d."
298 "\nThe result might be wrong.\n", REGEX_MAX_LINELENGTH);
299 }
300 } else {
301 printf("%d", total);
302 }
303 destroy_scanresult_t(result);
304 destroy_string_list_t(output);
305 destroy_settings_t(settings);
306 }
308 fflush(stdout);
309 fflush(stderr);
310 return 0;
311 }