1#!/usr/bin/perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, 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 http://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 24my $max_column = 79; 25my $indent = 2; 26 27my $warnings; 28my $errors; 29my $supressed; # whitelisted problems 30my $file; 31my $dir="."; 32my $wlist; 33my $windows_os = $^O eq 'MSWin32' || $^O eq 'msys' || $^O eq 'cygwin'; 34 35my %whitelist; 36 37sub readwhitelist { 38 open(W, "<$dir/checksrc.whitelist"); 39 my @all=<W>; 40 for(@all) { 41 $windows_os ? $_ =~ s/\r?\n$// : chomp; 42 $whitelist{$_}=1; 43 } 44 close(W); 45} 46 47sub checkwarn { 48 my ($num, $col, $file, $line, $msg, $error) = @_; 49 50 if($whitelist{$line}) { 51 $supressed++; 52 return; 53 } 54 55 my $w=$error?"error":"warning"; 56 57 if($w) { 58 $warnings++; 59 } 60 else { 61 $errors++; 62 } 63 64 $col++; 65 print "$file:$num:$col: $w: $msg\n"; 66 print " $line\n"; 67 68 if($col < 80) { 69 my $pref = (' ' x $col); 70 print "${pref}^\n"; 71 } 72} 73 74$file = shift @ARGV; 75 76while(1) { 77 78 if($file =~ /-D(.*)/) { 79 $dir = $1; 80 $file = shift @ARGV; 81 next; 82 } 83 elsif($file =~ /-W(.*)/) { 84 $wlist .= " $1 "; 85 $file = shift @ARGV; 86 next; 87 } 88 89 last; 90} 91 92if(!$file) { 93 print "checksrc.pl [option] <file1> [file2] ...\n"; 94 print " Options:\n"; 95 print " -D[DIR] Directory to prepend file names\n"; 96 print " -W[file] Whitelist the given file - ignore all its flaws\n"; 97 exit; 98} 99 100readwhitelist(); 101 102do { 103 if("$wlist" !~ / $file /) { 104 my $fullname = $file; 105 $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/'); 106 scanfile($fullname); 107 } 108 $file = shift @ARGV; 109 110} while($file); 111 112 113sub scanfile { 114 my ($file) = @_; 115 116 my $line = 1; 117 my $prevl; 118 my $l; 119 open(R, "<$file") || die "failed to open $file"; 120 121 my $copyright=0; 122 123 while(<R>) { 124 $windows_os ? $_ =~ s/\r?\n$// : chomp; 125 my $l = $_; 126 my $column = 0; 127 128 # check for a copyright statement 129 if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) { 130 $copyright=1; 131 } 132 133 # detect long lines 134 if(length($l) > $max_column) { 135 checkwarn($line, length($l), $file, $l, "Longer than $max_column columns"); 136 } 137 # detect TAB characters 138 if($l =~ /^(.*)\t/) { 139 checkwarn($line, length($1), $file, $l, "Contains TAB character", 1); 140 } 141 # detect trailing white space 142 if($l =~ /^(.*)[ \t]+\z/) { 143 checkwarn($line, length($1), $file, $l, "Trailing whitespace"); 144 } 145 146 # check spaces after for/if/while 147 if($l =~ /^(.*)(for|if|while) \(/) { 148 if($1 =~ / *\#/) { 149 # this is a #if, treat it differently 150 } 151 else { 152 checkwarn($line, length($1)+length($2), $file, $l, 153 "$2 with space"); 154 } 155 } 156 157 # check spaces after open paren after for/if/while 158 if($l =~ /^(.*)(for|if|while)\( /) { 159 if($1 =~ / *\#/) { 160 # this is a #if, treat it differently 161 } 162 else { 163 checkwarn($line, length($1)+length($2)+1, $file, $l, 164 "$2 with space first in condition"); 165 } 166 } 167 168 # check for "return(" without space 169 if($l =~ /^(.*)return\(/) { 170 if($1 =~ / *\#/) { 171 # this is a #if, treat it differently 172 } 173 else { 174 checkwarn($line, length($1)+6, $file, $l, 175 "return without space before paren"); 176 } 177 } 178 179 # check for comma without space 180 if($l =~ /^(.*),[^ \n]/) { 181 my $pref=$1; 182 my $ign=0; 183 if($pref =~ / *\#/) { 184 # this is a #if, treat it differently 185 $ign=1; 186 } 187 elsif($pref =~ /\/\*/) { 188 # this is a comment 189 $ign=1; 190 } 191 elsif($pref =~ /[\"\']/) { 192 $ign = 1; 193 # There is a quote here, figure out whether the comma is 194 # within a string or '' or not. 195 if($pref =~ /\"/) { 196 # withing a string 197 } 198 elsif($pref =~ /\'$/) { 199 # a single letter 200 } 201 else { 202 $ign = 0; 203 } 204 } 205 if(!$ign) { 206 checkwarn($line, length($pref)+1, $file, $l, 207 "comma without following space"); 208 } 209 } 210 211 # check for "} else" 212 if($l =~ /^(.*)\} *else/) { 213 checkwarn($line, length($1), $file, $l, "else after closing brace on same line"); 214 } 215 # check for "){" 216 if($l =~ /^(.*)\)\{/) { 217 checkwarn($line, length($1)+1, $file, $l, "missing space after close paren"); 218 } 219 220 # check for space before the semicolon last in a line 221 if($l =~ /^(.*[^ ].*) ;$/) { 222 checkwarn($line, length($1), $file, $l, "space before last semicolon"); 223 } 224 225 # scan for use of banned functions 226 if($l =~ /^(.*\W)(sprintf|vsprintf|strcat|strncat|gets)\s*\(/) { 227 checkwarn($line, length($1), $file, $l, 228 "use of $2 is banned"); 229 } 230 231 # scan for use of non-binary fopen without the macro 232 if($l =~ /^(.*\W)fopen\s*\([^"]*\"([^"]*)/) { 233 my $mode = $2; 234 if($mode !~ /b/) { 235 checkwarn($line, length($1), $file, $l, 236 "use of non-binary fopen without FOPEN_* macro"); 237 } 238 } 239 240 # check for open brace first on line but not first column 241 # only alert if previous line ended with a close paren and wasn't a cpp 242 # line 243 if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) { 244 checkwarn($line, length($1), $file, $l, "badly placed open brace"); 245 } 246 247 # if the previous line starts with if/while/for AND ends with an open 248 # brace, check that this line is indented $indent more steps, if not 249 # a cpp line 250 if($prevl =~ /^( *)(if|while|for)\(.*\{\z/) { 251 my $first = length($1); 252 253 # this line has some character besides spaces 254 if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) { 255 my $second = length($1); 256 my $expect = $first+$indent; 257 if($expect != $second) { 258 my $diff = $second - $first; 259 checkwarn($line, length($1), $file, $l, 260 "not indented $indent steps, uses $diff)"); 261 262 } 263 } 264 } 265 266 $line++; 267 $prevl = $l; 268 } 269 270 if(!$copyright) { 271 checkwarn(1, 0, $file, "", "Missing copyright statement", 1); 272 } 273 274 close(R); 275 276} 277 278 279if($errors || $warnings) { 280 printf "checksrc: %d errors and %d warnings\n", $errors, $warnings; 281 exit 5; # return failure 282} 283