# Print selected MAINTAINERS information for
# the files modified in a patch or for a file
#
-# usage: perl scripts/get_maintainers.pl [OPTIONS] <patch>
-# perl scripts/get_maintainers.pl [OPTIONS] -f <file>
+# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
+# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
#
# Licensed under the terms of the GNU GPL License version 2
use strict;
my $P = $0;
-my $V = '0.20';
+my $V = '0.21';
use Getopt::Long qw(:config no_auto_abbrev);
my $email_git_min_percent = 5;
my $email_git_since = "1-year-ago";
my $email_git_blame = 0;
+my $email_remove_duplicates = 1;
my $output_multiline = 1;
my $output_separator = ", ";
my $scm = 0;
my $web = 0;
my $subsystem = 0;
my $status = 0;
+my $keywords = 1;
my $from_filename = 0;
my $pattern_depth = 0;
my $version = 0;
'git-min-percent=i' => \$email_git_min_percent,
'git-since=s' => \$email_git_since,
'git-blame!' => \$email_git_blame,
+ 'remove-duplicates!' => \$email_remove_duplicates,
'm!' => \$email_maintainer,
'n!' => \$email_usename,
'l!' => \$email_list,
'scm!' => \$scm,
'web!' => \$web,
'pattern-depth=i' => \$pattern_depth,
+ 'k|keywords!' => \$keywords,
'f|file' => \$from_filename,
'v|version' => \$version,
'h|help' => \$help,
## Read MAINTAINERS for type/value pairs
my @typevalue = ();
+my %keyword_hash;
+
open(MAINT, "<${lk_path}MAINTAINERS") || die "$P: Can't open MAINTAINERS\n";
while (<MAINT>) {
my $line = $_;
if ((-d $value)) {
$value =~ s@([^/])$@$1/@;
}
+ } elsif ($type eq "K") {
+ $keyword_hash{@typevalue} = $value;
}
push(@typevalue, "$type:$value");
} elsif (!/^(\s)*$/) {
my %mailmap;
-open(MAILMAP, "<${lk_path}.mailmap") || warn "$P: Can't open .mailmap\n";
-while (<MAILMAP>) {
- my $line = $_;
+if ($email_remove_duplicates) {
+ open(MAILMAP, "<${lk_path}.mailmap") || warn "$P: Can't open .mailmap\n";
+ while (<MAILMAP>) {
+ my $line = $_;
- next if ($line =~ m/^\s*#/);
- next if ($line =~ m/^\s*$/);
+ next if ($line =~ m/^\s*#/);
+ next if ($line =~ m/^\s*$/);
- my ($name, $address) = parse_email($line);
- $line = format_email($name, $address);
+ my ($name, $address) = parse_email($line);
+ $line = format_email($name, $address);
- next if ($line =~ m/^\s*$/);
+ next if ($line =~ m/^\s*$/);
- if (exists($mailmap{$name})) {
- my $obj = $mailmap{$name};
- push(@$obj, $address);
- } else {
- my @arr = ($address);
- $mailmap{$name} = \@arr;
- }
-}
-close(MAILMAP);
-
-foreach my $name (sort {$mailmap{$a} <=> $mailmap{$b}} keys %mailmap) {
- my $obj = $mailmap{$name};
- foreach my $address (@$obj) {
+ if (exists($mailmap{$name})) {
+ my $obj = $mailmap{$name};
+ push(@$obj, $address);
+ } else {
+ my @arr = ($address);
+ $mailmap{$name} = \@arr;
+ }
}
+ close(MAILMAP);
}
## use the filenames on the command line or find the filenames in the patchfiles
my @files = ();
my @range = ();
+my @keyword_tvi = ();
foreach my $file (@ARGV) {
##if $file is a directory and it lacks a trailing slash, add one
}
if ($from_filename) {
push(@files, $file);
+ if (-f $file && $keywords) {
+ open(FILE, "<$file") or die "$P: Can't open ${file}\n";
+ while (<FILE>) {
+ my $patch_line = $_;
+ foreach my $line (keys %keyword_hash) {
+ if ($patch_line =~ m/^.*$keyword_hash{$line}/x) {
+ push(@keyword_tvi, $line);
+ }
+ }
+ }
+ close(FILE);
+ }
} else {
my $file_cnt = @files;
my $lastfile;
open(PATCH, "<$file") or die "$P: Can't open ${file}\n";
while (<PATCH>) {
+ my $patch_line = $_;
if (m/^\+\+\+\s+(\S+)/) {
my $filename = $1;
$filename =~ s@^[^/]*/@@;
if ($email_git_blame) {
push(@range, "$lastfile:$1:$2");
}
+ } elsif ($keywords) {
+ foreach my $line (keys %keyword_hash) {
+ if ($patch_line =~ m/^[+-].*$keyword_hash{$line}/x) {
+ push(@keyword_tvi, $line);
+ }
+ }
}
}
close(PATCH);
}
}
+if ($keywords) {
+ @keyword_tvi = sort_and_uniq(@keyword_tvi);
+ foreach my $line (@keyword_tvi) {
+ add_categories($line);
+ }
+}
+
if ($email) {
foreach my $chief (@penguin_chief) {
if ($chief =~ m/^(.*):(.*)/) {
}
if ($scm) {
- @scm = sort_and_uniq(@scm);
+ @scm = uniq(@scm);
output(@scm);
}
if ($status) {
- @status = sort_and_uniq(@status);
+ @status = uniq(@status);
output(@status);
}
if ($subsystem) {
- @subsystem = sort_and_uniq(@subsystem);
+ @subsystem = uniq(@subsystem);
output(@subsystem);
}
if ($web) {
- @web = sort_and_uniq(@web);
+ @web = uniq(@web);
output(@web);
}
--n => include name 'Full Name <addr\@domain.tld>'
--l => include list(s) if any
--s => include subscriber only list(s) if any
+ --remove-duplicates => minimize duplicate email names/addresses
--scm => print SCM tree(s) if any
--status => print status if any
--subsystem => print subsystem name if any
Other options:
--pattern-depth => Number of pattern directory traversals (default: 0 (all))
+ --keywords => scan patch for keywords (default: 1 (on))
--version => show version
--help => show this help information
Default options:
- [--email --git --m --n --l --multiline --pattern-depth=0]
+ [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates]
Notes:
Using "-f directory" may give unexpected results:
my $name = "";
my $address = "";
- if ($formatted_email =~ /^([^<]+)<(.*\@.*)>.*$/) {
+ if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
$name = $1;
$address = $2;
- } elsif ($formatted_email =~ /^\s*<(.*\@.*)>.*$/) {
+ } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
$address = $1;
- } elsif ($formatted_email =~ /^\s*(.*\@.*)$/) {
+ } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
$address = $1;
}
return $formatted_email;
}
-sub add_categories {
+sub find_starting_index {
+ my ($index) = @_;
+
+ while ($index > 0) {
+ my $tv = $typevalue[$index];
+ if (!($tv =~ m/^(\C):\s*(.*)/)) {
+ last;
+ }
+ $index--;
+ }
+
+ return $index;
+}
+
+sub find_ending_index {
my ($index) = @_;
- $index = $index - 1;
- while ($index >= 0) {
+ while ($index < @typevalue) {
my $tv = $typevalue[$index];
+ if (!($tv =~ m/^(\C):\s*(.*)/)) {
+ last;
+ }
+ $index++;
+ }
+
+ return $index;
+}
+
+sub add_categories {
+ my ($index) = @_;
+
+ my $i;
+ my $start = find_starting_index($index);
+ my $end = find_ending_index($index);
+
+ push(@subsystem, $typevalue[$start]);
+
+ for ($i = $start + 1; $i < $end; $i++) {
+ my $tv = $typevalue[$i];
if ($tv =~ m/^(\C):\s*(.*)/) {
my $ptype = $1;
my $pvalue = $2;
} elsif ($ptype eq "M") {
my ($name, $address) = parse_email($pvalue);
if ($name eq "") {
- if ($index >= 0) {
- my $tv = $typevalue[$index - 1];
+ if ($i > 0) {
+ my $tv = $typevalue[$i - 1];
if ($tv =~ m/^(\C):\s*(.*)/) {
if ($1 eq "P") {
$name = $2;
+ $pvalue = format_email($name, $address);
}
}
}
} elsif ($ptype eq "S") {
push(@status, $pvalue);
}
-
- $index--;
- } else {
- push(@subsystem,$tv);
- $index = -1;
}
}
}
-sub email_address_inuse {
- my ($test_address) = @_;
+my %email_hash_name;
+my %email_hash_address;
- foreach my $line (@email_to) {
- my ($name, $address) = parse_email($line);
+sub email_inuse {
+ my ($name, $address) = @_;
+
+ return 1 if (($name eq "") && ($address eq ""));
+ return 1 if (($name ne "") && exists($email_hash_name{$name}));
+ return 1 if (($address ne "") && exists($email_hash_address{$address}));
- return 1 if ($address eq $test_address);
- }
return 0;
}
my ($name, $address) = parse_email($line);
- if (!email_address_inuse($address)) {
+ if ($address eq "") {
+ return 0;
+ }
+
+ if (!$email_remove_duplicates) {
+ push(@email_to, format_email($name, $address));
+ } elsif (!email_inuse($name, $address)) {
push(@email_to, format_email($name, $address));
+ $email_hash_name{$name}++;
+ $email_hash_address{$address}++;
}
+
+ return 1;
}
sub push_email_addresses {
push_email_address($entry);
}
} else {
- warn("Invalid MAINTAINERS address: '" . $address . "'\n");
+ if (!push_email_address($address)) {
+ warn("Invalid MAINTAINERS address: '" . $address . "'\n");
+ }
}
}
my ($name, $address) = parse_email($line);
if (!exists($hash{$name})) {
$hash{$name} = $address;
+ } elsif ($address ne $hash{$name}) {
+ $address = $hash{$name};
+ $line = format_email($name, $address);
}
if (exists($mailmap{$name})) {
my $obj = $mailmap{$name};
$total_sign_offs = @lines;
- @lines = mailmap(@lines);
+ if ($email_remove_duplicates) {
+ @lines = mailmap(@lines);
+ }
@lines = sort(@lines);
+
# uniq -c
- foreach my $line (@lines) {
- $hash{$line}++;
- }
+ $hash{$_}++ for @lines;
+
# sort -rn
- @lines = ();
foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
- push(@lines,"$hash{$line} $line");
- }
-
- foreach my $line (@lines) {
- if ($line =~ m/([0-9]+)\s+(.*)/) {
- my $sign_offs = $1;
- $line = $2;
- $count++;
- if ($sign_offs < $email_git_min_signatures ||
- $count > $email_git_max_maintainers ||
- $sign_offs * 100 / $total_sign_offs < $email_git_min_percent) {
- last;
- }
- push_email_address($line);
- }
+ my $sign_offs = $hash{$line};
+ $count++;
+ last if ($sign_offs < $email_git_min_signatures ||
+ $count > $email_git_max_maintainers ||
+ $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
+ push_email_address($line);
}
}
$total_sign_offs += @lines;
- @lines = mailmap(@lines);
+ if ($email_remove_duplicates) {
+ @lines = mailmap(@lines);
+ }
$hash{$_}++ for @lines;
}