868 lines · 21.8 KB · Perl
RawDownload
#!/usr/bin/perl -w# (c) 2001, Dave Jones. <davej@codemonkey.org.uk> (the file handling bit)# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)# (c) 2007, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite, etc)# Licensed under the terms of the GNU GPL License version 2 use strict; my $P = $0;$P =~ s@.*/@@g; my $V = '0.04'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0;my $tree = 1;my $chk_signoff = 1;my $chk_patch = 1;GetOptions(	'q|quiet'	=> \$quiet,	'tree!'		=> \$tree,	'signoff!'	=> \$chk_signoff,	'patch!'	=> \$chk_patch,) or exit; my $exit = 0; if ($#ARGV < 0) {	print "usage: $P [options] patchfile\n";	print "version: $V\n";	print "options: -q           => quiet\n";	print "         --no-tree    => run without a kernel tree\n";	exit(1);} if ($tree && !top_of_kernel_tree()) {	print "Must be run from the top-level dir. of a kernel tree\n";	exit(2);} my @dep_includes = ();my @dep_functions = ();my $removal = 'Documentation/feature-removal-schedule.txt';if ($tree && -f $removal) {	open(REMOVE, "<$removal") || die "$P: $removal: open failed - $!\n";	while (<REMOVE>) {		if (/^Files:\s+(.*\S)/) {			for my $file (split(/[, ]+/, $1)) {				if ($file =~ m@include/(.*)@) {					push(@dep_includes, $1);				}			} 		} elsif (/^Funcs:\s+(.*\S)/) {			for my $func (split(/[, ]+/, $1)) {				push(@dep_functions, $func);			}		}	}} my @rawlines = ();while (<>) {	chomp;	push(@rawlines, $_);	if (eof(ARGV)) {		if (!process($ARGV, @rawlines)) {			$exit = 1;		}		@rawlines = ();	}} exit($exit); sub top_of_kernel_tree {	if ((-f "COPYING") && (-f "CREDITS") && (-f "Kbuild") &&	    (-f "MAINTAINERS") && (-f "Makefile") && (-f "README") &&	    (-d "Documentation") && (-d "arch") && (-d "include") &&	    (-d "drivers") && (-d "fs") && (-d "init") && (-d "ipc") &&	    (-d "kernel") && (-d "lib") && (-d "scripts")) {		return 1;	}	return 0;} sub expand_tabs {	my ($str) = @_; 	my $res = '';	my $n = 0;	for my $c (split(//, $str)) {		if ($c eq "\t") {			$res .= ' ';			$n++;			for (; ($n % 8) != 0; $n++) {				$res .= ' ';			}			next;		}		$res .= $c;		$n++;	} 	return $res;} sub line_stats {	my ($line) = @_; 	# Drop the diff line leader and expand tabs	$line =~ s/^.//;	$line = expand_tabs($line); 	# Pick the indent from the front of the line.	my ($white) = ($line =~ /^(\s*)/); 	return (length($line), length($white));} sub sanitise_line {	my ($line) = @_; 	my $res = '';	my $l = ''; 	my $quote = ''; 	foreach my $c (split(//, $line)) {		if ($l ne "\\" && ($c eq "'" || $c eq '"')) {			if ($quote eq '') {				$quote = $c;				$res .= $c;				$l = $c;				next;			} elsif ($quote eq $c) {				$quote = '';			}		}		if ($quote && $c ne "\t") {			$res .= "X";		} else {			$res .= $c;		} 		$l = $c;	} 	return $res;} sub ctx_block_get {	my ($linenr, $remain, $outer) = @_;	my $line;	my $start = $linenr - 1;	my $blk = '';	my @o;	my @c;	my @res = (); 	for ($line = $start; $remain > 0; $line++) {		next if ($rawlines[$line] =~ /^-/);		$remain--; 		$blk .= $rawlines[$line]; 		@o = ($blk =~ /\{/g);		@c = ($blk =~ /\}/g); 		if (!$outer || (scalar(@o) - scalar(@c)) == 1) {			push(@res, $rawlines[$line]);		} 		last if (scalar(@o) == scalar(@c));	} 	return @res;}sub ctx_block_outer {	my ($linenr, $remain) = @_; 	return ctx_block_get($linenr, $remain, 1);}sub ctx_block {	my ($linenr, $remain) = @_; 	return ctx_block_get($linenr, $remain, 0);} sub ctx_locate_comment {	my ($first_line, $end_line) = @_; 	# Catch a comment on the end of the line itself.	my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*$@);	return $current_comment if (defined $current_comment); 	# Look through the context and try and figure out if there is a	# comment.	my $in_comment = 0;	$current_comment = '';	for (my $linenr = $first_line; $linenr < $end_line; $linenr++) {		my $line = $rawlines[$linenr - 1];		#warn "           $line\n";		if ($linenr == $first_line and $line =~ m@^.\s*\*@) {			$in_comment = 1;		}		if ($line =~ m@/\*@) {			$in_comment = 1;		}		if (!$in_comment && $current_comment ne '') {			$current_comment = '';		}		$current_comment .= $line . "\n" if ($in_comment);		if ($line =~ m@\*/@) {			$in_comment = 0;		}	} 	chomp($current_comment);	return($current_comment);}sub ctx_has_comment {	my ($first_line, $end_line) = @_;	my $cmt = ctx_locate_comment($first_line, $end_line); 	##print "LINE: $rawlines[$end_line - 1 ]\n";	##print "CMMT: $cmt\n"; 	return ($cmt ne '');} sub cat_vet {	my ($vet) = @_; 	$vet =~ s/\t/^I/;	$vet =~ s/$/\$/; 	return $vet;} sub process {	my $filename = shift;	my @lines = @_; 	my $linenr=0;	my $prevline="";	my $stashline=""; 	my $length;	my $indent;	my $previndent=0;	my $stashindent=0; 	my $clean = 1;	my $signoff = 0;	my $is_patch = 0; 	# Trace the real file/line as we go.	my $realfile = '';	my $realline = 0;	my $realcnt = 0;	my $here = '';	my $in_comment = 0;	my $first_line = 0; 	foreach my $line (@lines) {		$linenr++; #extract the filename as it passes		if ($line=~/^\+\+\+\s+(\S+)/) {			$realfile=$1;			$realfile =~ s@^[^/]*/@@;			$in_comment = 0;			next;		}#extract the line range in the file after the patch is applied		if ($line=~/^\@\@ -\d+,\d+ \+(\d+)(,(\d+))? \@\@/) {			$is_patch = 1;			$first_line = $linenr + 1;			$in_comment = 0;			$realline=$1-1;			if (defined $2) {				$realcnt=$3+1;			} else {				$realcnt=1+1;			}			next;		} # track the line number as we move through the hunk, note that# new versions of GNU diff omit the leading space on completely# blank context lines so we need to count that too.		if ($line =~ /^( |\+|$)/) {			$realline++; 			# track any sort of multi-line comment.  Obviously if			# the added text or context do not include the whole			# comment we will not see it. Such is life.			#			# Guestimate if this is a continuing comment.  If this			# is the start of a diff block and this line starts			# ' *' then it is very likely a comment.			if ($linenr == $first_line and $line =~ m@^.\s*\*@) {				$in_comment = 1;			}			if ($line =~ m@/\*@) {				$in_comment = 1;			}			if ($line =~ m@\*/@) {				$in_comment = 0;			} 			# Measure the line length and indent.			($length, $indent) = line_stats($line); 			# Track the previous line.			($prevline, $stashline) = ($stashline, $line);			($previndent, $stashindent) = ($stashindent, $indent);		}		$realcnt-- if ($realcnt != 0); #make up the handle for any error we report on this line		$here = "#$linenr: ";		$here .= "FILE: $realfile:$realline:" if ($realcnt != 0); 		my $hereline = "$here\n$line\n";		my $herecurr = "$here\n$line\n\n";		my $hereprev = "$here\n$prevline\n$line\n\n"; #check the patch for a signoff:		if ($line =~ /^\s*Signed-off-by:\s/) {			$signoff++; 		} elsif ($line =~ /^\s*signed-off-by:/i) {			# This is a signoff, if ugly, so do not double report.			$signoff++;			if (!($line =~ /^\s*Signed-off-by:/)) {				print "use Signed-off-by:\n";				print "$herecurr";				$clean = 0;			}			if ($line =~ /^\s*signed-off-by:\S/i) {				print "need space after Signed-off-by:\n";				print "$herecurr";				$clean = 0;			}		} # Check for wrappage within a valid hunk of the file		if ($realcnt != 0 && $line !~ m{^(?:\+|-| |$)}) {			print "patch seems to be corrupt (line wrapped?) [$realcnt]\n";			print "$herecurr";			$clean = 0;		} #ignore lines being removed		if ($line=~/^-/) {next;} # check we are in a valid source file if not then ignore this hunk		next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); #trailing whitespace		if ($line=~/\+.*\S\s+$/) {			my $herevet = "$here\n" . cat_vet($line) . "\n\n";			print "trailing whitespace\n";			print "$herevet";			$clean = 0;		}#80 column limit		if ($line =~ /^\+/ && !($prevline=~/\/\*\*/) && $length > 80) {			print "line over 80 characters\n";			print "$herecurr";			$clean = 0;		} # check we are in a valid source file *.[hc] if not then ignore this hunk		next if ($realfile !~ /\.[hc]$/); # at the beginning of a line any tabs must come first and anything# more than 8 must use tabs.		if ($line=~/^\+\s* \t\s*\S/ or $line=~/^\+\s*        \s*/) {			my $herevet = "$here\n" . cat_vet($line) . "\n\n";			print "use tabs not spaces\n";			print "$herevet";			$clean = 0;		} 		#		# The rest of our checks refer specifically to C style		# only apply those _outside_ comments.		#		next if ($in_comment); 		# Remove comments from the line before processing.		$line =~ s@/\*.*\*/@@g;		$line =~ s@/\*.*@@;		$line =~ s@.*\*/@@; 		#		# Checks which may be anchored in the context.		# 		# Check for switch () and associated case and default		# statements should be at the same indent.		if ($line=~/\bswitch\s*\(.*\)/) {			my $err = '';			my $sep = '';			my @ctx = ctx_block_outer($linenr, $realcnt);			shift(@ctx);			for my $ctx (@ctx) {				my ($clen, $cindent) = line_stats($ctx);				if ($ctx =~ /^\+\s*(case\s+|default:)/ &&							$indent != $cindent) {					$err .= "$sep$ctx\n";					$sep = '';				} else {					$sep = "[...]\n";				}			}			if ($err ne '') {				print "switch and case should be at the same indent\n";				print "$here\n$line\n$err\n";				$clean = 0;			}		} #ignore lines not being added		if ($line=~/^[^\+]/) {next;} 		#		# Checks which are anchored on the added line.		# # no C99 // comments		if ($line =~ m{//}) {			print "do not use C99 // comments\n";			print "$herecurr";			$clean = 0;		}		# Remove C99 comments.		$line =~ s@//.*@@; 		# Standardise the strings and chars within the input		# to simplify matching.		$line = sanitise_line($line); #EXPORT_SYMBOL should immediately follow its function closing }.		if (($line =~ /EXPORT_SYMBOL.*\(.*\)/) ||		    ($line =~ /EXPORT_UNUSED_SYMBOL.*\(.*\)/)) {			if (($prevline !~ /^}/) &&			   ($prevline !~ /^\+}/) &&			   ($prevline !~ /^ }/)) {				print "EXPORT_SYMBOL(func); should immediately follow its function\n";				print "$herecurr";				$clean = 0;			}		} 		# check for static initialisers.		if ($line=~/\s*static\s.*=\s+(0|NULL);/) {			print "do not initialise statics to 0 or NULL\n";			print "$herecurr";			$clean = 0;		} 		# check for new typedefs.		if ($line=~/\s*typedef\s/) {			print "do not add new typedefs\n";			print "$herecurr";			$clean = 0;		} # * goes on variable not on type		my $type = '(?:char|short|int|long|unsigned|float|double|' .			   'struct\s+[A-Za-z\d_]+|' .			   'union\s+[A-Za-z\d_]+)'; 		if ($line =~ m{[A-Za-z\d_]+(\*+) [A-Za-z\d_]+}) {			print "\"foo$1 bar\" should be \"foo $1bar\"\n";			print "$herecurr";			$clean = 0;		}		if ($line =~ m{$type (\*) [A-Za-z\d_]+} ||		    $line =~ m{[A-Za-z\d_]+ (\*\*+) [A-Za-z\d_]+}) {			print "\"foo $1 bar\" should be \"foo $1bar\"\n";			print "$herecurr";			$clean = 0;		}		if ($line =~ m{\([A-Za-z\d_\s]+[A-Za-z\d_](\*+)\)}) {			print "\"(foo$1)\" should be \"(foo $1)\"\n";			print "$herecurr";			$clean = 0;		}		if ($line =~ m{\([A-Za-z\d_\s]+[A-Za-z\d_]\s+(\*+)\s+\)}) {			print "\"(foo $1 )\" should be \"(foo $1)\"\n";			print "$herecurr";			$clean = 0;		} 

Lines 1–500 of 868.