1075 lines · 28.9 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.08'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0;my $tree = 1;my $chk_signoff = 1;my $chk_patch = 1;my $tst_type = 0;GetOptions(	'q|quiet'	=> \$quiet,	'tree!'		=> \$tree,	'signoff!'	=> \$chk_signoff,	'patch!'	=> \$chk_patch,	'test-type!'	=> \$tst_type,) 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 (/^Check:\s+(.*\S)/) {			for my $entry (split(/[, ]+/, $1)) {				if ($entry =~ m@include/(.*)@) {					push(@dep_includes, $1); 				} elsif ($entry !~ m@/@) {					push(@dep_functions, $entry);				}			}		}	}} 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, $open, $close, $off) = @_;	my $line;	my $start = $linenr - 1;	my $blk = '';	my @o;	my @c;	my @res = (); 	my $level = 0;	for ($line = $start; $remain > 0; $line++) {		next if ($rawlines[$line] =~ /^-/);		$remain--; 		$blk .= $rawlines[$line];		foreach my $c (split(//, $rawlines[$line])) {			##print "C<$c>L<$level><$open$close>O<$off>\n";			if ($off > 0) {				$off--;				next;			} 			if ($c eq $close && $level > 0) {				$level--;				last if ($level == 0);			} elsif ($c eq $open) {				$level++;			}		} 		if (!$outer || $level <= 1) {			push(@res, $rawlines[$line]);		} 		last if ($level == 0);	} 	return ($level, @res);}sub ctx_block_outer {	my ($linenr, $remain) = @_; 	my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0);	return @r;}sub ctx_block {	my ($linenr, $remain) = @_; 	my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0);	return @r;}sub ctx_statement {	my ($linenr, $remain, $off) = @_; 	my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off);	return @r;}sub ctx_block_level {	my ($linenr, $remain) = @_; 	return ctx_block_get($linenr, $remain, 0, '{', '}', 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;} my @report = ();sub report {	push(@report, $_[0]);}sub report_dump {	@report;}sub ERROR {	report("ERROR: $_[0]\n");	our $clean = 0;}sub WARN {	report("WARNING: $_[0]\n");	our $clean = 0;}sub CHK {	report("CHECK: $_[0]\n");	our $clean = 0;} sub process {	my $filename = shift;	my @lines = @_; 	my $linenr=0;	my $prevline="";	my $stashline=""; 	my $length;	my $indent;	my $previndent=0;	my $stashindent=0; 	our $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; 	my $Ident	= qr{[A-Za-z\d_]+};	my $Storage	= qr{extern|static};	my $Sparse	= qr{__user|__kernel|__force|__iomem};	my $NonptrType	= qr{				\b				(?:const\s+)?				(?:unsigned\s+)?				(?:					void|					char|					short|					int|					long|					unsigned|					float|					double|					long\s+int|					long\s+long|					long\s+long\s+int|					u8|u16|u32|u64|					s8|s16|s32|s64|					struct\s+$Ident|					union\s+$Ident|					enum\s+$Ident|					${Ident}_t				)				(?:\s+$Sparse)*				\b			  }x;	my $Type	= qr{				\b$NonptrType\b				(?:\s*\*+\s*const|\s*\*+)?			  }x;	my $Declare	= qr{(?:$Storage\s+)?$Type};	my $Attribute	= qr{const|__read_mostly|__init|__initdata|__meminit}; 	my $Member	= qr{->$Ident|\.$Ident|\[[^]]*\]};	my $Lval	= qr{$Ident(?:$Member)*}; 	# Pre-scan the patch looking for any __setup documentation.	my @setup_docs = ();	my $setup_docs = 0;	foreach my $line (@lines) {		if ($line=~/^\+\+\+\s+(\S+)/) {			$setup_docs = 0;			if ($1 =~ m@Documentation/kernel-parameters.txt$@) {				$setup_docs = 1;			}			next;		} 		if ($setup_docs && $line =~ /^\+/) {			push(@setup_docs, $line);		}	} 	foreach my $line (@lines) {		$linenr++; 		my $rawline = $line; #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++;			$realcnt-- if ($realcnt != 0); 			# 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);		} elsif ($realcnt == 1) {			$realcnt--;		} #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";		my $hereprev = "$here\n$prevline\n$line\n"; #check the patch for a signoff:		if ($line =~ /^\s*signed-off-by:/i) {			# This is a signoff, if ugly, so do not double report.			$signoff++;			if (!($line =~ /^\s*Signed-off-by:/)) {				WARN("Signed-off-by: is the preferred form\n" .					$herecurr);			}			if ($line =~ /^\s*signed-off-by:\S/i) {				WARN("need space after Signed-off-by:\n" .					$herecurr);			}		} # Check for wrappage within a valid hunk of the file		if ($realcnt != 0 && $line !~ m{^(?:\+|-| |$)}) {			ERROR("patch seems to be corrupt (line wrapped?)\n" .				$herecurr);		} # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php		if (($realfile =~ /^$/ || $line =~ /^\+/) &&		     !($line =~ m/^(				[\x09\x0A\x0D\x20-\x7E]              # ASCII				| [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte				|  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs				| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte				|  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates				|  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3				| [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15				|  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16				)*$/x )) {			ERROR("Invalid UTF-8\n" . $herecurr);		} #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+$/ || $line =~ /^\+\s+$/) {			my $herevet = "$here\n" . cat_vet($line) . "\n";			ERROR("trailing whitespace\n" . $herevet);		}#80 column limit		if ($line =~ /^\+/ && !($prevline=~/\/\*\*/) && $length > 80) {			WARN("line over 80 characters\n" . $herecurr);		} # 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";			ERROR("use tabs not spaces\n" . $herevet);		} 		#		# The rest of our checks refer specifically to C style		# only apply those _outside_ comments.		#

Lines 1–500 of 1075.