8794 cstyle generates warnings with recent perl
Reviewed by: Andy Fiddaman <andy@omniosce.org>

   1 #!/usr/bin/perl -w
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 # Copyright 2015 Toomas Soome <tsoome@me.com>
  23 # Copyright 2016 Nexenta Systems, Inc.
  24 #
  25 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  26 # Use is subject to license terms.
  27 #
  28 # Copyright (c) 2015 by Delphix. All rights reserved.
  29 #
  30 # @(#)cstyle 1.58 98/09/09 (from shannon)
  31 #
  32 # cstyle - check for some common stylistic errors.
  33 #
  34 #       cstyle is a sort of "lint" for C coding style.
  35 #       It attempts to check for the style used in the
  36 #       kernel, sometimes known as "Bill Joy Normal Form".
  37 #
  38 #       There's a lot this can't check for, like proper indentation
  39 #       of code blocks.  There's also a lot more this could check for.
  40 #
  41 #       A note to the non perl literate:
  42 #
  43 #               perl regular expressions are pretty much like egrep
  44 #               regular expressions, with the following special symbols
  45 #
  46 #               \s      any space character
  47 #               \S      any non-space character
  48 #               \w      any "word" character [a-zA-Z0-9_]
  49 #               \W      any non-word character
  50 #               \d      a digit [0-9]
  51 #               \D      a non-digit
  52 #               \b      word boundary (between \w and \W)
  53 #               \B      non-word boundary
  54 #
  55 
  56 require 5.0;
  57 use IO::File;
  58 use Getopt::Std;
  59 use strict;
  60 
  61 my $usage =
  62 "usage: cstyle [-chpvCP] [-o constructs] file ...
  63         -c      check continuation indentation inside functions
  64         -h      perform heuristic checks that are sometimes wrong
  65         -p      perform some of the more picky checks
  66         -v      verbose
  67         -C      don't check anything in header block comments
  68         -P      check for use of non-POSIX types
  69         -o constructs
  70                 allow a comma-seperated list of optional constructs:
  71                     doxygen     allow doxygen-style block comments (/** /*!)
  72                     splint      allow splint-style lint comments (/*@ ... @*/)
  73 ";
  74 
  75 my %opts;
  76 
  77 if (!getopts("cho:pvCP", \%opts)) {
  78         print $usage;
  79         exit 2;
  80 }
  81 
  82 my $check_continuation = $opts{'c'};
  83 my $heuristic = $opts{'h'};
  84 my $picky = $opts{'p'};
  85 my $verbose = $opts{'v'};
  86 my $ignore_hdr_comment = $opts{'C'};
  87 my $check_posix_types = $opts{'P'};
  88 
  89 my $doxygen_comments = 0;
  90 my $splint_comments = 0;
  91 
  92 if (defined($opts{'o'})) {
  93         for my $x (split /,/, $opts{'o'}) {
  94                 if ($x eq "doxygen") {
  95                         $doxygen_comments = 1;
  96                 } elsif ($x eq "splint") {
  97                         $splint_comments = 1;
  98                 } else {
  99                         print "cstyle: unrecognized construct \"$x\"\n";
 100                         print $usage;
 101                         exit 2;
 102                 }
 103         }
 104 }
 105 
 106 my ($filename, $line, $prev);           # shared globals
 107 
 108 my $fmt;
 109 my $hdr_comment_start;
 110 
 111 if ($verbose) {
 112         $fmt = "%s: %d: %s\n%s\n";
 113 } else {
 114         $fmt = "%s: %d: %s\n";
 115 }
 116 
 117 if ($doxygen_comments) {
 118         # doxygen comments look like "/*!" or "/**"; allow them.
 119         $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/;
 120 } else {
 121         $hdr_comment_start = qr/^\s*\/\*$/;
 122 }
 123 
 124 # Note, following must be in single quotes so that \s and \w work right.
 125 my $typename = '(int|char|short|long|unsigned|float|double' .
 126     '|\w+_t|struct\s+\w+|union\s+\w+|FILE)';
 127 
 128 # mapping of old types to POSIX compatible types
 129 my %old2posix = (
 130         'unchar' => 'uchar_t',
 131         'ushort' => 'ushort_t',
 132         'uint' => 'uint_t',
 133         'ulong' => 'ulong_t',
 134         'u_int' => 'uint_t',
 135         'u_short' => 'ushort_t',
 136         'u_long' => 'ulong_t',
 137         'u_char' => 'uchar_t',
 138         'quad' => 'quad_t'
 139 );
 140 
 141 my $lint_re = qr/\/\*(?:
 142         ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*|
 143         CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY|
 144         FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*|
 145         PROTOLIB[0-9]*|SCANFLIKE[0-9]*|CSTYLED.*?
 146     )\*\//x;
 147 
 148 my $splint_re = qr/\/\*@.*?@\*\//x;
 149 
 150 my $warlock_re = qr/\/\*\s*(?:
 151         VARIABLES\ PROTECTED\ BY|
 152         MEMBERS\ PROTECTED\ BY|
 153         ALL\ MEMBERS\ PROTECTED\ BY|
 154         READ-ONLY\ VARIABLES:|
 155         READ-ONLY\ MEMBERS:|
 156         VARIABLES\ READABLE\ WITHOUT\ LOCK:|
 157         MEMBERS\ READABLE\ WITHOUT\ LOCK:|
 158         LOCKS\ COVERED\ BY|
 159         LOCK\ UNNEEDED\ BECAUSE|
 160         LOCK\ NEEDED:|
 161         LOCK\ HELD\ ON\ ENTRY:|
 162         READ\ LOCK\ HELD\ ON\ ENTRY:|
 163         WRITE\ LOCK\ HELD\ ON\ ENTRY:|
 164         LOCK\ ACQUIRED\ AS\ SIDE\ EFFECT:|
 165         READ\ LOCK\ ACQUIRED\ AS\ SIDE\ EFFECT:|
 166         WRITE\ LOCK\ ACQUIRED\ AS\ SIDE\ EFFECT:|
 167         LOCK\ RELEASED\ AS\ SIDE\ EFFECT:|
 168         LOCK\ UPGRADED\ AS\ SIDE\ EFFECT:|
 169         LOCK\ DOWNGRADED\ AS\ SIDE\ EFFECT:|
 170         FUNCTIONS\ CALLED\ THROUGH\ POINTER|
 171         FUNCTIONS\ CALLED\ THROUGH\ MEMBER|
 172         LOCK\ ORDER:
 173     )/x;
 174 
 175 my $err_stat = 0;               # exit status
 176 
 177 if ($#ARGV >= 0) {
 178         foreach my $arg (@ARGV) {
 179                 my $fh = new IO::File $arg, "r";
 180                 if (!defined($fh)) {
 181                         printf "%s: can not open\n", $arg;
 182                 } else {
 183                         &cstyle($arg, $fh);
 184                         close $fh;
 185                 }
 186         }
 187 } else {
 188         &cstyle("<stdin>", *STDIN);
 189 }
 190 exit $err_stat;
 191 
 192 my $no_errs = 0;                # set for CSTYLED-protected lines
 193 
 194 sub err($) {
 195         my ($error) = @_;
 196         unless ($no_errs) {
 197                 if ($verbose) {
 198                         printf $fmt, $filename, $., $error, $line;
 199                 } else {
 200                         printf $fmt, $filename, $., $error;
 201                 }
 202                 $err_stat = 1;
 203         }
 204 }
 205 
 206 sub err_prefix($$) {
 207         my ($prevline, $error) = @_;
 208         my $out = $prevline."\n".$line;
 209         unless ($no_errs) {
 210                 if ($verbose) {
 211                         printf $fmt, $filename, $., $error, $out;
 212                 } else {
 213                         printf $fmt, $filename, $., $error;
 214                 }
 215                 $err_stat = 1;
 216         }
 217 }
 218 
 219 sub err_prev($) {
 220         my ($error) = @_;
 221         unless ($no_errs) {
 222                 if ($verbose) {
 223                         printf $fmt, $filename, $. - 1, $error, $prev;
 224                 } else {
 225                         printf $fmt, $filename, $. - 1, $error;
 226                 }
 227                 $err_stat = 1;
 228         }
 229 }
 230 
 231 sub cstyle($$) {
 232 
 233 my ($fn, $filehandle) = @_;
 234 $filename = $fn;                        # share it globally
 235 
 236 my $in_cpp = 0;
 237 my $next_in_cpp = 0;
 238 
 239 my $in_comment = 0;
 240 my $in_header_comment = 0;
 241 my $comment_done = 0;
 242 my $in_warlock_comment = 0;
 243 my $in_function = 0;
 244 my $in_function_header = 0;
 245 my $function_header_full_indent = 0;
 246 my $in_declaration = 0;
 247 my $note_level = 0;
 248 my $nextok = 0;
 249 my $nocheck = 0;
 250 
 251 my $in_string = 0;
 252 
 253 my ($okmsg, $comment_prefix);
 254 
 255 $line = '';
 256 $prev = '';
 257 reset_indent();
 258 
 259 line: while (<$filehandle>) {
 260         s/\r?\n$//;     # strip return and newline
 261 
 262         # save the original line, then remove all text from within
 263         # double or single quotes, we do not want to check such text.
 264 
 265         $line = $_;
 266 
 267         #
 268         # C allows strings to be continued with a backslash at the end of
 269         # the line.  We translate that into a quoted string on the previous
 270         # line followed by an initial quote on the next line.
 271         #
 272         # (we assume that no-one will use backslash-continuation with character
 273         # constants)
 274         #
 275         $_ = '"' . $_           if ($in_string && !$nocheck && !$in_comment);
 276 
 277         #
 278         # normal strings and characters
 279         #
 280         s/'([^\\']|\\[^xX0]|\\0[0-9]*|\\[xX][0-9a-fA-F]*)'/''/g;
 281         s/"([^\\"]|\\.)*"/\"\"/g;
 282 
 283         #
 284         # detect string continuation
 285         #
 286         if ($nocheck || $in_comment) {
 287                 $in_string = 0;
 288         } else {
 289                 #
 290                 # Now that all full strings are replaced with "", we check
 291                 # for unfinished strings continuing onto the next line.
 292                 #
 293                 $in_string =
 294                     (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ ||
 295                     s/^("")*"([^\\"]|\\.)*\\$/""/);
 296         }
 297 
 298         #
 299         # figure out if we are in a cpp directive
 300         #
 301         $in_cpp = $next_in_cpp || /^\s*#/;      # continued or started
 302         $next_in_cpp = $in_cpp && /\\$/;        # only if continued
 303 
 304         # strip off trailing backslashes, which appear in long macros
 305         s/\s*\\$//;
 306 
 307         # an /* END CSTYLED */ comment ends a no-check block.
 308         if ($nocheck) {
 309                 if (/\/\* *END *CSTYLED *\*\//) {
 310                         $nocheck = 0;
 311                 } else {
 312                         reset_indent();
 313                         next line;
 314                 }
 315         }
 316 
 317         # a /*CSTYLED*/ comment indicates that the next line is ok.
 318         if ($nextok) {
 319                 if ($okmsg) {
 320                         err($okmsg);
 321                 }
 322                 $nextok = 0;
 323                 $okmsg = 0;
 324                 if (/\/\* *CSTYLED.*\*\//) {
 325                         /^.*\/\* *CSTYLED *(.*) *\*\/.*$/;
 326                         $okmsg = $1;
 327                         $nextok = 1;
 328                 }
 329                 $no_errs = 1;
 330         } elsif ($no_errs) {
 331                 $no_errs = 0;
 332         }
 333 
 334         # check length of line.
 335         # first, a quick check to see if there is any chance of being too long.
 336         if (($line =~ tr/\t/\t/) * 7 + length($line) > 80) {
 337                 # yes, there is a chance.
 338                 # replace tabs with spaces and check again.
 339                 my $eline = $line;
 340                 1 while $eline =~
 341                     s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
 342                 if (length($eline) > 80) {
 343                         err("line > 80 characters");
 344                 }
 345         }
 346 
 347         # ignore NOTE(...) annotations (assumes NOTE is on lines by itself).
 348         if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE
 349                 s/[^()]//g;                       # eliminate all non-parens
 350                 $note_level += s/\(//g - length;  # update paren nest level
 351                 next;
 352         }
 353 
 354         # a /* BEGIN CSTYLED */ comment starts a no-check block.
 355         if (/\/\* *BEGIN *CSTYLED *\*\//) {
 356                 $nocheck = 1;
 357         }
 358 
 359         # a /*CSTYLED*/ comment indicates that the next line is ok.
 360         if (/\/\* *CSTYLED.*\*\//) {
 361                 /^.*\/\* *CSTYLED *(.*) *\*\/.*$/;
 362                 $okmsg = $1;
 363                 $nextok = 1;
 364         }
 365         if (/\/\/ *CSTYLED/) {
 366                 /^.*\/\/ *CSTYLED *(.*)$/;
 367                 $okmsg = $1;
 368                 $nextok = 1;
 369         }
 370 
 371         # universal checks; apply to everything
 372         if (/\t +\t/) {
 373                 err("spaces between tabs");
 374         }
 375         if (/ \t+ /) {
 376                 err("tabs between spaces");
 377         }
 378         if (/\s$/) {
 379                 err("space or tab at end of line");
 380         }
 381         if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) {
 382                 err("comment preceded by non-blank");
 383         }
 384 
 385         # is this the beginning or ending of a function?
 386         # (not if "struct foo\n{\n")
 387         if (/^\{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) {
 388                 $in_function = 1;
 389                 $in_declaration = 1;
 390                 $in_function_header = 0;
 391                 $function_header_full_indent = 0;
 392                 $prev = $line;
 393                 next line;
 394         }
 395         if (/^\}\s*(\/\*.*\*\/\s*)*$/) {
 396                 if ($prev =~ /^\s*return\s*;/) {
 397                         err_prev("unneeded return at end of function");
 398                 }
 399                 $in_function = 0;
 400                 reset_indent();         # we don't check between functions
 401                 $prev = $line;
 402                 next line;
 403         }
 404         if ($in_function_header && ! /^    (\w|\.)/ ) {
 405                 if (/^\{\}$/) {
 406                         $in_function_header = 0;
 407                         $function_header_full_indent = 0;
 408                 } elsif ($picky && ! (/^\t/ && $function_header_full_indent != 0)) {
 409                         err("continuation line should be indented by 4 spaces");
 410                 }
 411         }
 412 
 413         #
 414         # If this matches something of form "foo(", it's probably a function
 415         # definition, unless it ends with ") bar;", in which case it's a declaration
 416         # that uses a macro to generate the type.
 417         #
 418         if (/^\w+\(/ && !/\) \w+;$/) {
 419                 $in_function_header = 1;
 420                 if (/\($/) {
 421                         $function_header_full_indent = 1;
 422                 }
 423         }
 424         if ($in_function_header && /^\{$/) {
 425                 $in_function_header = 0;
 426                 $function_header_full_indent = 0;
 427                 $in_function = 1;
 428         }
 429         if ($in_function_header && /\);$/) {
 430                 $in_function_header = 0;
 431                 $function_header_full_indent = 0;
 432         }
 433         if ($in_function_header && /\{$/ ) {
 434                 if ($picky) {
 435                         err("opening brace on same line as function header");
 436                 }
 437                 $in_function_header = 0;
 438                 $function_header_full_indent = 0;
 439                 $in_function = 1;
 440                 next line;
 441         }
 442 
 443         if ($in_warlock_comment && /\*\//) {
 444                 $in_warlock_comment = 0;
 445                 $prev = $line;
 446                 next line;
 447         }
 448 
 449         # a blank line terminates the declarations within a function.
 450         # XXX - but still a problem in sub-blocks.
 451         if ($in_declaration && /^$/) {
 452                 $in_declaration = 0;
 453         }
 454 
 455         if ($comment_done) {
 456                 $in_comment = 0;
 457                 $in_header_comment = 0;
 458                 $comment_done = 0;
 459         }
 460         # does this looks like the start of a block comment?
 461         if (/$hdr_comment_start/) {
 462                 if (!/^\t*\/\*/) {
 463                         err("block comment not indented by tabs");
 464                 }
 465                 $in_comment = 1;
 466                 /^(\s*)\//;
 467                 $comment_prefix = $1;
 468                 if ($comment_prefix eq "") {
 469                         $in_header_comment = 1;
 470                 }
 471                 $prev = $line;
 472                 next line;
 473         }
 474         # are we still in the block comment?
 475         if ($in_comment) {
 476                 if (/^$comment_prefix \*\/$/) {
 477                         $comment_done = 1;
 478                 } elsif (/\*\//) {
 479                         $comment_done = 1;
 480                         err("improper block comment close")
 481                             unless ($ignore_hdr_comment && $in_header_comment);
 482                 } elsif (!/^$comment_prefix \*[ \t]/ &&
 483                     !/^$comment_prefix \*$/) {
 484                         err("improper block comment")
 485                             unless ($ignore_hdr_comment && $in_header_comment);
 486                 }
 487         }
 488 
 489         if ($in_header_comment && $ignore_hdr_comment) {
 490                 $prev = $line;
 491                 next line;
 492         }
 493 
 494         # check for errors that might occur in comments and in code.
 495 
 496         # allow spaces to be used to draw pictures in header comments.
 497         if (/[^ ]     / && !/".*     .*"/ && !$in_header_comment) {
 498                 err("spaces instead of tabs");
 499         }
 500         if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ &&
 501             (!/^    (\w|\.)/ || $in_function != 0)) {
 502                 err("indent by spaces instead of tabs");
 503         }
 504         if (/^\t+ [^ \t\*]/ || /^\t+  \S/ || /^\t+   \S/) {
 505                 err("continuation line not indented by 4 spaces");
 506         }
 507         if (/$warlock_re/ && !/\*\//) {
 508                 $in_warlock_comment = 1;
 509                 $prev = $line;
 510                 next line;
 511         }
 512         if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) {
 513                 err("improper first line of block comment");
 514         }
 515 
 516         if ($in_comment) {      # still in comment, don't do further checks
 517                 $prev = $line;
 518                 next line;
 519         }
 520 
 521         if ((/[^(]\/\*\S/ || /^\/\*\S/) &&
 522             !(/$lint_re/ || ($splint_comments && /$splint_re/))) {
 523                 err("missing blank after open comment");
 524         }
 525         if (/\S\*\/[^)]|\S\*\/$/ &&
 526             !(/$lint_re/ || ($splint_comments && /$splint_re/))) {
 527                 err("missing blank before close comment");
 528         }
 529         if (/\/\/\S/) {         # C++ comments
 530                 err("missing blank after start comment");
 531         }
 532         # check for unterminated single line comments, but allow them when
 533         # they are used to comment out the argument list of a function
 534         # declaration.
 535         if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) {
 536                 err("unterminated single line comment");
 537         }
 538 
 539         if (/^(#else|#endif|#include)(.*)$/) {
 540                 $prev = $line;
 541                 if ($picky) {
 542                         my $directive = $1;
 543                         my $clause = $2;
 544                         # Enforce ANSI rules for #else and #endif: no noncomment
 545                         # identifiers are allowed after #endif or #else.  Allow
 546                         # C++ comments since they seem to be a fact of life.
 547                         if ((($1 eq "#endif") || ($1 eq "#else")) &&
 548                             ($clause ne "") &&
 549                             (!($clause =~ /^\s+\/\*.*\*\/$/)) &&
 550                             (!($clause =~ /^\s+\/\/.*$/))) {
 551                                 err("non-comment text following " .
 552                                     "$directive (or malformed $directive " .
 553                                     "directive)");
 554                         }
 555                 }
 556                 next line;
 557         }
 558 
 559         #
 560         # delete any comments and check everything else.  Note that
 561         # ".*?" is a non-greedy match, so that we don't get confused by
 562         # multiple comments on the same line.
 563         #
 564         s/\/\*.*?\*\///g;
 565         s/\/\/.*$//;           # C++ comments
 566 
 567         # delete any trailing whitespace; we have already checked for that.
 568         s/\s*$//;
 569 
 570         # following checks do not apply to text in comments.
 571 
 572         if (/[^<>\s][!<>=]=/ || /[^<>][!<>=]=[^\s,]/ ||
 573             (/[^->]>[^,=>\s]/ && !/[^->]>$/) ||
 574             (/[^<]<[^,=<\s]/ && !/[^<]<$/) ||
 575             /[^<\s]<[^<]/ || /[^->\s]>[^>]/) {
 576                 err("missing space around relational operator");
 577         }
 578         if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ ||
 579             (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) ||
 580             (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) {
 581                 # XXX - should only check this for C++ code
 582                 # XXX - there are probably other forms that should be allowed
 583                 if (!/\soperator=/) {
 584                         err("missing space around assignment operator");
 585                 }
 586         }
 587         if (/[,;]\S/ && !/\bfor \(;;\)/) {
 588                 err("comma or semicolon followed by non-blank");
 589         }
 590         # allow "for" statements to have empty "while" clauses
 591         if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) {
 592                 err("comma or semicolon preceded by blank");
 593         }
 594         if (/^\s*(&&|\|\|)/) {
 595                 err("improper boolean continuation");
 596         }
 597         if (/\S   *(&&|\|\|)/ || /(&&|\|\|)   *\S/) {
 598                 err("more than one space around boolean operator");
 599         }
 600         if (/\b(for|if|while|switch|sizeof|return|case)\(/) {
 601                 err("missing space between keyword and paren");
 602         }
 603         if (/(\b(for|if|while|switch|return)\b.*){2,}/ && !/^#define/) {
 604                 # multiple "case" and "sizeof" allowed
 605                 err("more than one keyword on line");
 606         }
 607         if (/\b(for|if|while|switch|sizeof|return|case)\s\s+\(/ &&
 608             !/^#if\s+\(/) {
 609                 err("extra space between keyword and paren");
 610         }
 611         # try to detect "func (x)" but not "if (x)" or
 612         # "#define foo (x)" or "int (*func)();"
 613         if (/\w\s\(/) {
 614                 my $s = $_;
 615                 # strip off all keywords on the line
 616                 s/\b(for|if|while|switch|return|case|sizeof)\s\(/XXX(/g;
 617                 s/#elif\s\(/XXX(/g;
 618                 s/^#define\s+\w+\s+\(/XXX(/;
 619                 # do not match things like "void (*f)();"
 620                 # or "typedef void (func_t)();"
 621                 s/\w\s\(+\*/XXX(*/g;
 622                 s/\b($typename|void)\s+\(+/XXX(/og;
 623                 if (/\w\s\(/) {
 624                         err("extra space between function name and left paren");
 625                 }
 626                 $_ = $s;
 627         }
 628         # try to detect "int foo(x)", but not "extern int foo(x);"
 629         # XXX - this still trips over too many legitimate things,
 630         # like "int foo(x,\n\ty);"
 631 #               if (/^(\w+(\s|\*)+)+\w+\(/ && !/\)[;,](\s|)*$/ &&
 632 #                   !/^(extern|static)\b/) {
 633 #                       err("return type of function not on separate line");
 634 #               }
 635         # this is a close approximation
 636         if (/^(\w+(\s|\*)+)+\w+\(.*\)(\s|)*$/ &&
 637             !/^(extern|static)\b/) {
 638                 err("return type of function not on separate line");
 639         }
 640         if (/^#define /) {
 641                 err("#define followed by space instead of tab");
 642         }
 643         if (/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) {
 644                 err("unparenthesized return expression");
 645         }
 646         if (/\bsizeof\b/ && !/\bsizeof\s*\(.*\)/) {
 647                 err("unparenthesized sizeof expression");
 648         }
 649         if (/\(\s/) {
 650                 err("whitespace after left paren");
 651         }
 652         # allow "for" statements to have empty "continue" clauses
 653         if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) {
 654                 err("whitespace before right paren");
 655         }
 656         if (/^\s*\(void\)[^ ]/) {
 657                 err("missing space after (void) cast");
 658         }
 659         if (/\S\{/ && !/\{\{/) {
 660                 err("missing space before left brace");
 661         }
 662         if ($in_function && /^\s+\{/ &&
 663             ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) {
 664                 err("left brace starting a line");
 665         }
 666         if (/\}(else|while)/) {
 667                 err("missing space after right brace");
 668         }
 669         if (/\}\s\s+(else|while)/) {
 670                 err("extra space after right brace");
 671         }
 672         if (/\b_VOID\b|\bVOID\b|\bSTATIC\b/) {
 673                 err("obsolete use of VOID or STATIC");
 674         }
 675         if (/\b$typename\*/o) {
 676                 err("missing space between type name and *");
 677         }
 678         if (/^\s+#/) {
 679                 err("preprocessor statement not in column 1");
 680         }
 681         if (/^#\s/) {
 682                 err("blank after preprocessor #");
 683         }
 684         if (/!\s*(strcmp|strncmp|bcmp)\s*\(/) {
 685                 err("don't use boolean ! with comparison functions");
 686         }
 687 
 688         #
 689         # We completely ignore, for purposes of indentation:
 690         #  * lines outside of functions
 691         #  * preprocessor lines
 692         #
 693         if ($check_continuation && $in_function && !$in_cpp) {
 694                 process_indent($_);
 695         }
 696         if ($picky) {
 697                 # try to detect spaces after casts, but allow (e.g.)
 698                 # "sizeof (int) + 1", "void (*funcptr)(int) = foo;", and
 699                 # "int foo(int) __NORETURN;"
 700                 if ((/^\($typename( \*+)?\)\s/o ||
 701                     /\W\($typename( \*+)?\)\s/o) &&
 702                     !/sizeof\s*\($typename( \*)?\)\s/o &&
 703                     !/\($typename( \*+)?\)\s+=[^=]/o) {
 704                         err("space after cast");
 705                 }
 706                 if (/\b$typename\s*\*\s/o &&
 707                     !/\b$typename\s*\*\s+const\b/o) {
 708                         err("unary * followed by space");
 709                 }
 710         }
 711         if ($check_posix_types) {
 712                 # try to detect old non-POSIX types.
 713                 # POSIX requires all non-standard typedefs to end in _t,
 714                 # but historically these have been used.
 715                 if (/\b(unchar|ushort|uint|ulong|u_int|u_short|u_long|u_char|quad)\b/) {
 716                         err("non-POSIX typedef $1 used: use $old2posix{$1} instead");
 717                 }
 718         }
 719         if ($heuristic) {
 720                 # cannot check this everywhere due to "struct {\n...\n} foo;"
 721                 if ($in_function && !$in_declaration &&
 722                     /\}./ && !/\}\s+=/ && !/\{.*\}[;,]$/ && !/\}(\s|)*$/ &&
 723                     !/\} (else|while)/ && !/\}\}/) {
 724                         err("possible bad text following right brace");
 725                 }
 726                 # cannot check this because sub-blocks in
 727                 # the middle of code are ok
 728                 if ($in_function && /^\s+\{/) {
 729                         err("possible left brace starting a line");
 730                 }
 731         }
 732         if (/^\s*else\W/) {
 733                 if ($prev =~ /^\s*\}$/) {
 734                         err_prefix($prev,
 735                             "else and right brace should be on same line");
 736                 }
 737         }
 738         $prev = $line;
 739 }
 740 
 741 if ($prev eq "") {
 742         err("last line in file is blank");
 743 }
 744 
 745 }
 746 
 747 #
 748 # Continuation-line checking
 749 #
 750 # The rest of this file contains the code for the continuation checking
 751 # engine.  It's a pretty simple state machine which tracks the expression
 752 # depth (unmatched '('s and '['s).
 753 #
 754 # Keep in mind that the argument to process_indent() has already been heavily
 755 # processed; all comments have been replaced by control-A, and the contents of
 756 # strings and character constants have been elided.
 757 #
 758 
 759 my $cont_in;            # currently inside of a continuation
 760 my $cont_off;           # skipping an initializer or definition
 761 my $cont_noerr;         # suppress cascading errors
 762 my $cont_start;         # the line being continued
 763 my $cont_base;          # the base indentation
 764 my $cont_first;         # this is the first line of a statement
 765 my $cont_multiseg;      # this continuation has multiple segments
 766 
 767 my $cont_special;       # this is a C statement (if, for, etc.)
 768 my $cont_macro;         # this is a macro
 769 my $cont_case;          # this is a multi-line case
 770 
 771 my @cont_paren;         # the stack of unmatched ( and [s we've seen
 772 
 773 sub
 774 reset_indent()
 775 {
 776         $cont_in = 0;
 777         $cont_off = 0;
 778 }
 779 
 780 sub
 781 delabel($)
 782 {
 783         #
 784         # replace labels with tabs.  Note that there may be multiple
 785         # labels on a line.
 786         #
 787         local $_ = $_[0];
 788 
 789         while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) {
 790                 my ($pre_tabs, $label, $rest) = ($1, $2, $3);
 791                 $_ = $pre_tabs;
 792                 while ($label =~ s/^([^\t]*)(\t+)//) {
 793                         $_ .= "\t" x (length($2) + length($1) / 8);
 794                 }
 795                 $_ .= ("\t" x (length($label) / 8)).$rest;
 796         }
 797 
 798         return ($_);
 799 }
 800 
 801 sub
 802 process_indent($)
 803 {
 804         require strict;
 805         local $_ = $_[0];                       # preserve the global $_
 806 
 807         s///g; # No comments
 808         s/\s+$//;       # Strip trailing whitespace
 809 
 810         return                  if (/^$/);      # skip empty lines
 811 
 812         # regexps used below; keywords taking (), macros, and continued cases
 813         my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b';
 814         my $macro = '[A-Z_][A-Z_0-9]*\(';
 815         my $case = 'case\b[^:]*$';
 816 
 817         # skip over enumerations, array definitions, initializers, etc.
 818         if ($cont_off <= 0 && !/^\s*$special/ &&
 819             (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*))\{/ ||
 820             (/^\s*\{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) {
 821                 $cont_in = 0;
 822                 $cont_off = tr/{/{/ - tr/}/}/;
 823                 return;
 824         }
 825         if ($cont_off) {
 826                 $cont_off += tr/{/{/ - tr/}/}/;
 827                 return;
 828         }
 829 
 830         if (!$cont_in) {
 831                 $cont_start = $line;
 832 
 833                 if (/^\t* /) {
 834                         err("non-continuation indented 4 spaces");
 835                         $cont_noerr = 1;                # stop reporting
 836                 }
 837                 $_ = delabel($_);       # replace labels with tabs
 838 
 839                 # check if the statement is complete
 840                 return          if (/^\s*\}?$/);
 841                 return          if (/^\s*\}?\s*else\s*\{?$/);
 842                 return          if (/^\s*do\s*\{?$/);
 843                 return          if (/\{$/);
 844                 return          if (/\}[,;]?$/);
 845 
 846                 # Allow macros on their own lines
 847                 return          if (/^\s*[A-Z_][A-Z_0-9]*$/);
 848 
 849                 # cases we don't deal with, generally non-kosher
 850                 if (/\{/) {
 851                         err("stuff after {");
 852                         return;
 853                 }
 854 
 855                 # Get the base line, and set up the state machine
 856                 /^(\t*)/;
 857                 $cont_base = $1;
 858                 $cont_in = 1;
 859                 @cont_paren = ();
 860                 $cont_first = 1;
 861                 $cont_multiseg = 0;
 862 
 863                 # certain things need special processing
 864                 $cont_special = /^\s*$special/? 1 : 0;
 865                 $cont_macro = /^\s*$macro/? 1 : 0;
 866                 $cont_case = /^\s*$case/? 1 : 0;
 867         } else {
 868                 $cont_first = 0;
 869 
 870                 # Strings may be pulled back to an earlier (half-)tabstop
 871                 unless ($cont_noerr || /^$cont_base    / ||
 872                     (/^\t*(?:    )?(?:gettext\()?\"/ && !/^$cont_base\t/)) {
 873                         err_prefix($cont_start,
 874                             "continuation should be indented 4 spaces");
 875                 }
 876         }
 877 
 878         my $rest = $_;                  # keeps the remainder of the line
 879 
 880         #
 881         # The split matches 0 characters, so that each 'special' character
 882         # is processed separately.  Parens and brackets are pushed and
 883         # popped off the @cont_paren stack.  For normal processing, we wait
 884         # until a ; or { terminates the statement.  "special" processing
 885         # (if/for/while/switch) is allowed to stop when the stack empties,
 886         # as is macro processing.  Case statements are terminated with a :
 887         # and an empty paren stack.
 888         #
 889         foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) {
 890                 next            if (length($_) == 0);
 891 
 892                 # rest contains the remainder of the line
 893                 my $rxp = "[^\Q$_\E]*\Q$_\E";
 894                 $rest =~ s/^$rxp//;
 895 
 896                 if (/\(/ || /\[/) {
 897                         push @cont_paren, $_;
 898                 } elsif (/\)/ || /\]/) {
 899                         my $cur = $_;
 900                         tr/\)\]/\(\[/;
 901 
 902                         my $old = (pop @cont_paren);
 903                         if (!defined($old)) {
 904                                 err("unexpected '$cur'");
 905                                 $cont_in = 0;
 906                                 last;
 907                         } elsif ($old ne $_) {
 908                                 err("'$cur' mismatched with '$old'");
 909                                 $cont_in = 0;
 910                                 last;
 911                         }
 912 
 913                         #
 914                         # If the stack is now empty, do special processing
 915                         # for if/for/while/switch and macro statements.
 916                         #
 917                         next            if (@cont_paren != 0);
 918                         if ($cont_special) {
 919                                 if ($rest =~ /^\s*\{?$/) {
 920                                         $cont_in = 0;
 921                                         last;
 922                                 }
 923                                 if ($rest =~ /^\s*;$/) {
 924                                         err("empty if/for/while body ".
 925                                             "not on its own line");
 926                                         $cont_in = 0;
 927                                         last;
 928                                 }
 929                                 if (!$cont_first && $cont_multiseg == 1) {
 930                                         err_prefix($cont_start,
 931                                             "multiple statements continued ".
 932                                             "over multiple lines");
 933                                         $cont_multiseg = 2;
 934                                 } elsif ($cont_multiseg == 0) {
 935                                         $cont_multiseg = 1;
 936                                 }
 937                                 # We've finished this section, start
 938                                 # processing the next.
 939                                 goto section_ended;
 940                         }
 941                         if ($cont_macro) {
 942                                 if ($rest =~ /^$/) {
 943                                         $cont_in = 0;
 944                                         last;
 945                                 }
 946                         }
 947                 } elsif (/\;/) {
 948                         if ($cont_case) {
 949                                 err("unexpected ;");
 950                         } elsif (!$cont_special) {
 951                                 err("unexpected ;")     if (@cont_paren != 0);
 952                                 if (!$cont_first && $cont_multiseg == 1) {
 953                                         err_prefix($cont_start,
 954                                             "multiple statements continued ".
 955                                             "over multiple lines");
 956                                         $cont_multiseg = 2;
 957                                 } elsif ($cont_multiseg == 0) {
 958                                         $cont_multiseg = 1;
 959                                 }
 960                                 if ($rest =~ /^$/) {
 961                                         $cont_in = 0;
 962                                         last;
 963                                 }
 964                                 if ($rest =~ /^\s*special/) {
 965                                         err("if/for/while/switch not started ".
 966                                             "on its own line");
 967                                 }
 968                                 goto section_ended;
 969                         }
 970                 } elsif (/\{/) {
 971                         err("{ while in parens/brackets") if (@cont_paren != 0);
 972                         err("stuff after {")            if ($rest =~ /[^\s}]/);
 973                         $cont_in = 0;
 974                         last;
 975                 } elsif (/\}/) {
 976                         err("} while in parens/brackets") if (@cont_paren != 0);
 977                         if (!$cont_special && $rest !~ /^\s*(while|else)\b/) {
 978                                 if ($rest =~ /^$/) {
 979                                         err("unexpected }");
 980                                 } else {
 981                                         err("stuff after }");
 982                                 }
 983                                 $cont_in = 0;
 984                                 last;
 985                         }
 986                 } elsif (/\:/ && $cont_case && @cont_paren == 0) {
 987                         err("stuff after multi-line case") if ($rest !~ /$^/);
 988                         $cont_in = 0;
 989                         last;
 990                 }
 991                 next;
 992 section_ended:
 993                 # End of a statement or if/while/for loop.  Reset
 994                 # cont_special and cont_macro based on the rest of the
 995                 # line.
 996                 $cont_special = ($rest =~ /^\s*$special/)? 1 : 0;
 997                 $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0;
 998                 $cont_case = 0;
 999                 next;
1000         }
1001         $cont_noerr = 0                 if (!$cont_in);
1002 }
--- EOF ---