Annotation of embedaddon/curl/lib/checksrc.pl, revision 1.1
1.1 ! misho 1: #!/usr/bin/env perl
! 2: #***************************************************************************
! 3: # _ _ ____ _
! 4: # Project ___| | | | _ \| |
! 5: # / __| | | | |_) | |
! 6: # | (__| |_| | _ <| |___
! 7: # \___|\___/|_| \_\_____|
! 8: #
! 9: # Copyright (C) 2011 - 2020, 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 https://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:
! 24: use strict;
! 25: use warnings;
! 26:
! 27: my $max_column = 79;
! 28: my $indent = 2;
! 29:
! 30: my $warnings = 0;
! 31: my $swarnings = 0;
! 32: my $errors = 0;
! 33: my $serrors = 0;
! 34: my $suppressed; # whitelisted problems
! 35: my $file;
! 36: my $dir=".";
! 37: my $wlist="";
! 38: my @alist;
! 39: my $windows_os = $^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys';
! 40: my $verbose;
! 41: my %whitelist;
! 42:
! 43: my %ignore;
! 44: my %ignore_set;
! 45: my %ignore_used;
! 46: my @ignore_line;
! 47:
! 48: my %warnings_extended = (
! 49: 'COPYRIGHTYEAR' => 'copyright year incorrect',
! 50: );
! 51:
! 52: my %warnings = (
! 53: 'LONGLINE' => "Line longer than $max_column",
! 54: 'TABS' => 'TAB characters not allowed',
! 55: 'TRAILINGSPACE' => 'Trailing white space on the line',
! 56: 'CPPCOMMENTS' => '// comment detected',
! 57: 'SPACEBEFOREPAREN' => 'space before an open parenthesis',
! 58: 'SPACEAFTERPAREN' => 'space after open parenthesis',
! 59: 'SPACEBEFORECLOSE' => 'space before a close parenthesis',
! 60: 'SPACEBEFORECOMMA' => 'space before a comma',
! 61: 'RETURNNOSPACE' => 'return without space',
! 62: 'COMMANOSPACE' => 'comma without following space',
! 63: 'BRACEELSE' => '} else on the same line',
! 64: 'PARENBRACE' => '){ without sufficient space',
! 65: 'SPACESEMICOLON' => 'space before semicolon',
! 66: 'BANNEDFUNC' => 'a banned function was used',
! 67: 'FOPENMODE' => 'fopen needs a macro for the mode string',
! 68: 'BRACEPOS' => 'wrong position for an open brace',
! 69: 'INDENTATION' => 'wrong start column for code',
! 70: 'COPYRIGHT' => 'file missing a copyright statement',
! 71: 'BADCOMMAND' => 'bad !checksrc! instruction',
! 72: 'UNUSEDIGNORE' => 'a warning ignore was not used',
! 73: 'OPENCOMMENT' => 'file ended with a /* comment still "open"',
! 74: 'ASTERISKSPACE' => 'pointer declared with space after asterisk',
! 75: 'ASTERISKNOSPACE' => 'pointer declared without space before asterisk',
! 76: 'ASSIGNWITHINCONDITION' => 'assignment within conditional expression',
! 77: 'EQUALSNOSPACE' => 'equals sign without following space',
! 78: 'NOSPACEEQUALS' => 'equals sign without preceding space',
! 79: 'SEMINOSPACE' => 'semicolon without following space',
! 80: 'MULTISPACE' => 'multiple spaces used when not suitable',
! 81: 'SIZEOFNOPAREN' => 'use of sizeof without parentheses',
! 82: 'SNPRINTF' => 'use of snprintf',
! 83: 'ONELINECONDITION' => 'conditional block on the same line as the if()',
! 84: );
! 85:
! 86: sub readwhitelist {
! 87: open(W, "<$dir/checksrc.whitelist") or return;
! 88: my @all=<W>;
! 89: for(@all) {
! 90: $windows_os ? $_ =~ s/\r?\n$// : chomp;
! 91: $whitelist{$_}=1;
! 92: }
! 93: close(W);
! 94: }
! 95:
! 96: # Reads the .checksrc in $dir for any extended warnings to enable locally.
! 97: # Currently there is no support for disabling warnings from the standard set,
! 98: # and since that's already handled via !checksrc! commands there is probably
! 99: # little use to add it.
! 100: sub readlocalfile {
! 101: my $i = 0;
! 102:
! 103: open(my $rcfile, "<", "$dir/.checksrc") or return;
! 104:
! 105: while(<$rcfile>) {
! 106: $i++;
! 107:
! 108: # Lines starting with '#' are considered comments
! 109: if (/^\s*(#.*)/) {
! 110: next;
! 111: }
! 112: elsif (/^\s*enable ([A-Z]+)$/) {
! 113: if(!defined($warnings_extended{$1})) {
! 114: print STDERR "invalid warning specified in .checksrc: \"$1\"\n";
! 115: next;
! 116: }
! 117: $warnings{$1} = $warnings_extended{$1};
! 118: }
! 119: else {
! 120: die "Invalid format in $dir/.checksrc on line $i\n";
! 121: }
! 122: }
! 123: }
! 124:
! 125: sub checkwarn {
! 126: my ($name, $num, $col, $file, $line, $msg, $error) = @_;
! 127:
! 128: my $w=$error?"error":"warning";
! 129: my $nowarn=0;
! 130:
! 131: #if(!$warnings{$name}) {
! 132: # print STDERR "Dev! there's no description for $name!\n";
! 133: #}
! 134:
! 135: # checksrc.whitelist
! 136: if($whitelist{$line}) {
! 137: $nowarn = 1;
! 138: }
! 139: # !checksrc! controlled
! 140: elsif($ignore{$name}) {
! 141: $ignore{$name}--;
! 142: $ignore_used{$name}++;
! 143: $nowarn = 1;
! 144: if(!$ignore{$name}) {
! 145: # reached zero, enable again
! 146: enable_warn($name, $num, $file, $line);
! 147: }
! 148: }
! 149:
! 150: if($nowarn) {
! 151: $suppressed++;
! 152: if($w) {
! 153: $swarnings++;
! 154: }
! 155: else {
! 156: $serrors++;
! 157: }
! 158: return;
! 159: }
! 160:
! 161: if($w) {
! 162: $warnings++;
! 163: }
! 164: else {
! 165: $errors++;
! 166: }
! 167:
! 168: $col++;
! 169: print "$file:$num:$col: $w: $msg ($name)\n";
! 170: print " $line\n";
! 171:
! 172: if($col < 80) {
! 173: my $pref = (' ' x $col);
! 174: print "${pref}^\n";
! 175: }
! 176: }
! 177:
! 178: $file = shift @ARGV;
! 179:
! 180: while(defined $file) {
! 181:
! 182: if($file =~ /-D(.*)/) {
! 183: $dir = $1;
! 184: $file = shift @ARGV;
! 185: next;
! 186: }
! 187: elsif($file =~ /-W(.*)/) {
! 188: $wlist .= " $1 ";
! 189: $file = shift @ARGV;
! 190: next;
! 191: }
! 192: elsif($file =~ /-A(.+)/) {
! 193: push @alist, $1;
! 194: $file = shift @ARGV;
! 195: next;
! 196: }
! 197: elsif($file =~ /-i([1-9])/) {
! 198: $indent = $1 + 0;
! 199: $file = shift @ARGV;
! 200: next;
! 201: }
! 202: elsif($file =~ /-m([0-9]+)/) {
! 203: $max_column = $1 + 0;
! 204: $file = shift @ARGV;
! 205: next;
! 206: }
! 207: elsif($file =~ /^(-h|--help)/) {
! 208: undef $file;
! 209: last;
! 210: }
! 211:
! 212: last;
! 213: }
! 214:
! 215: if(!$file) {
! 216: print "checksrc.pl [option] <file1> [file2] ...\n";
! 217: print " Options:\n";
! 218: print " -A[rule] Accept this violation, can be used multiple times\n";
! 219: print " -D[DIR] Directory to prepend file names\n";
! 220: print " -h Show help output\n";
! 221: print " -W[file] Whitelist the given file - ignore all its flaws\n";
! 222: print " -i<n> Indent spaces. Default: 2\n";
! 223: print " -m<n> Maximum line length. Default: 79\n";
! 224: print "\nDetects and warns for these problems:\n";
! 225: for(sort keys %warnings) {
! 226: printf (" %-18s: %s\n", $_, $warnings{$_});
! 227: }
! 228: exit;
! 229: }
! 230:
! 231: readwhitelist();
! 232: readlocalfile();
! 233:
! 234: do {
! 235: if("$wlist" !~ / $file /) {
! 236: my $fullname = $file;
! 237: $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
! 238: scanfile($fullname);
! 239: }
! 240: $file = shift @ARGV;
! 241:
! 242: } while($file);
! 243:
! 244: sub accept_violations {
! 245: for my $r (@alist) {
! 246: if(!$warnings{$r}) {
! 247: print "'$r' is not a warning to accept!\n";
! 248: exit;
! 249: }
! 250: $ignore{$r}=999999;
! 251: $ignore_used{$r}=0;
! 252: }
! 253: }
! 254:
! 255: sub checksrc_clear {
! 256: undef %ignore;
! 257: undef %ignore_set;
! 258: undef @ignore_line;
! 259: }
! 260:
! 261: sub checksrc_endoffile {
! 262: my ($file) = @_;
! 263: for(keys %ignore_set) {
! 264: if($ignore_set{$_} && !$ignore_used{$_}) {
! 265: checkwarn("UNUSEDIGNORE", $ignore_set{$_},
! 266: length($_)+11, $file,
! 267: $ignore_line[$ignore_set{$_}],
! 268: "Unused ignore: $_");
! 269: }
! 270: }
! 271: }
! 272:
! 273: sub enable_warn {
! 274: my ($what, $line, $file, $l) = @_;
! 275:
! 276: # switch it back on, but warn if not triggered!
! 277: if(!$ignore_used{$what}) {
! 278: checkwarn("UNUSEDIGNORE",
! 279: $line, length($what) + 11, $file, $l,
! 280: "No warning was inhibited!");
! 281: }
! 282: $ignore_set{$what}=0;
! 283: $ignore_used{$what}=0;
! 284: $ignore{$what}=0;
! 285: }
! 286: sub checksrc {
! 287: my ($cmd, $line, $file, $l) = @_;
! 288: if($cmd =~ / *([^ ]*) *(.*)/) {
! 289: my ($enable, $what) = ($1, $2);
! 290: $what =~ s: *\*/$::; # cut off end of C comment
! 291: # print "ENABLE $enable WHAT $what\n";
! 292: if($enable eq "disable") {
! 293: my ($warn, $scope)=($1, $2);
! 294: if($what =~ /([^ ]*) +(.*)/) {
! 295: ($warn, $scope)=($1, $2);
! 296: }
! 297: else {
! 298: $warn = $what;
! 299: $scope = 1;
! 300: }
! 301: # print "IGNORE $warn for SCOPE $scope\n";
! 302: if($scope eq "all") {
! 303: $scope=999999;
! 304: }
! 305:
! 306: # Comparing for a literal zero rather than the scalar value zero
! 307: # covers the case where $scope contains the ending '*' from the
! 308: # comment. If we use a scalar comparison (==) we induce warnings
! 309: # on non-scalar contents.
! 310: if($scope eq "0") {
! 311: checkwarn("BADCOMMAND",
! 312: $line, 0, $file, $l,
! 313: "Disable zero not supported, did you mean to enable?");
! 314: }
! 315: elsif($ignore_set{$warn}) {
! 316: checkwarn("BADCOMMAND",
! 317: $line, 0, $file, $l,
! 318: "$warn already disabled from line $ignore_set{$warn}");
! 319: }
! 320: else {
! 321: $ignore{$warn}=$scope;
! 322: $ignore_set{$warn}=$line;
! 323: $ignore_line[$line]=$l;
! 324: }
! 325: }
! 326: elsif($enable eq "enable") {
! 327: enable_warn($what, $line, $file, $l);
! 328: }
! 329: else {
! 330: checkwarn("BADCOMMAND",
! 331: $line, 0, $file, $l,
! 332: "Illegal !checksrc! command");
! 333: }
! 334: }
! 335: }
! 336:
! 337: sub nostrings {
! 338: my ($str) = @_;
! 339: $str =~ s/\".*\"//g;
! 340: return $str;
! 341: }
! 342:
! 343: sub scanfile {
! 344: my ($file) = @_;
! 345:
! 346: my $line = 1;
! 347: my $prevl="";
! 348: my $l;
! 349: open(R, "<$file") || die "failed to open $file";
! 350:
! 351: my $incomment=0;
! 352: my @copyright=();
! 353: checksrc_clear(); # for file based ignores
! 354: accept_violations();
! 355:
! 356: while(<R>) {
! 357: $windows_os ? $_ =~ s/\r?\n$// : chomp;
! 358: my $l = $_;
! 359: my $ol = $l; # keep the unmodified line for error reporting
! 360: my $column = 0;
! 361:
! 362: # check for !checksrc! commands
! 363: if($l =~ /\!checksrc\! (.*)/) {
! 364: my $cmd = $1;
! 365: checksrc($cmd, $line, $file, $l)
! 366: }
! 367:
! 368: # check for a copyright statement and save the years
! 369: if($l =~ /\* +copyright .* \d\d\d\d/i) {
! 370: while($l =~ /([\d]{4})/g) {
! 371: push @copyright, {
! 372: year => $1,
! 373: line => $line,
! 374: col => index($l, $1),
! 375: code => $l
! 376: };
! 377: }
! 378: }
! 379:
! 380: # detect long lines
! 381: if(length($l) > $max_column) {
! 382: checkwarn("LONGLINE", $line, length($l), $file, $l,
! 383: "Longer than $max_column columns");
! 384: }
! 385: # detect TAB characters
! 386: if($l =~ /^(.*)\t/) {
! 387: checkwarn("TABS",
! 388: $line, length($1), $file, $l, "Contains TAB character", 1);
! 389: }
! 390: # detect trailing white space
! 391: if($l =~ /^(.*)[ \t]+\z/) {
! 392: checkwarn("TRAILINGSPACE",
! 393: $line, length($1), $file, $l, "Trailing whitespace");
! 394: }
! 395:
! 396: # ------------------------------------------------------------
! 397: # Above this marker, the checks were done on lines *including*
! 398: # comments
! 399: # ------------------------------------------------------------
! 400:
! 401: # strip off C89 comments
! 402:
! 403: comment:
! 404: if(!$incomment) {
! 405: if($l =~ s/\/\*.*\*\// /g) {
! 406: # full /* comments */ were removed!
! 407: }
! 408: if($l =~ s/\/\*.*//) {
! 409: # start of /* comment was removed
! 410: $incomment = 1;
! 411: }
! 412: }
! 413: else {
! 414: if($l =~ s/.*\*\///) {
! 415: # end of comment */ was removed
! 416: $incomment = 0;
! 417: goto comment;
! 418: }
! 419: else {
! 420: # still within a comment
! 421: $l="";
! 422: }
! 423: }
! 424:
! 425: # ------------------------------------------------------------
! 426: # Below this marker, the checks were done on lines *without*
! 427: # comments
! 428: # ------------------------------------------------------------
! 429:
! 430: # crude attempt to detect // comments without too many false
! 431: # positives
! 432: if($l =~ /^([^"\*]*)[^:"]\/\//) {
! 433: checkwarn("CPPCOMMENTS",
! 434: $line, length($1), $file, $l, "\/\/ comment");
! 435: }
! 436:
! 437: my $nostr = nostrings($l);
! 438: # check spaces after for/if/while/function call
! 439: if($nostr =~ /^(.*)(for|if|while| ([a-zA-Z0-9_]+)) \((.)/) {
! 440: if($1 =~ / *\#/) {
! 441: # this is a #if, treat it differently
! 442: }
! 443: elsif(defined $3 && $3 eq "return") {
! 444: # return must have a space
! 445: }
! 446: elsif(defined $3 && $3 eq "case") {
! 447: # case must have a space
! 448: }
! 449: elsif($4 eq "*") {
! 450: # (* beginning makes the space OK!
! 451: }
! 452: elsif($1 =~ / *typedef/) {
! 453: # typedefs can use space-paren
! 454: }
! 455: else {
! 456: checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
! 457: "$2 with space");
! 458: }
! 459: }
! 460:
! 461: if($nostr =~ /^((.*\s)(if) *\()(.*)\)(.*)/) {
! 462: my $pos = length($1);
! 463: my $postparen = $5;
! 464: my $cond = $4;
! 465: if($cond =~ / = /) {
! 466: checkwarn("ASSIGNWITHINCONDITION",
! 467: $line, $pos+1, $file, $l,
! 468: "assignment within conditional expression");
! 469: }
! 470: my $temp = $cond;
! 471: $temp =~ s/\(//g; # remove open parens
! 472: my $openc = length($cond) - length($temp);
! 473:
! 474: $temp = $cond;
! 475: $temp =~ s/\)//g; # remove close parens
! 476: my $closec = length($cond) - length($temp);
! 477: my $even = $openc == $closec;
! 478:
! 479: if($l =~ / *\#/) {
! 480: # this is a #if, treat it differently
! 481: }
! 482: elsif($even && $postparen &&
! 483: ($postparen !~ /^ *$/) && ($postparen !~ /^ *[,{&|\\]+/)) {
! 484: print STDERR "5: '$postparen'\n";
! 485: checkwarn("ONELINECONDITION",
! 486: $line, length($l)-length($postparen), $file, $l,
! 487: "conditional block on the same line");
! 488: }
! 489: }
! 490: # check spaces after open parentheses
! 491: if($l =~ /^(.*[a-z])\( /i) {
! 492: checkwarn("SPACEAFTERPAREN",
! 493: $line, length($1)+1, $file, $l,
! 494: "space after open parenthesis");
! 495: }
! 496:
! 497: # check spaces before close parentheses, unless it was a space or a
! 498: # close parenthesis!
! 499: if($l =~ /(.*[^\) ]) \)/) {
! 500: checkwarn("SPACEBEFORECLOSE",
! 501: $line, length($1)+1, $file, $l,
! 502: "space before close parenthesis");
! 503: }
! 504:
! 505: # check spaces before comma!
! 506: if($l =~ /(.*[^ ]) ,/) {
! 507: checkwarn("SPACEBEFORECOMMA",
! 508: $line, length($1)+1, $file, $l,
! 509: "space before comma");
! 510: }
! 511:
! 512: # check for "return(" without space
! 513: if($l =~ /^(.*)return\(/) {
! 514: if($1 =~ / *\#/) {
! 515: # this is a #if, treat it differently
! 516: }
! 517: else {
! 518: checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
! 519: "return without space before paren");
! 520: }
! 521: }
! 522:
! 523: # check for "sizeof" without parenthesis
! 524: if(($l =~ /^(.*)sizeof *([ (])/) && ($2 ne "(")) {
! 525: if($1 =~ / *\#/) {
! 526: # this is a #if, treat it differently
! 527: }
! 528: else {
! 529: checkwarn("SIZEOFNOPAREN", $line, length($1)+6, $file, $l,
! 530: "sizeof without parenthesis");
! 531: }
! 532: }
! 533:
! 534: # check for comma without space
! 535: if($l =~ /^(.*),[^ \n]/) {
! 536: my $pref=$1;
! 537: my $ign=0;
! 538: if($pref =~ / *\#/) {
! 539: # this is a #if, treat it differently
! 540: $ign=1;
! 541: }
! 542: elsif($pref =~ /\/\*/) {
! 543: # this is a comment
! 544: $ign=1;
! 545: }
! 546: elsif($pref =~ /[\"\']/) {
! 547: $ign = 1;
! 548: # There is a quote here, figure out whether the comma is
! 549: # within a string or '' or not.
! 550: if($pref =~ /\"/) {
! 551: # within a string
! 552: }
! 553: elsif($pref =~ /\'$/) {
! 554: # a single letter
! 555: }
! 556: else {
! 557: $ign = 0;
! 558: }
! 559: }
! 560: if(!$ign) {
! 561: checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
! 562: "comma without following space");
! 563: }
! 564: }
! 565:
! 566: # check for "} else"
! 567: if($l =~ /^(.*)\} *else/) {
! 568: checkwarn("BRACEELSE",
! 569: $line, length($1), $file, $l, "else after closing brace on same line");
! 570: }
! 571: # check for "){"
! 572: if($l =~ /^(.*)\)\{/) {
! 573: checkwarn("PARENBRACE",
! 574: $line, length($1)+1, $file, $l, "missing space after close paren");
! 575: }
! 576:
! 577: # check for space before the semicolon last in a line
! 578: if($l =~ /^(.*[^ ].*) ;$/) {
! 579: checkwarn("SPACESEMICOLON",
! 580: $line, length($1), $file, $ol, "space before last semicolon");
! 581: }
! 582:
! 583: # scan for use of banned functions
! 584: if($l =~ /^(.*\W)
! 585: (gets|
! 586: strtok|
! 587: v?sprintf|
! 588: (str|_mbs|_tcs|_wcs)n?cat|
! 589: LoadLibrary(Ex)?(A|W)?)
! 590: \s*\(
! 591: /x) {
! 592: checkwarn("BANNEDFUNC",
! 593: $line, length($1), $file, $ol,
! 594: "use of $2 is banned");
! 595: }
! 596:
! 597: # scan for use of snprintf for curl-internals reasons
! 598: if($l =~ /^(.*\W)(v?snprintf)\s*\(/x) {
! 599: checkwarn("SNPRINTF",
! 600: $line, length($1), $file, $ol,
! 601: "use of $2 is banned");
! 602: }
! 603:
! 604: # scan for use of non-binary fopen without the macro
! 605: if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
! 606: my $mode = $2;
! 607: if($mode !~ /b/) {
! 608: checkwarn("FOPENMODE",
! 609: $line, length($1), $file, $ol,
! 610: "use of non-binary fopen without FOPEN_* macro: $mode");
! 611: }
! 612: }
! 613:
! 614: # check for open brace first on line but not first column
! 615: # only alert if previous line ended with a close paren and wasn't a cpp
! 616: # line
! 617: if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
! 618: checkwarn("BRACEPOS",
! 619: $line, length($1), $file, $ol, "badly placed open brace");
! 620: }
! 621:
! 622: # if the previous line starts with if/while/for AND ends with an open
! 623: # brace, or an else statement, check that this line is indented $indent
! 624: # more steps, if not a cpp line
! 625: if($prevl =~ /^( *)((if|while|for)\(.*\{|else)\z/) {
! 626: my $first = length($1);
! 627:
! 628: # this line has some character besides spaces
! 629: if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
! 630: my $second = length($1);
! 631: my $expect = $first+$indent;
! 632: if($expect != $second) {
! 633: my $diff = $second - $first;
! 634: checkwarn("INDENTATION", $line, length($1), $file, $ol,
! 635: "not indented $indent steps (uses $diff)");
! 636:
! 637: }
! 638: }
! 639: }
! 640:
! 641: # check for 'char * name'
! 642: if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost) *(\*+)) (\w+)/) && ($4 ne "const")) {
! 643: checkwarn("ASTERISKNOSPACE",
! 644: $line, length($1), $file, $ol,
! 645: "no space after declarative asterisk");
! 646: }
! 647: # check for 'char*'
! 648: if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost|sockaddr_in|FILE)\*)/)) {
! 649: checkwarn("ASTERISKNOSPACE",
! 650: $line, length($1)-1, $file, $ol,
! 651: "no space before asterisk");
! 652: }
! 653:
! 654: # check for 'void func() {', but avoid false positives by requiring
! 655: # both an open and closed parentheses before the open brace
! 656: if($l =~ /^((\w).*)\{\z/) {
! 657: my $k = $1;
! 658: $k =~ s/const *//;
! 659: $k =~ s/static *//;
! 660: if($k =~ /\(.*\)/) {
! 661: checkwarn("BRACEPOS",
! 662: $line, length($l)-1, $file, $ol,
! 663: "wrongly placed open brace");
! 664: }
! 665: }
! 666:
! 667: # check for equals sign without spaces next to it
! 668: if($nostr =~ /(.*)\=[a-z0-9]/i) {
! 669: checkwarn("EQUALSNOSPACE",
! 670: $line, length($1)+1, $file, $ol,
! 671: "no space after equals sign");
! 672: }
! 673: # check for equals sign without spaces before it
! 674: elsif($nostr =~ /(.*)[a-z0-9]\=/i) {
! 675: checkwarn("NOSPACEEQUALS",
! 676: $line, length($1)+1, $file, $ol,
! 677: "no space before equals sign");
! 678: }
! 679:
! 680: # check for plus signs without spaces next to it
! 681: if($nostr =~ /(.*)[^+]\+[a-z0-9]/i) {
! 682: checkwarn("PLUSNOSPACE",
! 683: $line, length($1)+1, $file, $ol,
! 684: "no space after plus sign");
! 685: }
! 686: # check for plus sign without spaces before it
! 687: elsif($nostr =~ /(.*)[a-z0-9]\+[^+]/i) {
! 688: checkwarn("NOSPACEPLUS",
! 689: $line, length($1)+1, $file, $ol,
! 690: "no space before plus sign");
! 691: }
! 692:
! 693: # check for semicolons without space next to it
! 694: if($nostr =~ /(.*)\;[a-z0-9]/i) {
! 695: checkwarn("SEMINOSPACE",
! 696: $line, length($1)+1, $file, $ol,
! 697: "no space after semicolon");
! 698: }
! 699:
! 700: # check for more than one consecutive space before open brace or
! 701: # question mark. Skip lines containing strings since they make it hard
! 702: # due to artificially getting multiple spaces
! 703: if(($l eq $nostr) &&
! 704: $nostr =~ /^(.*(\S)) + [{?]/i) {
! 705: checkwarn("MULTISPACE",
! 706: $line, length($1)+1, $file, $ol,
! 707: "multiple space");
! 708: print STDERR "L: $l\n";
! 709: print STDERR "nostr: $nostr\n";
! 710: }
! 711:
! 712: $line++;
! 713: $prevl = $ol;
! 714: }
! 715:
! 716: if(!scalar(@copyright)) {
! 717: checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
! 718: }
! 719:
! 720: # COPYRIGHTYEAR is a extended warning so we must first see if it has been
! 721: # enabled in .checksrc
! 722: if(defined($warnings{"COPYRIGHTYEAR"})) {
! 723: # The check for updated copyrightyear is overly complicated in order to
! 724: # not punish current hacking for past sins. The copyright years are
! 725: # right now a bit behind, so enforcing copyright year checking on all
! 726: # files would cause hundreds of errors. Instead we only look at files
! 727: # which are tracked in the Git repo and edited in the workdir, or
! 728: # committed locally on the branch without being in upstream master.
! 729: #
! 730: # The simple and naive test is to simply check for the current year,
! 731: # but updating the year even without an edit is against project policy
! 732: # (and it would fail every file on January 1st).
! 733: #
! 734: # A rather more interesting, and correct, check would be to not test
! 735: # only locally committed files but inspect all files wrt the year of
! 736: # their last commit. Removing the `git rev-list origin/master..HEAD`
! 737: # condition below will enfore copyright year checks against the year
! 738: # the file was last committed (and thus edited to some degree).
! 739: my $commityear = undef;
! 740: @copyright = sort {$$b{year} cmp $$a{year}} @copyright;
! 741:
! 742: # if the file is modified, assume commit year this year
! 743: if(`git status -s -- $file` =~ /^ [MARCU]/) {
! 744: $commityear = (localtime(time))[5] + 1900;
! 745: }
! 746: else {
! 747: # min-parents=1 to ignore wrong initial commit in truncated repos
! 748: my $grl = `git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- $file`;
! 749: if($grl) {
! 750: chomp $grl;
! 751: $commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900;
! 752: }
! 753: }
! 754:
! 755: if(defined($commityear) && scalar(@copyright) &&
! 756: $copyright[0]{year} != $commityear) {
! 757: checkwarn("COPYRIGHTYEAR", $copyright[0]{line}, $copyright[0]{col},
! 758: $file, $copyright[0]{code},
! 759: "Copyright year out of date, should be $commityear, " .
! 760: "is $copyright[0]{year}", 1);
! 761: }
! 762: }
! 763:
! 764: if($incomment) {
! 765: checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
! 766: }
! 767:
! 768: checksrc_endoffile($file);
! 769:
! 770: close(R);
! 771:
! 772: }
! 773:
! 774:
! 775: if($errors || $warnings || $verbose) {
! 776: printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
! 777: if($suppressed) {
! 778: printf "checksrc: %d errors and %d warnings suppressed\n",
! 779: $serrors,
! 780: $swarnings;
! 781: }
! 782: exit 5; # return failure
! 783: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>