Mon, 19 Mar 2018 16:36:14 +0100
regex parser was not properly reset before each file, sometimes resulting in wrong line counts, when the previous scanned file ended with a match
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * Copyright 2017 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 *
26 * cline.c
27 *
28 * Created on: 23.05.2011
29 * Author: Mike
30 */
32 #include "cline.h"
33 #include "scanner.h"
34 #include "settings.h"
35 #include "arguments.h"
36 #include "regex_parser.h"
38 void printHelpText() {
39 printf(
40 "\nUsage:"
41 "\n cline [Options] [Directories...]"
42 "\n cline [Options] [Directories...]"
43 "\n\nCounts the line terminator characters (\\n) within all"
44 " files in the specified\ndirectories."
45 "\n\nOptions:"
46 "\n -b <level> - binary file heuristics level (default medium)"
47 "\n One of: ignore low medium high"
48 "\n -E <pattern> - Excludes any line matching the <pattern>"
49 "\n -e <start> <end> - Excludes lines between <start> and <end>"
50 "\n You may use these options multiple times"
51 "\n -h, --help - this help text"
52 "\n -m - print information about matching files only"
53 "\n -s <suffixes> - only count files with these suffixes (separated"
54 "\n by commas)"
55 "\n -S <suffixes> - count any file except those with these suffixes"
56 "\n (separated by commas)"
57 "\n -r, -R - includes subdirectories"
58 "\n -v, --version - print out version information"
59 "\n -V - turn verbose output off, print the result only"
60 "\n\nShortcuts:"
61 "\n --exclude-cstyle-comments"
62 "\n = -E \"\\s*//\" -e \"\\s*/\\*\" \"\\*/\\s*\""
63 "\n\n"
64 "The default call without any options is:"
65 "\n cline ./\n\n"
66 "So each file in the working directory is counted. If you want to count C"
67 "\nsource code in your working directory and its subdirectories, type:"
68 "\n cline -rs .c\n"
69 "\nIf you want to exclude comment lines, you may use the -e/-E option."
70 "\nAfter a line matches the regex pattern <start> any following line is"
71 "\nnot counted unless a line matches the <end> pattern. A line is still "
72 "\ncounted when it does not start or end with the respective patterns."
73 "\nPlease note, that cline does not remove whitespace characters as this"
74 "\nmight not be reasonable in some cases."
75 "\n\nExample (C without comments):"
76 "\n cline -s .c,.h --exclude-cstyle-comments"
77 "\n");
78 }
80 int exit_with_version(settings_t* settings) {
81 printf("cline - Version: " VERSION "\n");
82 destroy_settings_t(settings);
83 return 0;
84 }
86 int exit_with_help(settings_t* settings, int code) {
87 printf("cline - Version: " VERSION "\n");
88 printHelpText();
89 destroy_settings_t(settings);
90 return code;
91 }
93 int main(int argc, char** argv) {
95 /* Settings */
96 settings_t *settings = new_settings_t();
97 if (settings == NULL) {
98 fprintf(stderr, "Memory allocation failed.\n");
99 return 1;
100 }
102 /* Get arguments */
103 string_list_t *directories = new_string_list_t();
104 if (directories == NULL) {
105 fprintf(stderr, "Memory allocation failed.\n");
106 return 1;
107 }
108 char* includeSuffix = NULL;
109 char* excludeSuffix = NULL;
110 int checked = 0;
112 for (int t = 1 ; t < argc ; t++) {
114 int argflags = checkArgument(argv[t], "hsSrRmvVbeE");
115 int paropt = 0;
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 /* h */
140 if ((argflags & 1) > 0 || strcmp(argv[t], "--help") == 0) {
141 return exit_with_help(settings, 0);
142 }
143 /* r, R */
144 if ((argflags & 24) > 0) {
145 if (registerArgument(&checked, 24)) {
146 return exit_with_help(settings, 1);
147 }
148 settings->recursive = true;
149 }
150 /* m */
151 if ((argflags & 32) > 0) {
152 if (registerArgument(&checked, 32)) {
153 return exit_with_help(settings, 1);
154 }
155 settings->matchesOnly = true;
156 }
157 /* v */
158 if ((argflags & 64) > 0 || strcmp(argv[t], "--version") == 0) {
159 return exit_with_version(settings);
160 }
161 /* V */
162 if ((argflags & 128) > 0) {
163 if (registerArgument(&checked, 128)) {
164 return exit_with_help(settings, 1);
165 }
166 settings->verbose = false;
167 }
168 /* b */
169 if ((argflags & 256) > 0) {
170 if (!checkParamOpt(&paropt) || registerArgument(&checked, 256)) {
171 return exit_with_help(settings, 1);
172 }
173 t++;
174 if (t >= argc) {
175 return exit_with_help(settings, 1);
176 }
177 if (strcasecmp(argv[t], "ignore") == 0) {
178 settings->bfileHeuristics->level = BFILE_IGNORE;
179 } else if (strcasecmp(argv[t], "low") == 0) {
180 settings->bfileHeuristics->level = BFILE_LOW_ACCURACY;
181 } else if (strcasecmp(argv[t], "medium") == 0) {
182 settings->bfileHeuristics->level = BFILE_MEDIUM_ACCURACY;
183 } else if (strcasecmp(argv[t], "high") == 0) {
184 settings->bfileHeuristics->level = BFILE_HIGH_ACCURACY;
185 } else {
186 return exit_with_help(settings, 1);
187 }
188 }
189 /* e */
190 if ((argflags & 512) > 0) {
191 if (!checkParamOpt(&paropt) || t + 2 >= argc) {
192 return exit_with_help(settings, 1);
193 }
194 t++; add_string(settings->regex->pattern_list, argv[t]);
195 t++; add_string(settings->regex->pattern_list, argv[t]);
196 }
197 /* E */
198 if ((argflags & 1024) > 0) {
199 t++;
200 if (!checkParamOpt(&paropt) || t >= argc) {
201 return exit_with_help(settings, 1);
202 }
203 add_string(settings->regex->pattern_list, argv[t]);
204 add_string(settings->regex->pattern_list, "$");
205 }
206 if (argflags == 0) {
207 /* SHORTCUTS */
208 /* exclude-cstyle-comments */
209 if (strcmp(argv[t], "--exclude-cstyle-comments") == 0) {
210 add_string(settings->regex->pattern_list, "\\s*//");
211 add_string(settings->regex->pattern_list, "$");
212 add_string(settings->regex->pattern_list, "\\s*/\\*");
213 add_string(settings->regex->pattern_list, "\\*/\\s*");
214 }
215 /* Path */
216 else {
217 add_string(directories, argv[t]);
218 }
219 }
220 }
222 /* Find tokens */
223 parseCSL(includeSuffix, settings->includeSuffixes);
224 parseCSL(excludeSuffix, settings->excludeSuffixes);
226 /* Scan directories */
227 if (regex_compile_all(settings->regex)) {
228 /* Don't waste memory when only the total sum is needed */
229 string_list_t *output = settings->verbose ? new_string_list_t() : NULL;
230 char *outbuf;
232 int lineSum = 0, lines;
233 if (directories->count == 0) {
234 add_string(directories, ".");
235 }
236 for (int t = 0 ; t < directories->count ; t++) {
237 lines = scanDirectory((scanner_t){directories->items[t], 0}, settings,
238 output);
239 lineSum += lines;
240 if (directories->count > 1 ) {
241 outbuf = (char*) malloc(81);
242 memset(outbuf, '-', 79);
243 outbuf[79] = '\n';
244 outbuf[80] = 0;
245 add_string(output, outbuf);
246 outbuf = (char*) malloc(81);
247 snprintf(outbuf, 81, "%-63s%10d lines\n", directories->items[t], lines);
248 add_string(output, outbuf);
249 outbuf = (char*) malloc(81);
250 memset(outbuf, '-', 79);
251 outbuf[79] = '\n';
252 outbuf[80] = 0;
253 add_string(output, outbuf);
254 }
255 }
256 destroy_string_list_t(directories);
258 /* Print result */
259 if (settings->verbose) {
260 for (int i = 0 ; i < output->count ; i++) {
261 printf("%s", output->items[i]);
262 free(output->items[i]);
263 }
265 for (int t = 0 ; t < 79 ; t++) {
266 printf("=");
267 }
268 printf("\n%73d lines\n", lineSum);
270 if (settings->confusing_lnlen &&
271 settings->regex->pattern_list->count > 0) {
273 printf("\nSome files contain too long lines.\n"
274 "The regex parser currently supports a maximum line length of %d."
275 "\nThe result might be wrong.\n", REGEX_MAX_LINELENGTH);
276 }
277 } else {
278 printf("%d", lineSum);
279 }
280 destroy_string_list_t(output);
281 destroy_settings_t(settings);
282 }
284 fflush(stdout);
285 fflush(stderr);
286 return 0;
287 }