#!/usr/bin/perl -w
# (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>
# (c) 2014 Gluster Community <gluster-devel@gluster.org>
# Licensed under the terms of the GNU GPL License version 2
use strict;
use POSIX;
my $P = $0;
$P =~ s@.*/@@g;
my $V = '0.32.1';
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 $file = 0;
my $check = 0;
my $check_orig = 0;
my $summary = 1;
my $mailback = 0;
my $summary_file = 0;
my $show_types = 0;
my $fix = 0;
my $fix_inplace = 0;
my $root;
my %debug;
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 $gerrit_url = $ENV{GERRIT_URL};
sub help {
my ($exitcode) = @_;
print << "EOM";
Usage: $P [OPTION]... [FILE]...
Version: $V
Options:
-q, --quiet quiet
--patch treat FILE as patchfile (default)
--emacs emacs compile window format
--gerrit-url=STRING URL the patch was reviewed at
--terse one line per report
-f, --file treat FILE as regular source file
--subjective, --strict enable more subjective tests
--types TYPE(,TYPE2...) show only these comma separated message types
--ignore TYPE(,TYPE2...) ignore various comma separated message types
--max-line-length=n set the maximum line length, if exceeded, warn
--show-types show the message "types" in the output
--root=PATH PATH to the glusterfs 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.
-h, --help, --version display this help and exit
When FILE is - read standard input.
EOM
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;
}
GetOptions(
'q|quiet+' => \$quiet,
'patch!' => \$chk_patch,
'emacs!' => \$emacs,
'gerrit-url=s' => \$gerrit_url,
'terse!' => \$terse,
'f|file!' => \$file,
'subjective!' => \$check,
'strict!' => \$check,
'ignore=s' => \@ignore,
'types=s' => \@use,
'show-types!' => \$show_types,
'max-line-length=i' => \$max_line_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,
'h|help' => \$help,
'version' => \$help
) or help(1);
help(0) if ($help);
$fix = 1 if ($fix_inplace);
$check_orig = $check;
my $exit = 0;
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 ($#ARGV < 0) {
print "$P: no input files\n";
exit(1);
}
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 ($quiet == 0 && keys %$hashRef) {
print "NOTE: $prefix message types:";
foreach my $word (sort keys %$hashRef) {
print " $word";
}
print "\n\n";
}
}
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_glusterfs_tree($root)) {
die "$P: $root: --root does not point at a valid tree\n";
}
} else {
if (top_of_glusterfs_tree('.')) {
$root = '.';
} elsif ($0 =~ m@(.*)/extras/[^/]*$@ &&
top_of_glusterfs_tree($1)) {
$root = $1;
}
}
if (!defined $root) {
print "Must be run from the top-level dir. of a GlusterFS 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|
__rcu
}x;
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|
__bitwise__|
__packed__|
__packed2__|
__naked|
__maybe_unused|
__always_unused|
__noreturn|
__used|
__cold|
__noclone|
__deprecated|
__read_mostly|
__kprobes|
$InitAttribute|
____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 $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
}x;
our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
our $NonptrType;
our $NonptrTypeWithAttr;
our $Type;
our $Declare;
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 $typeTypedefs = qr{(?x:
(?:__)?(?:u|s|be|le)(?:8|16|32|64)|
atomic_t
)};
our $logFunctions = qr{(?x:
printk(?:_ratelimited|_once|)|
(?:[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:|
Reviewed-on:|
Reported-by:|
Original-author:|
Original-Author:|
Original-Authors:|
Suggested-by:|
To:|
Cc:
)};
our $url_tags = qr{http:|https:};
our @typeList = (
qr{void},
qr{(?:unsigned\s+)?char},
qr{(?:unsigned\s+)?short},
qr{(?:unsigned\s+)?int},
qr{(?:unsigned\s+)?long},
qr{(?:unsigned\s+)?long\s+int},
qr{(?:unsigned\s+)?long\s+long},
qr{(?:unsigned\s+)?long\s+long\s+int},
qr{unsigned},
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},
);
our @typeListWithAttr = (
@typeList,
qr{struct\s+$InitAttribute\s+$Ident},
qr{union\s+$InitAttribute\s+$Ident},
);
our @modifierList = (
qr{fastcall},
);
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)_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];
}
our $declaration_macros = qr{(?x:
(?:$Storage\s+)?(?:DECLARE|DEFINE)_[A-Z]+\s*\(|
(?:$Storage\s+)?LIST_HEAD\s*\(
)};
our $allowed_asm_includes = qr{(?x:
irq|
memory
)};
# memory.h: ARM has a custom one
sub build_types {
my $mods = "(?x:\n" . join("|\n ", @modifierList) . "\n)";
my $all = "(?x:\n" . join("|\n ", @typeList) . "\n)";
|