Skip to content
Snippets Groups Projects
checkpatch.pl 187 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    NOTE: If any of the errors are false positives, please report
          them to the maintainer, see CHECKPATCH in MAINTAINERS.
    EOM
    	}
    
    }
    
    exit($exit);
    
    sub top_of_kernel_tree {
    	my ($root) = @_;
    
    	my @tree_check = (
    
    		"COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile",
    
    		"README", "Documentation", "arch", "include", "drivers",
    		"fs", "init", "ipc", "kernel", "lib", "scripts",
    	);
    
    	foreach my $check (@tree_check) {
    		if (! -e $root . '/' . $check) {
    			return 0;
    		}
    	}
    	return 1;
    
    
    sub parse_email {
    	my ($formatted_email) = @_;
    
    	my $name = "";
    	my $address = "";
    	my $comment = "";
    
    	if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) {
    		$name = $1;
    		$address = $2;
    		$comment = $3 if defined $3;
    	} elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) {
    		$address = $1;
    		$comment = $2 if defined $2;
    	} elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
    		$address = $1;
    		$comment = $2 if defined $2;
    		$formatted_email =~ s/$address.*$//;
    		$name = $formatted_email;
    
    		$name =~ s/^\"|\"$//g;
    		# If there's a name left after stripping spaces and
    		# leading quotes, and the address doesn't have both
    		# leading and trailing angle brackets, the address
    		# is invalid. ie:
    		#   "joe smith joe@smith.com" bad
    		#   "joe smith <joe@smith.com" bad
    		if ($name ne "" && $address !~ /^<[^>]+>$/) {
    			$name = "";
    			$address = "";
    			$comment = "";
    		}
    	}
    
    
    	$name =~ s/^\"|\"$//g;
    
    	$address = trim($address);
    
    	$address =~ s/^\<|\>$//g;
    
    	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
    		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
    		$name = "\"$name\"";
    	}
    
    	return ($name, $address, $comment);
    }
    
    sub format_email {
    	my ($name, $address) = @_;
    
    	my $formatted_email;
    
    
    	$name =~ s/^\"|\"$//g;
    
    	$address = trim($address);
    
    
    	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
    		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
    		$name = "\"$name\"";
    	}
    
    	if ("$name" eq "") {
    		$formatted_email = "$address";
    	} else {
    		$formatted_email = "$name <$address>";
    	}
    
    	return $formatted_email;
    }
    
    
    sub which {
    	my ($bin) = @_;
    
    	foreach my $path (split(/:/, $ENV{PATH})) {
    		if (-e "$path/$bin") {
    			return "$path/$bin";
    		}
    	}
    
    	return "";
    }
    
    
    sub which_conf {
    	my ($conf) = @_;
    
    	foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
    		if (-e "$path/$conf") {
    			return "$path/$conf";
    		}
    	}
    
    	return "";
    }
    
    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 copy_spacing {
    	(my $res = shift) =~ tr/\t/ /c;
    	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));
    }
    
    my $sanitise_quote = '';
    
    sub sanitise_line_reset {
    	my ($in_comment) = @_;
    
    	if ($in_comment) {
    		$sanitise_quote = '*/';
    	} else {
    		$sanitise_quote = '';
    	}
    }
    sub sanitise_line {
    	my ($line) = @_;
    
    	my $res = '';
    	my $l = '';
    
    	my $qlen = 0;
    	my $off = 0;
    	my $c;
    
    	# Always copy over the diff marker.
    	$res = substr($line, 0, 1);
    
    	for ($off = 1; $off < length($line); $off++) {
    		$c = substr($line, $off, 1);
    
    		# Comments we are wacking completly including the begin
    		# and end, all to $;.
    		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
    			$sanitise_quote = '*/';
    
    			substr($res, $off, 2, "$;$;");
    			$off++;
    			next;
    		}
    		if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
    			$sanitise_quote = '';
    			substr($res, $off, 2, "$;$;");
    			$off++;
    			next;
    		}
    		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
    			$sanitise_quote = '//';
    
    			substr($res, $off, 2, $sanitise_quote);
    			$off++;
    			next;
    		}
    
    		# A \ in a string means ignore the next character.
    		if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
    		    $c eq "\\") {
    			substr($res, $off, 2, 'XX');
    			$off++;
    			next;
    		}
    		# Regular quotes.
    		if ($c eq "'" || $c eq '"') {
    			if ($sanitise_quote eq '') {
    				$sanitise_quote = $c;
    
    				substr($res, $off, 1, $c);
    				next;
    			} elsif ($sanitise_quote eq $c) {
    				$sanitise_quote = '';
    			}
    		}
    
    		#print "c<$c> SQ<$sanitise_quote>\n";
    		if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
    			substr($res, $off, 1, $;);
    		} elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
    			substr($res, $off, 1, $;);
    		} elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
    			substr($res, $off, 1, 'X');
    		} else {
    			substr($res, $off, 1, $c);
    		}
    	}
    
    	if ($sanitise_quote eq '//') {
    		$sanitise_quote = '';
    	}
    
    	# The pathname on a #include may be surrounded by '<' and '>'.
    	if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
    		my $clean = 'X' x length($1);
    		$res =~ s@\<.*\>@<$clean>@;
    
    	# The whole of a #error is a string.
    	} elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
    		my $clean = 'X' x length($1);
    		$res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
    	}
    
    
    	if ($allow_c99_comments && $res =~ m@(//.*$)@) {
    		my $match = $1;
    		$res =~ s/\Q$match\E/"$;" x length($match)/e;
    	}
    
    
    sub get_quoted_string {
    	my ($line, $rawline) = @_;
    
    
    	return "" if ($line !~ m/($String)/g);
    
    	return substr($rawline, $-[0], $+[0] - $-[0]);
    }
    
    
    sub ctx_statement_block {
    	my ($linenr, $remain, $off) = @_;
    	my $line = $linenr - 1;
    	my $blk = '';
    	my $soff = $off;
    	my $coff = $off - 1;
    	my $coff_set = 0;
    
    	my $loff = 0;
    
    	my $type = '';
    	my $level = 0;
    	my @stack = ();
    	my $p;
    	my $c;
    	my $len = 0;
    
    	my $remainder;
    	while (1) {
    		@stack = (['', 0]) if ($#stack == -1);
    
    		#warn "CSB: blk<$blk> remain<$remain>\n";
    		# If we are about to drop off the end, pull in more
    		# context.
    		if ($off >= $len) {
    			for (; $remain > 0; $line++) {
    				last if (!defined $lines[$line]);
    				next if ($lines[$line] =~ /^-/);
    				$remain--;
    				$loff = $len;
    				$blk .= $lines[$line] . "\n";
    				$len = length($blk);
    				$line++;
    				last;
    			}
    			# Bail if there is no further context.
    			#warn "CSB: blk<$blk> off<$off> len<$len>\n";
    			if ($off >= $len) {
    				last;
    			}
    
    			if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) {
    				$level++;
    				$type = '#';
    			}
    
    		}
    		$p = $c;
    		$c = substr($blk, $off, 1);
    		$remainder = substr($blk, $off);
    
    		#warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
    
    		# Handle nested #if/#else.
    		if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
    			push(@stack, [ $type, $level ]);
    		} elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
    			($type, $level) = @{$stack[$#stack - 1]};
    		} elsif ($remainder =~ /^#\s*endif\b/) {
    			($type, $level) = @{pop(@stack)};
    		}
    
    		# Statement ends at the ';' or a close '}' at the
    		# outermost level.
    		if ($level == 0 && $c eq ';') {
    			last;
    		}
    
    		# An else is really a conditional as long as its not else if
    		if ($level == 0 && $coff_set == 0 &&
    				(!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
    				$remainder =~ /^(else)(?:\s|{)/ &&
    				$remainder !~ /^else\s+if\b/) {
    			$coff = $off + length($1) - 1;
    			$coff_set = 1;
    			#warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
    			#warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
    		}
    
    		if (($type eq '' || $type eq '(') && $c eq '(') {
    			$level++;
    			$type = '(';
    		}
    		if ($type eq '(' && $c eq ')') {
    			$level--;
    			$type = ($level != 0)? '(' : '';
    
    			if ($level == 0 && $coff < $soff) {
    				$coff = $off;
    				$coff_set = 1;
    				#warn "CSB: mark coff<$coff>\n";
    			}
    		}
    		if (($type eq '' || $type eq '{') && $c eq '{') {
    			$level++;
    			$type = '{';
    		}
    		if ($type eq '{' && $c eq '}') {
    			$level--;
    			$type = ($level != 0)? '{' : '';
    
    			if ($level == 0) {
    				if (substr($blk, $off + 1, 1) eq ';') {
    					$off++;
    				}
    				last;
    			}
    		}
    
    		# Preprocessor commands end at the newline unless escaped.
    		if ($type eq '#' && $c eq "\n" && $p ne "\\") {
    			$level--;
    			$type = '';
    			$off++;
    			last;
    		}
    
    		$off++;
    	}
    	# We are truly at the end, so shuffle to the next line.
    	if ($off == $len) {
    		$loff = $len + 1;
    		$line++;
    		$remain--;
    	}
    
    	my $statement = substr($blk, $soff, $off - $soff + 1);
    	my $condition = substr($blk, $soff, $coff - $soff + 1);
    
    	#warn "STATEMENT<$statement>\n";
    	#warn "CONDITION<$condition>\n";
    
    	#print "coff<$coff> soff<$off> loff<$loff>\n";
    
    	return ($statement, $condition,
    			$line, $remain + 1, $off - $loff + 1, $level);
    }
    
    sub statement_lines {
    	my ($stmt) = @_;
    
    	# Strip the diff line prefixes and rip blank lines at start and end.
    	$stmt =~ s/(^|\n)./$1/g;
    	$stmt =~ s/^\s*//;
    	$stmt =~ s/\s*$//;
    
    	my @stmt_lines = ($stmt =~ /\n/g);
    
    	return $#stmt_lines + 2;
    }
    
    sub statement_rawlines {
    	my ($stmt) = @_;
    
    	my @stmt_lines = ($stmt =~ /\n/g);
    
    	return $#stmt_lines + 2;
    }
    
    sub statement_block_size {
    	my ($stmt) = @_;
    
    	$stmt =~ s/(^|\n)./$1/g;
    	$stmt =~ s/^\s*{//;
    	$stmt =~ s/}\s*$//;
    	$stmt =~ s/^\s*//;
    	$stmt =~ s/\s*$//;
    
    	my @stmt_lines = ($stmt =~ /\n/g);
    	my @stmt_statements = ($stmt =~ /;/g);
    
    	my $stmt_lines = $#stmt_lines + 2;
    	my $stmt_statements = $#stmt_statements + 1;
    
    	if ($stmt_lines > $stmt_statements) {
    		return $stmt_lines;
    	} else {
    		return $stmt_statements;
    	}
    }
    
    sub ctx_statement_full {
    	my ($linenr, $remain, $off) = @_;
    	my ($statement, $condition, $level);
    
    	my (@chunks);
    
    	# Grab the first conditional/block pair.
    	($statement, $condition, $linenr, $remain, $off, $level) =
    				ctx_statement_block($linenr, $remain, $off);
    	#print "F: c<$condition> s<$statement> remain<$remain>\n";
    	push(@chunks, [ $condition, $statement ]);
    	if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) {
    		return ($level, $linenr, @chunks);
    	}
    
    	# Pull in the following conditional/block pairs and see if they
    	# could continue the statement.
    	for (;;) {
    		($statement, $condition, $linenr, $remain, $off, $level) =
    				ctx_statement_block($linenr, $remain, $off);
    		#print "C: c<$condition> s<$statement> remain<$remain>\n";
    		last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s));
    		#print "C: push\n";
    		push(@chunks, [ $condition, $statement ]);
    	}
    
    	return ($level, $linenr, @chunks);
    }
    
    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;
    	my @stack = ($level);
    	for ($line = $start; $remain > 0; $line++) {
    		next if ($rawlines[$line] =~ /^-/);
    		$remain--;
    
    		$blk .= $rawlines[$line];
    
    		# Handle nested #if/#else.
    		if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
    			push(@stack, $level);
    		} elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
    			$level = $stack[$#stack - 1];
    		} elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) {
    			$level = pop(@stack);
    		}
    
    		foreach my $c (split(//, $lines[$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_statement_level {
    	my ($linenr, $remain, $off) = @_;
    
    	return ctx_block_get($linenr, $remain, 0, '(', ')', $off);
    }
    
    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*(?:\\\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 raw_line {
    	my ($linenr, $cnt) = @_;
    
    	my $offset = $linenr - 1;
    	$cnt++;
    
    	my $line;
    	while ($cnt) {
    		$line = $rawlines[$offset++];
    		next if (defined($line) && $line =~ /^-/);
    		$cnt--;
    	}
    
    	return $line;
    }
    
    sub cat_vet {
    	my ($vet) = @_;
    	my ($res, $coded);
    
    	$res = '';
    	while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) {
    		$res .= $1;
    		if ($2 ne '') {
    			$coded = sprintf("^%c", unpack('C', $2) + 64);
    			$res .= $coded;
    		}
    	}
    	$res =~ s/$/\$/;
    
    	return $res;
    }
    
    my $av_preprocessor = 0;
    my $av_pending;
    my @av_paren_type;
    my $av_pend_colon;
    
    sub annotate_reset {
    	$av_preprocessor = 0;
    	$av_pending = '_';
    	@av_paren_type = ('E');
    	$av_pend_colon = 'O';
    }
    
    sub annotate_values {
    	my ($stream, $type) = @_;
    
    	my $res;
    	my $var = '_' x length($stream);
    	my $cur = $stream;
    
    	print "$stream\n" if ($dbg_values > 1);
    
    	while (length($cur)) {
    		@av_paren_type = ('E') if ($#av_paren_type < 0);
    		print " <" . join('', @av_paren_type) .
    				"> <$type> <$av_pending>" if ($dbg_values > 1);
    		if ($cur =~ /^(\s+)/o) {
    			print "WS($1)\n" if ($dbg_values > 1);
    			if ($1 =~ /\n/ && $av_preprocessor) {
    				$type = pop(@av_paren_type);
    				$av_preprocessor = 0;
    			}
    
    		} elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') {
    			print "CAST($1)\n" if ($dbg_values > 1);
    			push(@av_paren_type, $type);
    
    
    		} elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) {
    			print "DECLARE($1)\n" if ($dbg_values > 1);
    			$type = 'T';
    
    		} elsif ($cur =~ /^($Modifier)\s*/) {
    			print "MODIFIER($1)\n" if ($dbg_values > 1);
    			$type = 'T';
    
    		} elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
    			print "DEFINE($1,$2)\n" if ($dbg_values > 1);
    			$av_preprocessor = 1;
    			push(@av_paren_type, $type);
    			if ($2 ne '') {
    				$av_pending = 'N';
    			}
    			$type = 'E';
    
    		} elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) {
    			print "UNDEF($1)\n" if ($dbg_values > 1);
    			$av_preprocessor = 1;
    			push(@av_paren_type, $type);
    
    		} elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) {
    			print "PRE_START($1)\n" if ($dbg_values > 1);
    			$av_preprocessor = 1;
    
    			push(@av_paren_type, $type);
    			push(@av_paren_type, $type);
    			$type = 'E';
    
    		} elsif ($cur =~ /^(\#\s*(?:else|elif))/o) {
    			print "PRE_RESTART($1)\n" if ($dbg_values > 1);
    			$av_preprocessor = 1;
    
    			push(@av_paren_type, $av_paren_type[$#av_paren_type]);
    
    			$type = 'E';
    
    		} elsif ($cur =~ /^(\#\s*(?:endif))/o) {
    			print "PRE_END($1)\n" if ($dbg_values > 1);
    
    			$av_preprocessor = 1;
    
    			# Assume all arms of the conditional end as this
    			# one does, and continue as if the #endif was not here.
    			pop(@av_paren_type);
    			push(@av_paren_type, $type);
    			$type = 'E';
    
    		} elsif ($cur =~ /^(\\\n)/o) {
    			print "PRECONT($1)\n" if ($dbg_values > 1);
    
    		} elsif ($cur =~ /^(__attribute__)\s*\(?/o) {
    			print "ATTR($1)\n" if ($dbg_values > 1);
    			$av_pending = $type;
    			$type = 'N';
    
    		} elsif ($cur =~ /^(sizeof)\s*(\()?/o) {
    			print "SIZEOF($1)\n" if ($dbg_values > 1);
    			if (defined $2) {
    				$av_pending = 'V';
    			}
    			$type = 'N';
    
    		} elsif ($cur =~ /^(if|while|for)\b/o) {
    			print "COND($1)\n" if ($dbg_values > 1);
    			$av_pending = 'E';
    			$type = 'N';
    
    		} elsif ($cur =~/^(case)/o) {
    			print "CASE($1)\n" if ($dbg_values > 1);
    			$av_pend_colon = 'C';
    			$type = 'N';
    
    		} elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
    			print "KEYWORD($1)\n" if ($dbg_values > 1);
    			$type = 'N';
    
    		} elsif ($cur =~ /^(\()/o) {
    			print "PAREN('$1')\n" if ($dbg_values > 1);
    			push(@av_paren_type, $av_pending);
    			$av_pending = '_';
    			$type = 'N';
    
    		} elsif ($cur =~ /^(\))/o) {
    			my $new_type = pop(@av_paren_type);
    			if ($new_type ne '_') {
    				$type = $new_type;
    				print "PAREN('$1') -> $type\n"
    							if ($dbg_values > 1);
    			} else {
    				print "PAREN('$1')\n" if ($dbg_values > 1);
    			}
    
    		} elsif ($cur =~ /^($Ident)\s*\(/o) {
    			print "FUNC($1)\n" if ($dbg_values > 1);
    			$type = 'V';
    			$av_pending = 'V';
    
    		} elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
    			if (defined $2 && $type eq 'C' || $type eq 'T') {
    				$av_pend_colon = 'B';
    			} elsif ($type eq 'E') {
    				$av_pend_colon = 'L';
    			}
    			print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
    			$type = 'V';
    
    		} elsif ($cur =~ /^($Ident|$Constant)/o) {
    			print "IDENT($1)\n" if ($dbg_values > 1);
    			$type = 'V';
    
    		} elsif ($cur =~ /^($Assignment)/o) {
    			print "ASSIGN($1)\n" if ($dbg_values > 1);
    			$type = 'N';
    
    		} elsif ($cur =~/^(;|{|})/) {
    			print "END($1)\n" if ($dbg_values > 1);
    			$type = 'E';
    			$av_pend_colon = 'O';
    
    		} elsif ($cur =~/^(,)/) {
    			print "COMMA($1)\n" if ($dbg_values > 1);
    			$type = 'C';
    
    		} elsif ($cur =~ /^(\?)/o) {
    			print "QUESTION($1)\n" if ($dbg_values > 1);
    			$type = 'N';
    
    		} elsif ($cur =~ /^(:)/o) {
    			print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
    
    			substr($var, length($res), 1, $av_pend_colon);
    			if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
    				$type = 'E';
    			} else {
    				$type = 'N';
    			}
    			$av_pend_colon = 'O';
    
    		} elsif ($cur =~ /^(\[)/o) {
    			print "CLOSE($1)\n" if ($dbg_values > 1);
    			$type = 'N';
    
    		} elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
    			my $variant;
    
    			print "OPV($1)\n" if ($dbg_values > 1);
    			if ($type eq 'V') {
    				$variant = 'B';
    			} else {
    				$variant = 'U';
    			}
    
    			substr($var, length($res), 1, $variant);
    			$type = 'N';
    
    		} elsif ($cur =~ /^($Operators)/o) {
    			print "OP($1)\n" if ($dbg_values > 1);
    			if ($1 ne '++' && $1 ne '--') {
    				$type = 'N';
    			}
    
    		} elsif ($cur =~ /(^.)/o) {
    			print "C($1)\n" if ($dbg_values > 1);
    		}
    		if (defined $1) {
    			$cur = substr($cur, length($1));
    			$res .= $type x length($1);
    		}
    	}
    
    	return ($res, $var);
    }
    
    sub possible {
    	my ($possible, $line) = @_;
    	my $notPermitted = qr{(?:
    		^(?:
    			$Modifier|
    			$Storage|
    			$Type|
    			DEFINE_\S+
    		)$|
    		^(?:
    			goto|
    			return|
    			case|
    			else|
    			asm|__asm__|
    
    		)(?:\s|$)|
    		^(?:typedef|struct|enum)\b
    	    )}x;
    	warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
    	if ($possible !~ $notPermitted) {
    		# Check for modifiers.
    		$possible =~ s/\s*$Storage\s*//g;
    		$possible =~ s/\s*$Sparse\s*//g;
    		if ($possible =~ /^\s*$/) {
    
    		} elsif ($possible =~ /\s/) {
    			$possible =~ s/\s*$Type\s*//g;
    			for my $modifier (split(' ', $possible)) {
    				if ($modifier !~ $notPermitted) {
    					warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
    
    					push(@modifierListFile, $modifier);
    
    				}
    			}
    
    		} else {
    			warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
    
    			push(@typeListFile, $possible);
    
    		}
    		build_types();
    	} else {
    		warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
    	}
    }
    
    my $prefix = '';
    
    sub show_type {
    
    	my ($type) = @_;
    
    	$type =~ tr/[a-z]/[A-Z]/;
    
    	return defined $use_type{$type} if (scalar keys %use_type > 0);
    
    	return !defined $ignore_type{$type};
    
    	my ($level, $type, $msg) = @_;
    
    	if (!show_type($type) ||
    	    (defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
    
    	my $output = '';
    	if ($color) {
    		if ($level eq 'ERROR') {
    			$output .= RED;
    		} elsif ($level eq 'WARNING') {
    			$output .= YELLOW;
    		} else {
    			$output .= GREEN;
    		}
    	}
    	$output .= $prefix . $level . ':';
    
    		$output .= BLUE if ($color);
    		$output .= "$type:";
    	}
    	$output .= RESET if ($color);
    	$output .= ' ' . $msg . "\n";
    
    	if ($showfile) {
    		my @lines = split("\n", $output, -1);
    		splice(@lines, 1, 1);
    		$output = join("\n", @lines);
    
    	$output = (split('\n', $output))[0] . "\n" if ($terse);
    
    	push(our @report, $output);
    
    sub fixup_current_range {
    	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++;
    	}