Skip to content
Snippets Groups Projects
checkpatch.pl 191 KiB
Newer Older
  • Learn to ignore specific revisions
  • #!/usr/bin/env perl
    
    # (c) 2001, Dave Jones. (the file handling bit)
    # (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
    # (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
    # (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
    # Licensed under the terms of the GNU GPL License version 2
    
    use strict;
    
    use File::Basename;
    use Cwd 'abs_path';
    
    use Term::ANSIColor qw(:constants);
    
    my $D = dirname(abs_path($P));
    
    
    my $V = '0.32';
    
    use Getopt::Long qw(:config no_auto_abbrev);
    
    my $quiet = 0;
    my $tree = 1;
    my $chk_signoff = 1;
    my $chk_patch = 1;
    my $tst_only;
    my $emacs = 0;
    my $terse = 0;
    
    my $showfile = 0;
    
    my $git = 0;
    my %git_commits = ();
    
    my $check_orig = 0;
    
    my $summary = 1;
    my $mailback = 0;
    my $summary_file = 0;
    my $show_types = 0;
    
    my $list_types = 0;
    
    my $fix = 0;
    my $fix_inplace = 0;
    
    my %camelcase = ();
    my %use_type = ();
    my @use = ();
    
    my %ignore_type = ();
    my @ignore = ();
    my $help = 0;
    my $configuration_file = ".checkpatch.conf";
    
    my $max_line_length = 80;
    
    my $ignore_perl_version = 0;
    my $minimum_perl_version = 5.10.0;
    
    my $min_conf_desc_length = 4;
    
    my $spelling_file = "$D/spelling.txt";
    my $codespell = 0;
    my $codespellfile = "/usr/share/codespell/dictionary.txt";
    
    my $conststructsfile = "$D/const_structs.checkpatch";
    my $typedefsfile = "";
    my $color = "auto";
    my $allow_c99_comments = 1;
    
    
    sub help {
    	my ($exitcode) = @_;
    
    	print << "EOM";
    Usage: $P [OPTION]... [FILE]...
    Version: $V
    
    Options:
      -q, --quiet                quiet
      --no-tree                  run without a kernel tree
      --no-signoff               do not check for 'Signed-off-by' line
      --patch                    treat FILE as patchfile (default)
      --emacs                    emacs compile window format
      --terse                    one line per report
    
      --showfile                 emit diffed file position, not input file position
      -g, --git                  treat FILE as a single commit or git revision range
                                 single git commit with:
                                   <rev>
                                   <rev>^
                                   <rev>~n
                                 multiple git commits with:
                                   <rev1>..<rev2>
                                   <rev1>...<rev2>
                                   <rev>-<count>
                                 git merges are ignored
    
      -f, --file                 treat FILE as regular source file
      --subjective, --strict     enable more subjective tests
    
      --list-types               list the possible message types
    
      --types TYPE(,TYPE2...)    show only these comma separated message types
    
      --ignore TYPE(,TYPE2...)   ignore various comma separated message types
    
      --show-types               show the specific message type in the output
    
      --max-line-length=n        set the maximum line length, if exceeded, warn
    
      --min-conf-desc-length=n   set the min description length, if shorter, warn
    
      --root=PATH                PATH to the kernel tree root
      --no-summary               suppress the per-file summary
      --mailback                 only produce a report in case of warnings/errors
      --summary-file             include the filename in summary
      --debug KEY=[0|1]          turn on/off debugging of KEY, where KEY is one of
                                 'values', 'possible', 'type', and 'attr' (default
                                 is all off)
      --test-only=WORD           report only warnings/errors containing WORD
                                 literally
    
      --fix                      EXPERIMENTAL - may create horrible results
                                 If correctable single-line errors exist, create
                                 "<inputfile>.EXPERIMENTAL-checkpatch-fixes"
                                 with potential errors corrected to the preferred
                                 checkpatch style
      --fix-inplace              EXPERIMENTAL - may create horrible results
                                 Is the same as --fix, but overwrites the input
                                 file.  It's your fault if there's no backup or git
      --ignore-perl-version      override checking of perl version.  expect
                                 runtime errors.
    
      --codespell                Use the codespell dictionary for spelling/typos
    
                                 (default:/usr/share/codespell/dictionary.txt)
    
      --codespellfile            Use this codespell dictionary
    
      --typedefsfile             Read additional types from this file
      --color[=WHEN]             Use colors 'always', 'never', or only when output
                                 is a terminal ('auto'). Default is 'auto'.
    
      -h, --help, --version      display this help and exit
    
    When FILE is - read standard input.
    EOM
    
    	exit($exitcode);
    }
    
    
    sub uniq {
    	my %seen;
    	return grep { !$seen{$_}++ } @_;
    }
    
    sub list_types {
    	my ($exitcode) = @_;
    
    	my $count = 0;
    
    	local $/ = undef;
    
    	open(my $script, '<', abs_path($P)) or
    	    die "$P: Can't read '$P' $!\n";
    
    	my $text = <$script>;
    	close($script);
    
    	my @types = ();
    
    	# Also catch when type or level is passed through a variable
    	for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
    
    		push (@types, $_);
    	}
    	@types = sort(uniq(@types));
    	print("#\tMessage type\n\n");
    	foreach my $type (@types) {
    		print(++$count . "\t" . $type . "\n");
    	}
    
    	exit($exitcode);
    }
    
    
    my $conf = which_conf($configuration_file);
    if (-f $conf) {
    	my @conf_args;
    	open(my $conffile, '<', "$conf")
    	    or warn "$P: Can't find a readable $configuration_file file $!\n";
    
    	while (<$conffile>) {
    		my $line = $_;
    
    		$line =~ s/\s*\n?$//g;
    		$line =~ s/^\s*//g;
    		$line =~ s/\s+/ /g;
    
    		next if ($line =~ m/^\s*#/);
    		next if ($line =~ m/^\s*$/);
    
    		my @words = split(" ", $line);
    		foreach my $word (@words) {
    			last if ($word =~ m/^#/);
    			push (@conf_args, $word);
    		}
    	}
    	close($conffile);
    	unshift(@ARGV, @conf_args) if @conf_args;
    }
    
    
    # Perl's Getopt::Long allows options to take optional arguments after a space.
    # Prevent --color by itself from consuming other arguments
    foreach (@ARGV) {
    	if ($_ eq "--color" || $_ eq "-color") {
    		$_ = "--color=$color";
    	}
    }
    
    
    GetOptions(
    	'q|quiet+'	=> \$quiet,
    	'tree!'		=> \$tree,
    	'signoff!'	=> \$chk_signoff,
    	'patch!'	=> \$chk_patch,
    	'emacs!'	=> \$emacs,
    	'terse!'	=> \$terse,
    
    	'showfile!'	=> \$showfile,
    
    	'f|file!'	=> \$file,
    
    	'g|git!'	=> \$git,
    
    	'subjective!'	=> \$check,
    	'strict!'	=> \$check,
    	'ignore=s'	=> \@ignore,
    
    	'show-types!'	=> \$show_types,
    
    	'list-types!'	=> \$list_types,
    
    	'max-line-length=i' => \$max_line_length,
    
    	'min-conf-desc-length=i' => \$min_conf_desc_length,
    
    	'root=s'	=> \$root,
    	'summary!'	=> \$summary,
    	'mailback!'	=> \$mailback,
    	'summary-file!'	=> \$summary_file,
    
    	'fix!'		=> \$fix,
    	'fix-inplace!'	=> \$fix_inplace,
    	'ignore-perl-version!' => \$ignore_perl_version,
    
    	'debug=s'	=> \%debug,
    	'test-only=s'	=> \$tst_only,
    
    	'codespell!'	=> \$codespell,
    	'codespellfile=s'	=> \$codespellfile,
    	'typedefsfile=s'	=> \$typedefsfile,
    	'color=s'	=> \$color,
    	'no-color'	=> \$color,	#keep old behaviors of -nocolor
    	'nocolor'	=> \$color,	#keep old behaviors of -nocolor
    
    	'h|help'	=> \$help,
    	'version'	=> \$help
    ) or help(1);
    
    help(0) if ($help);
    
    
    list_types(0) if ($list_types);
    
    
    $fix = 1 if ($fix_inplace);
    
    $check_orig = $check;
    
    if ($^V && $^V lt $minimum_perl_version) {
    	printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
    	if (!$ignore_perl_version) {
    		exit(1);
    	}
    }
    
    
    #if no filenames are given, push '-' to read patch from stdin
    
    	push(@ARGV, '-');
    }
    
    if ($color =~ /^[01]$/) {
    	$color = !$color;
    } elsif ($color =~ /^always$/i) {
    	$color = 1;
    } elsif ($color =~ /^never$/i) {
    	$color = 0;
    } elsif ($color =~ /^auto$/i) {
    	$color = (-t STDOUT);
    } else {
    	die "Invalid color mode: $color\n";
    
    sub hash_save_array_words {
    	my ($hashRef, $arrayRef) = @_;
    
    	my @array = split(/,/, join(',', @$arrayRef));
    	foreach my $word (@array) {
    		$word =~ s/\s*\n?$//g;
    		$word =~ s/^\s*//g;
    		$word =~ s/\s+/ /g;
    		$word =~ tr/[a-z]/[A-Z]/;
    
    		next if ($word =~ m/^\s*#/);
    		next if ($word =~ m/^\s*$/);
    
    		$hashRef->{$word}++;
    	}
    
    sub hash_show_words {
    	my ($hashRef, $prefix) = @_;
    
    
    	if (keys %$hashRef) {
    		print "\nNOTE: $prefix message types:";
    
    		foreach my $word (sort keys %$hashRef) {
    			print " $word";
    		}
    
    	}
    }
    
    hash_save_array_words(\%ignore_type, \@ignore);
    hash_save_array_words(\%use_type, \@use);
    
    
    my $dbg_values = 0;
    my $dbg_possible = 0;
    my $dbg_type = 0;
    my $dbg_attr = 0;
    for my $key (keys %debug) {
    	## no critic
    	eval "\${dbg_$key} = '$debug{$key}';";
    	die "$@" if ($@);
    }
    
    my $rpt_cleaners = 0;
    
    if ($terse) {
    	$emacs = 1;
    	$quiet++;
    }
    
    if ($tree) {
    	if (defined $root) {
    		if (!top_of_kernel_tree($root)) {
    			die "$P: $root: --root does not point at a valid tree\n";
    		}
    	} else {
    		if (top_of_kernel_tree('.')) {
    			$root = '.';
    		} elsif ($0 =~ m@(.*)/scripts/[^/]*$@ &&
    						top_of_kernel_tree($1)) {
    			$root = $1;
    		}
    	}
    
    	if (!defined $root) {
    		print "Must be run from the top-level dir. of a kernel tree\n";
    		exit(2);
    	}
    }
    
    my $emitted_corrupt = 0;
    
    our $Ident	= qr{
    			[A-Za-z_][A-Za-z\d_]*
    			(?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
    		}x;
    our $Storage	= qr{extern|static|asmlinkage};
    our $Sparse	= qr{
    			__user|
    			__kernel|
    			__force|
    			__iomem|
    			__must_check|
    			__init_refok|
    			__kprobes|
    			__ref|
    
    our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
    our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
    our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)};
    our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)};
    our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit};
    
    
    # Notes to $Attribute:
    # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
    our $Attribute	= qr{
    			const|
    			__percpu|
    			__nocast|
    			__safe|
    
    			__packed__|
    			__packed2__|
    			__naked|
    			__maybe_unused|
    			__always_unused|
    			__noreturn|
    			__used|
    			__cold|
    
    			__noclone|
    			__deprecated|
    			__read_mostly|
    			__kprobes|
    
    			____cacheline_aligned|
    			____cacheline_aligned_in_smp|
    			____cacheline_internodealigned_in_smp|
    			__weak
    		  }x;
    our $Modifier;
    
    our $Inline	= qr{inline|__always_inline|noinline|__inline|__inline__};
    
    our $Member	= qr{->$Ident|\.$Ident|\[[^]]*\]};
    our $Lval	= qr{$Ident(?:$Member)*};
    
    
    our $Int_type	= qr{(?i)llu|ull|ll|lu|ul|l|u};
    our $Binary	= qr{(?i)0b[01]+$Int_type?};
    our $Hex	= qr{(?i)0x[0-9a-f]+$Int_type?};
    our $Int	= qr{[0-9]+$Int_type?};
    
    our $Octal	= qr{0[0-7]+$Int_type?};
    
    our $String	= qr{"[X\t]*"};
    
    our $Float_hex	= qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
    our $Float_dec	= qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
    our $Float_int	= qr{(?i)[0-9]+e-?[0-9]+[fl]?};
    our $Float	= qr{$Float_hex|$Float_dec|$Float_int};
    
    our $Constant	= qr{$Float|$Binary|$Octal|$Hex|$Int};
    
    our $Assignment	= qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
    
    our $Compare    = qr{<=|>=|==|!=|<|(?<!-)>};
    
    our $Arithmetic = qr{\+|-|\*|\/|%};
    
    our $Operators	= qr{
    			<=|>=|==|!=|
    			=>|->|<<|>>|<|>|!|~|
    
    			&&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
    
    our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
    
    our $BasicType;
    
    our $NonptrTypeMisordered;
    
    our $NonptrTypeWithAttr;
    
    our $TypeMisordered;
    
    our $DeclareMisordered;
    
    our $NON_ASCII_UTF8	= qr{
    	[\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;
    
    
    our $UTF8	= qr{
    	[\x09\x0A\x0D\x20-\x7E]              # ASCII
    	| $NON_ASCII_UTF8
    }x;
    
    
    our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t};
    our $typeOtherOSTypedefs = qr{(?x:
    	u_(?:char|short|int|long) |          # bsd
    	u(?:nchar|short|int|long)            # sysv
    )};
    our $typeKernelTypedefs = qr{(?x:
    
    	(?:__)?(?:u|s|be|le)(?:8|16|32|64)|
    	atomic_t
    )};
    
    our $typeTypedefs = qr{(?x:
    	$typeC99Typedefs\b|
    	$typeOtherOSTypedefs\b|
    	$typeKernelTypedefs\b
    )};
    
    our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b};
    
    
    our $logFunctions = qr{(?x:
    
    	printk(?:_ratelimited|_once|_deferred_once|_deferred|)|
    
    	(?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)|
    
    	WARN(?:_RATELIMIT|_ONCE|)|
    	panic|
    
    	MODULE_[A-Z_]+|
    	seq_vprintf|seq_printf|seq_puts
    
    )};
    
    our $signature_tags = qr{(?xi:
    	Signed-off-by:|
    	Acked-by:|
    	Tested-by:|
    	Reviewed-by:|
    	Reported-by:|
    
    our @typeListMisordered = (
    	qr{char\s+(?:un)?signed},
    	qr{int\s+(?:(?:un)?signed\s+)?short\s},
    	qr{int\s+short(?:\s+(?:un)?signed)},
    	qr{short\s+int(?:\s+(?:un)?signed)},
    	qr{(?:un)?signed\s+int\s+short},
    	qr{short\s+(?:un)?signed},
    	qr{long\s+int\s+(?:un)?signed},
    	qr{int\s+long\s+(?:un)?signed},
    	qr{long\s+(?:un)?signed\s+int},
    	qr{int\s+(?:un)?signed\s+long},
    	qr{int\s+(?:un)?signed},
    	qr{int\s+long\s+long\s+(?:un)?signed},
    	qr{long\s+long\s+int\s+(?:un)?signed},
    	qr{long\s+long\s+(?:un)?signed\s+int},
    	qr{long\s+long\s+(?:un)?signed},
    	qr{long\s+(?:un)?signed},
    );
    
    
    our @typeList = (
    	qr{void},
    
    	qr{(?:(?:un)?signed\s+)?char},
    	qr{(?:(?:un)?signed\s+)?short\s+int},
    	qr{(?:(?:un)?signed\s+)?short},
    	qr{(?:(?:un)?signed\s+)?int},
    	qr{(?:(?:un)?signed\s+)?long\s+int},
    	qr{(?:(?:un)?signed\s+)?long\s+long\s+int},
    	qr{(?:(?:un)?signed\s+)?long\s+long},
    	qr{(?:(?:un)?signed\s+)?long},
    	qr{(?:un)?signed},
    
    	qr{float},
    	qr{double},
    	qr{bool},
    	qr{struct\s+$Ident},
    	qr{union\s+$Ident},
    	qr{enum\s+$Ident},
    	qr{${Ident}_t},
    	qr{${Ident}_handler},
    	qr{${Ident}_handler_fn},
    
    	@typeListMisordered,
    
    
    our $C90_int_types = qr{(?x:
    	long\s+long\s+int\s+(?:un)?signed|
    	long\s+long\s+(?:un)?signed\s+int|
    	long\s+long\s+(?:un)?signed|
    	(?:(?:un)?signed\s+)?long\s+long\s+int|
    	(?:(?:un)?signed\s+)?long\s+long|
    	int\s+long\s+long\s+(?:un)?signed|
    	int\s+(?:(?:un)?signed\s+)?long\s+long|
    
    	long\s+int\s+(?:un)?signed|
    	long\s+(?:un)?signed\s+int|
    	long\s+(?:un)?signed|
    	(?:(?:un)?signed\s+)?long\s+int|
    	(?:(?:un)?signed\s+)?long|
    	int\s+long\s+(?:un)?signed|
    	int\s+(?:(?:un)?signed\s+)?long|
    
    	int\s+(?:un)?signed|
    	(?:(?:un)?signed\s+)?int
    )};
    
    our @typeListFile = ();
    
    our @typeListWithAttr = (
    	@typeList,
    	qr{struct\s+$InitAttribute\s+$Ident},
    	qr{union\s+$InitAttribute\s+$Ident},
    );
    
    
    our @modifierList = (
    	qr{fastcall},
    );
    
    our @modifierListFile = ();
    
    our @mode_permission_funcs = (
    	["module_param", 3],
    	["module_param_(?:array|named|string)", 4],
    	["module_param_array_named", 5],
    	["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
    	["proc_create(?:_data|)", 2],
    	["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
    	["IIO_DEV_ATTR_[A-Z_]+", 1],
    	["SENSOR_(?:DEVICE_|)ATTR_2", 2],
    	["SENSOR_TEMPLATE(?:_2|)", 3],
    	["__ATTR", 2],
    );
    
    #Create a search pattern for all these functions to speed up a loop below
    our $mode_perms_search = "";
    foreach my $entry (@mode_permission_funcs) {
    	$mode_perms_search .= '|' if ($mode_perms_search ne "");
    	$mode_perms_search .= $entry->[0];
    }
    
    $mode_perms_search = "(?:${mode_perms_search})";
    
    
    our $mode_perms_world_writable = qr{
    	S_IWUGO		|
    	S_IWOTH		|
    	S_IRWXUGO	|
    	S_IALLUGO	|
    	0[0-7][0-7][2367]
    }x;
    
    our %mode_permission_string_types = (
    	"S_IRWXU" => 0700,
    	"S_IRUSR" => 0400,
    	"S_IWUSR" => 0200,
    	"S_IXUSR" => 0100,
    	"S_IRWXG" => 0070,
    	"S_IRGRP" => 0040,
    	"S_IWGRP" => 0020,
    	"S_IXGRP" => 0010,
    	"S_IRWXO" => 0007,
    	"S_IROTH" => 0004,
    	"S_IWOTH" => 0002,
    	"S_IXOTH" => 0001,
    	"S_IRWXUGO" => 0777,
    	"S_IRUGO" => 0444,
    	"S_IWUGO" => 0222,
    	"S_IXUGO" => 0111,
    );
    
    #Create a search pattern for all these strings to speed up a loop below
    our $mode_perms_string_search = "";
    foreach my $entry (keys %mode_permission_string_types) {
    	$mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
    	$mode_perms_string_search .= $entry;
    }
    
    our $single_mode_perms_string_search = "(?:${mode_perms_string_search})";
    our $multi_mode_perms_string_search = qr{
    	${single_mode_perms_string_search}
    	(?:\s*\|\s*${single_mode_perms_string_search})*
    }x;
    
    sub perms_to_octal {
    	my ($string) = @_;
    
    	return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/);
    
    	my $val = "";
    	my $oval = "";
    	my $to = 0;
    	my $curpos = 0;
    	my $lastpos = 0;
    	while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) {
    		$curpos = pos($string);
    		my $match = $2;
    		my $omatch = $1;
    		last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos));
    		$lastpos = $curpos;
    		$to |= $mode_permission_string_types{$match};
    		$val .= '\s*\|\s*' if ($val ne "");
    		$val .= $match;
    		$oval .= $omatch;
    	}
    	$oval =~ s/^\s*\|\s*//;
    	$oval =~ s/\s*\|\s*$//;
    	return sprintf("%04o", $to);
    }
    
    
    our $allowed_asm_includes = qr{(?x:
    	irq|
    
    )};
    # memory.h: ARM has a custom one
    
    
    # Load common spelling mistakes and build regular expression list.
    my $misspellings;
    my %spelling_fix;
    
    if (open(my $spelling, '<', $spelling_file)) {
    	while (<$spelling>) {
    		my $line = $_;
    
    		$line =~ s/\s*\n?$//g;
    		$line =~ s/^\s*//g;
    
    		next if ($line =~ m/^\s*#/);
    		next if ($line =~ m/^\s*$/);
    
    		my ($suspect, $fix) = split(/\|\|/, $line);
    
    		$spelling_fix{$suspect} = $fix;
    	}
    	close($spelling);
    } else {
    	warn "No typos will be found - file '$spelling_file': $!\n";
    }
    
    if ($codespell) {
    	if (open(my $spelling, '<', $codespellfile)) {
    		while (<$spelling>) {
    			my $line = $_;
    
    			$line =~ s/\s*\n?$//g;
    			$line =~ s/^\s*//g;
    
    			next if ($line =~ m/^\s*#/);
    			next if ($line =~ m/^\s*$/);
    			next if ($line =~ m/, disabled/i);
    
    			$line =~ s/,.*$//;
    
    			my ($suspect, $fix) = split(/->/, $line);
    
    			$spelling_fix{$suspect} = $fix;
    		}
    		close($spelling);
    	} else {
    		warn "No codespell typos will be found - file '$codespellfile': $!\n";
    	}
    }
    
    $misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;
    
    
    sub read_words {
    	my ($wordsRef, $file) = @_;
    
    	if (open(my $words, '<', $file)) {
    		while (<$words>) {
    			my $line = $_;
    
    			$line =~ s/\s*\n?$//g;
    			$line =~ s/^\s*//g;
    
    			next if ($line =~ m/^\s*#/);
    			next if ($line =~ m/^\s*$/);
    			if ($line =~ /\s/) {
    				print("$file: '$line' invalid - ignored\n");
    				next;
    			}
    
    			$$wordsRef .= '|' if ($$wordsRef ne "");
    			$$wordsRef .= $line;
    		}
    		close($file);
    		return 1;
    	}
    
    	return 0;
    }
    
    my $const_structs = "";
    read_words(\$const_structs, $conststructsfile)
        or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
    
    my $typeOtherTypedefs = "";
    if (length($typedefsfile)) {
    	read_words(\$typeOtherTypedefs, $typedefsfile)
    	    or warn "No additional types will be considered - file '$typedefsfile': $!\n";
    }
    $typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne "");
    
    	my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
    	my $all = "(?x:  \n" . join("|\n  ", (@typeList, @typeListFile)) . "\n)";
    	my $Misordered = "(?x:  \n" . join("|\n  ", @typeListMisordered) . "\n)";
    
    	my $allWithAttr = "(?x:  \n" . join("|\n  ", @typeListWithAttr) . "\n)";
    
    	$Modifier	= qr{(?:$Attribute|$Sparse|$mods)};
    
    	$BasicType	= qr{
    				(?:$typeTypedefs\b)|
    				(?:${all}\b)
    		}x;
    
    	$NonptrType	= qr{
    			(?:$Modifier\s+|const\s+)*
    			(?:
    
    				(?:typeof|__typeof__)\s*\([^\)]*\)|
    
    				(?:$typeTypedefs\b)|
    				(?:${all}\b)
    			)
    			(?:\s+$Modifier|\s+const)*
    		  }x;
    
    	$NonptrTypeMisordered	= qr{
    			(?:$Modifier\s+|const\s+)*
    			(?:
    				(?:${Misordered}\b)
    			)
    			(?:\s+$Modifier|\s+const)*
    		  }x;
    
    	$NonptrTypeWithAttr	= qr{
    			(?:$Modifier\s+|const\s+)*
    			(?:
    				(?:typeof|__typeof__)\s*\([^\)]*\)|
    				(?:$typeTypedefs\b)|
    				(?:${allWithAttr}\b)
    			)
    			(?:\s+$Modifier|\s+const)*
    		  }x;
    
    	$Type	= qr{
    			$NonptrType
    
    			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
    			(?:\s+$Inline|\s+$Modifier)*
    		  }x;
    	$TypeMisordered	= qr{
    			$NonptrTypeMisordered
    			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
    
    			(?:\s+$Inline|\s+$Modifier)*
    		  }x;
    
    	$Declare	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
    	$DeclareMisordered	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered};
    
    }
    build_types();
    
    our $Typecast	= qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
    
    
    # Using $balanced_parens, $LvalOrFunc, or $FuncArg
    # requires at least perl version v5.10.0
    # Any use must be runtime checked with $^V
    
    our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
    
    our $LvalOrFunc	= qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
    our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
    
    our $declaration_macros = qr{(?x:
    	(?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
    	(?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
    
    	(?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(|
    	(?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(
    
    
    sub deparenthesize {
    	my ($string) = @_;
    	return "" if (!defined($string));
    
    
    	while ($string =~ /^\s*\(.*\)\s*$/) {
    		$string =~ s@^\s*\(\s*@@;
    		$string =~ s@\s*\)\s*$@@;
    	}
    
    
    	$string =~ s@\s+@ @g;
    
    sub seed_camelcase_file {
    	my ($file) = @_;
    
    	return if (!(-f $file));
    
    	local $/;
    
    	open(my $include_file, '<', "$file")
    	    or warn "$P: Can't read '$file' $!\n";
    	my $text = <$include_file>;
    	close($include_file);
    
    	my @lines = split('\n', $text);
    
    	foreach my $line (@lines) {
    		next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/);
    		if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) {
    			$camelcase{$1} = 1;
    		} elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) {
    			$camelcase{$1} = 1;
    		} elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) {
    			$camelcase{$1} = 1;
    		}
    	}
    }
    
    
    sub is_maintained_obsolete {
    	my ($filename) = @_;
    
    	return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
    
    	my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
    
    	return $status =~ /obsolete/i;
    }
    
    
    my $camelcase_seeded = 0;
    sub seed_camelcase_includes {
    	return if ($camelcase_seeded);
    
    	my $files;
    	my $camelcase_cache = "";
    	my @include_files = ();
    
    	$camelcase_seeded = 1;
    
    	if (-e ".git") {
    		my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`;
    		chomp $git_last_include_commit;
    		$camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
    	} else {
    		my $last_mod_date = 0;
    		$files = `find $root/include -name "*.h"`;
    		@include_files = split('\n', $files);
    		foreach my $file (@include_files) {
    			my $date = POSIX::strftime("%Y%m%d%H%M",
    						   localtime((stat $file)[9]));
    			$last_mod_date = $date if ($last_mod_date < $date);
    		}
    		$camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date";
    	}
    
    	if ($camelcase_cache ne "" && -f $camelcase_cache) {
    		open(my $camelcase_file, '<', "$camelcase_cache")
    		    or warn "$P: Can't read '$camelcase_cache' $!\n";
    		while (<$camelcase_file>) {
    			chomp;
    			$camelcase{$_} = 1;
    		}
    		close($camelcase_file);
    
    		return;
    	}
    
    	if (-e ".git") {
    		$files = `git ls-files "include/*.h"`;
    		@include_files = split('\n', $files);
    	}
    
    	foreach my $file (@include_files) {
    		seed_camelcase_file($file);
    	}
    
    	if ($camelcase_cache ne "") {
    		unlink glob ".checkpatch-camelcase.*";
    		open(my $camelcase_file, '>', "$camelcase_cache")
    		    or warn "$P: Can't write '$camelcase_cache' $!\n";
    		foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) {
    			print $camelcase_file ("$_\n");
    		}
    		close($camelcase_file);
    	}
    }
    
    
    sub git_commit_info {
    	my ($commit, $id, $desc) = @_;
    
    	return ($id, $desc) if ((which("git") eq "") || !(-e ".git"));
    
    	my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`;
    	$output =~ s/^\s*//gm;
    	my @lines = split("\n", $output);
    
    	return ($id, $desc) if ($#lines < 0);
    
    	if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) {
    # Maybe one day convert this block of bash into something that returns
    # all matching commit ids, but it's very slow...
    #
    #		echo "checking commits $1..."
    #		git rev-list --remotes | grep -i "^$1" |
    #		while read line ; do
    #		    git log --format='%H %s' -1 $line |
    #		    echo "commit $(cut -c 1-12,41-)"
    #		done
    	} elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) {
    		$id = undef;
    	} else {
    		$id = substr($lines[0], 0, 12);
    		$desc = substr($lines[0], 41);
    	}
    
    	return ($id, $desc);
    }
    
    
    $chk_signoff = 0 if ($file);
    
    my @rawlines = ();
    my @lines = ();
    
    my @fixed_inserted = ();
    my @fixed_deleted = ();
    
    my $fixlinenr = -1;
    
    
    # If input is git commits, extract all commits from the commit expressions.
    # For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
    die "$P: No git repository found\n" if ($git && !-e ".git");
    
    if ($git) {
    	my @commits = ();
    	foreach my $commit_expr (@ARGV) {
    		my $git_range;
    		if ($commit_expr =~ m/^(.*)-(\d+)$/) {
    			$git_range = "-$2 $1";
    		} elsif ($commit_expr =~ m/\.\./) {
    			$git_range = "$commit_expr";
    		} else {
    			$git_range = "-1 $commit_expr";
    		}
    		my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
    		foreach my $line (split(/\n/, $lines)) {
    			$line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
    			next if (!defined($1) || !defined($2));
    			my $sha1 = $1;
    			my $subject = $2;
    			unshift(@commits, $sha1);
    			$git_commits{$sha1} = $subject;
    		}
    	}
    	die "$P: no git commits after extraction!\n" if (@commits == 0);
    	@ARGV = @commits;
    }
    
    my $vname;
    
    for my $filename (@ARGV) {
    	my $FILE;
    
    	if ($git) {
    		open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
    			die "$P: $filename: git format-patch failed - $!\n";
    	} elsif ($file) {
    
    		open($FILE, '-|', "diff -u /dev/null $filename") ||
    			die "$P: $filename: diff failed - $!\n";
    	} elsif ($filename eq '-') {
    		open($FILE, '<&STDIN');
    	} else {
    		open($FILE, '<', "$filename") ||
    			die "$P: $filename: open failed - $!\n";
    	}
    	if ($filename eq '-') {
    		$vname = 'Your patch';
    
    	} elsif ($git) {
    		$vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
    
    	} else {
    		$vname = $filename;
    	}
    	while (<$FILE>) {
    		chomp;
    		push(@rawlines, $_);
    	}