Sat, 25 Jul 2020 18:28:01 +0200
preparing changes for individual sum feature
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 -m - print information about matching files only"
49 "\n -s <suffixes> - only count files with these suffixes (separated"
50 "\n by commas)"
51 "\n -S <suffixes> - count any file except those with these suffixes"
52 "\n (separated by commas)"
53 "\n -r, -R - includes subdirectories"
54 "\n -v, --version - print out version information"
55 "\n -V - turn verbose output off, print the result only"
56 "\n\nShortcuts:"
57 "\n --exclude-cstyle-comments : -E '\\s*//' -e '\\s*/\\*' '\\*/\\s*'"
58 "\n --exclude-blank-lines : -E '^\\s*$'"
59 "\n\n"
60 "The default call without any options is:"
61 "\n cline ./\n\n"
62 "So each file in the working directory is counted. If you want to count C"
63 "\nsource code in your working directory and its subdirectories, type:"
64 "\n cline -rs .c\n"
65 "\nIf you want to exclude comment lines, you may use the -e/-E option."
66 "\nAfter a line matches the regex pattern <start> any following line is"
67 "\nnot counted unless a line matches the <end> pattern. A line is still "
68 "\ncounted when it does not start or end with the respective patterns."
69 "\nPlease note, that cline does not remove whitespace characters as this"
70 "\nmight not be reasonable in some cases."
71 "\n\nExample (C without comments):"
72 "\n cline -s .c,.h --exclude-cstyle-comments"
73 "\n");
74 }
76 int exit_with_version(settings_t* settings) {
77 printf("cline - Version: " VERSION "\n");
78 destroy_settings_t(settings);
79 return 0;
80 }
82 int exit_with_help(settings_t* settings, int code) {
83 printf("cline - Version: " VERSION "\n");
84 printHelpText();
85 destroy_settings_t(settings);
86 return code;
87 }
89 int main(int argc, char** argv) {
91 /* Settings */
92 settings_t *settings = new_settings_t();
93 if (settings == NULL) {
94 fprintf(stderr, "Memory allocation failed.\n");
95 return 1;
96 }
98 /* Get arguments */
99 string_list_t *directories = new_string_list_t();
100 if (directories == NULL) {
101 fprintf(stderr, "Memory allocation failed.\n");
102 return 1;
103 }
104 char* includeSuffix = NULL;
105 char* excludeSuffix = NULL;
106 int checked = 0;
108 for (int t = 1 ; t < argc ; t++) {
110 int argflags = checkArgument(argv[t], "hsSrRmvVbeEi");
111 int paropt = 0;
113 /* h */
114 if ((argflags & 1) > 0 || strcmp(argv[t], "--help") == 0) {
115 return exit_with_help(settings, 0);
116 }
117 /* s */
118 if ((argflags & 2) > 0) {
119 if (!checkParamOpt(&paropt) || registerArgument(&checked, 2)) {
120 return exit_with_help(settings, 1);
121 }
122 t++;
123 if (t >= argc) {
124 return exit_with_help(settings, 1);
125 }
126 includeSuffix = argv[t];
127 }
128 /* S */
129 if ((argflags & 4) > 0) {
130 if (!checkParamOpt(&paropt) || registerArgument(&checked, 4)) {
131 return exit_with_help(settings, 1);
132 }
133 t++;
134 if (t >= argc) {
135 return exit_with_help(settings, 1);
136 }
137 excludeSuffix = argv[t];
138 }
139 /* r, R */
140 if ((argflags & 24) > 0) {
141 if (registerArgument(&checked, 24)) {
142 return exit_with_help(settings, 1);
143 }
144 settings->recursive = true;
145 }
146 /* m */
147 if ((argflags & 32) > 0) {
148 if (registerArgument(&checked, 32)) {
149 return exit_with_help(settings, 1);
150 }
151 settings->matchesOnly = true;
152 }
153 /* v */
154 if ((argflags & 64) > 0 || strcmp(argv[t], "--version") == 0) {
155 return exit_with_version(settings);
156 }
157 /* V */
158 if ((argflags & 128) > 0) {
159 if (registerArgument(&checked, 128)) {
160 return exit_with_help(settings, 1);
161 }
162 settings->verbose = false;
163 }
164 /* b */
165 if ((argflags & 256) > 0) {
166 if (!checkParamOpt(&paropt) || registerArgument(&checked, 256)) {
167 return exit_with_help(settings, 1);
168 }
169 t++;
170 if (t >= argc) {
171 return exit_with_help(settings, 1);
172 }
173 if (strcasecmp(argv[t], "ignore") == 0) {
174 settings->bfileHeuristics->level = BFILE_IGNORE;
175 } else if (strcasecmp(argv[t], "low") == 0) {
176 settings->bfileHeuristics->level = BFILE_LOW_ACCURACY;
177 } else if (strcasecmp(argv[t], "medium") == 0) {
178 settings->bfileHeuristics->level = BFILE_MEDIUM_ACCURACY;
179 } else if (strcasecmp(argv[t], "high") == 0) {
180 settings->bfileHeuristics->level = BFILE_HIGH_ACCURACY;
181 } else {
182 return exit_with_help(settings, 1);
183 }
184 }
185 /* e */
186 if ((argflags & 512) > 0) {
187 if (!checkParamOpt(&paropt) || t + 2 >= argc) {
188 return exit_with_help(settings, 1);
189 }
190 t++; add_string(settings->regex->pattern_list, argv[t]);
191 t++; add_string(settings->regex->pattern_list, argv[t]);
192 }
193 /* E */
194 if ((argflags & 1024) > 0) {
195 t++;
196 if (!checkParamOpt(&paropt) || t >= argc) {
197 return exit_with_help(settings, 1);
198 }
199 add_string(settings->regex->pattern_list, argv[t]);
200 add_string(settings->regex->pattern_list, "$");
201 }
202 if ((argflags & 2048) > 0) {
203 settings->individual_sums = true;
204 }
205 if (argflags == 0) {
206 /* SHORTCUTS */
207 if (strcmp(argv[t], "--exclude-cstyle-comments") == 0) {
208 add_string(settings->regex->pattern_list, "\\s*//");
209 add_string(settings->regex->pattern_list, "$");
210 add_string(settings->regex->pattern_list, "\\s*/\\*");
211 add_string(settings->regex->pattern_list, "\\*/\\s*");
212 } else if (strcmp(argv[t], "--exclude-blank-lines") == 0) {
213 add_string(settings->regex->pattern_list, "^\\s*$");
214 add_string(settings->regex->pattern_list, "$");
215 }
216 /* Path */
217 else {
218 add_string(directories, argv[t]);
219 }
220 }
221 }
223 /* Find tokens */
224 parseCSL(includeSuffix, settings->includeSuffixes);
225 parseCSL(excludeSuffix, settings->excludeSuffixes);
227 /* Scan directories */
228 if (regex_compile_all(settings->regex)) {
229 scanresult_t result;
230 /* Don't waste memory when only the total sum is needed */
231 string_list_t *output = settings->verbose ? new_string_list_t() : NULL;
232 char *outbuf;
234 int total = 0;
235 if (directories->count == 0) {
236 add_string(directories, ".");
237 }
238 for (int t = 0 ; t < directories->count ; t++) {
239 scanDirectory((scanner_t){directories->items[t], 0}, settings,
240 output, &result);
241 total += result.directory;
242 if (directories->count > 1 ) {
243 outbuf = (char*) malloc(81);
244 memset(outbuf, '-', 79);
245 outbuf[79] = '\n';
246 outbuf[80] = 0;
247 add_string(output, outbuf);
248 outbuf = (char*) malloc(81);
249 snprintf(outbuf, 81, "%-63s%10d lines\n", directories->items[t],
250 result.directory);
251 add_string(output, outbuf);
252 outbuf = (char*) malloc(81);
253 memset(outbuf, '-', 79);
254 outbuf[79] = '\n';
255 outbuf[80] = 0;
256 add_string(output, outbuf);
257 }
258 }
259 destroy_string_list_t(directories);
261 /* Print result */
262 if (settings->verbose) {
263 for (int i = 0 ; i < output->count ; i++) {
264 printf("%s", output->items[i]);
265 free(output->items[i]);
266 }
268 for (int t = 0 ; t < 79 ; t++) {
269 printf("=");
270 }
271 printf("\n%73d lines\n", total);
273 if (settings->confusing_lnlen &&
274 settings->regex->pattern_list->count > 0) {
276 printf("\nSome files contain too long lines.\n"
277 "The regex parser currently supports a maximum line length of %d."
278 "\nThe result might be wrong.\n", REGEX_MAX_LINELENGTH);
279 }
280 } else {
281 printf("%d", total);
282 }
283 destroy_string_list_t(output);
284 destroy_settings_t(settings);
285 }
287 fflush(stdout);
288 fflush(stderr);
289 return 0;
290 }