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"',
59 'ASTERISKSPACE' => 'pointer declared with space after asterisk',
60 'ASTERISKNOSPACE' => 'pointer declared without space before asterisk',
61 'ASSIGNWITHINCONDITION' => 'assignment within conditional expression'
65 open(W, "<$dir/checksrc.whitelist");
68 $windows_os ? $_ =~ s/\r?\n$// : chomp;
75 my ($name, $num, $col, $file, $line, $msg, $error) = @_;
77 my $w=$error?"error":"warning";
80 #if(!$warnings{$name}) {
81 # print STDERR "Dev! there's no description for $name!\n";
85 if($whitelist{$line}) {
88 # !checksrc! controlled
89 elsif($ignore{$name}) {
91 $ignore_used{$name}++;
94 # reached zero, enable again
95 enable_warn($name, $line, $file, $l);
118 print "$file:$num:$col: $w: $msg ($name)\n";
122 my $pref = (' ' x $col);
131 if($file =~ /-D(.*)/) {
136 elsif($file =~ /-W(.*)/) {
141 elsif($file =~ /^(-h|--help)/) {
150 print "checksrc.pl [option] <file1> [file2] ...\n";
152 print " -D[DIR] Directory to prepend file names\n";
153 print " -h Show help output\n";
154 print " -W[file] Whitelist the given file - ignore all its flaws\n";
155 print "\nDetects and warns for these problems:\n";
156 for(sort keys %warnings) {
157 printf (" %-18s: %s\n", $_, $warnings{$_});
165 if("$wlist" !~ / $file /) {
166 my $fullname = $file;
167 $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
180 sub checksrc_endoffile {
182 for(keys %ignore_set) {
183 if($ignore_set{$_} && !$ignore_used{$_}) {
184 checkwarn("UNUSEDIGNORE", $ignore_set{$_},
185 length($_)+11, $file,
186 $ignore_line[$ignore_set{$_}],
187 "Unused ignore: $_");
193 my ($what, $line, $file, $l) = @_;
195 # switch it back on, but warn if not triggered!
196 if(!$ignore_used{$what}) {
197 checkwarn("UNUSEDIGNORE",
198 $line, length($what) + 11, $file, $l,
199 "No warning was inhibited!");
201 $ignore_set{$what}=0;
202 $ignore_used{$what}=0;
206 my ($cmd, $line, $file, $l) = @_;
207 if($cmd =~ / *([^ ]*) *(.*)/) {
208 my ($enable, $what) = ($1, $2);
209 $what =~ s: *\*/$::; # cut off end of C comment
210 # print "ENABLE $enable WHAT $what\n";
211 if($enable eq "disable") {
212 my ($warn, $scope)=($1, $2);
213 if($what =~ /([^ ]*) +(.*)/) {
214 ($warn, $scope)=($1, $2);
220 # print "IGNORE $warn for SCOPE $scope\n";
221 if($scope eq "all") {
225 if($ignore_set{$warn}) {
226 checkwarn("BADCOMMAND",
228 "$warn already disabled from line $ignore_set{$warn}");
231 $ignore{$warn}=$scope;
232 $ignore_set{$warn}=$line;
233 $ignore_line[$line]=$l;
236 elsif($enable eq "enable") {
237 enable_warn($what, $line, $file, $l);
240 checkwarn("BADCOMMAND",
242 "Illegal !checksrc! command");
259 open(R, "<$file") || die "failed to open $file";
263 checksrc_clear(); # for file based ignores
266 $windows_os ? $_ =~ s/\r?\n$// : chomp;
268 my $ol = $l; # keep the unmodified line for error reporting
271 # check for !checksrc! commands
272 if($l =~ /\!checksrc\! (.*)/) {
274 checksrc($cmd, $line, $file, $l)
277 # check for a copyright statement
278 if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) {
283 if(length($l) > $max_column) {
284 checkwarn("LONGLINE", $line, length($l), $file, $l,
285 "Longer than $max_column columns");
287 # detect TAB characters
288 if($l =~ /^(.*)\t/) {
290 $line, length($1), $file, $l, "Contains TAB character", 1);
292 # detect trailing white space
293 if($l =~ /^(.*)[ \t]+\z/) {
294 checkwarn("TRAILINGSPACE",
295 $line, length($1), $file, $l, "Trailing whitespace");
298 # ------------------------------------------------------------
299 # Above this marker, the checks were done on lines *including*
301 # ------------------------------------------------------------
303 # strip off C89 comments
307 if($l =~ s/\/\*.*\*\// /g) {
308 # full /* comments */ were removed!
310 if($l =~ s/\/\*.*//) {
311 # start of /* comment was removed
316 if($l =~ s/.*\*\///) {
317 # end of comment */ was removed
322 # still within a comment
327 # ------------------------------------------------------------
328 # Below this marker, the checks were done on lines *without*
330 # ------------------------------------------------------------
332 # crude attempt to detect // comments without too many false
334 if($l =~ /^([^"\*]*)[^:"]\/\//) {
335 checkwarn("CPPCOMMENTS",
336 $line, length($1), $file, $l, "\/\/ comment");
339 my $nostr = nostrings($l);
340 # check spaces after for/if/while/function call
341 if($nostr =~ /^(.*)(for|if|while| ([a-zA-Z0-9_]+)) \((.)/) {
343 # this is a #if, treat it differently
345 elsif($3 eq "return") {
346 # return must have a space
349 # (* beginning makes the space OK!
351 elsif($1 =~ / *typedef/) {
352 # typedefs can use space-paren
355 checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
360 if($nostr =~ /^((.*)(if) *\()(.*)\)/) {
361 my $pos = length($1);
363 checkwarn("ASSIGNWITHINCONDITION",
364 $line, $pos+1, $file, $l,
365 "assignment within conditional expression");
368 # check spaces after open parentheses
369 if($l =~ /^(.*[a-z])\( /i) {
370 checkwarn("SPACEAFTERPAREN",
371 $line, length($1)+1, $file, $l,
372 "space after open parenthesis");
375 # check spaces before close parentheses, unless it was a space or a
377 if($l =~ /(.*[^\) ]) \)/) {
378 checkwarn("SPACEBEFORECLOSE",
379 $line, length($1)+1, $file, $l,
380 "space before close parenthesis");
383 # check spaces before comma!
384 if($l =~ /(.*[^ ]) ,/) {
385 checkwarn("SPACEBEFORECOMMA",
386 $line, length($1)+1, $file, $l,
387 "space before comma");
390 # check for "return(" without space
391 if($l =~ /^(.*)return\(/) {
393 # this is a #if, treat it differently
396 checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
397 "return without space before paren");
401 # check for comma without space
402 if($l =~ /^(.*),[^ \n]/) {
405 if($pref =~ / *\#/) {
406 # this is a #if, treat it differently
409 elsif($pref =~ /\/\*/) {
413 elsif($pref =~ /[\"\']/) {
415 # There is a quote here, figure out whether the comma is
416 # within a string or '' or not.
420 elsif($pref =~ /\'$/) {
428 checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
429 "comma without following space");
434 if($l =~ /^(.*)\} *else/) {
435 checkwarn("BRACEELSE",
436 $line, length($1), $file, $l, "else after closing brace on same line");
439 if($l =~ /^(.*)\)\{/) {
440 checkwarn("PARENBRACE",
441 $line, length($1)+1, $file, $l, "missing space after close paren");
444 # check for space before the semicolon last in a line
445 if($l =~ /^(.*[^ ].*) ;$/) {
446 checkwarn("SPACESEMILCOLON",
447 $line, length($1), $file, $ol, "space before last semicolon");
450 # scan for use of banned functions
455 (str|_mbs|_tcs|_wcs)n?cat|
456 LoadLibrary(Ex)?(A|W)?)
459 checkwarn("BANNEDFUNC",
460 $line, length($1), $file, $ol,
461 "use of $2 is banned");
464 # scan for use of non-binary fopen without the macro
465 if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
468 checkwarn("FOPENMODE",
469 $line, length($1), $file, $ol,
470 "use of non-binary fopen without FOPEN_* macro: $mode");
474 # check for open brace first on line but not first column
475 # only alert if previous line ended with a close paren and wasn't a cpp
477 if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
478 checkwarn("BRACEPOS",
479 $line, length($1), $file, $ol, "badly placed open brace");
482 # if the previous line starts with if/while/for AND ends with an open
483 # brace, check that this line is indented $indent more steps, if not
485 if($prevl =~ /^( *)(if|while|for)\(.*\{\z/) {
486 my $first = length($1);
488 # this line has some character besides spaces
489 if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
490 my $second = length($1);
491 my $expect = $first+$indent;
492 if($expect != $second) {
493 my $diff = $second - $first;
494 checkwarn("INDENTATION", $line, length($1), $file, $ol,
495 "not indented $indent steps, uses $diff)");
501 # check for 'char * name'
502 if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost) *(\*+)) (\w+)/) && ($4 ne "const")) {
503 checkwarn("ASTERISKNOSPACE",
504 $line, length($1), $file, $ol,
505 "no space after declarative asterisk");
508 if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost|sockaddr_in|FILE)\*)/)) {
509 checkwarn("ASTERISKNOSPACE",
510 $line, length($1)-1, $file, $ol,
511 "no space before asterisk");
514 # check for 'void func() {', but avoid false positives by requiring
515 # both an open and closed parentheses before the open brace
516 if($l =~ /^((\w).*){\z/) {
521 checkwarn("BRACEPOS",
522 $line, length($l)-1, $file, $ol,
523 "wrongly placed open brace");
531 checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
534 checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
537 checksrc_endoffile($file);
544 if($errors || $warnings || $verbose) {
545 printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
547 printf "checksrc: %d errors and %d warnings suppressed\n",
551 exit 5; # return failure