File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / checksrc.pl
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (4 years, 1 month ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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: }
  784: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>