aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.modpost12
-rwxr-xr-xscripts/checkpatch.pl106
-rwxr-xr-xscripts/get_maintainer.pl421
3 files changed, 416 insertions, 123 deletions
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index f4053dc7b5d6..8f14c81abbc7 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -13,7 +13,6 @@
13# 2) modpost is then used to 13# 2) modpost is then used to
14# 3) create one <module>.mod.c file pr. module 14# 3) create one <module>.mod.c file pr. module
15# 4) create one Module.symvers file with CRC for all exported symbols 15# 4) create one Module.symvers file with CRC for all exported symbols
16# 4a) [CONFIG_MARKERS] create one Module.markers file listing defined markers
17# 5) compile all <module>.mod.c files 16# 5) compile all <module>.mod.c files
18# 6) final link of the module to a <module.ko> file 17# 6) final link of the module to a <module.ko> file
19 18
@@ -59,10 +58,6 @@ include scripts/Makefile.lib
59 58
60kernelsymfile := $(objtree)/Module.symvers 59kernelsymfile := $(objtree)/Module.symvers
61modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers 60modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers
62kernelmarkersfile := $(objtree)/Module.markers
63modulemarkersfile := $(firstword $(KBUILD_EXTMOD))/Module.markers
64
65markersfile = $(if $(KBUILD_EXTMOD),$(modulemarkersfile),$(kernelmarkersfile))
66 61
67# Step 1), find all modules listed in $(MODVERDIR)/ 62# Step 1), find all modules listed in $(MODVERDIR)/
68__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) 63__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod)))
@@ -85,8 +80,6 @@ modpost = scripts/mod/modpost \
85 $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \ 80 $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \
86 $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ 81 $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \
87 $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ 82 $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \
88 $(if $(CONFIG_MARKERS),-K $(kernelmarkersfile)) \
89 $(if $(CONFIG_MARKERS),-M $(markersfile)) \
90 $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ 83 $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \
91 $(if $(cross_build),-c) 84 $(if $(cross_build),-c)
92 85
@@ -101,17 +94,12 @@ quiet_cmd_kernel-mod = MODPOST $@
101 cmd_kernel-mod = $(modpost) $@ 94 cmd_kernel-mod = $(modpost) $@
102 95
103vmlinux.o: FORCE 96vmlinux.o: FORCE
104 @rm -fr $(kernelmarkersfile)
105 $(call cmd,kernel-mod) 97 $(call cmd,kernel-mod)
106 98
107# Declare generated files as targets for modpost 99# Declare generated files as targets for modpost
108$(symverfile): __modpost ; 100$(symverfile): __modpost ;
109$(modules:.ko=.mod.c): __modpost ; 101$(modules:.ko=.mod.c): __modpost ;
110 102
111ifdef CONFIG_MARKERS
112$(markersfile): __modpost ;
113endif
114
115 103
116# Step 5), compile all *.mod.c files 104# Step 5), compile all *.mod.c files
117 105
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 2d5ece798c4c..87bbb8bce9bf 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -10,7 +10,7 @@ use strict;
10my $P = $0; 10my $P = $0;
11$P =~ s@.*/@@g; 11$P =~ s@.*/@@g;
12 12
13my $V = '0.28'; 13my $V = '0.29';
14 14
15use Getopt::Long qw(:config no_auto_abbrev); 15use Getopt::Long qw(:config no_auto_abbrev);
16 16
@@ -28,6 +28,41 @@ my $mailback = 0;
28my $summary_file = 0; 28my $summary_file = 0;
29my $root; 29my $root;
30my %debug; 30my %debug;
31my $help = 0;
32
33sub help {
34 my ($exitcode) = @_;
35
36 print << "EOM";
37Usage: $P [OPTION]... [FILE]...
38Version: $V
39
40Options:
41 -q, --quiet quiet
42 --no-tree run without a kernel tree
43 --no-signoff do not check for 'Signed-off-by' line
44 --patch treat FILE as patchfile (default)
45 --emacs emacs compile window format
46 --terse one line per report
47 -f, --file treat FILE as regular source file
48 --subjective, --strict enable more subjective tests
49 --root=PATH PATH to the kernel tree root
50 --no-summary suppress the per-file summary
51 --mailback only produce a report in case of warnings/errors
52 --summary-file include the filename in summary
53 --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of
54 'values', 'possible', 'type', and 'attr' (default
55 is all off)
56 --test-only=WORD report only warnings/errors containing WORD
57 literally
58 -h, --help, --version display this help and exit
59
60When FILE is - read standard input.
61EOM
62
63 exit($exitcode);
64}
65
31GetOptions( 66GetOptions(
32 'q|quiet+' => \$quiet, 67 'q|quiet+' => \$quiet,
33 'tree!' => \$tree, 68 'tree!' => \$tree,
@@ -35,7 +70,7 @@ GetOptions(
35 'patch!' => \$chk_patch, 70 'patch!' => \$chk_patch,
36 'emacs!' => \$emacs, 71 'emacs!' => \$emacs,
37 'terse!' => \$terse, 72 'terse!' => \$terse,
38 'file!' => \$file, 73 'f|file!' => \$file,
39 'subjective!' => \$check, 74 'subjective!' => \$check,
40 'strict!' => \$check, 75 'strict!' => \$check,
41 'root=s' => \$root, 76 'root=s' => \$root,
@@ -45,22 +80,16 @@ GetOptions(
45 80
46 'debug=s' => \%debug, 81 'debug=s' => \%debug,
47 'test-only=s' => \$tst_only, 82 'test-only=s' => \$tst_only,
48) or exit; 83 'h|help' => \$help,
84 'version' => \$help
85) or help(1);
86
87help(0) if ($help);
49 88
50my $exit = 0; 89my $exit = 0;
51 90
52if ($#ARGV < 0) { 91if ($#ARGV < 0) {
53 print "usage: $P [options] patchfile\n"; 92 print "$P: no input files\n";
54 print "version: $V\n";
55 print "options: -q => quiet\n";
56 print " --no-tree => run without a kernel tree\n";
57 print " --terse => one line per report\n";
58 print " --emacs => emacs compile window format\n";
59 print " --file => check a source file\n";
60 print " --strict => enable more subjective tests\n";
61 print " --root => path to the kernel tree root\n";
62 print " --no-summary => suppress the per-file summary\n";
63 print " --summary-file => include the filename in summary\n";
64 exit(1); 93 exit(1);
65} 94}
66 95
@@ -153,7 +182,7 @@ our $UTF8 = qr {
153}x; 182}x;
154 183
155our $typeTypedefs = qr{(?x: 184our $typeTypedefs = qr{(?x:
156 (?:__)?(?:u|s|be|le)(?:\d|\d\d)| 185 (?:__)?(?:u|s|be|le)(?:8|16|32|64)|
157 atomic_t 186 atomic_t
158)}; 187)};
159 188
@@ -356,6 +385,13 @@ sub sanitise_line {
356 $off++; 385 $off++;
357 next; 386 next;
358 } 387 }
388 if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
389 $sanitise_quote = '//';
390
391 substr($res, $off, 2, $sanitise_quote);
392 $off++;
393 next;
394 }
359 395
360 # A \ in a string means ignore the next character. 396 # A \ in a string means ignore the next character.
361 if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && 397 if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
@@ -379,6 +415,8 @@ sub sanitise_line {
379 #print "c<$c> SQ<$sanitise_quote>\n"; 415 #print "c<$c> SQ<$sanitise_quote>\n";
380 if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { 416 if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
381 substr($res, $off, 1, $;); 417 substr($res, $off, 1, $;);
418 } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
419 substr($res, $off, 1, $;);
382 } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { 420 } elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
383 substr($res, $off, 1, 'X'); 421 substr($res, $off, 1, 'X');
384 } else { 422 } else {
@@ -386,6 +424,10 @@ sub sanitise_line {
386 } 424 }
387 } 425 }
388 426
427 if ($sanitise_quote eq '//') {
428 $sanitise_quote = '';
429 }
430
389 # The pathname on a #include may be surrounded by '<' and '>'. 431 # The pathname on a #include may be surrounded by '<' and '>'.
390 if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { 432 if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
391 my $clean = 'X' x length($1); 433 my $clean = 'X' x length($1);
@@ -1336,6 +1378,18 @@ sub process {
1336 WARN("adding a line without newline at end of file\n" . $herecurr); 1378 WARN("adding a line without newline at end of file\n" . $herecurr);
1337 } 1379 }
1338 1380
1381# Blackfin: use hi/lo macros
1382 if ($realfile =~ m@arch/blackfin/.*\.S$@) {
1383 if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) {
1384 my $herevet = "$here\n" . cat_vet($line) . "\n";
1385 ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet);
1386 }
1387 if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) {
1388 my $herevet = "$here\n" . cat_vet($line) . "\n";
1389 ERROR("use the HI() macro, not (... >> 16)\n" . $herevet);
1390 }
1391 }
1392
1339# check we are in a valid source file C or perl if not then ignore this hunk 1393# check we are in a valid source file C or perl if not then ignore this hunk
1340 next if ($realfile !~ /\.(h|c|pl)$/); 1394 next if ($realfile !~ /\.(h|c|pl)$/);
1341 1395
@@ -1355,6 +1409,16 @@ sub process {
1355 WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); 1409 WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr);
1356 } 1410 }
1357 1411
1412# Blackfin: don't use __builtin_bfin_[cs]sync
1413 if ($line =~ /__builtin_bfin_csync/) {
1414 my $herevet = "$here\n" . cat_vet($line) . "\n";
1415 ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet);
1416 }
1417 if ($line =~ /__builtin_bfin_ssync/) {
1418 my $herevet = "$here\n" . cat_vet($line) . "\n";
1419 ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet);
1420 }
1421
1358# Check for potential 'bare' types 1422# Check for potential 'bare' types
1359 my ($stat, $cond, $line_nr_next, $remain_next, $off_next); 1423 my ($stat, $cond, $line_nr_next, $remain_next, $off_next);
1360 if ($realcnt && $line =~ /.\s*\S/) { 1424 if ($realcnt && $line =~ /.\s*\S/) {
@@ -1372,6 +1436,8 @@ sub process {
1372 # Ignore functions being called 1436 # Ignore functions being called
1373 } elsif ($s =~ /^.\s*$Ident\s*\(/s) { 1437 } elsif ($s =~ /^.\s*$Ident\s*\(/s) {
1374 1438
1439 } elsif ($s =~ /^.\s*else\b/s) {
1440
1375 # declarations always start with types 1441 # declarations always start with types
1376 } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { 1442 } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
1377 my $type = $1; 1443 my $type = $1;
@@ -1532,8 +1598,9 @@ sub process {
1532 $s =~ /^\s*#\s*?/ || 1598 $s =~ /^\s*#\s*?/ ||
1533 $s =~ /^\s*$Ident\s*:/) { 1599 $s =~ /^\s*$Ident\s*:/) {
1534 $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; 1600 $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
1535 $s =~ s/^.*?\n//; 1601 if ($s =~ s/^.*?\n//) {
1536 $cond_lines++; 1602 $cond_lines++;
1603 }
1537 } 1604 }
1538 } 1605 }
1539 1606
@@ -1891,7 +1958,7 @@ sub process {
1891 # A unary '*' may be const 1958 # A unary '*' may be const
1892 1959
1893 } elsif ($ctx =~ /.xW/) { 1960 } elsif ($ctx =~ /.xW/) {
1894 ERROR("Aspace prohibited after that '$op' $at\n" . $hereptr); 1961 ERROR("space prohibited after that '$op' $at\n" . $hereptr);
1895 } 1962 }
1896 1963
1897 # unary ++ and unary -- are allowed no space on one side. 1964 # unary ++ and unary -- are allowed no space on one side.
@@ -2243,7 +2310,8 @@ sub process {
2243 DECLARE_PER_CPU| 2310 DECLARE_PER_CPU|
2244 DEFINE_PER_CPU| 2311 DEFINE_PER_CPU|
2245 __typeof__\(| 2312 __typeof__\(|
2246 \.$Ident\s*=\s* 2313 \.$Ident\s*=\s*|
2314 ^\"|\"$
2247 }x; 2315 }x;
2248 #print "REST<$rest> dstat<$dstat>\n"; 2316 #print "REST<$rest> dstat<$dstat>\n";
2249 if ($rest ne '') { 2317 if ($rest ne '') {
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index 278a45bd45a5..cdb44b63342e 100755
--- a/scripts/get_maintainer.pl
+++ b/scripts/get_maintainer.pl
@@ -13,7 +13,7 @@
13use strict; 13use strict;
14 14
15my $P = $0; 15my $P = $0;
16my $V = '0.17'; 16my $V = '0.20';
17 17
18use Getopt::Long qw(:config no_auto_abbrev); 18use Getopt::Long qw(:config no_auto_abbrev);
19 19
@@ -29,6 +29,8 @@ my $email_git_min_signatures = 1;
29my $email_git_max_maintainers = 5; 29my $email_git_max_maintainers = 5;
30my $email_git_min_percent = 5; 30my $email_git_min_percent = 5;
31my $email_git_since = "1-year-ago"; 31my $email_git_since = "1-year-ago";
32my $email_git_blame = 0;
33my $email_remove_duplicates = 1;
32my $output_multiline = 1; 34my $output_multiline = 1;
33my $output_separator = ", "; 35my $output_separator = ", ";
34my $scm = 0; 36my $scm = 0;
@@ -36,6 +38,7 @@ my $web = 0;
36my $subsystem = 0; 38my $subsystem = 0;
37my $status = 0; 39my $status = 0;
38my $from_filename = 0; 40my $from_filename = 0;
41my $pattern_depth = 0;
39my $version = 0; 42my $version = 0;
40my $help = 0; 43my $help = 0;
41 44
@@ -68,6 +71,8 @@ if (!GetOptions(
68 'git-max-maintainers=i' => \$email_git_max_maintainers, 71 'git-max-maintainers=i' => \$email_git_max_maintainers,
69 'git-min-percent=i' => \$email_git_min_percent, 72 'git-min-percent=i' => \$email_git_min_percent,
70 'git-since=s' => \$email_git_since, 73 'git-since=s' => \$email_git_since,
74 'git-blame!' => \$email_git_blame,
75 'remove-duplicates!' => \$email_remove_duplicates,
71 'm!' => \$email_maintainer, 76 'm!' => \$email_maintainer,
72 'n!' => \$email_usename, 77 'n!' => \$email_usename,
73 'l!' => \$email_list, 78 'l!' => \$email_list,
@@ -78,6 +83,7 @@ if (!GetOptions(
78 'status!' => \$status, 83 'status!' => \$status,
79 'scm!' => \$scm, 84 'scm!' => \$scm,
80 'web!' => \$web, 85 'web!' => \$web,
86 'pattern-depth=i' => \$pattern_depth,
81 'f|file' => \$from_filename, 87 'f|file' => \$from_filename,
82 'v|version' => \$version, 88 'v|version' => \$version,
83 'h|help' => \$help, 89 'h|help' => \$help,
@@ -101,14 +107,19 @@ if ($#ARGV < 0) {
101 die "$P: argument missing: patchfile or -f file please\n"; 107 die "$P: argument missing: patchfile or -f file please\n";
102} 108}
103 109
110if ($output_separator ne ", ") {
111 $output_multiline = 0;
112}
113
104my $selections = $email + $scm + $status + $subsystem + $web; 114my $selections = $email + $scm + $status + $subsystem + $web;
105if ($selections == 0) { 115if ($selections == 0) {
106 usage(); 116 usage();
107 die "$P: Missing required option: email, scm, status, subsystem or web\n"; 117 die "$P: Missing required option: email, scm, status, subsystem or web\n";
108} 118}
109 119
110if ($email && ($email_maintainer + $email_list + $email_subscriber_list 120if ($email &&
111 + $email_git + $email_git_penguin_chiefs) == 0) { 121 ($email_maintainer + $email_list + $email_subscriber_list +
122 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
112 usage(); 123 usage();
113 die "$P: Please select at least 1 email option\n"; 124 die "$P: Please select at least 1 email option\n";
114} 125}
@@ -147,9 +158,36 @@ while (<MAINT>) {
147} 158}
148close(MAINT); 159close(MAINT);
149 160
161my %mailmap;
162
163if ($email_remove_duplicates) {
164 open(MAILMAP, "<${lk_path}.mailmap") || warn "$P: Can't open .mailmap\n";
165 while (<MAILMAP>) {
166 my $line = $_;
167
168 next if ($line =~ m/^\s*#/);
169 next if ($line =~ m/^\s*$/);
170
171 my ($name, $address) = parse_email($line);
172 $line = format_email($name, $address);
173
174 next if ($line =~ m/^\s*$/);
175
176 if (exists($mailmap{$name})) {
177 my $obj = $mailmap{$name};
178 push(@$obj, $address);
179 } else {
180 my @arr = ($address);
181 $mailmap{$name} = \@arr;
182 }
183 }
184 close(MAILMAP);
185}
186
150## use the filenames on the command line or find the filenames in the patchfiles 187## use the filenames on the command line or find the filenames in the patchfiles
151 188
152my @files = (); 189my @files = ();
190my @range = ();
153 191
154foreach my $file (@ARGV) { 192foreach my $file (@ARGV) {
155 ##if $file is a directory and it lacks a trailing slash, add one 193 ##if $file is a directory and it lacks a trailing slash, add one
@@ -162,13 +200,19 @@ foreach my $file (@ARGV) {
162 push(@files, $file); 200 push(@files, $file);
163 } else { 201 } else {
164 my $file_cnt = @files; 202 my $file_cnt = @files;
203 my $lastfile;
165 open(PATCH, "<$file") or die "$P: Can't open ${file}\n"; 204 open(PATCH, "<$file") or die "$P: Can't open ${file}\n";
166 while (<PATCH>) { 205 while (<PATCH>) {
167 if (m/^\+\+\+\s+(\S+)/) { 206 if (m/^\+\+\+\s+(\S+)/) {
168 my $filename = $1; 207 my $filename = $1;
169 $filename =~ s@^[^/]*/@@; 208 $filename =~ s@^[^/]*/@@;
170 $filename =~ s@\n@@; 209 $filename =~ s@\n@@;
210 $lastfile = $filename;
171 push(@files, $filename); 211 push(@files, $filename);
212 } elsif (m/^\@\@ -(\d+),(\d+)/) {
213 if ($email_git_blame) {
214 push(@range, "$lastfile:$1:$2");
215 }
172 } 216 }
173 } 217 }
174 close(PATCH); 218 close(PATCH);
@@ -201,6 +245,7 @@ foreach my $file (@files) {
201 if ($type eq 'X') { 245 if ($type eq 'X') {
202 if (file_match_pattern($file, $value)) { 246 if (file_match_pattern($file, $value)) {
203 $exclude = 1; 247 $exclude = 1;
248 last;
204 } 249 }
205 } 250 }
206 } 251 }
@@ -208,35 +253,45 @@ foreach my $file (@files) {
208 253
209 if (!$exclude) { 254 if (!$exclude) {
210 my $tvi = 0; 255 my $tvi = 0;
256 my %hash;
211 foreach my $line (@typevalue) { 257 foreach my $line (@typevalue) {
212 if ($line =~ m/^(\C):\s*(.*)/) { 258 if ($line =~ m/^(\C):\s*(.*)/) {
213 my $type = $1; 259 my $type = $1;
214 my $value = $2; 260 my $value = $2;
215 if ($type eq 'F') { 261 if ($type eq 'F') {
216 if (file_match_pattern($file, $value)) { 262 if (file_match_pattern($file, $value)) {
217 add_categories($tvi); 263 my $value_pd = ($value =~ tr@/@@);
264 my $file_pd = ($file =~ tr@/@@);
265 $value_pd++ if (substr($value,-1,1) ne "/");
266 if ($pattern_depth == 0 ||
267 (($file_pd - $value_pd) < $pattern_depth)) {
268 $hash{$tvi} = $value_pd;
269 }
218 } 270 }
219 } 271 }
220 } 272 }
221 $tvi++; 273 $tvi++;
222 } 274 }
275 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
276 add_categories($line);
277 }
223 } 278 }
224 279
225 if ($email && $email_git) { 280 if ($email && $email_git) {
226 recent_git_signoffs($file); 281 recent_git_signoffs($file);
227 } 282 }
228 283
284 if ($email && $email_git_blame) {
285 git_assign_blame($file);
286 }
229} 287}
230 288
231if ($email) { 289if ($email) {
232 foreach my $chief (@penguin_chief) { 290 foreach my $chief (@penguin_chief) {
233 if ($chief =~ m/^(.*):(.*)/) { 291 if ($chief =~ m/^(.*):(.*)/) {
234 my $email_address; 292 my $email_address;
235 if ($email_usename) { 293
236 $email_address = format_email($1, $2); 294 $email_address = format_email($1, $2);
237 } else {
238 $email_address = $2;
239 }
240 if ($email_git_penguin_chiefs) { 295 if ($email_git_penguin_chiefs) {
241 push(@email_to, $email_address); 296 push(@email_to, $email_address);
242 } else { 297 } else {
@@ -258,22 +313,22 @@ if ($email || $email_list) {
258} 313}
259 314
260if ($scm) { 315if ($scm) {
261 @scm = sort_and_uniq(@scm); 316 @scm = uniq(@scm);
262 output(@scm); 317 output(@scm);
263} 318}
264 319
265if ($status) { 320if ($status) {
266 @status = sort_and_uniq(@status); 321 @status = uniq(@status);
267 output(@status); 322 output(@status);
268} 323}
269 324
270if ($subsystem) { 325if ($subsystem) {
271 @subsystem = sort_and_uniq(@subsystem); 326 @subsystem = uniq(@subsystem);
272 output(@subsystem); 327 output(@subsystem);
273} 328}
274 329
275if ($web) { 330if ($web) {
276 @web = sort_and_uniq(@web); 331 @web = uniq(@web);
277 output(@web); 332 output(@web);
278} 333}
279 334
@@ -311,10 +366,12 @@ MAINTAINER field selection options:
311 --git-max-maintainers => maximum maintainers to add (default: 5) 366 --git-max-maintainers => maximum maintainers to add (default: 5)
312 --git-min-percent => minimum percentage of commits required (default: 5) 367 --git-min-percent => minimum percentage of commits required (default: 5)
313 --git-since => git history to use (default: 1-year-ago) 368 --git-since => git history to use (default: 1-year-ago)
369 --git-blame => use git blame to find modified commits for patch or file
314 --m => include maintainer(s) if any 370 --m => include maintainer(s) if any
315 --n => include name 'Full Name <addr\@domain.tld>' 371 --n => include name 'Full Name <addr\@domain.tld>'
316 --l => include list(s) if any 372 --l => include list(s) if any
317 --s => include subscriber only list(s) if any 373 --s => include subscriber only list(s) if any
374 --remove-duplicates => minimize duplicate email names/addresses
318 --scm => print SCM tree(s) if any 375 --scm => print SCM tree(s) if any
319 --status => print status if any 376 --status => print status if any
320 --subsystem => print subsystem name if any 377 --subsystem => print subsystem name if any
@@ -322,24 +379,28 @@ MAINTAINER field selection options:
322 379
323Output type options: 380Output type options:
324 --separator [, ] => separator for multiple entries on 1 line 381 --separator [, ] => separator for multiple entries on 1 line
382 using --separator also sets --nomultiline if --separator is not [, ]
325 --multiline => print 1 entry per line 383 --multiline => print 1 entry per line
326 384
327Default options:
328 [--email --git --m --n --l --multiline]
329
330Other options: 385Other options:
386 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
331 --version => show version 387 --version => show version
332 --help => show this help information 388 --help => show this help information
333 389
390Default options:
391 [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates]
392
334Notes: 393Notes:
335 Using "-f directory" may give unexpected results: 394 Using "-f directory" may give unexpected results:
336 395 Used with "--git", git signators for _all_ files in and below
337 Used with "--git", git signators for _all_ files in and below 396 directory are examined as git recurses directories.
338 directory are examined as git recurses directories. 397 Any specified X: (exclude) pattern matches are _not_ ignored.
339 Any specified X: (exclude) pattern matches are _not_ ignored. 398 Used with "--nogit", directory is used as a pattern match,
340 Used with "--nogit", directory is used as a pattern match, 399 no individual file within the directory or subdirectory
341 no individual file within the directory or subdirectory 400 is matched.
342 is matched. 401 Used with "--git-blame", does not iterate all files in directory
402 Using "--git-blame" is slow and may add old committers and authors
403 that are no longer active maintainers to the output.
343EOT 404EOT
344} 405}
345 406
@@ -370,30 +431,100 @@ sub top_of_kernel_tree {
370 return 0; 431 return 0;
371} 432}
372 433
373sub format_email { 434sub parse_email {
374 my ($name, $email) = @_; 435 my ($formatted_email) = @_;
436
437 my $name = "";
438 my $address = "";
439
440 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
441 $name = $1;
442 $address = $2;
443 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
444 $address = $1;
445 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
446 $address = $1;
447 }
375 448
376 $name =~ s/^\s+|\s+$//g; 449 $name =~ s/^\s+|\s+$//g;
377 $name =~ s/^\"|\"$//g; 450 $name =~ s/^\"|\"$//g;
378 $email =~ s/^\s+|\s+$//g; 451 $address =~ s/^\s+|\s+$//g;
452
453 if ($name =~ /[^a-z0-9 \.\-]/i) { ##has "must quote" chars
454 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
455 $name = "\"$name\"";
456 }
457
458 return ($name, $address);
459}
460
461sub format_email {
462 my ($name, $address) = @_;
379 463
380 my $formatted_email = ""; 464 my $formatted_email;
465
466 $name =~ s/^\s+|\s+$//g;
467 $name =~ s/^\"|\"$//g;
468 $address =~ s/^\s+|\s+$//g;
381 469
382 if ($name =~ /[^a-z0-9 \.\-]/i) { ##has "must quote" chars 470 if ($name =~ /[^a-z0-9 \.\-]/i) { ##has "must quote" chars
383 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes 471 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
384 $formatted_email = "\"${name}\"\ \<${email}\>"; 472 $name = "\"$name\"";
473 }
474
475 if ($email_usename) {
476 if ("$name" eq "") {
477 $formatted_email = "$address";
478 } else {
479 $formatted_email = "$name <${address}>";
480 }
385 } else { 481 } else {
386 $formatted_email = "${name} \<${email}\>"; 482 $formatted_email = $address;
387 } 483 }
484
388 return $formatted_email; 485 return $formatted_email;
389} 486}
390 487
391sub add_categories { 488sub find_starting_index {
489
392 my ($index) = @_; 490 my ($index) = @_;
393 491
394 $index = $index - 1; 492 while ($index > 0) {
395 while ($index >= 0) {
396 my $tv = $typevalue[$index]; 493 my $tv = $typevalue[$index];
494 if (!($tv =~ m/^(\C):\s*(.*)/)) {
495 last;
496 }
497 $index--;
498 }
499
500 return $index;
501}
502
503sub find_ending_index {
504 my ($index) = @_;
505
506 while ($index < @typevalue) {
507 my $tv = $typevalue[$index];
508 if (!($tv =~ m/^(\C):\s*(.*)/)) {
509 last;
510 }
511 $index++;
512 }
513
514 return $index;
515}
516
517sub add_categories {
518 my ($index) = @_;
519
520 my $i;
521 my $start = find_starting_index($index);
522 my $end = find_ending_index($index);
523
524 push(@subsystem, $typevalue[$start]);
525
526 for ($i = $start + 1; $i < $end; $i++) {
527 my $tv = $typevalue[$i];
397 if ($tv =~ m/^(\C):\s*(.*)/) { 528 if ($tv =~ m/^(\C):\s*(.*)/) {
398 my $ptype = $1; 529 my $ptype = $1;
399 my $pvalue = $2; 530 my $pvalue = $2;
@@ -414,19 +545,19 @@ sub add_categories {
414 } 545 }
415 } 546 }
416 } elsif ($ptype eq "M") { 547 } elsif ($ptype eq "M") {
417 my $p_used = 0; 548 my ($name, $address) = parse_email($pvalue);
418 if ($index >= 0) { 549 if ($name eq "") {
419 my $tv = $typevalue[$index - 1]; 550 if ($i > 0) {
420 if ($tv =~ m/^(\C):\s*(.*)/) { 551 my $tv = $typevalue[$i - 1];
421 if ($1 eq "P") { 552 if ($tv =~ m/^(\C):\s*(.*)/) {
422 if ($email_usename) { 553 if ($1 eq "P") {
423 push_email_address(format_email($2, $pvalue)); 554 $name = $2;
424 $p_used = 1; 555 $pvalue = format_email($name, $address);
425 } 556 }
426 } 557 }
427 } 558 }
428 } 559 }
429 if (!$p_used) { 560 if ($email_maintainer) {
430 push_email_addresses($pvalue); 561 push_email_addresses($pvalue);
431 } 562 }
432 } elsif ($ptype eq "T") { 563 } elsif ($ptype eq "T") {
@@ -436,31 +567,41 @@ sub add_categories {
436 } elsif ($ptype eq "S") { 567 } elsif ($ptype eq "S") {
437 push(@status, $pvalue); 568 push(@status, $pvalue);
438 } 569 }
439
440 $index--;
441 } else {
442 push(@subsystem,$tv);
443 $index = -1;
444 } 570 }
445 } 571 }
446} 572}
447 573
574my %email_hash_name;
575my %email_hash_address;
576
577sub email_inuse {
578 my ($name, $address) = @_;
579
580 return 1 if (($name eq "") && ($address eq ""));
581 return 1 if (($name ne "") && exists($email_hash_name{$name}));
582 return 1 if (($address ne "") && exists($email_hash_address{$address}));
583
584 return 0;
585}
586
448sub push_email_address { 587sub push_email_address {
449 my ($email_address) = @_; 588 my ($line) = @_;
589
590 my ($name, $address) = parse_email($line);
450 591
451 my $email_name = ""; 592 if ($address eq "") {
452 if ($email_address =~ m/([^<]+)<(.*\@.*)>$/) { 593 return 0;
453 $email_name = $1;
454 $email_address = $2;
455 } 594 }
456 595
457 if ($email_maintainer) { 596 if (!$email_remove_duplicates) {
458 if ($email_usename && $email_name) { 597 push(@email_to, format_email($name, $address));
459 push(@email_to, format_email($email_name, $email_address)); 598 } elsif (!email_inuse($name, $address)) {
460 } else { 599 push(@email_to, format_email($name, $address));
461 push(@email_to, $email_address); 600 $email_hash_name{$name}++;
462 } 601 $email_hash_address{$address}++;
463 } 602 }
603
604 return 1;
464} 605}
465 606
466sub push_email_addresses { 607sub push_email_addresses {
@@ -476,7 +617,9 @@ sub push_email_addresses {
476 push_email_address($entry); 617 push_email_address($entry);
477 } 618 }
478 } else { 619 } else {
479 warn("Invalid MAINTAINERS address: '" . $address . "'\n"); 620 if (!push_email_address($address)) {
621 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
622 }
480 } 623 }
481} 624}
482 625
@@ -492,6 +635,32 @@ sub which {
492 return ""; 635 return "";
493} 636}
494 637
638sub mailmap {
639 my @lines = @_;
640 my %hash;
641
642 foreach my $line (@lines) {
643 my ($name, $address) = parse_email($line);
644 if (!exists($hash{$name})) {
645 $hash{$name} = $address;
646 } elsif ($address ne $hash{$name}) {
647 $address = $hash{$name};
648 $line = format_email($name, $address);
649 }
650 if (exists($mailmap{$name})) {
651 my $obj = $mailmap{$name};
652 foreach my $map_address (@$obj) {
653 if (($map_address eq $address) &&
654 ($map_address ne $hash{$name})) {
655 $line = format_email($name, $hash{$name});
656 }
657 }
658 }
659 }
660
661 return @lines;
662}
663
495sub recent_git_signoffs { 664sub recent_git_signoffs {
496 my ($file) = @_; 665 my ($file) = @_;
497 666
@@ -500,6 +669,7 @@ sub recent_git_signoffs {
500 my $output = ""; 669 my $output = "";
501 my $count = 0; 670 my $count = 0;
502 my @lines = (); 671 my @lines = ();
672 my %hash;
503 my $total_sign_offs; 673 my $total_sign_offs;
504 674
505 if (which("git") eq "") { 675 if (which("git") eq "") {
@@ -513,52 +683,119 @@ sub recent_git_signoffs {
513 } 683 }
514 684
515 $cmd = "git log --since=${email_git_since} -- ${file}"; 685 $cmd = "git log --since=${email_git_since} -- ${file}";
516 $cmd .= " | grep -Ei \"^[-_ a-z]+by:.*\\\@.*\$\"";
517 if (!$email_git_penguin_chiefs) {
518 $cmd .= " | grep -Ev \"${penguin_chiefs}\"";
519 }
520 $cmd .= " | cut -f2- -d\":\"";
521 $cmd .= " | sort | uniq -c | sort -rn";
522 686
523 $output = `${cmd}`; 687 $output = `${cmd}`;
524 $output =~ s/^\s*//gm; 688 $output =~ s/^\s*//gm;
525 689
526 @lines = split("\n", $output); 690 @lines = split("\n", $output);
527 691
528 $total_sign_offs = 0; 692 @lines = grep(/^[-_ a-z]+by:.*\@.*$/i, @lines);
693 if (!$email_git_penguin_chiefs) {
694 @lines = grep(!/${penguin_chiefs}/i, @lines);
695 }
696 # cut -f2- -d":"
697 s/.*:\s*(.+)\s*/$1/ for (@lines);
698
699 $total_sign_offs = @lines;
700
701 if ($email_remove_duplicates) {
702 @lines = mailmap(@lines);
703 }
704
705 @lines = sort(@lines);
706
707 # uniq -c
708 $hash{$_}++ for @lines;
709
710 # sort -rn
711 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
712 my $sign_offs = $hash{$line};
713 $count++;
714 last if ($sign_offs < $email_git_min_signatures ||
715 $count > $email_git_max_maintainers ||
716 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
717 push_email_address($line);
718 }
719}
720
721sub save_commits {
722 my ($cmd, @commits) = @_;
723 my $output;
724 my @lines = ();
725
726 $output = `${cmd}`;
727
728 @lines = split("\n", $output);
529 foreach my $line (@lines) { 729 foreach my $line (@lines) {
530 if ($line =~ m/([0-9]+)\s+(.*)/) { 730 if ($line =~ m/^(\w+) /) {
531 $total_sign_offs += $1; 731 push (@commits, $1);
532 } else {
533 die("$P: Unexpected git output: ${line}\n");
534 } 732 }
535 } 733 }
734 return @commits;
735}
536 736
537 foreach my $line (@lines) { 737sub git_assign_blame {
538 if ($line =~ m/([0-9]+)\s+(.*)/) { 738 my ($file) = @_;
539 my $sign_offs = $1; 739
540 $line = $2; 740 my @lines = ();
541 $count++; 741 my @commits = ();
542 if ($sign_offs < $email_git_min_signatures || 742 my $cmd;
543 $count > $email_git_max_maintainers || 743 my $output;
544 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent) { 744 my %hash;
545 last; 745 my $total_sign_offs;
546 } 746 my $count;
747
748 if (@range) {
749 foreach my $file_range_diff (@range) {
750 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
751 my $diff_file = $1;
752 my $diff_start = $2;
753 my $diff_length = $3;
754 next if (!("$file" eq "$diff_file"));
755 $cmd = "git blame -l -L $diff_start,+$diff_length $file";
756 @commits = save_commits($cmd, @commits);
547 } 757 }
548 if ($line =~ m/(.+)<(.+)>/) { 758 } else {
549 my $git_name = $1; 759 if (-f $file) {
550 my $git_addr = $2; 760 $cmd = "git blame -l $file";
551 if ($email_usename) { 761 @commits = save_commits($cmd, @commits);
552 push(@email_to, format_email($git_name, $git_addr)); 762 }
553 } else { 763 }
554 push(@email_to, $git_addr); 764
555 } 765 $total_sign_offs = 0;
556 } elsif ($line =~ m/<(.+)>/) { 766 @commits = uniq(@commits);
557 my $git_addr = $1; 767 foreach my $commit (@commits) {
558 push(@email_to, $git_addr); 768 $cmd = "git log -1 ${commit}";
559 } else { 769
560 push(@email_to, $line); 770 $output = `${cmd}`;
771 $output =~ s/^\s*//gm;
772 @lines = split("\n", $output);
773
774 @lines = grep(/^[-_ a-z]+by:.*\@.*$/i, @lines);
775 if (!$email_git_penguin_chiefs) {
776 @lines = grep(!/${penguin_chiefs}/i, @lines);
777 }
778
779 # cut -f2- -d":"
780 s/.*:\s*(.+)\s*/$1/ for (@lines);
781
782 $total_sign_offs += @lines;
783
784 if ($email_remove_duplicates) {
785 @lines = mailmap(@lines);
561 } 786 }
787
788 $hash{$_}++ for @lines;
789 }
790
791 $count = 0;
792 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
793 my $sign_offs = $hash{$line};
794 $count++;
795 last if ($sign_offs < $email_git_min_signatures ||
796 $count > $email_git_max_maintainers ||
797 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
798 push_email_address($line);
562 } 799 }
563} 800}
564 801