2 #***************************************************************************
4 # Project ___| | | | _ \| |
6 # | (__| |_| | _ <| |___
7 # \___|\___/|_| \_\_____|
9 # Copyright (C) 2011 - 2018, 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 $suppressed; # 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',
62 'EQUALSNOSPACE' => 'equals sign without following space',
63 'NOSPACEEQUALS' => 'equals sign without preceding space',
64 'SEMINOSPACE' => 'semicolon without following space',
65 'MULTISPACE' => 'multiple spaces used when not suitable',
69 open(W, "<$dir/checksrc.whitelist");
72 $windows_os ? $_ =~ s/\r?\n$// : chomp;
79 my ($name, $num, $col, $file, $line, $msg, $error) = @_;
81 my $w=$error?"error":"warning";
84 #if(!$warnings{$name}) {
85 # print STDERR "Dev! there's no description for $name!\n";
89 if($whitelist{$line}) {
92 # !checksrc! controlled
93 elsif($ignore{$name}) {
95 $ignore_used{$name}++;
98 # reached zero, enable again
99 enable_warn($name, $line, $file, $l);
122 print "$file:$num:$col: $w: $msg ($name)\n";
126 my $pref = (' ' x $col);
135 if($file =~ /-D(.*)/) {
140 elsif($file =~ /-W(.*)/) {
145 elsif($file =~ /-i([1-9])/) {
150 elsif($file =~ /-m([0-9]+)/) {
151 $max_column = $1 + 0;
155 elsif($file =~ /^(-h|--help)/) {
164 print "checksrc.pl [option] <file1> [file2] ...\n";
166 print " -D[DIR] Directory to prepend file names\n";
167 print " -h Show help output\n";
168 print " -W[file] Whitelist the given file - ignore all its flaws\n";
169 print " -i<n> Indent spaces. Default: 2\n";
170 print " -m<n> Maximum line length. Default: 79\n";
171 print "\nDetects and warns for these problems:\n";
172 for(sort keys %warnings) {
173 printf (" %-18s: %s\n", $_, $warnings{$_});
181 if("$wlist" !~ / $file /) {
182 my $fullname = $file;
183 $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
196 sub checksrc_endoffile {
198 for(keys %ignore_set) {
199 if($ignore_set{$_} && !$ignore_used{$_}) {
200 checkwarn("UNUSEDIGNORE", $ignore_set{$_},
201 length($_)+11, $file,
202 $ignore_line[$ignore_set{$_}],
203 "Unused ignore: $_");
209 my ($what, $line, $file, $l) = @_;
211 # switch it back on, but warn if not triggered!
212 if(!$ignore_used{$what}) {
213 checkwarn("UNUSEDIGNORE",
214 $line, length($what) + 11, $file, $l,
215 "No warning was inhibited!");
217 $ignore_set{$what}=0;
218 $ignore_used{$what}=0;
222 my ($cmd, $line, $file, $l) = @_;
223 if($cmd =~ / *([^ ]*) *(.*)/) {
224 my ($enable, $what) = ($1, $2);
225 $what =~ s: *\*/$::; # cut off end of C comment
226 # print "ENABLE $enable WHAT $what\n";
227 if($enable eq "disable") {
228 my ($warn, $scope)=($1, $2);
229 if($what =~ /([^ ]*) +(.*)/) {
230 ($warn, $scope)=($1, $2);
236 # print "IGNORE $warn for SCOPE $scope\n";
237 if($scope eq "all") {
241 if($ignore_set{$warn}) {
242 checkwarn("BADCOMMAND",
244 "$warn already disabled from line $ignore_set{$warn}");
247 $ignore{$warn}=$scope;
248 $ignore_set{$warn}=$line;
249 $ignore_line[$line]=$l;
252 elsif($enable eq "enable") {
253 enable_warn($what, $line, $file, $l);
256 checkwarn("BADCOMMAND",
258 "Illegal !checksrc! command");
275 open(R, "<$file") || die "failed to open $file";
279 checksrc_clear(); # for file based ignores
282 $windows_os ? $_ =~ s/\r?\n$// : chomp;
284 my $ol = $l; # keep the unmodified line for error reporting
287 # check for !checksrc! commands
288 if($l =~ /\!checksrc\! (.*)/) {
290 checksrc($cmd, $line, $file, $l)
293 # check for a copyright statement
294 if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) {
299 if(length($l) > $max_column) {
300 checkwarn("LONGLINE", $line, length($l), $file, $l,
301 "Longer than $max_column columns");
303 # detect TAB characters
304 if($l =~ /^(.*)\t/) {
306 $line, length($1), $file, $l, "Contains TAB character", 1);
308 # detect trailing white space
309 if($l =~ /^(.*)[ \t]+\z/) {
310 checkwarn("TRAILINGSPACE",
311 $line, length($1), $file, $l, "Trailing whitespace");
314 # ------------------------------------------------------------
315 # Above this marker, the checks were done on lines *including*
317 # ------------------------------------------------------------
319 # strip off C89 comments
323 if($l =~ s/\/\*.*\*\// /g) {
324 # full /* comments */ were removed!
326 if($l =~ s/\/\*.*//) {
327 # start of /* comment was removed
332 if($l =~ s/.*\*\///) {
333 # end of comment */ was removed
338 # still within a comment
343 # ------------------------------------------------------------
344 # Below this marker, the checks were done on lines *without*
346 # ------------------------------------------------------------
348 # crude attempt to detect // comments without too many false
350 if($l =~ /^([^"\*]*)[^:"]\/\//) {
351 checkwarn("CPPCOMMENTS",
352 $line, length($1), $file, $l, "\/\/ comment");
355 my $nostr = nostrings($l);
356 # check spaces after for/if/while/function call
357 if($nostr =~ /^(.*)(for|if|while| ([a-zA-Z0-9_]+)) \((.)/) {
359 # this is a #if, treat it differently
361 elsif($3 eq "return") {
362 # return must have a space
364 elsif($3 eq "case") {
365 # case must have a space
368 # (* beginning makes the space OK!
370 elsif($1 =~ / *typedef/) {
371 # typedefs can use space-paren
374 checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
379 if($nostr =~ /^((.*)(if) *\()(.*)\)/) {
380 my $pos = length($1);
382 checkwarn("ASSIGNWITHINCONDITION",
383 $line, $pos+1, $file, $l,
384 "assignment within conditional expression");
387 # check spaces after open parentheses
388 if($l =~ /^(.*[a-z])\( /i) {
389 checkwarn("SPACEAFTERPAREN",
390 $line, length($1)+1, $file, $l,
391 "space after open parenthesis");
394 # check spaces before close parentheses, unless it was a space or a
396 if($l =~ /(.*[^\) ]) \)/) {
397 checkwarn("SPACEBEFORECLOSE",
398 $line, length($1)+1, $file, $l,
399 "space before close parenthesis");
402 # check spaces before comma!
403 if($l =~ /(.*[^ ]) ,/) {
404 checkwarn("SPACEBEFORECOMMA",
405 $line, length($1)+1, $file, $l,
406 "space before comma");
409 # check for "return(" without space
410 if($l =~ /^(.*)return\(/) {
412 # this is a #if, treat it differently
415 checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
416 "return without space before paren");
420 # check for comma without space
421 if($l =~ /^(.*),[^ \n]/) {
424 if($pref =~ / *\#/) {
425 # this is a #if, treat it differently
428 elsif($pref =~ /\/\*/) {
432 elsif($pref =~ /[\"\']/) {
434 # There is a quote here, figure out whether the comma is
435 # within a string or '' or not.
439 elsif($pref =~ /\'$/) {
447 checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
448 "comma without following space");
453 if($l =~ /^(.*)\} *else/) {
454 checkwarn("BRACEELSE",
455 $line, length($1), $file, $l, "else after closing brace on same line");
458 if($l =~ /^(.*)\)\{/) {
459 checkwarn("PARENBRACE",
460 $line, length($1)+1, $file, $l, "missing space after close paren");
463 # check for space before the semicolon last in a line
464 if($l =~ /^(.*[^ ].*) ;$/) {
465 checkwarn("SPACESEMILCOLON",
466 $line, length($1), $file, $ol, "space before last semicolon");
469 # scan for use of banned functions
474 (str|_mbs|_tcs|_wcs)n?cat|
475 LoadLibrary(Ex)?(A|W)?)
478 checkwarn("BANNEDFUNC",
479 $line, length($1), $file, $ol,
480 "use of $2 is banned");
483 # scan for use of non-binary fopen without the macro
484 if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
487 checkwarn("FOPENMODE",
488 $line, length($1), $file, $ol,
489 "use of non-binary fopen without FOPEN_* macro: $mode");
493 # check for open brace first on line but not first column
494 # only alert if previous line ended with a close paren and wasn't a cpp
496 if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
497 checkwarn("BRACEPOS",
498 $line, length($1), $file, $ol, "badly placed open brace");
501 # if the previous line starts with if/while/for AND ends with an open
502 # brace, check that this line is indented $indent more steps, if not
504 if($prevl =~ /^( *)(if|while|for)\(.*\{\z/) {
505 my $first = length($1);
507 # this line has some character besides spaces
508 if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
509 my $second = length($1);
510 my $expect = $first+$indent;
511 if($expect != $second) {
512 my $diff = $second - $first;
513 checkwarn("INDENTATION", $line, length($1), $file, $ol,
514 "not indented $indent steps, uses $diff)");
520 # check for 'char * name'
521 if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost) *(\*+)) (\w+)/) && ($4 ne "const")) {
522 checkwarn("ASTERISKNOSPACE",
523 $line, length($1), $file, $ol,
524 "no space after declarative asterisk");
527 if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost|sockaddr_in|FILE)\*)/)) {
528 checkwarn("ASTERISKNOSPACE",
529 $line, length($1)-1, $file, $ol,
530 "no space before asterisk");
533 # check for 'void func() {', but avoid false positives by requiring
534 # both an open and closed parentheses before the open brace
535 if($l =~ /^((\w).*)\{\z/) {
540 checkwarn("BRACEPOS",
541 $line, length($l)-1, $file, $ol,
542 "wrongly placed open brace");
546 # check for equals sign without spaces next to it
547 if($nostr =~ /(.*)\=[a-z0-9]/i) {
548 checkwarn("EQUALSNOSPACE",
549 $line, length($1)+1, $file, $ol,
550 "no space after equals sign");
552 # check for equals sign without spaces before it
553 elsif($nostr =~ /(.*)[a-z0-9]\=/i) {
554 checkwarn("NOSPACEEQUALS",
555 $line, length($1)+1, $file, $ol,
556 "no space before equals sign");
559 # check for plus signs without spaces next to it
560 if($nostr =~ /(.*)[^+]\+[a-z0-9]/i) {
561 checkwarn("PLUSNOSPACE",
562 $line, length($1)+1, $file, $ol,
563 "no space after plus sign");
565 # check for plus sign without spaces before it
566 elsif($nostr =~ /(.*)[a-z0-9]\+[^+]/i) {
567 checkwarn("NOSPACEPLUS",
568 $line, length($1)+1, $file, $ol,
569 "no space before plus sign");
572 # check for semicolons without space next to it
573 if($nostr =~ /(.*)\;[a-z0-9]/i) {
574 checkwarn("SEMINOSPACE",
575 $line, length($1)+1, $file, $ol,
576 "no space after semilcolon");
579 # check for more than one consecutive space before open brace or
580 # question mark. Skip lines containing strings since they make it hard
581 # due to artificially getting multiple spaces
583 $nostr =~ /^(.*(\S)) + [{?]/i) {
584 checkwarn("MULTISPACE",
585 $line, length($1)+1, $file, $ol,
587 print STDERR "L: $l\n";
588 print STDERR "nostr: $nostr\n";
596 checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
599 checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
602 checksrc_endoffile($file);
609 if($errors || $warnings || $verbose) {
610 printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
612 printf "checksrc: %d errors and %d warnings suppressed\n",
616 exit 5; # return failure