2 #***************************************************************************
4 # Project ___| | | | _ \| |
6 # | (__| |_| | _ <| |___
7 # \___|\___/|_| \_\_____|
9 # Copyright (C) 2011 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
19 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 # KIND, either express or implied.
22 ###########################################################################
29 my $supressed; # whitelisted problems
33 my $windows_os = $^O eq 'MSWin32' || $^O eq 'msys' || $^O eq 'cygwin';
38 'LONGLINE' => "Line longer than $max_column",
39 'TABS' => 'TAB characters not allowed',
40 'TRAILINGSPACE' => 'Trailing white space on the line',
41 'CPPCOMMENTS' => '// comment detected',
42 'SPACEBEFOREPAREN' => 'space before an open parenthesis',
43 'SPACEAFTERPAREN' => 'space after open parenthesis',
44 'SPACEBEFORECLOSE' => 'space before a close parenthesis',
45 'SPACEBEFORECOMMA' => 'space before a comma',
46 'RETURNNOSPACE' => 'return without space',
47 'COMMANOSPACE' => 'comma without following space',
48 'BRACEELSE' => '} else on the same line',
49 'PARENBRACE' => '){ without sufficient space',
50 'SPACESEMILCOLON' => 'space before semicolon',
51 'BANNEDFUNC' => 'a banned function was used',
52 'FOPENMODE' => 'fopen needs a macro for the mode string',
53 'BRACEPOS' => 'wrong position for an open brace',
54 'INDENTATION' => 'wrong start column for code',
55 'COPYRIGHT' => 'file missing a copyright statement',
56 'BADCOMMAND' => 'bad !checksrc! instruction',
57 'UNUSEDIGNORE' => 'a warning ignore was not used',
58 'OPENCOMMENT' => 'file ended with a /* comment still "open"'
62 open(W, "<$dir/checksrc.whitelist");
65 $windows_os ? $_ =~ s/\r?\n$// : chomp;
72 my ($name, $num, $col, $file, $line, $msg, $error) = @_;
74 my $w=$error?"error":"warning";
77 #if(!$warnings{$name}) {
78 # print STDERR "Dev! there's no description for $name!\n";
82 if($whitelist{$line}) {
85 # !checksrc! controlled
86 elsif($ignore{$name}) {
88 $ignore_used{$name}++;
91 # reached zero, enable again
92 enable_warn($name, $line, $file, $l);
115 print "$file:$num:$col: $w: $msg ($name)\n";
119 my $pref = (' ' x $col);
128 if($file =~ /-D(.*)/) {
133 elsif($file =~ /-W(.*)/) {
138 elsif($file =~ /^(-h|--help)/) {
147 print "checksrc.pl [option] <file1> [file2] ...\n";
149 print " -D[DIR] Directory to prepend file names\n";
150 print " -h Show help output\n";
151 print " -W[file] Whitelist the given file - ignore all its flaws\n";
152 print "\nDetects and warns for these problems:\n";
153 for(sort keys %warnings) {
154 printf (" %-18s: %s\n", $_, $warnings{$_});
162 if("$wlist" !~ / $file /) {
163 my $fullname = $file;
164 $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
177 sub checksrc_endoffile {
179 for(keys %ignore_set) {
180 if($ignore_set{$_} && !$ignore_used{$_}) {
181 checkwarn("UNUSEDIGNORE", $ignore_set{$_},
182 length($_)+11, $file,
183 $ignore_line[$ignore_set{$_}],
184 "Unused ignore: $_");
190 my ($what, $line, $file, $l) = @_;
192 # switch it back on, but warn if not triggered!
193 if(!$ignore_used{$what}) {
194 checkwarn("UNUSEDIGNORE",
195 $line, length($what) + 11, $file, $l,
196 "No warning was inhibited!");
198 $ignore_set{$what}=0;
199 $ignore_used{$what}=0;
203 my ($cmd, $line, $file, $l) = @_;
204 if($cmd =~ / *([^ ]*) *(.*)/) {
205 my ($enable, $what) = ($1, $2);
206 $what =~ s: *\*/$::; # cut off end of C comment
207 # print "ENABLE $enable WHAT $what\n";
208 if($enable eq "disable") {
209 my ($warn, $scope)=($1, $2);
210 if($what =~ /([^ ]*) +(.*)/) {
211 ($warn, $scope)=($1, $2);
217 # print "IGNORE $warn for SCOPE $scope\n";
218 if($scope eq "all") {
222 if($ignore_set{$warn}) {
223 checkwarn("BADCOMMAND",
225 "$warn already disabled from line $ignore_set{$warn}");
228 $ignore{$warn}=$scope;
229 $ignore_set{$warn}=$line;
230 $ignore_line[$line]=$l;
233 elsif($enable eq "enable") {
234 enable_warn($what, $line, $file, $l);
237 checkwarn("BADCOMMAND",
239 "Illegal !checksrc! command");
250 open(R, "<$file") || die "failed to open $file";
254 checksrc_clear(); # for file based ignores
257 $windows_os ? $_ =~ s/\r?\n$// : chomp;
259 my $ol = $l; # keep the unmodified line for error reporting
262 # check for !checksrc! commands
263 if($l =~ /\!checksrc\! (.*)/) {
265 checksrc($cmd, $line, $file, $l)
268 # check for a copyright statement
269 if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) {
274 if(length($l) > $max_column) {
275 checkwarn("LONGLINE", $line, length($l), $file, $l,
276 "Longer than $max_column columns");
278 # detect TAB characters
279 if($l =~ /^(.*)\t/) {
281 $line, length($1), $file, $l, "Contains TAB character", 1);
283 # detect trailing white space
284 if($l =~ /^(.*)[ \t]+\z/) {
285 checkwarn("TRAILINGSPACE",
286 $line, length($1), $file, $l, "Trailing whitespace");
289 # ------------------------------------------------------------
290 # Above this marker, the checks were done on lines *including*
292 # ------------------------------------------------------------
294 # strip off C89 comments
298 if($l =~ s/\/\*.*\*\// /g) {
299 # full /* comments */ were removed!
301 if($l =~ s/\/\*.*//) {
302 # start of /* comment was removed
307 if($l =~ s/.*\*\///) {
308 # end of comment */ was removed
313 # still within a comment
318 # ------------------------------------------------------------
319 # Below this marker, the checks were done on lines *without*
321 # ------------------------------------------------------------
323 # crude attempt to detect // comments without too many false
325 if($l =~ /^([^"\*]*)[^:"]\/\//) {
326 checkwarn("CPPCOMMENTS",
327 $line, length($1), $file, $l, "\/\/ comment");
330 # check spaces after for/if/while
331 if($l =~ /^(.*)(for|if|while) \(/) {
333 # this is a #if, treat it differently
336 checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
341 # check spaces after open parentheses
342 if($l =~ /^(.*[a-z])\( /i) {
343 checkwarn("SPACEAFTERPAREN",
344 $line, length($1)+1, $file, $l,
345 "space after open parenthesis");
348 # check spaces before close parentheses, unless it was a space or a
350 if($l =~ /(.*[^\) ]) \)/) {
351 checkwarn("SPACEBEFORECLOSE",
352 $line, length($1)+1, $file, $l,
353 "space before close parenthesis");
356 # check spaces before comma!
357 if($l =~ /(.*[^ ]) ,/) {
358 checkwarn("SPACEBEFORECOMMA",
359 $line, length($1)+1, $file, $l,
360 "space before comma");
363 # check for "return(" without space
364 if($l =~ /^(.*)return\(/) {
366 # this is a #if, treat it differently
369 checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
370 "return without space before paren");
374 # check for comma without space
375 if($l =~ /^(.*),[^ \n]/) {
378 if($pref =~ / *\#/) {
379 # this is a #if, treat it differently
382 elsif($pref =~ /\/\*/) {
386 elsif($pref =~ /[\"\']/) {
388 # There is a quote here, figure out whether the comma is
389 # within a string or '' or not.
393 elsif($pref =~ /\'$/) {
401 checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
402 "comma without following space");
407 if($l =~ /^(.*)\} *else/) {
408 checkwarn("BRACEELSE",
409 $line, length($1), $file, $l, "else after closing brace on same line");
412 if($l =~ /^(.*)\)\{/) {
413 checkwarn("PARENBRACE",
414 $line, length($1)+1, $file, $l, "missing space after close paren");
417 # check for space before the semicolon last in a line
418 if($l =~ /^(.*[^ ].*) ;$/) {
419 checkwarn("SPACESEMILCOLON",
420 $line, length($1), $file, $ol, "space before last semicolon");
423 # scan for use of banned functions
427 (str|_mbs|_tcs|_wcs)n?cat|
428 LoadLibrary(Ex)?(A|W)?)
431 checkwarn("BANNEDFUNC",
432 $line, length($1), $file, $ol,
433 "use of $2 is banned");
436 # scan for use of non-binary fopen without the macro
437 if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
440 checkwarn("FOPENMODE",
441 $line, length($1), $file, $ol,
442 "use of non-binary fopen without FOPEN_* macro: $mode");
446 # check for open brace first on line but not first column
447 # only alert if previous line ended with a close paren and wasn't a cpp
449 if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
450 checkwarn("BRACEPOS",
451 $line, length($1), $file, $ol, "badly placed open brace");
454 # if the previous line starts with if/while/for AND ends with an open
455 # brace, check that this line is indented $indent more steps, if not
457 if($prevl =~ /^( *)(if|while|for)\(.*\{\z/) {
458 my $first = length($1);
460 # this line has some character besides spaces
461 if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
462 my $second = length($1);
463 my $expect = $first+$indent;
464 if($expect != $second) {
465 my $diff = $second - $first;
466 checkwarn("INDENTATION", $line, length($1), $file, $ol,
467 "not indented $indent steps, uses $diff)");
478 checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
481 checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
484 checksrc_endoffile($file);
491 if($errors || $warnings || $verbose) {
492 printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
494 printf "checksrc: %d errors and %d warnings suppressed\n",
498 exit 5; # return failure