1 | #!/usr/bin/env perl
|
---|
2 | #***************************************************************************
|
---|
3 | # _ _ ____ _
|
---|
4 | # Project ___| | | | _ \| |
|
---|
5 | # / __| | | | |_) | |
|
---|
6 | # | (__| |_| | _ <| |___
|
---|
7 | # \___|\___/|_| \_\_____|
|
---|
8 | #
|
---|
9 | # Copyright (C) 2011 - 2018, Daniel Stenberg, <[email protected]>, et al.
|
---|
10 | #
|
---|
11 | # This software is licensed as described in the file COPYING, which
|
---|
12 | # you should have received as part of this distribution. The terms
|
---|
13 | # are also available at https://curl.haxx.se/docs/copyright.html.
|
---|
14 | #
|
---|
15 | # You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
16 | # copies of the Software, and permit persons to whom the Software is
|
---|
17 | # furnished to do so, under the terms of the COPYING file.
|
---|
18 | #
|
---|
19 | # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
20 | # KIND, either express or implied.
|
---|
21 | #
|
---|
22 | ###########################################################################
|
---|
23 |
|
---|
24 | use strict;
|
---|
25 | use warnings;
|
---|
26 |
|
---|
27 | my $max_column = 79;
|
---|
28 | my $indent = 2;
|
---|
29 |
|
---|
30 | my $warnings = 0;
|
---|
31 | my $swarnings = 0;
|
---|
32 | my $errors = 0;
|
---|
33 | my $serrors = 0;
|
---|
34 | my $suppressed; # whitelisted problems
|
---|
35 | my $file;
|
---|
36 | my $dir=".";
|
---|
37 | my $wlist="";
|
---|
38 | my @alist;
|
---|
39 | my $windows_os = $^O eq 'MSWin32' || $^O eq 'msys' || $^O eq 'cygwin';
|
---|
40 | my $verbose;
|
---|
41 | my %whitelist;
|
---|
42 |
|
---|
43 | my %ignore;
|
---|
44 | my %ignore_set;
|
---|
45 | my %ignore_used;
|
---|
46 | my @ignore_line;
|
---|
47 |
|
---|
48 | my %warnings_extended = (
|
---|
49 | 'COPYRIGHTYEAR' => 'copyright year incorrect',
|
---|
50 | );
|
---|
51 |
|
---|
52 | my %warnings = (
|
---|
53 | 'LONGLINE' => "Line longer than $max_column",
|
---|
54 | 'TABS' => 'TAB characters not allowed',
|
---|
55 | 'TRAILINGSPACE' => 'Trailing white space on the line',
|
---|
56 | 'CPPCOMMENTS' => '// comment detected',
|
---|
57 | 'SPACEBEFOREPAREN' => 'space before an open parenthesis',
|
---|
58 | 'SPACEAFTERPAREN' => 'space after open parenthesis',
|
---|
59 | 'SPACEBEFORECLOSE' => 'space before a close parenthesis',
|
---|
60 | 'SPACEBEFORECOMMA' => 'space before a comma',
|
---|
61 | 'RETURNNOSPACE' => 'return without space',
|
---|
62 | 'COMMANOSPACE' => 'comma without following space',
|
---|
63 | 'BRACEELSE' => '} else on the same line',
|
---|
64 | 'PARENBRACE' => '){ without sufficient space',
|
---|
65 | 'SPACESEMICOLON' => 'space before semicolon',
|
---|
66 | 'BANNEDFUNC' => 'a banned function was used',
|
---|
67 | 'FOPENMODE' => 'fopen needs a macro for the mode string',
|
---|
68 | 'BRACEPOS' => 'wrong position for an open brace',
|
---|
69 | 'INDENTATION' => 'wrong start column for code',
|
---|
70 | 'COPYRIGHT' => 'file missing a copyright statement',
|
---|
71 | 'BADCOMMAND' => 'bad !checksrc! instruction',
|
---|
72 | 'UNUSEDIGNORE' => 'a warning ignore was not used',
|
---|
73 | 'OPENCOMMENT' => 'file ended with a /* comment still "open"',
|
---|
74 | 'ASTERISKSPACE' => 'pointer declared with space after asterisk',
|
---|
75 | 'ASTERISKNOSPACE' => 'pointer declared without space before asterisk',
|
---|
76 | 'ASSIGNWITHINCONDITION' => 'assignment within conditional expression',
|
---|
77 | 'EQUALSNOSPACE' => 'equals sign without following space',
|
---|
78 | 'NOSPACEEQUALS' => 'equals sign without preceding space',
|
---|
79 | 'SEMINOSPACE' => 'semicolon without following space',
|
---|
80 | 'MULTISPACE' => 'multiple spaces used when not suitable',
|
---|
81 | 'SIZEOFNOPAREN' => 'use of sizeof without parentheses',
|
---|
82 | 'SNPRINTF' => 'use of snprintf',
|
---|
83 | );
|
---|
84 |
|
---|
85 | sub readwhitelist {
|
---|
86 | open(W, "<$dir/checksrc.whitelist") or return;
|
---|
87 | my @all=<W>;
|
---|
88 | for(@all) {
|
---|
89 | $windows_os ? $_ =~ s/\r?\n$// : chomp;
|
---|
90 | $whitelist{$_}=1;
|
---|
91 | }
|
---|
92 | close(W);
|
---|
93 | }
|
---|
94 |
|
---|
95 | # Reads the .checksrc in $dir for any extended warnings to enable locally.
|
---|
96 | # Currently there is no support for disabling warnings from the standard set,
|
---|
97 | # and since that's already handled via !checksrc! commands there is probably
|
---|
98 | # little use to add it.
|
---|
99 | sub readlocalfile {
|
---|
100 | my $i = 0;
|
---|
101 |
|
---|
102 | open(my $rcfile, "<", "$dir/.checksrc") or return;
|
---|
103 |
|
---|
104 | while(<$rcfile>) {
|
---|
105 | $i++;
|
---|
106 |
|
---|
107 | # Lines starting with '#' are considered comments
|
---|
108 | if (/^\s*(#.*)/) {
|
---|
109 | next;
|
---|
110 | }
|
---|
111 | elsif (/^\s*enable ([A-Z]+)$/) {
|
---|
112 | if(!defined($warnings_extended{$1})) {
|
---|
113 | print STDERR "invalid warning specified in .checksrc: \"$1\"\n";
|
---|
114 | next;
|
---|
115 | }
|
---|
116 | $warnings{$1} = $warnings_extended{$1};
|
---|
117 | }
|
---|
118 | else {
|
---|
119 | die "Invalid format in $dir/.checksrc on line $i\n";
|
---|
120 | }
|
---|
121 | }
|
---|
122 | }
|
---|
123 |
|
---|
124 | sub checkwarn {
|
---|
125 | my ($name, $num, $col, $file, $line, $msg, $error) = @_;
|
---|
126 |
|
---|
127 | my $w=$error?"error":"warning";
|
---|
128 | my $nowarn=0;
|
---|
129 |
|
---|
130 | #if(!$warnings{$name}) {
|
---|
131 | # print STDERR "Dev! there's no description for $name!\n";
|
---|
132 | #}
|
---|
133 |
|
---|
134 | # checksrc.whitelist
|
---|
135 | if($whitelist{$line}) {
|
---|
136 | $nowarn = 1;
|
---|
137 | }
|
---|
138 | # !checksrc! controlled
|
---|
139 | elsif($ignore{$name}) {
|
---|
140 | $ignore{$name}--;
|
---|
141 | $ignore_used{$name}++;
|
---|
142 | $nowarn = 1;
|
---|
143 | if(!$ignore{$name}) {
|
---|
144 | # reached zero, enable again
|
---|
145 | enable_warn($name, $num, $file, $line);
|
---|
146 | }
|
---|
147 | }
|
---|
148 |
|
---|
149 | if($nowarn) {
|
---|
150 | $suppressed++;
|
---|
151 | if($w) {
|
---|
152 | $swarnings++;
|
---|
153 | }
|
---|
154 | else {
|
---|
155 | $serrors++;
|
---|
156 | }
|
---|
157 | return;
|
---|
158 | }
|
---|
159 |
|
---|
160 | if($w) {
|
---|
161 | $warnings++;
|
---|
162 | }
|
---|
163 | else {
|
---|
164 | $errors++;
|
---|
165 | }
|
---|
166 |
|
---|
167 | $col++;
|
---|
168 | print "$file:$num:$col: $w: $msg ($name)\n";
|
---|
169 | print " $line\n";
|
---|
170 |
|
---|
171 | if($col < 80) {
|
---|
172 | my $pref = (' ' x $col);
|
---|
173 | print "${pref}^\n";
|
---|
174 | }
|
---|
175 | }
|
---|
176 |
|
---|
177 | $file = shift @ARGV;
|
---|
178 |
|
---|
179 | while(1) {
|
---|
180 |
|
---|
181 | if($file =~ /-D(.*)/) {
|
---|
182 | $dir = $1;
|
---|
183 | $file = shift @ARGV;
|
---|
184 | next;
|
---|
185 | }
|
---|
186 | elsif($file =~ /-W(.*)/) {
|
---|
187 | $wlist .= " $1 ";
|
---|
188 | $file = shift @ARGV;
|
---|
189 | next;
|
---|
190 | }
|
---|
191 | elsif($file =~ /-A(.+)/) {
|
---|
192 | push @alist, $1;
|
---|
193 | $file = shift @ARGV;
|
---|
194 | next;
|
---|
195 | }
|
---|
196 | elsif($file =~ /-i([1-9])/) {
|
---|
197 | $indent = $1 + 0;
|
---|
198 | $file = shift @ARGV;
|
---|
199 | next;
|
---|
200 | }
|
---|
201 | elsif($file =~ /-m([0-9]+)/) {
|
---|
202 | $max_column = $1 + 0;
|
---|
203 | $file = shift @ARGV;
|
---|
204 | next;
|
---|
205 | }
|
---|
206 | elsif($file =~ /^(-h|--help)/) {
|
---|
207 | undef $file;
|
---|
208 | last;
|
---|
209 | }
|
---|
210 |
|
---|
211 | last;
|
---|
212 | }
|
---|
213 |
|
---|
214 | if(!$file) {
|
---|
215 | print "checksrc.pl [option] <file1> [file2] ...\n";
|
---|
216 | print " Options:\n";
|
---|
217 | print " -A[rule] Accept this violation, can be used multiple times\n";
|
---|
218 | print " -D[DIR] Directory to prepend file names\n";
|
---|
219 | print " -h Show help output\n";
|
---|
220 | print " -W[file] Whitelist the given file - ignore all its flaws\n";
|
---|
221 | print " -i<n> Indent spaces. Default: 2\n";
|
---|
222 | print " -m<n> Maximum line length. Default: 79\n";
|
---|
223 | print "\nDetects and warns for these problems:\n";
|
---|
224 | for(sort keys %warnings) {
|
---|
225 | printf (" %-18s: %s\n", $_, $warnings{$_});
|
---|
226 | }
|
---|
227 | exit;
|
---|
228 | }
|
---|
229 |
|
---|
230 | readwhitelist();
|
---|
231 | readlocalfile();
|
---|
232 |
|
---|
233 | do {
|
---|
234 | if("$wlist" !~ / $file /) {
|
---|
235 | my $fullname = $file;
|
---|
236 | $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
|
---|
237 | scanfile($fullname);
|
---|
238 | }
|
---|
239 | $file = shift @ARGV;
|
---|
240 |
|
---|
241 | } while($file);
|
---|
242 |
|
---|
243 | sub accept_violations {
|
---|
244 | for my $r (@alist) {
|
---|
245 | if(!$warnings{$r}) {
|
---|
246 | print "'$r' is not a warning to accept!\n";
|
---|
247 | exit;
|
---|
248 | }
|
---|
249 | $ignore{$r}=999999;
|
---|
250 | $ignore_used{$r}=0;
|
---|
251 | }
|
---|
252 | }
|
---|
253 |
|
---|
254 | sub checksrc_clear {
|
---|
255 | undef %ignore;
|
---|
256 | undef %ignore_set;
|
---|
257 | undef @ignore_line;
|
---|
258 | }
|
---|
259 |
|
---|
260 | sub checksrc_endoffile {
|
---|
261 | my ($file) = @_;
|
---|
262 | for(keys %ignore_set) {
|
---|
263 | if($ignore_set{$_} && !$ignore_used{$_}) {
|
---|
264 | checkwarn("UNUSEDIGNORE", $ignore_set{$_},
|
---|
265 | length($_)+11, $file,
|
---|
266 | $ignore_line[$ignore_set{$_}],
|
---|
267 | "Unused ignore: $_");
|
---|
268 | }
|
---|
269 | }
|
---|
270 | }
|
---|
271 |
|
---|
272 | sub enable_warn {
|
---|
273 | my ($what, $line, $file, $l) = @_;
|
---|
274 |
|
---|
275 | # switch it back on, but warn if not triggered!
|
---|
276 | if(!$ignore_used{$what}) {
|
---|
277 | checkwarn("UNUSEDIGNORE",
|
---|
278 | $line, length($what) + 11, $file, $l,
|
---|
279 | "No warning was inhibited!");
|
---|
280 | }
|
---|
281 | $ignore_set{$what}=0;
|
---|
282 | $ignore_used{$what}=0;
|
---|
283 | $ignore{$what}=0;
|
---|
284 | }
|
---|
285 | sub checksrc {
|
---|
286 | my ($cmd, $line, $file, $l) = @_;
|
---|
287 | if($cmd =~ / *([^ ]*) *(.*)/) {
|
---|
288 | my ($enable, $what) = ($1, $2);
|
---|
289 | $what =~ s: *\*/$::; # cut off end of C comment
|
---|
290 | # print "ENABLE $enable WHAT $what\n";
|
---|
291 | if($enable eq "disable") {
|
---|
292 | my ($warn, $scope)=($1, $2);
|
---|
293 | if($what =~ /([^ ]*) +(.*)/) {
|
---|
294 | ($warn, $scope)=($1, $2);
|
---|
295 | }
|
---|
296 | else {
|
---|
297 | $warn = $what;
|
---|
298 | $scope = 1;
|
---|
299 | }
|
---|
300 | # print "IGNORE $warn for SCOPE $scope\n";
|
---|
301 | if($scope eq "all") {
|
---|
302 | $scope=999999;
|
---|
303 | }
|
---|
304 |
|
---|
305 | # Comparing for a literal zero rather than the scalar value zero
|
---|
306 | # covers the case where $scope contains the ending '*' from the
|
---|
307 | # comment. If we use a scalar comparison (==) we induce warnings
|
---|
308 | # on non-scalar contents.
|
---|
309 | if($scope eq "0") {
|
---|
310 | checkwarn("BADCOMMAND",
|
---|
311 | $line, 0, $file, $l,
|
---|
312 | "Disable zero not supported, did you mean to enable?");
|
---|
313 | }
|
---|
314 | elsif($ignore_set{$warn}) {
|
---|
315 | checkwarn("BADCOMMAND",
|
---|
316 | $line, 0, $file, $l,
|
---|
317 | "$warn already disabled from line $ignore_set{$warn}");
|
---|
318 | }
|
---|
319 | else {
|
---|
320 | $ignore{$warn}=$scope;
|
---|
321 | $ignore_set{$warn}=$line;
|
---|
322 | $ignore_line[$line]=$l;
|
---|
323 | }
|
---|
324 | }
|
---|
325 | elsif($enable eq "enable") {
|
---|
326 | enable_warn($what, $line, $file, $l);
|
---|
327 | }
|
---|
328 | else {
|
---|
329 | checkwarn("BADCOMMAND",
|
---|
330 | $line, 0, $file, $l,
|
---|
331 | "Illegal !checksrc! command");
|
---|
332 | }
|
---|
333 | }
|
---|
334 | }
|
---|
335 |
|
---|
336 | sub nostrings {
|
---|
337 | my ($str) = @_;
|
---|
338 | $str =~ s/\".*\"//g;
|
---|
339 | return $str;
|
---|
340 | }
|
---|
341 |
|
---|
342 | sub scanfile {
|
---|
343 | my ($file) = @_;
|
---|
344 |
|
---|
345 | my $line = 1;
|
---|
346 | my $prevl="";
|
---|
347 | my $l;
|
---|
348 | open(R, "<$file") || die "failed to open $file";
|
---|
349 |
|
---|
350 | my $incomment=0;
|
---|
351 | my @copyright=();
|
---|
352 | checksrc_clear(); # for file based ignores
|
---|
353 | accept_violations();
|
---|
354 |
|
---|
355 | while(<R>) {
|
---|
356 | $windows_os ? $_ =~ s/\r?\n$// : chomp;
|
---|
357 | my $l = $_;
|
---|
358 | my $ol = $l; # keep the unmodified line for error reporting
|
---|
359 | my $column = 0;
|
---|
360 |
|
---|
361 | # check for !checksrc! commands
|
---|
362 | if($l =~ /\!checksrc\! (.*)/) {
|
---|
363 | my $cmd = $1;
|
---|
364 | checksrc($cmd, $line, $file, $l)
|
---|
365 | }
|
---|
366 |
|
---|
367 | # check for a copyright statement and save the years
|
---|
368 | if($l =~ /\* +copyright .* \d\d\d\d/i) {
|
---|
369 | while($l =~ /([\d]{4})/g) {
|
---|
370 | push @copyright, {
|
---|
371 | year => $1,
|
---|
372 | line => $line,
|
---|
373 | col => index($l, $1),
|
---|
374 | code => $l
|
---|
375 | };
|
---|
376 | }
|
---|
377 | }
|
---|
378 |
|
---|
379 | # detect long lines
|
---|
380 | if(length($l) > $max_column) {
|
---|
381 | checkwarn("LONGLINE", $line, length($l), $file, $l,
|
---|
382 | "Longer than $max_column columns");
|
---|
383 | }
|
---|
384 | # detect TAB characters
|
---|
385 | if($l =~ /^(.*)\t/) {
|
---|
386 | checkwarn("TABS",
|
---|
387 | $line, length($1), $file, $l, "Contains TAB character", 1);
|
---|
388 | }
|
---|
389 | # detect trailing white space
|
---|
390 | if($l =~ /^(.*)[ \t]+\z/) {
|
---|
391 | checkwarn("TRAILINGSPACE",
|
---|
392 | $line, length($1), $file, $l, "Trailing whitespace");
|
---|
393 | }
|
---|
394 |
|
---|
395 | # ------------------------------------------------------------
|
---|
396 | # Above this marker, the checks were done on lines *including*
|
---|
397 | # comments
|
---|
398 | # ------------------------------------------------------------
|
---|
399 |
|
---|
400 | # strip off C89 comments
|
---|
401 |
|
---|
402 | comment:
|
---|
403 | if(!$incomment) {
|
---|
404 | if($l =~ s/\/\*.*\*\// /g) {
|
---|
405 | # full /* comments */ were removed!
|
---|
406 | }
|
---|
407 | if($l =~ s/\/\*.*//) {
|
---|
408 | # start of /* comment was removed
|
---|
409 | $incomment = 1;
|
---|
410 | }
|
---|
411 | }
|
---|
412 | else {
|
---|
413 | if($l =~ s/.*\*\///) {
|
---|
414 | # end of comment */ was removed
|
---|
415 | $incomment = 0;
|
---|
416 | goto comment;
|
---|
417 | }
|
---|
418 | else {
|
---|
419 | # still within a comment
|
---|
420 | $l="";
|
---|
421 | }
|
---|
422 | }
|
---|
423 |
|
---|
424 | # ------------------------------------------------------------
|
---|
425 | # Below this marker, the checks were done on lines *without*
|
---|
426 | # comments
|
---|
427 | # ------------------------------------------------------------
|
---|
428 |
|
---|
429 | # crude attempt to detect // comments without too many false
|
---|
430 | # positives
|
---|
431 | if($l =~ /^([^"\*]*)[^:"]\/\//) {
|
---|
432 | checkwarn("CPPCOMMENTS",
|
---|
433 | $line, length($1), $file, $l, "\/\/ comment");
|
---|
434 | }
|
---|
435 |
|
---|
436 | my $nostr = nostrings($l);
|
---|
437 | # check spaces after for/if/while/function call
|
---|
438 | if($nostr =~ /^(.*)(for|if|while| ([a-zA-Z0-9_]+)) \((.)/) {
|
---|
439 | if($1 =~ / *\#/) {
|
---|
440 | # this is a #if, treat it differently
|
---|
441 | }
|
---|
442 | elsif(defined $3 && $3 eq "return") {
|
---|
443 | # return must have a space
|
---|
444 | }
|
---|
445 | elsif(defined $3 && $3 eq "case") {
|
---|
446 | # case must have a space
|
---|
447 | }
|
---|
448 | elsif($4 eq "*") {
|
---|
449 | # (* beginning makes the space OK!
|
---|
450 | }
|
---|
451 | elsif($1 =~ / *typedef/) {
|
---|
452 | # typedefs can use space-paren
|
---|
453 | }
|
---|
454 | else {
|
---|
455 | checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
|
---|
456 | "$2 with space");
|
---|
457 | }
|
---|
458 | }
|
---|
459 |
|
---|
460 | if($nostr =~ /^((.*)(if) *\()(.*)\)/) {
|
---|
461 | my $pos = length($1);
|
---|
462 | if($4 =~ / = /) {
|
---|
463 | checkwarn("ASSIGNWITHINCONDITION",
|
---|
464 | $line, $pos+1, $file, $l,
|
---|
465 | "assignment within conditional expression");
|
---|
466 | }
|
---|
467 | }
|
---|
468 | # check spaces after open parentheses
|
---|
469 | if($l =~ /^(.*[a-z])\( /i) {
|
---|
470 | checkwarn("SPACEAFTERPAREN",
|
---|
471 | $line, length($1)+1, $file, $l,
|
---|
472 | "space after open parenthesis");
|
---|
473 | }
|
---|
474 |
|
---|
475 | # check spaces before close parentheses, unless it was a space or a
|
---|
476 | # close parenthesis!
|
---|
477 | if($l =~ /(.*[^\) ]) \)/) {
|
---|
478 | checkwarn("SPACEBEFORECLOSE",
|
---|
479 | $line, length($1)+1, $file, $l,
|
---|
480 | "space before close parenthesis");
|
---|
481 | }
|
---|
482 |
|
---|
483 | # check spaces before comma!
|
---|
484 | if($l =~ /(.*[^ ]) ,/) {
|
---|
485 | checkwarn("SPACEBEFORECOMMA",
|
---|
486 | $line, length($1)+1, $file, $l,
|
---|
487 | "space before comma");
|
---|
488 | }
|
---|
489 |
|
---|
490 | # check for "return(" without space
|
---|
491 | if($l =~ /^(.*)return\(/) {
|
---|
492 | if($1 =~ / *\#/) {
|
---|
493 | # this is a #if, treat it differently
|
---|
494 | }
|
---|
495 | else {
|
---|
496 | checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
|
---|
497 | "return without space before paren");
|
---|
498 | }
|
---|
499 | }
|
---|
500 |
|
---|
501 | # check for "sizeof" without parenthesis
|
---|
502 | if(($l =~ /^(.*)sizeof *([ (])/) && ($2 ne "(")) {
|
---|
503 | if($1 =~ / *\#/) {
|
---|
504 | # this is a #if, treat it differently
|
---|
505 | }
|
---|
506 | else {
|
---|
507 | checkwarn("SIZEOFNOPAREN", $line, length($1)+6, $file, $l,
|
---|
508 | "sizeof without parenthesis");
|
---|
509 | }
|
---|
510 | }
|
---|
511 |
|
---|
512 | # check for comma without space
|
---|
513 | if($l =~ /^(.*),[^ \n]/) {
|
---|
514 | my $pref=$1;
|
---|
515 | my $ign=0;
|
---|
516 | if($pref =~ / *\#/) {
|
---|
517 | # this is a #if, treat it differently
|
---|
518 | $ign=1;
|
---|
519 | }
|
---|
520 | elsif($pref =~ /\/\*/) {
|
---|
521 | # this is a comment
|
---|
522 | $ign=1;
|
---|
523 | }
|
---|
524 | elsif($pref =~ /[\"\']/) {
|
---|
525 | $ign = 1;
|
---|
526 | # There is a quote here, figure out whether the comma is
|
---|
527 | # within a string or '' or not.
|
---|
528 | if($pref =~ /\"/) {
|
---|
529 | # within a string
|
---|
530 | }
|
---|
531 | elsif($pref =~ /\'$/) {
|
---|
532 | # a single letter
|
---|
533 | }
|
---|
534 | else {
|
---|
535 | $ign = 0;
|
---|
536 | }
|
---|
537 | }
|
---|
538 | if(!$ign) {
|
---|
539 | checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
|
---|
540 | "comma without following space");
|
---|
541 | }
|
---|
542 | }
|
---|
543 |
|
---|
544 | # check for "} else"
|
---|
545 | if($l =~ /^(.*)\} *else/) {
|
---|
546 | checkwarn("BRACEELSE",
|
---|
547 | $line, length($1), $file, $l, "else after closing brace on same line");
|
---|
548 | }
|
---|
549 | # check for "){"
|
---|
550 | if($l =~ /^(.*)\)\{/) {
|
---|
551 | checkwarn("PARENBRACE",
|
---|
552 | $line, length($1)+1, $file, $l, "missing space after close paren");
|
---|
553 | }
|
---|
554 |
|
---|
555 | # check for space before the semicolon last in a line
|
---|
556 | if($l =~ /^(.*[^ ].*) ;$/) {
|
---|
557 | checkwarn("SPACESEMICOLON",
|
---|
558 | $line, length($1), $file, $ol, "space before last semicolon");
|
---|
559 | }
|
---|
560 |
|
---|
561 | # scan for use of banned functions
|
---|
562 | if($l =~ /^(.*\W)
|
---|
563 | (gets|
|
---|
564 | strtok|
|
---|
565 | v?sprintf|
|
---|
566 | (str|_mbs|_tcs|_wcs)n?cat|
|
---|
567 | LoadLibrary(Ex)?(A|W)?)
|
---|
568 | \s*\(
|
---|
569 | /x) {
|
---|
570 | checkwarn("BANNEDFUNC",
|
---|
571 | $line, length($1), $file, $ol,
|
---|
572 | "use of $2 is banned");
|
---|
573 | }
|
---|
574 |
|
---|
575 | # scan for use of snprintf for curl-internals reasons
|
---|
576 | if($l =~ /^(.*\W)(v?snprintf)\s*\(/x) {
|
---|
577 | checkwarn("SNPRINTF",
|
---|
578 | $line, length($1), $file, $ol,
|
---|
579 | "use of $2 is banned");
|
---|
580 | }
|
---|
581 |
|
---|
582 | # scan for use of non-binary fopen without the macro
|
---|
583 | if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
|
---|
584 | my $mode = $2;
|
---|
585 | if($mode !~ /b/) {
|
---|
586 | checkwarn("FOPENMODE",
|
---|
587 | $line, length($1), $file, $ol,
|
---|
588 | "use of non-binary fopen without FOPEN_* macro: $mode");
|
---|
589 | }
|
---|
590 | }
|
---|
591 |
|
---|
592 | # check for open brace first on line but not first column
|
---|
593 | # only alert if previous line ended with a close paren and wasn't a cpp
|
---|
594 | # line
|
---|
595 | if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
|
---|
596 | checkwarn("BRACEPOS",
|
---|
597 | $line, length($1), $file, $ol, "badly placed open brace");
|
---|
598 | }
|
---|
599 |
|
---|
600 | # if the previous line starts with if/while/for AND ends with an open
|
---|
601 | # brace, or an else statement, check that this line is indented $indent
|
---|
602 | # more steps, if not a cpp line
|
---|
603 | if($prevl =~ /^( *)((if|while|for)\(.*\{|else)\z/) {
|
---|
604 | my $first = length($1);
|
---|
605 |
|
---|
606 | # this line has some character besides spaces
|
---|
607 | if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
|
---|
608 | my $second = length($1);
|
---|
609 | my $expect = $first+$indent;
|
---|
610 | if($expect != $second) {
|
---|
611 | my $diff = $second - $first;
|
---|
612 | checkwarn("INDENTATION", $line, length($1), $file, $ol,
|
---|
613 | "not indented $indent steps (uses $diff)");
|
---|
614 |
|
---|
615 | }
|
---|
616 | }
|
---|
617 | }
|
---|
618 |
|
---|
619 | # check for 'char * name'
|
---|
620 | if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost) *(\*+)) (\w+)/) && ($4 ne "const")) {
|
---|
621 | checkwarn("ASTERISKNOSPACE",
|
---|
622 | $line, length($1), $file, $ol,
|
---|
623 | "no space after declarative asterisk");
|
---|
624 | }
|
---|
625 | # check for 'char*'
|
---|
626 | if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost|sockaddr_in|FILE)\*)/)) {
|
---|
627 | checkwarn("ASTERISKNOSPACE",
|
---|
628 | $line, length($1)-1, $file, $ol,
|
---|
629 | "no space before asterisk");
|
---|
630 | }
|
---|
631 |
|
---|
632 | # check for 'void func() {', but avoid false positives by requiring
|
---|
633 | # both an open and closed parentheses before the open brace
|
---|
634 | if($l =~ /^((\w).*)\{\z/) {
|
---|
635 | my $k = $1;
|
---|
636 | $k =~ s/const *//;
|
---|
637 | $k =~ s/static *//;
|
---|
638 | if($k =~ /\(.*\)/) {
|
---|
639 | checkwarn("BRACEPOS",
|
---|
640 | $line, length($l)-1, $file, $ol,
|
---|
641 | "wrongly placed open brace");
|
---|
642 | }
|
---|
643 | }
|
---|
644 |
|
---|
645 | # check for equals sign without spaces next to it
|
---|
646 | if($nostr =~ /(.*)\=[a-z0-9]/i) {
|
---|
647 | checkwarn("EQUALSNOSPACE",
|
---|
648 | $line, length($1)+1, $file, $ol,
|
---|
649 | "no space after equals sign");
|
---|
650 | }
|
---|
651 | # check for equals sign without spaces before it
|
---|
652 | elsif($nostr =~ /(.*)[a-z0-9]\=/i) {
|
---|
653 | checkwarn("NOSPACEEQUALS",
|
---|
654 | $line, length($1)+1, $file, $ol,
|
---|
655 | "no space before equals sign");
|
---|
656 | }
|
---|
657 |
|
---|
658 | # check for plus signs without spaces next to it
|
---|
659 | if($nostr =~ /(.*)[^+]\+[a-z0-9]/i) {
|
---|
660 | checkwarn("PLUSNOSPACE",
|
---|
661 | $line, length($1)+1, $file, $ol,
|
---|
662 | "no space after plus sign");
|
---|
663 | }
|
---|
664 | # check for plus sign without spaces before it
|
---|
665 | elsif($nostr =~ /(.*)[a-z0-9]\+[^+]/i) {
|
---|
666 | checkwarn("NOSPACEPLUS",
|
---|
667 | $line, length($1)+1, $file, $ol,
|
---|
668 | "no space before plus sign");
|
---|
669 | }
|
---|
670 |
|
---|
671 | # check for semicolons without space next to it
|
---|
672 | if($nostr =~ /(.*)\;[a-z0-9]/i) {
|
---|
673 | checkwarn("SEMINOSPACE",
|
---|
674 | $line, length($1)+1, $file, $ol,
|
---|
675 | "no space after semicolon");
|
---|
676 | }
|
---|
677 |
|
---|
678 | # check for more than one consecutive space before open brace or
|
---|
679 | # question mark. Skip lines containing strings since they make it hard
|
---|
680 | # due to artificially getting multiple spaces
|
---|
681 | if(($l eq $nostr) &&
|
---|
682 | $nostr =~ /^(.*(\S)) + [{?]/i) {
|
---|
683 | checkwarn("MULTISPACE",
|
---|
684 | $line, length($1)+1, $file, $ol,
|
---|
685 | "multiple space");
|
---|
686 | print STDERR "L: $l\n";
|
---|
687 | print STDERR "nostr: $nostr\n";
|
---|
688 | }
|
---|
689 |
|
---|
690 | $line++;
|
---|
691 | $prevl = $ol;
|
---|
692 | }
|
---|
693 |
|
---|
694 | if(!scalar(@copyright)) {
|
---|
695 | checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
|
---|
696 | }
|
---|
697 |
|
---|
698 | # COPYRIGHTYEAR is a extended warning so we must first see if it has been
|
---|
699 | # enabled in .checksrc
|
---|
700 | if(defined($warnings{"COPYRIGHTYEAR"})) {
|
---|
701 | # The check for updated copyrightyear is overly complicated in order to
|
---|
702 | # not punish current hacking for past sins. The copyright years are
|
---|
703 | # right now a bit behind, so enforcing copyright year checking on all
|
---|
704 | # files would cause hundreds of errors. Instead we only look at files
|
---|
705 | # which are tracked in the Git repo and edited in the workdir, or
|
---|
706 | # committed locally on the branch without being in upstream master.
|
---|
707 | #
|
---|
708 | # The simple and naive test is to simply check for the current year,
|
---|
709 | # but updating the year even without an edit is against project policy
|
---|
710 | # (and it would fail every file on January 1st).
|
---|
711 | #
|
---|
712 | # A rather more interesting, and correct, check would be to not test
|
---|
713 | # only locally committed files but inspect all files wrt the year of
|
---|
714 | # their last commit. Removing the `git rev-list origin/master..HEAD`
|
---|
715 | # condition below will enfore copyright year checks against the year
|
---|
716 | # the file was last committed (and thus edited to some degree).
|
---|
717 | my $commityear = undef;
|
---|
718 | @copyright = sort {$$b{year} cmp $$a{year}} @copyright;
|
---|
719 |
|
---|
720 | if(`git status -s -- $file` =~ /^ [MARCU]/) {
|
---|
721 | $commityear = (localtime(time))[5] + 1900;
|
---|
722 | }
|
---|
723 | elsif (`git rev-list --count origin/master..HEAD -- $file` !~ /^0/) {
|
---|
724 | my $grl = `git rev-list --max-count=1 --timestamp HEAD -- $file`;
|
---|
725 | $commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900;
|
---|
726 | }
|
---|
727 |
|
---|
728 | if(defined($commityear) && scalar(@copyright) &&
|
---|
729 | $copyright[0]{year} != $commityear) {
|
---|
730 | checkwarn("COPYRIGHTYEAR", $copyright[0]{line}, $copyright[0]{col},
|
---|
731 | $file, $copyright[0]{code},
|
---|
732 | "Copyright year out of date, should be $commityear, " .
|
---|
733 | "is $copyright[0]{year}", 1);
|
---|
734 | }
|
---|
735 | }
|
---|
736 |
|
---|
737 | if($incomment) {
|
---|
738 | checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
|
---|
739 | }
|
---|
740 |
|
---|
741 | checksrc_endoffile($file);
|
---|
742 |
|
---|
743 | close(R);
|
---|
744 |
|
---|
745 | }
|
---|
746 |
|
---|
747 |
|
---|
748 | if($errors || $warnings || $verbose) {
|
---|
749 | printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
|
---|
750 | if($suppressed) {
|
---|
751 | printf "checksrc: %d errors and %d warnings suppressed\n",
|
---|
752 | $serrors,
|
---|
753 | $swarnings;
|
---|
754 | }
|
---|
755 | exit 5; # return failure
|
---|
756 | }
|
---|