aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/get_maintainer.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/get_maintainer.pl')
-rwxr-xr-xscripts/get_maintainer.pl234
1 files changed, 189 insertions, 45 deletions
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index 60dc0c48c929..3e733146cd51 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.15'; 16my $V = '0.16';
17 17
18use Getopt::Long qw(:config no_auto_abbrev); 18use Getopt::Long qw(:config no_auto_abbrev);
19 19
@@ -55,6 +55,10 @@ foreach my $chief (@penguin_chief) {
55} 55}
56my $penguin_chiefs = "\(" . join("|",@penguin_chief_names) . "\)"; 56my $penguin_chiefs = "\(" . join("|",@penguin_chief_names) . "\)";
57 57
58# rfc822 email address - preloaded methods go here.
59my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
60my $rfc822_char = '[\\000-\\377]';
61
58if (!GetOptions( 62if (!GetOptions(
59 'email!' => \$email, 63 'email!' => \$email,
60 'git!' => \$email_git, 64 'git!' => \$email_git,
@@ -161,7 +165,7 @@ foreach my $file (@ARGV) {
161 } 165 }
162 close(PATCH); 166 close(PATCH);
163 if ($file_cnt == @files) { 167 if ($file_cnt == @files) {
164 die "$P: file '${file}' doesn't appear to be a patch. " 168 warn "$P: file '${file}' doesn't appear to be a patch. "
165 . "Add -f to options?\n"; 169 . "Add -f to options?\n";
166 } 170 }
167 @files = sort_and_uniq(@files); 171 @files = sort_and_uniq(@files);
@@ -169,6 +173,7 @@ foreach my $file (@ARGV) {
169} 173}
170 174
171my @email_to = (); 175my @email_to = ();
176my @list_to = ();
172my @scm = (); 177my @scm = ();
173my @web = (); 178my @web = ();
174my @subsystem = (); 179my @subsystem = ();
@@ -182,7 +187,7 @@ foreach my $file (@files) {
182 187
183 my $exclude = 0; 188 my $exclude = 0;
184 foreach my $line (@typevalue) { 189 foreach my $line (@typevalue) {
185 if ($line =~ m/^(\C):(.*)/) { 190 if ($line =~ m/^(\C):\s*(.*)/) {
186 my $type = $1; 191 my $type = $1;
187 my $value = $2; 192 my $value = $2;
188 if ($type eq 'X') { 193 if ($type eq 'X') {
@@ -196,7 +201,7 @@ foreach my $file (@files) {
196 if (!$exclude) { 201 if (!$exclude) {
197 my $tvi = 0; 202 my $tvi = 0;
198 foreach my $line (@typevalue) { 203 foreach my $line (@typevalue) {
199 if ($line =~ m/^(\C):(.*)/) { 204 if ($line =~ m/^(\C):\s*(.*)/) {
200 my $type = $1; 205 my $type = $1;
201 my $value = $2; 206 my $value = $2;
202 if ($type eq 'F') { 207 if ($type eq 'F') {
@@ -215,29 +220,33 @@ foreach my $file (@files) {
215 220
216} 221}
217 222
218if ($email_git_penguin_chiefs) { 223if ($email) {
219 foreach my $chief (@penguin_chief) { 224 foreach my $chief (@penguin_chief) {
220 if ($chief =~ m/^(.*):(.*)/) { 225 if ($chief =~ m/^(.*):(.*)/) {
221 my $chief_name = $1; 226 my $email_address;
222 my $chief_addr = $2;
223 if ($email_usename) { 227 if ($email_usename) {
224 push(@email_to, format_email($chief_name, $chief_addr)); 228 $email_address = format_email($1, $2);
229 } else {
230 $email_address = $2;
231 }
232 if ($email_git_penguin_chiefs) {
233 push(@email_to, $email_address);
225 } else { 234 } else {
226 push(@email_to, $chief_addr); 235 @email_to = grep(!/${email_address}/, @email_to);
227 } 236 }
228 } 237 }
229 } 238 }
230} 239}
231 240
232if ($email) { 241if ($email || $email_list) {
233 my $address_cnt = @email_to; 242 my @to = ();
234 if ($address_cnt == 0 && $email_list) { 243 if ($email) {
235 push(@email_to, "linux-kernel\@vger.kernel.org"); 244 @to = (@to, @email_to);
236 } 245 }
237 246 if ($email_list) {
238#Don't sort email address list, but do remove duplicates 247 @to = (@to, @list_to);
239 @email_to = uniq(@email_to); 248 }
240 output(@email_to); 249 output(uniq(@to));
241} 250}
242 251
243if ($scm) { 252if ($scm) {
@@ -307,10 +316,10 @@ Output type options:
307 --multiline => print 1 entry per line 316 --multiline => print 1 entry per line
308 317
309Default options: 318Default options:
310 [--email --git --m --l --multiline] 319 [--email --git --m --n --l --multiline]
311 320
312Other options: 321Other options:
313 --version -> show version 322 --version => show version
314 --help => show this help information 323 --help => show this help information
315 324
316EOT 325EOT
@@ -347,6 +356,7 @@ sub format_email {
347 my ($name, $email) = @_; 356 my ($name, $email) = @_;
348 357
349 $name =~ s/^\s+|\s+$//g; 358 $name =~ s/^\s+|\s+$//g;
359 $name =~ s/^\"|\"$//g;
350 $email =~ s/^\s+|\s+$//g; 360 $email =~ s/^\s+|\s+$//g;
351 361
352 my $formatted_email = ""; 362 my $formatted_email = "";
@@ -366,36 +376,41 @@ sub add_categories {
366 $index = $index - 1; 376 $index = $index - 1;
367 while ($index >= 0) { 377 while ($index >= 0) {
368 my $tv = $typevalue[$index]; 378 my $tv = $typevalue[$index];
369 if ($tv =~ m/^(\C):(.*)/) { 379 if ($tv =~ m/^(\C):\s*(.*)/) {
370 my $ptype = $1; 380 my $ptype = $1;
371 my $pvalue = $2; 381 my $pvalue = $2;
372 if ($ptype eq "L") { 382 if ($ptype eq "L") {
373 my $subscr = $pvalue; 383 my $list_address = $pvalue;
374 if ($subscr =~ m/\s*\(subscribers-only\)/) { 384 my $list_additional = "";
385 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
386 $list_address = $1;
387 $list_additional = $2;
388 }
389 if ($list_additional =~ m/subscribers-only/) {
375 if ($email_subscriber_list) { 390 if ($email_subscriber_list) {
376 $subscr =~ s/\s*\(subscribers-only\)//g; 391 push(@list_to, $list_address);
377 push(@email_to, $subscr);
378 } 392 }
379 } else { 393 } else {
380 if ($email_list) { 394 if ($email_list) {
381 push(@email_to, $pvalue); 395 push(@list_to, $list_address);
382 } 396 }
383 } 397 }
384 } elsif ($ptype eq "M") { 398 } elsif ($ptype eq "M") {
385 if ($email_maintainer) { 399 my $p_used = 0;
386 if ($index >= 0) { 400 if ($index >= 0) {
387 my $tv = $typevalue[$index - 1]; 401 my $tv = $typevalue[$index - 1];
388 if ($tv =~ m/^(\C):(.*)/) { 402 if ($tv =~ m/^(\C):\s*(.*)/) {
389 if ($1 eq "P" && $email_usename) { 403 if ($1 eq "P") {
390 push(@email_to, format_email($2, $pvalue)); 404 if ($email_usename) {
391 } else { 405 push_email_address(format_email($2, $pvalue));
392 push(@email_to, $pvalue); 406 $p_used = 1;
393 } 407 }
394 } 408 }
395 } else {
396 push(@email_to, $pvalue);
397 } 409 }
398 } 410 }
411 if (!$p_used) {
412 push_email_addresses($pvalue);
413 }
399 } elsif ($ptype eq "T") { 414 } elsif ($ptype eq "T") {
400 push(@scm, $pvalue); 415 push(@scm, $pvalue);
401 } elsif ($ptype eq "W") { 416 } elsif ($ptype eq "W") {
@@ -412,10 +427,45 @@ sub add_categories {
412 } 427 }
413} 428}
414 429
430sub push_email_address {
431 my ($email_address) = @_;
432
433 my $email_name = "";
434 if ($email_address =~ m/([^<]+)<(.*\@.*)>$/) {
435 $email_name = $1;
436 $email_address = $2;
437 }
438
439 if ($email_maintainer) {
440 if ($email_usename && $email_name) {
441 push(@email_to, format_email($email_name, $email_address));
442 } else {
443 push(@email_to, $email_address);
444 }
445 }
446}
447
448sub push_email_addresses {
449 my ($address) = @_;
450
451 my @address_list = ();
452
453 if (rfc822_valid($address)) {
454 push_email_address($address);
455 } elsif (@address_list = rfc822_validlist($address)) {
456 my $array_count = shift(@address_list);
457 while (my $entry = shift(@address_list)) {
458 push_email_address($entry);
459 }
460 } else {
461 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
462 }
463}
464
415sub which { 465sub which {
416 my ($bin) = @_; 466 my ($bin) = @_;
417 467
418 foreach my $path (split /:/, $ENV{PATH}) { 468 foreach my $path (split(/:/, $ENV{PATH})) {
419 if (-e "$path/$bin") { 469 if (-e "$path/$bin") {
420 return "$path/$bin"; 470 return "$path/$bin";
421 } 471 }
@@ -434,16 +484,21 @@ sub recent_git_signoffs {
434 my @lines = (); 484 my @lines = ();
435 485
436 if (which("git") eq "") { 486 if (which("git") eq "") {
437 die("$P: git not found. Add --nogit to options?\n"); 487 warn("$P: git not found. Add --nogit to options?\n");
488 return;
489 }
490 if (!(-d ".git")) {
491 warn("$P: .git directory not found. Use a git repository for better results.\n");
492 warn("$P: perhaps 'git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git'\n");
493 return;
438 } 494 }
439 495
440 $cmd = "git log --since=${email_git_since} -- ${file}"; 496 $cmd = "git log --since=${email_git_since} -- ${file}";
441 $cmd .= " | grep -Pi \"^[-_ a-z]+by:.*\\\@\""; 497 $cmd .= " | grep -Ei \"^[-_ a-z]+by:.*\\\@.*\$\"";
442 if (!$email_git_penguin_chiefs) { 498 if (!$email_git_penguin_chiefs) {
443 $cmd .= " | grep -Pv \"${penguin_chiefs}\""; 499 $cmd .= " | grep -Ev \"${penguin_chiefs}\"";
444 } 500 }
445 $cmd .= " | cut -f2- -d\":\""; 501 $cmd .= " | cut -f2- -d\":\"";
446 $cmd .= " | sed -e \"s/^\\s+//g\"";
447 $cmd .= " | sort | uniq -c | sort -rn"; 502 $cmd .= " | sort | uniq -c | sort -rn";
448 503
449 $output = `${cmd}`; 504 $output = `${cmd}`;
@@ -465,10 +520,6 @@ sub recent_git_signoffs {
465 if ($line =~ m/(.+)<(.+)>/) { 520 if ($line =~ m/(.+)<(.+)>/) {
466 my $git_name = $1; 521 my $git_name = $1;
467 my $git_addr = $2; 522 my $git_addr = $2;
468 $git_name =~ tr/^\"//;
469 $git_name =~ tr/^\\s*//;
470 $git_name =~ tr/\"$//;
471 $git_name =~ tr/\\s*$//;
472 if ($email_usename) { 523 if ($email_usename) {
473 push(@email_to, format_email($git_name, $git_addr)); 524 push(@email_to, format_email($git_name, $git_addr));
474 } else { 525 } else {
@@ -481,7 +532,6 @@ sub recent_git_signoffs {
481 push(@email_to, $line); 532 push(@email_to, $line);
482 } 533 }
483 } 534 }
484 return $output;
485} 535}
486 536
487sub uniq { 537sub uniq {
@@ -513,3 +563,97 @@ sub output {
513 print("\n"); 563 print("\n");
514 } 564 }
515} 565}
566
567my $rfc822re;
568
569sub make_rfc822re {
570# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
571# comment. We must allow for rfc822_lwsp (or comments) after each of these.
572# This regexp will only work on addresses which have had comments stripped
573# and replaced with rfc822_lwsp.
574
575 my $specials = '()<>@,;:\\\\".\\[\\]';
576 my $controls = '\\000-\\037\\177';
577
578 my $dtext = "[^\\[\\]\\r\\\\]";
579 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
580
581 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
582
583# Use zero-width assertion to spot the limit of an atom. A simple
584# $rfc822_lwsp* causes the regexp engine to hang occasionally.
585 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
586 my $word = "(?:$atom|$quoted_string)";
587 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
588
589 my $sub_domain = "(?:$atom|$domain_literal)";
590 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
591
592 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
593
594 my $phrase = "$word*";
595 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
596 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
597 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
598
599 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
600 my $address = "(?:$mailbox|$group)";
601
602 return "$rfc822_lwsp*$address";
603}
604
605sub rfc822_strip_comments {
606 my $s = shift;
607# Recursively remove comments, and replace with a single space. The simpler
608# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
609# chars in atoms, for example.
610
611 while ($s =~ s/^((?:[^"\\]|\\.)*
612 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
613 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
614 return $s;
615}
616
617# valid: returns true if the parameter is an RFC822 valid address
618#
619sub rfc822_valid ($) {
620 my $s = rfc822_strip_comments(shift);
621
622 if (!$rfc822re) {
623 $rfc822re = make_rfc822re();
624 }
625
626 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
627}
628
629# validlist: In scalar context, returns true if the parameter is an RFC822
630# valid list of addresses.
631#
632# In list context, returns an empty list on failure (an invalid
633# address was found); otherwise a list whose first element is the
634# number of addresses found and whose remaining elements are the
635# addresses. This is needed to disambiguate failure (invalid)
636# from success with no addresses found, because an empty string is
637# a valid list.
638
639sub rfc822_validlist ($) {
640 my $s = rfc822_strip_comments(shift);
641
642 if (!$rfc822re) {
643 $rfc822re = make_rfc822re();
644 }
645 # * null list items are valid according to the RFC
646 # * the '1' business is to aid in distinguishing failure from no results
647
648 my @r;
649 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
650 $s =~ m/^$rfc822_char*$/) {
651 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
652 push @r, $1;
653 }
654 return wantarray ? (scalar(@r), @r) : 1;
655 }
656 else {
657 return wantarray ? () : 0;
658 }
659}