Imported Upstream version 7.50.2
[platform/upstream/curl.git] / lib / checksrc.pl
index 0c16746..f31083a 100755 (executable)
@@ -6,7 +6,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 2011 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
 #
 # This software is licensed as described in the file COPYING, which
 # you should have received as part of this distribution. The terms
@@ -31,9 +31,33 @@ my $file;
 my $dir=".";
 my $wlist;
 my $windows_os = $^O eq 'MSWin32' || $^O eq 'msys' || $^O eq 'cygwin';
-
+my $verbose;
 my %whitelist;
 
+my %warnings = (
+    'LONGLINE' =>         "Line longer than $max_column",
+    'TABS' =>             'TAB characters not allowed',
+    'TRAILINGSPACE' =>    'Trailing white space on the line',
+    'CPPCOMMENTS' =>      '// comment detected',
+    'SPACEBEFOREPAREN' => 'space before an open parenthesis',
+    'SPACEAFTERPAREN'  => 'space after open parenthesis',
+    'SPACEBEFORECLOSE' => 'space before a close parenthesis',
+    'SPACEBEFORECOMMA' => 'space before a comma',
+    'RETURNNOSPACE'    => 'return without space',
+    'COMMANOSPACE'     => 'comma without following space',
+    'BRACEELSE'        => '} else on the same line',
+    'PARENBRACE'       => '){ without sufficient space',
+    'SPACESEMILCOLON'  => 'space before semicolon',
+    'BANNEDFUNC'       => 'a banned function was used',
+    'FOPENMODE'        => 'fopen needs a macro for the mode string',
+    'BRACEPOS'         => 'wrong position for an open brace',
+    'INDENTATION'      => 'wrong start column for code',
+    'COPYRIGHT'        => 'file missing a copyright statement',
+    'BADCOMMAND'       => 'bad !checksrc! instruction',
+    'UNUSEDIGNORE'     => 'a warning ignore was not used',
+    'OPENCOMMENT'      => 'file ended with a /* comment still "open"'
+    );
+
 sub readwhitelist {
     open(W, "<$dir/checksrc.whitelist");
     my @all=<W>;
@@ -45,14 +69,40 @@ sub readwhitelist {
 }
 
 sub checkwarn {
-    my ($num, $col, $file, $line, $msg, $error) = @_;
+    my ($name, $num, $col, $file, $line, $msg, $error) = @_;
+
+    my $w=$error?"error":"warning";
+    my $nowarn=0;
 
+    #if(!$warnings{$name}) {
+    #    print STDERR "Dev! there's no description for $name!\n";
+    #}
+
+    # checksrc.whitelist
     if($whitelist{$line}) {
+        $nowarn = 1;
+    }
+    # !checksrc! controlled
+    elsif($ignore{$name}) {
+        $ignore{$name}--;
+        $ignore_used{$name}++;
+        $nowarn = 1;
+        if(!$ignore{$name}) {
+            # reached zero, enable again
+            enable_warn($name, $line, $file, $l);
+        }
+    }
+
+    if($nowarn) {
         $supressed++;
+        if($w) {
+            $swarnings++;
+        }
+        else {
+            $serrors++;
+        }
         return;
     }
-    
-    my $w=$error?"error":"warning";
 
     if($w) {
         $warnings++;
@@ -62,7 +112,7 @@ sub checkwarn {
     }
 
     $col++;
-    print "$file:$num:$col: $w: $msg\n";
+    print "$file:$num:$col: $w: $msg ($name)\n";
     print " $line\n";
 
     if($col < 80) {
@@ -85,6 +135,10 @@ while(1) {
         $file = shift @ARGV;
         next;
     }
+    elsif($file =~ /^(-h|--help)/) {
+        undef $file;
+        last;
+    }
 
     last;
 }
@@ -93,7 +147,12 @@ if(!$file) {
     print "checksrc.pl [option] <file1> [file2] ...\n";
     print " Options:\n";
     print "  -D[DIR]   Directory to prepend file names\n";
+    print "  -h        Show help output\n";
     print "  -W[file]  Whitelist the given file - ignore all its flaws\n";
+    print "\nDetects and warns for these problems:\n";
+    for(sort keys %warnings) {
+        printf (" %-18s: %s\n", $_, $warnings{$_});
+    }
     exit;
 }
 
@@ -109,6 +168,78 @@ do {
 
 } while($file);
 
+sub checksrc_clear {
+    undef %ignore;
+    undef %ignore_set;
+    undef @ignore_line;
+}
+
+sub checksrc_endoffile {
+    my ($file) = @_;
+    for(keys %ignore_set) {
+        if($ignore_set{$_} && !$ignore_used{$_}) {
+            checkwarn("UNUSEDIGNORE", $ignore_set{$_},
+                      length($_)+11, $file,
+                      $ignore_line[$ignore_set{$_}],
+                      "Unused ignore: $_");
+        }
+    }
+}
+
+sub enable_warn {
+    my ($what, $line, $file, $l) = @_;
+
+    # switch it back on, but warn if not triggered!
+    if(!$ignore_used{$what}) {
+        checkwarn("UNUSEDIGNORE",
+                  $line, length($what) + 11, $file, $l,
+                  "No warning was inhibited!");
+    }
+    $ignore_set{$what}=0;
+    $ignore_used{$what}=0;
+    $ignore{$what}=0;
+}
+sub checksrc {
+    my ($cmd, $line, $file, $l) = @_;
+    if($cmd =~ / *([^ ]*) *(.*)/) {
+        my ($enable, $what) = ($1, $2);
+        $what =~ s: *\*/$::; # cut off end of C comment
+        # print "ENABLE $enable WHAT $what\n";
+        if($enable eq "disable") {
+            my ($warn, $scope)=($1, $2);
+            if($what =~ /([^ ]*) +(.*)/) {
+                ($warn, $scope)=($1, $2);
+            }
+            else {
+                $warn = $what;
+                $scope = 1;
+            }
+            # print "IGNORE $warn for SCOPE $scope\n";
+            if($scope eq "all") {
+                $scope=999999;
+            }
+
+            if($ignore_set{$warn}) {
+                checkwarn("BADCOMMAND",
+                          $line, 0, $file, $l,
+                          "$warn already disabled from line $ignore_set{$warn}");
+            }
+            else {
+                $ignore{$warn}=$scope;
+                $ignore_set{$warn}=$line;
+                $ignore_line[$line]=$l;
+            }
+        }
+        elsif($enable eq "enable") {
+            enable_warn($what, $line, $file, $l);
+        }
+        else {
+            checkwarn("BADCOMMAND",
+                      $line, 0, $file, $l,
+                      "Illegal !checksrc! command");
+        }
+    }
+}
 
 sub scanfile {
     my ($file) = @_;
@@ -118,13 +249,22 @@ sub scanfile {
     my $l;
     open(R, "<$file") || die "failed to open $file";
 
+    my $incomment=0;
     my $copyright=0;
+    checksrc_clear(); # for file based ignores
 
     while(<R>) {
         $windows_os ? $_ =~ s/\r?\n$// : chomp;
         my $l = $_;
+        my $ol = $l; # keep the unmodified line for error reporting
         my $column = 0;
 
+        # check for !checksrc! commands
+        if($l =~ /\!checksrc\! (.*)/) {
+            my $cmd = $1;
+            checksrc($cmd, $line, $file, $l)
+        }
+
         # check for a copyright statement
         if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) {
             $copyright=1;
@@ -132,42 +272,92 @@ sub scanfile {
 
         # detect long lines
         if(length($l) > $max_column) {
-            checkwarn($line, length($l), $file, $l, "Longer than $max_column columns");
+            checkwarn("LONGLINE", $line, length($l), $file, $l,
+                      "Longer than $max_column columns");
         }
         # detect TAB characters
         if($l =~ /^(.*)\t/) {
-            checkwarn($line, length($1), $file, $l, "Contains TAB character", 1);
+            checkwarn("TABS",
+                      $line, length($1), $file, $l, "Contains TAB character", 1);
         }
         # detect trailing white space
         if($l =~ /^(.*)[ \t]+\z/) {
-            checkwarn($line, length($1), $file, $l, "Trailing whitespace");
+            checkwarn("TRAILINGSPACE",
+                      $line, length($1), $file, $l, "Trailing whitespace");
+        }
+
+        # ------------------------------------------------------------
+        # Above this marker, the checks were done on lines *including*
+        # comments
+        # ------------------------------------------------------------
+
+        # strip off C89 comments
+
+      comment:
+        if(!$incomment) {
+            if($l =~ s/\/\*.*\*\// /g) {
+                # full /* comments */ were removed!
+            }
+            if($l =~ s/\/\*.*//) {
+                # start of /* comment was removed
+                $incomment = 1;
+            }
+        }
+        else {
+            if($l =~ s/.*\*\///) {
+                # end of comment */ was removed
+                $incomment = 0;
+                goto comment;
+            }
+            else {
+                # still within a comment
+                $l="";
+            }
         }
 
+        # ------------------------------------------------------------
+        # Below this marker, the checks were done on lines *without*
+        # comments
+        # ------------------------------------------------------------
+
         # crude attempt to detect // comments without too many false
         # positives
         if($l =~ /^([^"\*]*)[^:"]\/\//) {
-            checkwarn($line, length($1), $file, $l, "\/\/ comment");
+            checkwarn("CPPCOMMENTS",
+                      $line, length($1), $file, $l, "\/\/ comment");
         }
+
         # check spaces after for/if/while
         if($l =~ /^(.*)(for|if|while) \(/) {
             if($1 =~ / *\#/) {
                 # this is a #if, treat it differently
             }
             else {
-                checkwarn($line, length($1)+length($2), $file, $l,
+                checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
                           "$2 with space");
             }
         }
 
-        # check spaces after open paren after for/if/while
-        if($l =~ /^(.*)(for|if|while)\( /) {
-            if($1 =~ / *\#/) {
-                # this is a #if, treat it differently
-            }
-            else {
-                checkwarn($line, length($1)+length($2)+1, $file, $l,
-                          "$2 with space first in condition");
-            }
+        # check spaces after open parentheses
+        if($l =~ /^(.*[a-z])\( /i) {
+            checkwarn("SPACEAFTERPAREN",
+                      $line, length($1)+1, $file, $l,
+                      "space after open parenthesis");
+        }
+
+        # check spaces before close parentheses, unless it was a space or a
+        # close parenthesis!
+        if($l =~ /(.*[^\) ]) \)/) {
+            checkwarn("SPACEBEFORECLOSE",
+                      $line, length($1)+1, $file, $l,
+                      "space before close parenthesis");
+        }
+
+        # check spaces before comma!
+        if($l =~ /(.*[^ ]) ,/) {
+            checkwarn("SPACEBEFORECOMMA",
+                      $line, length($1)+1, $file, $l,
+                      "space before comma");
         }
 
         # check for "return(" without space
@@ -176,7 +366,7 @@ sub scanfile {
                 # this is a #if, treat it differently
             }
             else {
-                checkwarn($line, length($1)+6, $file, $l,
+                checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
                           "return without space before paren");
             }
         }
@@ -208,37 +398,48 @@ sub scanfile {
                 }
             }
             if(!$ign) {
-                checkwarn($line, length($pref)+1, $file, $l,
+                checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
                           "comma without following space");
             }
         }
-        
+
         # check for "} else"
         if($l =~ /^(.*)\} *else/) {
-            checkwarn($line, length($1), $file, $l, "else after closing brace on same line");
+            checkwarn("BRACEELSE",
+                      $line, length($1), $file, $l, "else after closing brace on same line");
         }
         # check for "){"
         if($l =~ /^(.*)\)\{/) {
-            checkwarn($line, length($1)+1, $file, $l, "missing space after close paren");
+            checkwarn("PARENBRACE",
+                      $line, length($1)+1, $file, $l, "missing space after close paren");
         }
 
         # check for space before the semicolon last in a line
         if($l =~ /^(.*[^ ].*) ;$/) {
-            checkwarn($line, length($1), $file, $l, "space before last semicolon");
+            checkwarn("SPACESEMILCOLON",
+                      $line, length($1), $file, $ol, "space before last semicolon");
         }
 
         # scan for use of banned functions
-        if($l =~ /^(.*\W)(sprintf|vsprintf|strcat|strncat|gets)\s*\(/) {
-            checkwarn($line, length($1), $file, $l,
+        if($l =~ /^(.*\W)
+                   (gets|
+                    v?sprintf|
+                    (str|_mbs|_tcs|_wcs)n?cat|
+                    LoadLibrary(Ex)?(A|W)?)
+                   \s*\(
+                 /x) {
+            checkwarn("BANNEDFUNC",
+                      $line, length($1), $file, $ol,
                       "use of $2 is banned");
         }
 
         # scan for use of non-binary fopen without the macro
-        if($l =~ /^(.*\W)fopen\s*\([^"]*\"([^"]*)/) {
+        if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
             my $mode = $2;
             if($mode !~ /b/) {
-                checkwarn($line, length($1), $file, $l,
-                          "use of non-binary fopen without FOPEN_* macro");
+                checkwarn("FOPENMODE",
+                          $line, length($1), $file, $ol,
+                          "use of non-binary fopen without FOPEN_* macro: $mode");
             }
         }
 
@@ -246,7 +447,8 @@ sub scanfile {
         # only alert if previous line ended with a close paren and wasn't a cpp
         # line
         if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
-            checkwarn($line, length($1), $file, $l, "badly placed open brace");
+            checkwarn("BRACEPOS",
+                      $line, length($1), $file, $ol, "badly placed open brace");
         }
 
         # if the previous line starts with if/while/for AND ends with an open
@@ -261,7 +463,7 @@ sub scanfile {
                 my $expect = $first+$indent;
                 if($expect != $second) {
                     my $diff = $second - $first;
-                    checkwarn($line, length($1), $file, $l,
+                    checkwarn("INDENTATION", $line, length($1), $file, $ol,
                               "not indented $indent steps, uses $diff)");
 
                 }
@@ -269,19 +471,29 @@ sub scanfile {
         }
 
         $line++;
-        $prevl = $l;
+        $prevl = $ol;
     }
 
     if(!$copyright) {
-        checkwarn(1, 0, $file, "", "Missing copyright statement", 1);
+        checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
+    }
+    if($incomment) {
+        checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
     }
 
+    checksrc_endoffile($file);
+
     close(R);
 
 }
 
 
-if($errors || $warnings) {
+if($errors || $warnings || $verbose) {
     printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
+    if($supressed) {
+        printf "checksrc: %d errors and %d warnings suppressed\n",
+        $serrors,
+        $swarnings;
+    }
     exit 5; # return failure
 }