Skip to content
Snippets Groups Projects
checkpatch.pl 191 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	my ($lineRef, $offset, $length) = @_;
    
    	if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) {
    		my $o = $1;
    		my $l = $2;
    		my $no = $o + $offset;
    		my $nl = $l + $length;
    		$$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/;
    	}
    }
    
    sub fix_inserted_deleted_lines {
    	my ($linesRef, $insertedRef, $deletedRef) = @_;
    
    	my $range_last_linenr = 0;
    	my $delta_offset = 0;
    
    	my $old_linenr = 0;
    	my $new_linenr = 0;
    
    	my $next_insert = 0;
    	my $next_delete = 0;
    
    	my @lines = ();
    
    	my $inserted = @{$insertedRef}[$next_insert++];
    	my $deleted = @{$deletedRef}[$next_delete++];
    
    	foreach my $old_line (@{$linesRef}) {
    		my $save_line = 1;
    		my $line = $old_line;	#don't modify the array
    		if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) {	#new filename
    			$delta_offset = 0;
    		} elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) {	#new hunk
    			$range_last_linenr = $new_linenr;
    			fixup_current_range(\$line, $delta_offset, 0);
    		}
    
    		while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) {
    			$deleted = @{$deletedRef}[$next_delete++];
    			$save_line = 0;
    			fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1);
    		}
    
    		while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) {
    			push(@lines, ${$inserted}{'LINE'});
    			$inserted = @{$insertedRef}[$next_insert++];
    			$new_linenr++;
    			fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1);
    		}
    
    		if ($save_line) {
    			push(@lines, $line);
    			$new_linenr++;
    		}
    
    		$old_linenr++;
    	}
    
    	return @lines;
    }
    
    sub fix_insert_line {
    	my ($linenr, $line) = @_;
    
    	my $inserted = {
    		LINENR => $linenr,
    		LINE => $line,
    	};
    	push(@fixed_inserted, $inserted);
    }
    
    sub fix_delete_line {
    	my ($linenr, $line) = @_;
    
    	my $deleted = {
    		LINENR => $linenr,
    		LINE => $line,
    	};
    
    	push(@fixed_deleted, $deleted);
    }
    
    
    	my ($type, $msg) = @_;
    
    	if (report("ERROR", $type, $msg)) {
    
    		our $clean = 0;
    		our $cnt_error++;
    
    	my ($type, $msg) = @_;
    
    	if (report("WARNING", $type, $msg)) {
    
    		our $clean = 0;
    		our $cnt_warn++;
    
    	my ($type, $msg) = @_;
    
    	if ($check && report("CHECK", $type, $msg)) {
    
    		our $clean = 0;
    		our $cnt_chk++;
    
    }
    
    sub check_absolute_file {
    	my ($absolute, $herecurr) = @_;
    	my $file = $absolute;
    
    	##print "absolute<$absolute>\n";
    
    	# See if any suffix of this path is a path within the tree.
    	while ($file =~ s@^[^/]*/@@) {
    		if (-f "$root/$file") {
    			##print "file<$file>\n";
    			last;
    		}
    	}
    	if (! -f _)  {
    		return 0;
    	}
    
    	# It is, so see if the prefix is acceptable.
    	my $prefix = $absolute;
    	substr($prefix, -length($file)) = '';
    
    	##print "prefix<$prefix>\n";
    	if ($prefix ne ".../") {
    		WARN("USE_RELATIVE_PATH",
    		     "use relative pathname instead of absolute in changelog text\n" . $herecurr);
    	}
    }
    
    
    sub trim {
    	my ($string) = @_;
    
    	$string =~ s/^\s+|\s+$//g;
    
    	return $string;
    }
    
    sub ltrim {
    	my ($string) = @_;
    
    	$string =~ s/^\s+//;
    
    	return $string;
    }
    
    sub rtrim {
    	my ($string) = @_;
    
    	$string =~ s/\s+$//;
    
    	return $string;
    }
    
    sub string_find_replace {
    	my ($string, $find, $replace) = @_;
    
    	$string =~ s/$find/$replace/g;
    
    	return $string;
    }
    
    sub tabify {
    	my ($leading) = @_;
    
    	my $source_indent = 8;
    	my $max_spaces_before_tab = $source_indent - 1;
    	my $spaces_to_tab = " " x $source_indent;
    
    	#convert leading spaces to tabs
    	1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g;
    	#Remove spaces before a tab
    	1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g;
    
    	return "$leading";
    }
    
    
    sub pos_last_openparen {
    	my ($line) = @_;
    
    	my $pos = 0;
    
    	my $opens = $line =~ tr/\(/\(/;
    	my $closes = $line =~ tr/\)/\)/;
    
    	my $last_openparen = 0;
    
    	if (($opens == 0) || ($closes >= $opens)) {
    		return -1;
    	}
    
    	my $len = length($line);
    
    	for ($pos = 0; $pos < $len; $pos++) {
    		my $string = substr($line, $pos);
    		if ($string =~ /^($FuncArg|$balanced_parens)/) {
    			$pos += length($1) - 1;
    		} elsif (substr($line, $pos, 1) eq '(') {
    			$last_openparen = $pos;
    		} elsif (index($string, '(') == -1) {
    			last;
    		}
    	}
    
    
    	return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
    
    sub process {
    	my $filename = shift;
    
    	my $linenr=0;
    	my $prevline="";
    	my $prevrawline="";
    	my $stashline="";
    	my $stashrawline="";
    
    	my $length;
    	my $indent;
    	my $previndent=0;
    	my $stashindent=0;
    
    	our $clean = 1;
    	my $signoff = 0;
    	my $is_patch = 0;
    
    	my $in_header_lines = $file ? 0 : 1;
    
    	my $in_commit_log = 0;		#Scanning lines before patch
    
    	my $has_commit_log = 0;		#Encountered lines before patch
    	my $commit_log_possible_stack_dump = 0;
    	my $commit_log_long_line = 0;
    	my $commit_log_has_diff = 0;
    	my $reported_maintainer_file = 0;
    
    	my $last_blank_line = 0;
    	my $last_coalesced_string_linenr = -1;
    
    
    	our @report = ();
    	our $cnt_lines = 0;
    	our $cnt_error = 0;
    	our $cnt_warn = 0;
    	our $cnt_chk = 0;
    
    	# Trace the real file/line as we go.
    	my $realfile = '';
    	my $realline = 0;
    	my $realcnt = 0;
    	my $here = '';
    
    	my $context_function;		#undef'd unless there's a known function
    
    	my $in_comment = 0;
    	my $comment_edge = 0;
    	my $first_line = 0;
    	my $p1_prefix = '';
    
    	my $prev_values = 'E';
    
    	# suppression flags
    	my %suppress_ifbraces;
    	my %suppress_whiletrailers;
    	my %suppress_export;
    
    	my $suppress_statement = 0;
    
    
    
    	# Pre-scan the patch sanitizing the lines.
    	# Pre-scan the patch looking for any __setup documentation.
    	#
    	my @setup_docs = ();
    	my $setup_docs = 0;
    
    
    	my $camelcase_file_seeded = 0;
    
    
    	sanitise_line_reset();
    	my $line;
    	foreach my $rawline (@rawlines) {
    		$linenr++;
    		$line = $rawline;
    
    
    		push(@fixed, $rawline) if ($fix);
    
    
    		if ($rawline=~/^\+\+\+\s+(\S+)/) {
    			$setup_docs = 0;
    
    			if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) {
    
    		if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
    
    			$realline=$1-1;
    			if (defined $2) {
    				$realcnt=$3+1;
    			} else {
    				$realcnt=1+1;
    			}
    			$in_comment = 0;
    
    			# Guestimate if this is a continuing comment.  Run
    			# the context looking for a comment "edge".  If this
    			# edge is a close comment then we must be in a comment
    			# at context start.
    			my $edge;
    			my $cnt = $realcnt;
    			for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
    				next if (defined $rawlines[$ln - 1] &&
    					 $rawlines[$ln - 1] =~ /^-/);
    				$cnt--;
    				#print "RAW<$rawlines[$ln - 1]>\n";
    				last if (!defined $rawlines[$ln - 1]);
    				if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
    				    $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
    					($edge) = $1;
    					last;
    				}
    			}
    			if (defined $edge && $edge eq '*/') {
    				$in_comment = 1;
    			}
    
    			# 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 (!defined $edge &&
    			    $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
    			{
    				$in_comment = 1;
    			}
    
    			##print "COMMENT:$in_comment edge<$edge> $rawline\n";
    			sanitise_line_reset($in_comment);
    
    		} elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) {
    			# Standardise the strings and chars within the input to
    			# simplify matching -- only bother with positive lines.
    			$line = sanitise_line($rawline);
    		}
    		push(@lines, $line);
    
    		if ($realcnt > 1) {
    			$realcnt-- if ($line =~ /^(?:\+| |$)/);
    		} else {
    			$realcnt = 0;
    		}
    
    		#print "==>$rawline\n";
    		#print "-->$line\n";
    
    		if ($setup_docs && $line =~ /^\+/) {
    			push(@setup_docs, $line);
    		}
    	}
    
    	$prefix = '';
    
    	$realcnt = 0;
    	$linenr = 0;
    
    	foreach my $line (@lines) {
    		$linenr++;
    
    		my $sline = $line;	#copy of $line
    		$sline =~ s/$;/ /g;	#with comments as spaces
    
    
    		my $rawline = $rawlines[$linenr - 1];
    
    #extract the line range in the file after the patch is applied
    
    		if (!$in_commit_log &&
    		    $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) {
    			my $context = $4;
    
    			$is_patch = 1;
    			$first_line = $linenr + 1;
    			$realline=$1-1;
    			if (defined $2) {
    				$realcnt=$3+1;
    			} else {
    				$realcnt=1+1;
    			}
    			annotate_reset();
    			$prev_values = 'E';
    
    			%suppress_ifbraces = ();
    			%suppress_whiletrailers = ();
    			%suppress_export = ();
    
    			$suppress_statement = 0;
    
    			if ($context =~ /\b(\w+)\s*\(/) {
    				$context_function = $1;
    			} else {
    				undef $context_function;
    			}
    
    			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.
    		} elsif ($line =~ /^( |\+|$)/) {
    			$realline++;
    			$realcnt-- if ($realcnt != 0);
    
    			# Measure the line length and indent.
    			($length, $indent) = line_stats($rawline);
    
    			# Track the previous line.
    			($prevline, $stashline) = ($stashline, $line);
    			($previndent, $stashindent) = ($stashindent, $indent);
    			($prevrawline, $stashrawline) = ($stashrawline, $rawline);
    
    			#warn "line<$line>\n";
    
    		} elsif ($realcnt == 1) {
    			$realcnt--;
    		}
    
    		my $hunk_line = ($realcnt != 0);
    
    		$here = "#$linenr: " if (!$file);
    		$here = "#$realline: " if ($file);
    
    
    		my $found_file = 0;
    
    		# extract the filename as it passes
    		if ($line =~ /^diff --git.*?(\S+)$/) {
    			$realfile = $1;
    
    			$realfile =~ s@^([^/]*)/@@ if (!$file);
    
    		} elsif ($line =~ /^\+\+\+\s+(\S+)/) {
    			$realfile = $1;
    
    			$realfile =~ s@^([^/]*)/@@ if (!$file);
    
    
    			$p1_prefix = $1;
    			if (!$file && $tree && $p1_prefix ne '' &&
    			    -e "$root/$p1_prefix") {
    				WARN("PATCH_PREFIX",
    				     "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
    			}
    
    			if ($realfile =~ m@^include/asm/@) {
    				ERROR("MODIFIED_INCLUDE_ASM",
    				      "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
    			}
    
    			$found_file = 1;
    		}
    
    #make up the handle for any error we report on this line
    		if ($showfile) {
    			$prefix = "$realfile:$realline: "
    		} elsif ($emacs) {
    			if ($file) {
    				$prefix = "$filename:$realline: ";
    			} else {
    				$prefix = "$filename:$linenr: ";
    			}
    		}
    
    		if ($found_file) {
    			if (is_maintained_obsolete($realfile)) {
    				WARN("OBSOLETE",
    				     "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy.  No unnecessary modifications please.\n");
    			}
    			if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) {
    				$check = 1;
    			} else {
    				$check = $check_orig;
    			}
    
    			$checklicenseline = 1;
    
    			next;
    		}
    
    		$here .= "FILE: $realfile:$realline:" if ($realcnt != 0);
    
    		my $hereline = "$here\n$rawline\n";
    		my $herecurr = "$here\n$rawline\n";
    		my $hereprev = "$here\n$prevrawline\n$rawline\n";
    
    		$cnt_lines++ if ($realcnt != 0);
    
    
    # Check if the commit log has what seems like a diff which can confuse patch
    		if ($in_commit_log && !$commit_log_has_diff &&
    		    (($line =~ m@^\s+diff\b.*a/[\w/]+@ &&
    		      $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) ||
    		     $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ ||
    		     $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) {
    			ERROR("DIFF_IN_COMMIT_MSG",
    			      "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr);
    			$commit_log_has_diff = 1;
    		}
    
    
    # Check for incorrect file permissions
    		if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
    			my $permhere = $here . "FILE: $realfile\n";
    
    			if ($realfile !~ m@scripts/@ &&
    			    $realfile !~ /\.(py|pl|awk|sh)$/) {
    
    				ERROR("EXECUTE_PERMISSIONS",
    				      "do not set execute permissions for source files\n" . $permhere);
    			}
    		}
    
    # Check the patch for a signoff:
    		if ($line =~ /^\s*signed-off-by:/i) {
    			$signoff++;
    
    # Check if MAINTAINERS is being updated.  If so, there's probably no need to
    # emit the "does MAINTAINERS need updating?" message on file add/move/delete
    		if ($line =~ /^\s*MAINTAINERS\s*\|/) {
    			$reported_maintainer_file = 1;
    		}
    
    
    # Check signature styles
    
    		if (!$in_header_lines &&
    		    $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) {
    
    			my $space_before = $1;
    			my $sign_off = $2;
    			my $space_after = $3;
    			my $email = $4;
    			my $ucfirst_sign_off = ucfirst(lc($sign_off));
    
    
    			if ($sign_off !~ /$signature_tags/) {
    				WARN("BAD_SIGN_OFF",
    				     "Non-standard signature: $sign_off\n" . $herecurr);
    			}
    
    			if (defined $space_before && $space_before ne "") {
    
    				if (WARN("BAD_SIGN_OFF",
    					 "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) &&
    				    $fix) {
    
    					$fixed[$fixlinenr] =
    
    					    "$ucfirst_sign_off $email";
    				}
    
    			}
    			if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) {
    
    				if (WARN("BAD_SIGN_OFF",
    					 "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) &&
    				    $fix) {
    
    					$fixed[$fixlinenr] =
    
    			}
    			if (!defined $space_after || $space_after ne " ") {
    
    				if (WARN("BAD_SIGN_OFF",
    					 "Use a single space after $ucfirst_sign_off\n" . $herecurr) &&
    				    $fix) {
    
    					$fixed[$fixlinenr] =
    
    					    "$ucfirst_sign_off $email";
    				}
    
    			}
    
    			my ($email_name, $email_address, $comment) = parse_email($email);
    			my $suggested_email = format_email(($email_name, $email_address));
    			if ($suggested_email eq "") {
    				ERROR("BAD_SIGN_OFF",
    				      "Unrecognized email address: '$email'\n" . $herecurr);
    			} else {
    				my $dequoted = $suggested_email;
    				$dequoted =~ s/^"//;
    				$dequoted =~ s/" </ </;
    				# Don't force email to have quotes
    				# Allow just an angle bracketed address
    				if ("$dequoted$comment" ne $email &&
    				    "<$email_address>$comment" ne $email &&
    				    "$suggested_email$comment" ne $email) {
    					WARN("BAD_SIGN_OFF",
    					     "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr);
    				}
    			}
    
    
    # Check for duplicate signatures
    			my $sig_nospace = $line;
    			$sig_nospace =~ s/\s//g;
    			$sig_nospace = lc($sig_nospace);
    			if (defined $signatures{$sig_nospace}) {
    				WARN("BAD_SIGN_OFF",
    				     "Duplicate signature\n" . $herecurr);
    			} else {
    				$signatures{$sig_nospace} = 1;
    			}
    
    # Check email subject for common tools that don't need to be mentioned
    		if ($in_header_lines &&
    		    $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) {
    			WARN("EMAIL_SUBJECT",
    			     "A patch subject line should describe the change not the tool that found it\n" . $herecurr);
    		}
    
    # Check for old stable address
    		if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) {
    			ERROR("STABLE_ADDRESS",
    			      "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr);
    		}
    
    # Check for unwanted Gerrit info
    		if ($in_commit_log && $line =~ /^\s*change-id:/i) {
    			ERROR("GERRIT_CHANGE_ID",
    			      "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr);
    		}
    
    # Check if the commit log is in a possible stack dump
    		if ($in_commit_log && !$commit_log_possible_stack_dump &&
    		    ($line =~ /^\s*(?:WARNING:|BUG:)/ ||
    		     $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
    					# timestamp
    		     $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) {
    					# stack dump address
    			$commit_log_possible_stack_dump = 1;
    		}
    
    # Check for line lengths > 75 in commit log, warn once
    		if ($in_commit_log && !$commit_log_long_line &&
    		    length($line) > 75 &&
    		    !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ ||
    					# file delta changes
    		      $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ ||
    					# filename then :
    		      $line =~ /^\s*(?:Fixes:|Link:)/i ||
    					# A Fixes: or Link: line
    		      $commit_log_possible_stack_dump)) {
    			WARN("COMMIT_LOG_LONG_LINE",
    			     "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
    			$commit_log_long_line = 1;
    		}
    
    # Reset possible stack dump if a blank line is found
    		if ($in_commit_log && $commit_log_possible_stack_dump &&
    		    $line =~ /^\s*$/) {
    			$commit_log_possible_stack_dump = 0;
    		}
    
    # Check for git id commit length and improperly formed commit descriptions
    		if ($in_commit_log && !$commit_log_possible_stack_dump &&
    		    $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i &&
    		    $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
    		    ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
    		     ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
    		      $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
    		      $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) {
    			my $init_char = "c";
    			my $orig_commit = "";
    			my $short = 1;
    			my $long = 0;
    			my $case = 1;
    			my $space = 1;
    			my $hasdesc = 0;
    			my $hasparens = 0;
    			my $id = '0123456789ab';
    			my $orig_desc = "commit description";
    			my $description = "";
    
    			if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) {
    				$init_char = $1;
    				$orig_commit = lc($2);
    			} elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) {
    				$orig_commit = lc($1);
    			}
    
    			$short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i);
    			$long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i);
    			$space = 0 if ($line =~ /\bcommit [0-9a-f]/i);
    			$case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/);
    			if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) {
    				$orig_desc = $1;
    				$hasparens = 1;
    			} elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i &&
    				 defined $rawlines[$linenr] &&
    				 $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) {
    				$orig_desc = $1;
    				$hasparens = 1;
    			} elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i &&
    				 defined $rawlines[$linenr] &&
    				 $rawlines[$linenr] =~ /^\s*[^"]+"\)/) {
    				$line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i;
    				$orig_desc = $1;
    				$rawlines[$linenr] =~ /^\s*([^"]+)"\)/;
    				$orig_desc .= " " . $1;
    				$hasparens = 1;
    			}
    
    			($id, $description) = git_commit_info($orig_commit,
    							      $id, $orig_desc);
    
    			if (defined($id) &&
    			   ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) {
    				ERROR("GIT_COMMIT_ID",
    				      "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr);
    			}
    		}
    
    # Check for added, moved or deleted files
    		if (!$reported_maintainer_file && !$in_commit_log &&
    		    ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ ||
    		     $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ ||
    		     ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ &&
    		      (defined($1) || defined($2))))) {
    			$is_patch = 1;
    			$reported_maintainer_file = 1;
    			WARN("FILE_PATH_CHANGES",
    			     "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
    		}
    
    
    # Check for wrappage within a valid hunk of the file
    		if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
    			ERROR("CORRUPTED_PATCH",
    			      "patch seems to be corrupt (line wrapped?)\n" .
    				$herecurr) if (!$emitted_corrupt++);
    		}
    
    # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
    		if (($realfile =~ /^$/ || $line =~ /^\+/) &&
    		    $rawline !~ m/^$UTF8*$/) {
    			my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/);
    
    			my $blank = copy_spacing($rawline);
    			my $ptr = substr($blank, 0, length($utf8_prefix)) . "^";
    			my $hereptr = "$hereline$ptr\n";
    
    			CHK("INVALID_UTF8",
    			    "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
    		}
    
    
    # Check if it's the start of a commit log
    # (not a header line and we haven't seen the patch filename)
    		if ($in_header_lines && $realfile =~ /^$/ &&
    
    		    !($rawline =~ /^\s+(?:\S|$)/ ||
    		      $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) {
    
    			$in_header_lines = 0;
    			$in_commit_log = 1;
    
    			$has_commit_log = 1;
    
    		}
    
    # Check if there is UTF-8 in a commit log when a mail header has explicitly
    # declined it, i.e defined some charset where it is missing.
    		if ($in_header_lines &&
    		    $rawline =~ /^Content-Type:.+charset="(.+)".*$/ &&
    		    $1 !~ /utf-8/i) {
    			$non_utf8_charset = 1;
    		}
    
    		if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ &&
    		    $rawline =~ /$NON_ASCII_UTF8/) {
    			WARN("UTF8_BEFORE_PATCH",
    			    "8-bit UTF-8 used in possible commit log\n" . $herecurr);
    		}
    
    
    # Check for absolute kernel paths in commit message
    		if ($tree && $in_commit_log) {
    			while ($line =~ m{(?:^|\s)(/\S*)}g) {
    				my $file = $1;
    
    				if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
    				    check_absolute_file($1, $herecurr)) {
    					#
    				} else {
    					check_absolute_file($file, $herecurr);
    				}
    			}
    		}
    
    
    # Check for various typo / spelling mistakes
    		if (defined($misspellings) &&
    		    ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
    			while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) {
    				my $typo = $1;
    				my $typo_fix = $spelling_fix{lc($typo)};
    				$typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/);
    				$typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/);
    
    				my $msg_level = \&WARN;
    				$msg_level = \&CHK if ($file);
    				if (&{$msg_level}("TYPO_SPELLING",
    						  "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) &&
    
    				    $fix) {
    					$fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/;
    				}
    			}
    		}
    
    
    # ignore non-hunk lines and lines being removed
    		next if (!$hunk_line || $line =~ /^-/);
    
    #trailing whitespace
    		if ($line =~ /^\+.*\015/) {
    			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
    
    			if (ERROR("DOS_LINE_ENDINGS",
    				  "DOS line endings\n" . $herevet) &&
    			    $fix) {
    
    				$fixed[$fixlinenr] =~ s/[\s\015]+$//;
    
    		} elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) {
    			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
    
    			if (ERROR("TRAILING_WHITESPACE",
    				  "trailing whitespace\n" . $herevet) &&
    			    $fix) {
    
    				$fixed[$fixlinenr] =~ s/\s+$//;
    
    # Check for FSF mailing addresses.
    		if ($rawline =~ /\bwrite to the Free/i ||
    
    		    $rawline =~ /\b675\s+Mass\s+Ave/i ||
    
    		    $rawline =~ /\b59\s+Temple\s+Pl/i ||
    		    $rawline =~ /\b51\s+Franklin\s+St/i) {
    			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
    
    			my $msg_level = \&ERROR;
    			$msg_level = \&CHK if ($file);
    			&{$msg_level}("FSF_MAILING_ADDRESS",
    				      "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
    
    # check for Kconfig help text having a real description
    # Only applies when adding the entry originally, after that we do not have
    # sufficient context to determine whether it is indeed long enough.
    		if ($realfile =~ /Kconfig/ &&
    
    		    # 'choice' is usually the last thing on the line (though
    		    # Kconfig supports named choices), so use a word boundary
    		    # (\b) rather than a whitespace character (\s)
    		    $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) {
    
    			my $length = 0;
    			my $cnt = $realcnt;
    			my $ln = $linenr + 1;
    			my $f;
    
    			for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
    
    				$f = $lines[$ln - 1];
    				$cnt-- if ($lines[$ln - 1] !~ /^-/);
    				$is_end = $lines[$ln - 1] =~ /^\+/;
    
    				next if ($f =~ /^-/);
    
    				last if (!$file && $f =~ /^\@\@/);
    
    				if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
    
    				} elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) {
    					if ($lines[$ln - 1] =~ "---help---") {
    						WARN("CONFIG_DESCRIPTION",
    						     "prefer 'help' over '---help---' for new help texts\n" . $herecurr);
    					}
    
    				$f =~ s/^.//;
    				$f =~ s/#.*//;
    				$f =~ s/^\s+//;
    				next if ($f =~ /^$/);
    
    
    				# This only checks context lines in the patch
    				# and so hopefully shouldn't trigger false
    				# positives, even though some of these are
    				# common words in help texts
    				if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice|
    						  if|endif|menu|endmenu|source)\b/x) {
    
    			if ($is_start && $is_end && $length < $min_conf_desc_length) {
    				WARN("CONFIG_DESCRIPTION",
    				     "please write a paragraph that describes the config symbol fully\n" . $herecurr);
    			}
    
    			#print "is_start<$is_start> is_end<$is_end> length<$length>\n";
    		}
    
    
    # check for MAINTAINERS entries that don't have the right form
    		if ($realfile =~ /^MAINTAINERS$/ &&
    		    $rawline =~ /^\+[A-Z]:/ &&
    		    $rawline !~ /^\+[A-Z]:\t\S/) {
    			if (WARN("MAINTAINERS_STYLE",
    				 "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
    			    $fix) {
    				$fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
    			}
    		}
    
    # discourage the use of boolean for type definition attributes of Kconfig options
    
    		if ($realfile =~ /Kconfig/ &&
    
    		    $line =~ /^\+\s*\bboolean\b/) {
    			WARN("CONFIG_TYPE_BOOLEAN",
    			     "Use of boolean is deprecated, please use bool instead.\n" . $herecurr);
    
    		}
    
    		if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
    		    ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) {
    			my $flag = $1;
    			my $replacement = {
    				'EXTRA_AFLAGS' =>   'asflags-y',
    				'EXTRA_CFLAGS' =>   'ccflags-y',
    				'EXTRA_CPPFLAGS' => 'cppflags-y',
    				'EXTRA_LDFLAGS' =>  'ldflags-y',
    			};
    
    			WARN("DEPRECATED_VARIABLE",
    			     "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag});
    
    # check for DT compatible documentation
    
    		if (defined $root &&
    			(($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) ||
    			 ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) {
    
    
    			my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g;
    
    
    			my $dt_path = $root . "/Documentation/devicetree/bindings/";
    			my $vp_file = $dt_path . "vendor-prefixes.txt";
    
    
    			foreach my $compat (@compats) {
    				my $compat2 = $compat;
    
    				$compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/;
    				my $compat3 = $compat;
    				$compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/;
    				`grep -Erq "$compat|$compat2|$compat3" $dt_path`;
    
    				if ( $? >> 8 ) {
    					WARN("UNDOCUMENTED_DT_STRING",
    					     "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr);
    				}
    
    
    				next if $compat !~ /^([a-zA-Z0-9\-]+)\,/;
    				my $vendor = $1;
    				`grep -Eq "^$vendor\\b" $vp_file`;
    
    				if ( $? >> 8 ) {
    					WARN("UNDOCUMENTED_DT_STRING",
    
    					     "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr);
    
    # check for using SPDX license tag at beginning of files
    		if ($realline == $checklicenseline) {
    			if ($rawline =~ /^[ \+]\s*\#\!\s*\//) {
    				$checklicenseline = 2;
    			} elsif ($rawline =~ /^\+/) {
    				my $comment = "";
    				if ($realfile =~ /\.(h|s|S)$/) {
    					$comment = '/*';
    				} elsif ($realfile =~ /\.(c|dts|dtsi)$/) {
    					$comment = '//';
    				} elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc)$/) {
    					$comment = '#';
    				} elsif ($realfile =~ /\.rst$/) {
    					$comment = '..';
    				}
    
    				if ($comment !~ /^$/ &&
    				    $rawline !~ /^\+\Q$comment\E SPDX-License-Identifier: /) {
    					WARN("SPDX_LICENSE_TAG",
    					     "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr);
    				}
    			}
    		}
    
    
    # check we are in a valid source file if not then ignore this hunk
    
    		next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/);
    
    # line length limit (with some exclusions)
    #
    # There are a few types of lines that may extend beyond $max_line_length:
    #	logging functions like pr_info that end in a string
    #	lines with a single string
    #	#defines that are a single string
    
    #	lines with an RFC3986 like URL
    
    #
    # There are 3 different line length message types:
    
    # LONG_LINE_COMMENT	a comment starts before but extends beyond $max_line_length
    
    # LONG_LINE_STRING	a string starts before but extends beyond $max_line_length
    # LONG_LINE		all other lines longer than $max_line_length
    #
    # if LONG_LINE is ignored, the other 2 types are also ignored
    #
    
    		if ($line =~ /^\+/ && $length > $max_line_length) {
    			my $msg_type = "LONG_LINE";
    
    			# Check the allowed long line types first
    
    			# logging functions that end in a string that starts
    			# before $max_line_length
    			if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ &&
    			    length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
    				$msg_type = "";
    
    			# lines with only strings (w/ possible termination)
    			# #defines with only strings
    			} elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ ||
    				 $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) {
    				$msg_type = "";