diff options
Diffstat (limited to 'scripts/get_maintainer.pl')
-rwxr-xr-x | scripts/get_maintainer.pl | 234 |
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 @@ | |||
13 | use strict; | 13 | use strict; |
14 | 14 | ||
15 | my $P = $0; | 15 | my $P = $0; |
16 | my $V = '0.15'; | 16 | my $V = '0.16'; |
17 | 17 | ||
18 | use Getopt::Long qw(:config no_auto_abbrev); | 18 | use Getopt::Long qw(:config no_auto_abbrev); |
19 | 19 | ||
@@ -55,6 +55,10 @@ foreach my $chief (@penguin_chief) { | |||
55 | } | 55 | } |
56 | my $penguin_chiefs = "\(" . join("|",@penguin_chief_names) . "\)"; | 56 | my $penguin_chiefs = "\(" . join("|",@penguin_chief_names) . "\)"; |
57 | 57 | ||
58 | # rfc822 email address - preloaded methods go here. | ||
59 | my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])"; | ||
60 | my $rfc822_char = '[\\000-\\377]'; | ||
61 | |||
58 | if (!GetOptions( | 62 | if (!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 | ||
171 | my @email_to = (); | 175 | my @email_to = (); |
176 | my @list_to = (); | ||
172 | my @scm = (); | 177 | my @scm = (); |
173 | my @web = (); | 178 | my @web = (); |
174 | my @subsystem = (); | 179 | my @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 | ||
218 | if ($email_git_penguin_chiefs) { | 223 | if ($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 | ||
232 | if ($email) { | 241 | if ($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 | ||
243 | if ($scm) { | 252 | if ($scm) { |
@@ -307,10 +316,10 @@ Output type options: | |||
307 | --multiline => print 1 entry per line | 316 | --multiline => print 1 entry per line |
308 | 317 | ||
309 | Default options: | 318 | Default options: |
310 | [--email --git --m --l --multiline] | 319 | [--email --git --m --n --l --multiline] |
311 | 320 | ||
312 | Other options: | 321 | Other options: |
313 | --version -> show version | 322 | --version => show version |
314 | --help => show this help information | 323 | --help => show this help information |
315 | 324 | ||
316 | EOT | 325 | EOT |
@@ -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 | ||
430 | sub 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 | |||
448 | sub 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 | |||
415 | sub which { | 465 | sub 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 | ||
487 | sub uniq { | 537 | sub uniq { |
@@ -513,3 +563,97 @@ sub output { | |||
513 | print("\n"); | 563 | print("\n"); |
514 | } | 564 | } |
515 | } | 565 | } |
566 | |||
567 | my $rfc822re; | ||
568 | |||
569 | sub 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 | |||
605 | sub 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 | # | ||
619 | sub 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 | |||
639 | sub 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 | } | ||