~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/scripts/get_maintainer.pl

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

Diff markup

Differences between /scripts/get_maintainer.pl (Version linux-6.11.5) and /scripts/get_maintainer.pl (Version linux-6.2.16)


  1 #!/usr/bin/env perl                                 1 #!/usr/bin/env perl
  2 # SPDX-License-Identifier: GPL-2.0                  2 # SPDX-License-Identifier: GPL-2.0
  3 #                                                   3 #
  4 # (c) 2007, Joe Perches <joe@perches.com>            4 # (c) 2007, Joe Perches <joe@perches.com>
  5 #           created from checkpatch.pl              5 #           created from checkpatch.pl
  6 #                                                   6 #
  7 # Print selected MAINTAINERS information for        7 # Print selected MAINTAINERS information for
  8 # the files modified in a patch or for a file       8 # the files modified in a patch or for a file
  9 #                                                   9 #
 10 # usage: perl scripts/get_maintainer.pl [OPTIO     10 # usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
 11 #        perl scripts/get_maintainer.pl [OPTIO     11 #        perl scripts/get_maintainer.pl [OPTIONS] -f <file>
 12                                                    12 
 13 use warnings;                                      13 use warnings;
 14 use strict;                                        14 use strict;
 15                                                    15 
 16 my $P = $0;                                        16 my $P = $0;
 17 my $V = '0.26';                                    17 my $V = '0.26';
 18                                                    18 
 19 use Getopt::Long qw(:config no_auto_abbrev);       19 use Getopt::Long qw(:config no_auto_abbrev);
 20 use Cwd;                                           20 use Cwd;
 21 use File::Find;                                    21 use File::Find;
 22 use File::Spec::Functions;                         22 use File::Spec::Functions;
 23 use open qw(:std :encoding(UTF-8));            << 
 24                                                    23 
 25 my $cur_path = fastgetcwd() . '/';                 24 my $cur_path = fastgetcwd() . '/';
 26 my $lk_path = "./";                                25 my $lk_path = "./";
 27 my $email = 1;                                     26 my $email = 1;
 28 my $email_usename = 1;                             27 my $email_usename = 1;
 29 my $email_maintainer = 1;                          28 my $email_maintainer = 1;
 30 my $email_reviewer = 1;                            29 my $email_reviewer = 1;
 31 my $email_fixes = 1;                               30 my $email_fixes = 1;
 32 my $email_list = 1;                                31 my $email_list = 1;
 33 my $email_moderated_list = 1;                      32 my $email_moderated_list = 1;
 34 my $email_subscriber_list = 0;                     33 my $email_subscriber_list = 0;
 35 my $email_git_penguin_chiefs = 0;                  34 my $email_git_penguin_chiefs = 0;
 36 my $email_git = 0;                                 35 my $email_git = 0;
 37 my $email_git_all_signature_types = 0;             36 my $email_git_all_signature_types = 0;
 38 my $email_git_blame = 0;                           37 my $email_git_blame = 0;
 39 my $email_git_blame_signatures = 1;                38 my $email_git_blame_signatures = 1;
 40 my $email_git_fallback = 1;                        39 my $email_git_fallback = 1;
 41 my $email_git_min_signatures = 1;                  40 my $email_git_min_signatures = 1;
 42 my $email_git_max_maintainers = 5;                 41 my $email_git_max_maintainers = 5;
 43 my $email_git_min_percent = 5;                     42 my $email_git_min_percent = 5;
 44 my $email_git_since = "1-year-ago";                43 my $email_git_since = "1-year-ago";
 45 my $email_hg_since = "-365";                       44 my $email_hg_since = "-365";
 46 my $interactive = 0;                               45 my $interactive = 0;
 47 my $email_remove_duplicates = 1;                   46 my $email_remove_duplicates = 1;
 48 my $email_use_mailmap = 1;                         47 my $email_use_mailmap = 1;
 49 my $output_multiline = 1;                          48 my $output_multiline = 1;
 50 my $output_separator = ", ";                       49 my $output_separator = ", ";
 51 my $output_roles = 0;                              50 my $output_roles = 0;
 52 my $output_rolestats = 1;                          51 my $output_rolestats = 1;
 53 my $output_section_maxlen = 50;                    52 my $output_section_maxlen = 50;
 54 my $scm = 0;                                       53 my $scm = 0;
 55 my $tree = 1;                                      54 my $tree = 1;
 56 my $web = 0;                                       55 my $web = 0;
 57 my $subsystem = 0;                                 56 my $subsystem = 0;
 58 my $status = 0;                                    57 my $status = 0;
 59 my $letters = "";                                  58 my $letters = "";
 60 my $keywords = 1;                                  59 my $keywords = 1;
 61 my $keywords_in_file = 0;                      << 
 62 my $sections = 0;                                  60 my $sections = 0;
 63 my $email_file_emails = 0;                         61 my $email_file_emails = 0;
 64 my $from_filename = 0;                             62 my $from_filename = 0;
 65 my $pattern_depth = 0;                             63 my $pattern_depth = 0;
 66 my $self_test = undef;                             64 my $self_test = undef;
 67 my $version = 0;                                   65 my $version = 0;
 68 my $help = 0;                                      66 my $help = 0;
 69 my $find_maintainer_files = 0;                     67 my $find_maintainer_files = 0;
 70 my $maintainer_path;                               68 my $maintainer_path;
 71 my $vcs_used = 0;                                  69 my $vcs_used = 0;
 72                                                    70 
 73 my $exit = 0;                                      71 my $exit = 0;
 74                                                    72 
 75 my @files = ();                                    73 my @files = ();
 76 my @fixes = ();                 # If a patch d     74 my @fixes = ();                 # If a patch description includes Fixes: lines
 77 my @range = ();                                    75 my @range = ();
 78 my @keyword_tvi = ();                              76 my @keyword_tvi = ();
 79 my @file_emails = ();                              77 my @file_emails = ();
 80                                                    78 
 81 my %commit_author_hash;                            79 my %commit_author_hash;
 82 my %commit_signer_hash;                            80 my %commit_signer_hash;
 83                                                    81 
 84 my @penguin_chief = ();                            82 my @penguin_chief = ();
 85 push(@penguin_chief, "Linus Torvalds:torvalds\     83 push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
 86 #Andrew wants in on most everything - 2009/01/     84 #Andrew wants in on most everything - 2009/01/14
 87 #push(@penguin_chief, "Andrew Morton:akpm\@lin     85 #push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
 88                                                    86 
 89 my @penguin_chief_names = ();                      87 my @penguin_chief_names = ();
 90 foreach my $chief (@penguin_chief) {               88 foreach my $chief (@penguin_chief) {
 91     if ($chief =~ m/^(.*):(.*)/) {                 89     if ($chief =~ m/^(.*):(.*)/) {
 92         my $chief_name = $1;                       90         my $chief_name = $1;
 93         my $chief_addr = $2;                       91         my $chief_addr = $2;
 94         push(@penguin_chief_names, $chief_name     92         push(@penguin_chief_names, $chief_name);
 95     }                                              93     }
 96 }                                                  94 }
 97 my $penguin_chiefs = "\(" . join("|", @penguin     95 my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
 98                                                    96 
 99 # Signature types of people who are either         97 # Signature types of people who are either
100 #       a) responsible for the code in questio     98 #       a) responsible for the code in question, or
101 #       b) familiar enough with it to give rel     99 #       b) familiar enough with it to give relevant feedback
102 my @signature_tags = ();                          100 my @signature_tags = ();
103 push(@signature_tags, "Signed-off-by:");          101 push(@signature_tags, "Signed-off-by:");
104 push(@signature_tags, "Reviewed-by:");            102 push(@signature_tags, "Reviewed-by:");
105 push(@signature_tags, "Acked-by:");               103 push(@signature_tags, "Acked-by:");
106                                                   104 
107 my $signature_pattern = "\(" . join("|", @sign    105 my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
108                                                   106 
109 # rfc822 email address - preloaded methods go     107 # rfc822 email address - preloaded methods go here.
110 my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";        108 my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
111 my $rfc822_char = '[\\000-\\377]';                109 my $rfc822_char = '[\\000-\\377]';
112                                                   110 
113 # VCS command support: class-like functions an    111 # VCS command support: class-like functions and strings
114                                                   112 
115 my %VCS_cmds;                                     113 my %VCS_cmds;
116                                                   114 
117 my %VCS_cmds_git = (                              115 my %VCS_cmds_git = (
118     "execute_cmd" => \&git_execute_cmd,           116     "execute_cmd" => \&git_execute_cmd,
119     "available" => '(which("git") ne "") && (-    117     "available" => '(which("git") ne "") && (-e ".git")',
120     "find_signers_cmd" =>                         118     "find_signers_cmd" =>
121         "git log --no-color --follow --since=\    119         "git log --no-color --follow --since=\$email_git_since " .
122             '--numstat --no-merges ' .            120             '--numstat --no-merges ' .
123             '--format="GitCommit: %H%n' .         121             '--format="GitCommit: %H%n' .
124                       'GitAuthor: %an <%ae>%n'    122                       'GitAuthor: %an <%ae>%n' .
125                       'GitDate: %aD%n' .          123                       'GitDate: %aD%n' .
126                       'GitSubject: %s%n' .        124                       'GitSubject: %s%n' .
127                       '%b%n"' .                   125                       '%b%n"' .
128             " -- \$file",                         126             " -- \$file",
129     "find_commit_signers_cmd" =>                  127     "find_commit_signers_cmd" =>
130         "git log --no-color " .                   128         "git log --no-color " .
131             '--numstat ' .                        129             '--numstat ' .
132             '--format="GitCommit: %H%n' .         130             '--format="GitCommit: %H%n' .
133                       'GitAuthor: %an <%ae>%n'    131                       'GitAuthor: %an <%ae>%n' .
134                       'GitDate: %aD%n' .          132                       'GitDate: %aD%n' .
135                       'GitSubject: %s%n' .        133                       'GitSubject: %s%n' .
136                       '%b%n"' .                   134                       '%b%n"' .
137             " -1 \$commit",                       135             " -1 \$commit",
138     "find_commit_author_cmd" =>                   136     "find_commit_author_cmd" =>
139         "git log --no-color " .                   137         "git log --no-color " .
140             '--numstat ' .                        138             '--numstat ' .
141             '--format="GitCommit: %H%n' .         139             '--format="GitCommit: %H%n' .
142                       'GitAuthor: %an <%ae>%n'    140                       'GitAuthor: %an <%ae>%n' .
143                       'GitDate: %aD%n' .          141                       'GitDate: %aD%n' .
144                       'GitSubject: %s%n"' .       142                       'GitSubject: %s%n"' .
145             " -1 \$commit",                       143             " -1 \$commit",
146     "blame_range_cmd" => "git blame -l -L \$di    144     "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
147     "blame_file_cmd" => "git blame -l \$file",    145     "blame_file_cmd" => "git blame -l \$file",
148     "commit_pattern" => "^GitCommit: ([0-9a-f]    146     "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
149     "blame_commit_pattern" => "^([0-9a-f]+) ",    147     "blame_commit_pattern" => "^([0-9a-f]+) ",
150     "author_pattern" => "^GitAuthor: (.*)",       148     "author_pattern" => "^GitAuthor: (.*)",
151     "subject_pattern" => "^GitSubject: (.*)",     149     "subject_pattern" => "^GitSubject: (.*)",
152     "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$fi    150     "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
153     "file_exists_cmd" => "git ls-files \$file"    151     "file_exists_cmd" => "git ls-files \$file",
154     "list_files_cmd" => "git ls-files \$file",    152     "list_files_cmd" => "git ls-files \$file",
155 );                                                153 );
156                                                   154 
157 my %VCS_cmds_hg = (                               155 my %VCS_cmds_hg = (
158     "execute_cmd" => \&hg_execute_cmd,            156     "execute_cmd" => \&hg_execute_cmd,
159     "available" => '(which("hg") ne "") && (-d    157     "available" => '(which("hg") ne "") && (-d ".hg")',
160     "find_signers_cmd" =>                         158     "find_signers_cmd" =>
161         "hg log --date=\$email_hg_since " .       159         "hg log --date=\$email_hg_since " .
162             "--template='HgCommit: {node}\\n"     160             "--template='HgCommit: {node}\\n" .
163                         "HgAuthor: {author}\\n    161                         "HgAuthor: {author}\\n" .
164                         "HgSubject: {desc}\\n'    162                         "HgSubject: {desc}\\n'" .
165             " -- \$file",                         163             " -- \$file",
166     "find_commit_signers_cmd" =>                  164     "find_commit_signers_cmd" =>
167         "hg log " .                               165         "hg log " .
168             "--template='HgSubject: {desc}\\n'    166             "--template='HgSubject: {desc}\\n'" .
169             " -r \$commit",                       167             " -r \$commit",
170     "find_commit_author_cmd" =>                   168     "find_commit_author_cmd" =>
171         "hg log " .                               169         "hg log " .
172             "--template='HgCommit: {node}\\n"     170             "--template='HgCommit: {node}\\n" .
173                         "HgAuthor: {author}\\n    171                         "HgAuthor: {author}\\n" .
174                         "HgSubject: {desc|firs    172                         "HgSubject: {desc|firstline}\\n'" .
175             " -r \$commit",                       173             " -r \$commit",
176     "blame_range_cmd" => "",            # not     174     "blame_range_cmd" => "",            # not supported
177     "blame_file_cmd" => "hg blame -n \$file",     175     "blame_file_cmd" => "hg blame -n \$file",
178     "commit_pattern" => "^HgCommit: ([0-9a-f]{    176     "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
179     "blame_commit_pattern" => "^([ 0-9a-f]+):"    177     "blame_commit_pattern" => "^([ 0-9a-f]+):",
180     "author_pattern" => "^HgAuthor: (.*)",        178     "author_pattern" => "^HgAuthor: (.*)",
181     "subject_pattern" => "^HgSubject: (.*)",      179     "subject_pattern" => "^HgSubject: (.*)",
182     "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file    180     "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
183     "file_exists_cmd" => "hg files \$file",       181     "file_exists_cmd" => "hg files \$file",
184     "list_files_cmd" => "hg manifest -R \$file    182     "list_files_cmd" => "hg manifest -R \$file",
185 );                                                183 );
186                                                   184 
187 my $conf = which_conf(".get_maintainer.conf");    185 my $conf = which_conf(".get_maintainer.conf");
188 if (-f $conf) {                                   186 if (-f $conf) {
189     my @conf_args;                                187     my @conf_args;
190     open(my $conffile, '<', "$conf")              188     open(my $conffile, '<', "$conf")
191         or warn "$P: Can't find a readable .ge    189         or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
192                                                   190 
193     while (<$conffile>) {                         191     while (<$conffile>) {
194         my $line = $_;                            192         my $line = $_;
195                                                   193 
196         $line =~ s/\s*\n?$//g;                    194         $line =~ s/\s*\n?$//g;
197         $line =~ s/^\s*//g;                       195         $line =~ s/^\s*//g;
198         $line =~ s/\s+/ /g;                       196         $line =~ s/\s+/ /g;
199                                                   197 
200         next if ($line =~ m/^\s*#/);              198         next if ($line =~ m/^\s*#/);
201         next if ($line =~ m/^\s*$/);              199         next if ($line =~ m/^\s*$/);
202                                                   200 
203         my @words = split(" ", $line);            201         my @words = split(" ", $line);
204         foreach my $word (@words) {               202         foreach my $word (@words) {
205             last if ($word =~ m/^#/);             203             last if ($word =~ m/^#/);
206             push (@conf_args, $word);             204             push (@conf_args, $word);
207         }                                         205         }
208     }                                             206     }
209     close($conffile);                             207     close($conffile);
210     unshift(@ARGV, @conf_args) if @conf_args;     208     unshift(@ARGV, @conf_args) if @conf_args;
211 }                                                 209 }
212                                                   210 
213 my @ignore_emails = ();                           211 my @ignore_emails = ();
214 my $ignore_file = which_conf(".get_maintainer.    212 my $ignore_file = which_conf(".get_maintainer.ignore");
215 if (-f $ignore_file) {                            213 if (-f $ignore_file) {
216     open(my $ignore, '<', "$ignore_file")         214     open(my $ignore, '<', "$ignore_file")
217         or warn "$P: Can't find a readable .ge    215         or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
218     while (<$ignore>) {                           216     while (<$ignore>) {
219         my $line = $_;                            217         my $line = $_;
220                                                   218 
221         $line =~ s/\s*\n?$//;                     219         $line =~ s/\s*\n?$//;
222         $line =~ s/^\s*//;                        220         $line =~ s/^\s*//;
223         $line =~ s/\s+$//;                        221         $line =~ s/\s+$//;
224         $line =~ s/#.*$//;                        222         $line =~ s/#.*$//;
225                                                   223 
226         next if ($line =~ m/^\s*$/);              224         next if ($line =~ m/^\s*$/);
227         if (rfc822_valid($line)) {                225         if (rfc822_valid($line)) {
228             push(@ignore_emails, $line);          226             push(@ignore_emails, $line);
229         }                                         227         }
230     }                                             228     }
231     close($ignore);                               229     close($ignore);
232 }                                                 230 }
233                                                   231 
234 if ($#ARGV > 0) {                                 232 if ($#ARGV > 0) {
235     foreach (@ARGV) {                             233     foreach (@ARGV) {
236         if ($_ =~ /^-{1,2}self-test(?:=|$)/) {    234         if ($_ =~ /^-{1,2}self-test(?:=|$)/) {
237             die "$P: using --self-test does no    235             die "$P: using --self-test does not allow any other option or argument\n";
238         }                                         236         }
239     }                                             237     }
240 }                                                 238 }
241                                                   239 
242 if (!GetOptions(                                  240 if (!GetOptions(
243                 'email!' => \$email,              241                 'email!' => \$email,
244                 'git!' => \$email_git,            242                 'git!' => \$email_git,
245                 'git-all-signature-types!' =>     243                 'git-all-signature-types!' => \$email_git_all_signature_types,
246                 'git-blame!' => \$email_git_bl    244                 'git-blame!' => \$email_git_blame,
247                 'git-blame-signatures!' => \$e    245                 'git-blame-signatures!' => \$email_git_blame_signatures,
248                 'git-fallback!' => \$email_git    246                 'git-fallback!' => \$email_git_fallback,
249                 'git-chief-penguins!' => \$ema    247                 'git-chief-penguins!' => \$email_git_penguin_chiefs,
250                 'git-min-signatures=i' => \$em    248                 'git-min-signatures=i' => \$email_git_min_signatures,
251                 'git-max-maintainers=i' => \$e    249                 'git-max-maintainers=i' => \$email_git_max_maintainers,
252                 'git-min-percent=i' => \$email    250                 'git-min-percent=i' => \$email_git_min_percent,
253                 'git-since=s' => \$email_git_s    251                 'git-since=s' => \$email_git_since,
254                 'hg-since=s' => \$email_hg_sin    252                 'hg-since=s' => \$email_hg_since,
255                 'i|interactive!' => \$interact    253                 'i|interactive!' => \$interactive,
256                 'remove-duplicates!' => \$emai    254                 'remove-duplicates!' => \$email_remove_duplicates,
257                 'mailmap!' => \$email_use_mail    255                 'mailmap!' => \$email_use_mailmap,
258                 'm!' => \$email_maintainer,       256                 'm!' => \$email_maintainer,
259                 'r!' => \$email_reviewer,         257                 'r!' => \$email_reviewer,
260                 'n!' => \$email_usename,          258                 'n!' => \$email_usename,
261                 'l!' => \$email_list,             259                 'l!' => \$email_list,
262                 'fixes!' => \$email_fixes,        260                 'fixes!' => \$email_fixes,
263                 'moderated!' => \$email_modera    261                 'moderated!' => \$email_moderated_list,
264                 's!' => \$email_subscriber_lis    262                 's!' => \$email_subscriber_list,
265                 'multiline!' => \$output_multi    263                 'multiline!' => \$output_multiline,
266                 'roles!' => \$output_roles,       264                 'roles!' => \$output_roles,
267                 'rolestats!' => \$output_roles    265                 'rolestats!' => \$output_rolestats,
268                 'separator=s' => \$output_sepa    266                 'separator=s' => \$output_separator,
269                 'subsystem!' => \$subsystem,      267                 'subsystem!' => \$subsystem,
270                 'status!' => \$status,            268                 'status!' => \$status,
271                 'scm!' => \$scm,                  269                 'scm!' => \$scm,
272                 'tree!' => \$tree,                270                 'tree!' => \$tree,
273                 'web!' => \$web,                  271                 'web!' => \$web,
274                 'letters=s' => \$letters,         272                 'letters=s' => \$letters,
275                 'pattern-depth=i' => \$pattern    273                 'pattern-depth=i' => \$pattern_depth,
276                 'k|keywords!' => \$keywords,      274                 'k|keywords!' => \$keywords,
277                 'kf|keywords-in-file!' => \$ke << 
278                 'sections!' => \$sections,        275                 'sections!' => \$sections,
279                 'fe|file-emails!' => \$email_f    276                 'fe|file-emails!' => \$email_file_emails,
280                 'f|file' => \$from_filename,      277                 'f|file' => \$from_filename,
281                 'find-maintainer-files' => \$f    278                 'find-maintainer-files' => \$find_maintainer_files,
282                 'mpath|maintainer-path=s' => \    279                 'mpath|maintainer-path=s' => \$maintainer_path,
283                 'self-test:s' => \$self_test,     280                 'self-test:s' => \$self_test,
284                 'v|version' => \$version,         281                 'v|version' => \$version,
285                 'h|help|usage' => \$help,         282                 'h|help|usage' => \$help,
286                 )) {                              283                 )) {
287     die "$P: invalid argument - use --help if     284     die "$P: invalid argument - use --help if necessary\n";
288 }                                                 285 }
289                                                   286 
290 if ($help != 0) {                                 287 if ($help != 0) {
291     usage();                                      288     usage();
292     exit 0;                                       289     exit 0;
293 }                                                 290 }
294                                                   291 
295 if ($version != 0) {                              292 if ($version != 0) {
296     print("${P} ${V}\n");                         293     print("${P} ${V}\n");
297     exit 0;                                       294     exit 0;
298 }                                                 295 }
299                                                   296 
300 if (defined $self_test) {                         297 if (defined $self_test) {
301     read_all_maintainer_files();                  298     read_all_maintainer_files();
302     self_test();                                  299     self_test();
303     exit 0;                                       300     exit 0;
304 }                                                 301 }
305                                                   302 
306 if (-t STDIN && !@ARGV) {                         303 if (-t STDIN && !@ARGV) {
307     # We're talking to a terminal, but have no    304     # We're talking to a terminal, but have no command line arguments.
308     die "$P: missing patchfile or -f file - us    305     die "$P: missing patchfile or -f file - use --help if necessary\n";
309 }                                                 306 }
310                                                   307 
311 $output_multiline = 0 if ($output_separator ne    308 $output_multiline = 0 if ($output_separator ne ", ");
312 $output_rolestats = 1 if ($interactive);          309 $output_rolestats = 1 if ($interactive);
313 $output_roles = 1 if ($output_rolestats);         310 $output_roles = 1 if ($output_rolestats);
314                                                   311 
315 if ($sections || $letters ne "") {                312 if ($sections || $letters ne "") {
316     $sections = 1;                                313     $sections = 1;
317     $email = 0;                                   314     $email = 0;
318     $email_list = 0;                              315     $email_list = 0;
319     $scm = 0;                                     316     $scm = 0;
320     $status = 0;                                  317     $status = 0;
321     $subsystem = 0;                               318     $subsystem = 0;
322     $web = 0;                                     319     $web = 0;
323     $keywords = 0;                                320     $keywords = 0;
324     $keywords_in_file = 0;                     << 
325     $interactive = 0;                             321     $interactive = 0;
326 } else {                                          322 } else {
327     my $selections = $email + $scm + $status +    323     my $selections = $email + $scm + $status + $subsystem + $web;
328     if ($selections == 0) {                       324     if ($selections == 0) {
329         die "$P:  Missing required option: ema    325         die "$P:  Missing required option: email, scm, status, subsystem or web\n";
330     }                                             326     }
331 }                                                 327 }
332                                                   328 
333 if ($email &&                                     329 if ($email &&
334     ($email_maintainer + $email_reviewer +        330     ($email_maintainer + $email_reviewer +
335      $email_list + $email_subscriber_list +       331      $email_list + $email_subscriber_list +
336      $email_git + $email_git_penguin_chiefs +     332      $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
337     die "$P: Please select at least 1 email op    333     die "$P: Please select at least 1 email option\n";
338 }                                                 334 }
339                                                   335 
340 if ($tree && !top_of_kernel_tree($lk_path)) {     336 if ($tree && !top_of_kernel_tree($lk_path)) {
341     die "$P: The current directory does not ap    337     die "$P: The current directory does not appear to be "
342         . "a linux kernel source tree.\n";        338         . "a linux kernel source tree.\n";
343 }                                                 339 }
344                                                   340 
345 ## Read MAINTAINERS for type/value pairs          341 ## Read MAINTAINERS for type/value pairs
346                                                   342 
347 my @typevalue = ();                               343 my @typevalue = ();
348 my %keyword_hash;                                 344 my %keyword_hash;
349 my @mfiles = ();                                  345 my @mfiles = ();
350 my @self_test_info = ();                          346 my @self_test_info = ();
351                                                   347 
352 sub read_maintainer_file {                        348 sub read_maintainer_file {
353     my ($file) = @_;                              349     my ($file) = @_;
354                                                   350 
355     open (my $maint, '<', "$file")                351     open (my $maint, '<', "$file")
356         or die "$P: Can't open MAINTAINERS fil    352         or die "$P: Can't open MAINTAINERS file '$file': $!\n";
357     my $i = 1;                                    353     my $i = 1;
358     while (<$maint>) {                            354     while (<$maint>) {
359         my $line = $_;                            355         my $line = $_;
360         chomp $line;                              356         chomp $line;
361                                                   357 
362         if ($line =~ m/^([A-Z]):\s*(.*)/) {       358         if ($line =~ m/^([A-Z]):\s*(.*)/) {
363             my $type = $1;                        359             my $type = $1;
364             my $value = $2;                       360             my $value = $2;
365                                                   361 
366             ##Filename pattern matching           362             ##Filename pattern matching
367             if ($type eq "F" || $type eq "X")     363             if ($type eq "F" || $type eq "X") {
368                 $value =~ s@\.@\\\.@g;       #    364                 $value =~ s@\.@\\\.@g;       ##Convert . to \.
369                 $value =~ s/\*/\.\*/g;       #    365                 $value =~ s/\*/\.\*/g;       ##Convert * to .*
370                 $value =~ s/\?/\./g;         #    366                 $value =~ s/\?/\./g;         ##Convert ? to .
371                 ##if pattern is a directory an    367                 ##if pattern is a directory and it lacks a trailing slash, add one
372                 if ((-d $value)) {                368                 if ((-d $value)) {
373                     $value =~ s@([^/])$@$1/@;     369                     $value =~ s@([^/])$@$1/@;
374                 }                                 370                 }
375             } elsif ($type eq "K") {              371             } elsif ($type eq "K") {
376                 $keyword_hash{@typevalue} = $v    372                 $keyword_hash{@typevalue} = $value;
377             }                                     373             }
378             push(@typevalue, "$type:$value");     374             push(@typevalue, "$type:$value");
379         } elsif (!(/^\s*$/ || /^\s*\#/)) {        375         } elsif (!(/^\s*$/ || /^\s*\#/)) {
380             push(@typevalue, $line);              376             push(@typevalue, $line);
381         }                                         377         }
382         if (defined $self_test) {                 378         if (defined $self_test) {
383             push(@self_test_info, {file=>$file    379             push(@self_test_info, {file=>$file, linenr=>$i, line=>$line});
384         }                                         380         }
385         $i++;                                     381         $i++;
386     }                                             382     }
387     close($maint);                                383     close($maint);
388 }                                                 384 }
389                                                   385 
390 sub find_is_maintainer_file {                     386 sub find_is_maintainer_file {
391     my ($file) = $_;                              387     my ($file) = $_;
392     return if ($file !~ m@/MAINTAINERS$@);        388     return if ($file !~ m@/MAINTAINERS$@);
393     $file = $File::Find::name;                    389     $file = $File::Find::name;
394     return if (! -f $file);                       390     return if (! -f $file);
395     push(@mfiles, $file);                         391     push(@mfiles, $file);
396 }                                                 392 }
397                                                   393 
398 sub find_ignore_git {                             394 sub find_ignore_git {
399     return grep { $_ !~ /^\.git$/; } @_;          395     return grep { $_ !~ /^\.git$/; } @_;
400 }                                                 396 }
401                                                   397 
402 read_all_maintainer_files();                      398 read_all_maintainer_files();
403                                                   399 
404 sub read_all_maintainer_files {                   400 sub read_all_maintainer_files {
405     my $path = "${lk_path}MAINTAINERS";           401     my $path = "${lk_path}MAINTAINERS";
406     if (defined $maintainer_path) {               402     if (defined $maintainer_path) {
407         $path = $maintainer_path;                 403         $path = $maintainer_path;
408         # Perl Cookbook tilde expansion if nec    404         # Perl Cookbook tilde expansion if necessary
409         $path =~ s@^~([^/]*)@ $1 ? (getpwnam($    405         $path =~ s@^~([^/]*)@ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7])@ex;
410     }                                             406     }
411                                                   407 
412     if (-d $path) {                               408     if (-d $path) {
413         $path .= '/' if ($path !~ m@/$@);         409         $path .= '/' if ($path !~ m@/$@);
414         if ($find_maintainer_files) {             410         if ($find_maintainer_files) {
415             find( { wanted => \&find_is_mainta    411             find( { wanted => \&find_is_maintainer_file,
416                     preprocess => \&find_ignor    412                     preprocess => \&find_ignore_git,
417                     no_chdir => 1,                413                     no_chdir => 1,
418                 }, "$path");                      414                 }, "$path");
419         } else {                                  415         } else {
420             opendir(DIR, "$path") or die $!;      416             opendir(DIR, "$path") or die $!;
421             my @files = readdir(DIR);             417             my @files = readdir(DIR);
422             closedir(DIR);                        418             closedir(DIR);
423             foreach my $file (@files) {           419             foreach my $file (@files) {
424                 push(@mfiles, "$path$file") if    420                 push(@mfiles, "$path$file") if ($file !~ /^\./);
425             }                                     421             }
426         }                                         422         }
427     } elsif (-f "$path") {                        423     } elsif (-f "$path") {
428         push(@mfiles, "$path");                   424         push(@mfiles, "$path");
429     } else {                                      425     } else {
430         die "$P: MAINTAINER file not found '$p    426         die "$P: MAINTAINER file not found '$path'\n";
431     }                                             427     }
432     die "$P: No MAINTAINER files found in '$pa    428     die "$P: No MAINTAINER files found in '$path'\n" if (scalar(@mfiles) == 0);
433     foreach my $file (@mfiles) {                  429     foreach my $file (@mfiles) {
434         read_maintainer_file("$file");            430         read_maintainer_file("$file");
435     }                                             431     }
436 }                                                 432 }
437                                                   433 
438 sub maintainers_in_file {                         434 sub maintainers_in_file {
439     my ($file) = @_;                              435     my ($file) = @_;
440                                                   436 
441     return if ($file =~ m@\bMAINTAINERS$@);       437     return if ($file =~ m@\bMAINTAINERS$@);
442                                                   438 
443     if (-f $file && ($email_file_emails || $fi    439     if (-f $file && ($email_file_emails || $file =~ /\.yaml$/)) {
444         open(my $f, '<', $file)                   440         open(my $f, '<', $file)
445             or die "$P: Can't open $file: $!\n    441             or die "$P: Can't open $file: $!\n";
446         my $text = do { local($/) ; <$f> };       442         my $text = do { local($/) ; <$f> };
447         close($f);                                443         close($f);
448                                                   444 
449         my @poss_addr = $text =~ m$[\p{L}\"\' < !! 445         my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
450         push(@file_emails, clean_file_emails(@    446         push(@file_emails, clean_file_emails(@poss_addr));
451     }                                             447     }
452 }                                                 448 }
453                                                   449 
454 #                                                 450 #
455 # Read mail address map                           451 # Read mail address map
456 #                                                 452 #
457                                                   453 
458 my $mailmap;                                      454 my $mailmap;
459                                                   455 
460 read_mailmap();                                   456 read_mailmap();
461                                                   457 
462 sub read_mailmap {                                458 sub read_mailmap {
463     $mailmap = {                                  459     $mailmap = {
464         names => {},                              460         names => {},
465         addresses => {}                           461         addresses => {}
466     };                                            462     };
467                                                   463 
468     return if (!$email_use_mailmap || !(-f "${    464     return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
469                                                   465 
470     open(my $mailmap_file, '<', "${lk_path}.ma    466     open(my $mailmap_file, '<', "${lk_path}.mailmap")
471         or warn "$P: Can't open .mailmap: $!\n    467         or warn "$P: Can't open .mailmap: $!\n";
472                                                   468 
473     while (<$mailmap_file>) {                     469     while (<$mailmap_file>) {
474         s/#.*$//; #strip comments                 470         s/#.*$//; #strip comments
475         s/^\s+|\s+$//g; #trim                     471         s/^\s+|\s+$//g; #trim
476                                                   472 
477         next if (/^\s*$/); #skip empty lines      473         next if (/^\s*$/); #skip empty lines
478         #entries have one of the following for    474         #entries have one of the following formats:
479         # name1 <mail1>                           475         # name1 <mail1>
480         # <mail1> <mail2>                         476         # <mail1> <mail2>
481         # name1 <mail1> <mail2>                   477         # name1 <mail1> <mail2>
482         # name1 <mail1> name2 <mail2>             478         # name1 <mail1> name2 <mail2>
483         # (see man git-shortlog)                  479         # (see man git-shortlog)
484                                                   480 
485         if (/^([^<]+)<([^>]+)>$/) {               481         if (/^([^<]+)<([^>]+)>$/) {
486             my $real_name = $1;                   482             my $real_name = $1;
487             my $address = $2;                     483             my $address = $2;
488                                                   484 
489             $real_name =~ s/\s+$//;               485             $real_name =~ s/\s+$//;
490             ($real_name, $address) = parse_ema    486             ($real_name, $address) = parse_email("$real_name <$address>");
491             $mailmap->{names}->{$address} = $r    487             $mailmap->{names}->{$address} = $real_name;
492                                                   488 
493         } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {     489         } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
494             my $real_address = $1;                490             my $real_address = $1;
495             my $wrong_address = $2;               491             my $wrong_address = $2;
496                                                   492 
497             $mailmap->{addresses}->{$wrong_add    493             $mailmap->{addresses}->{$wrong_address} = $real_address;
498                                                   494 
499         } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/    495         } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
500             my $real_name = $1;                   496             my $real_name = $1;
501             my $real_address = $2;                497             my $real_address = $2;
502             my $wrong_address = $3;               498             my $wrong_address = $3;
503                                                   499 
504             $real_name =~ s/\s+$//;               500             $real_name =~ s/\s+$//;
505             ($real_name, $real_address) =         501             ($real_name, $real_address) =
506                 parse_email("$real_name <$real    502                 parse_email("$real_name <$real_address>");
507             $mailmap->{names}->{$wrong_address    503             $mailmap->{names}->{$wrong_address} = $real_name;
508             $mailmap->{addresses}->{$wrong_add    504             $mailmap->{addresses}->{$wrong_address} = $real_address;
509                                                   505 
510         } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^    506         } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
511             my $real_name = $1;                   507             my $real_name = $1;
512             my $real_address = $2;                508             my $real_address = $2;
513             my $wrong_name = $3;                  509             my $wrong_name = $3;
514             my $wrong_address = $4;               510             my $wrong_address = $4;
515                                                   511 
516             $real_name =~ s/\s+$//;               512             $real_name =~ s/\s+$//;
517             ($real_name, $real_address) =         513             ($real_name, $real_address) =
518                 parse_email("$real_name <$real    514                 parse_email("$real_name <$real_address>");
519                                                   515 
520             $wrong_name =~ s/\s+$//;              516             $wrong_name =~ s/\s+$//;
521             ($wrong_name, $wrong_address) =       517             ($wrong_name, $wrong_address) =
522                 parse_email("$wrong_name <$wro    518                 parse_email("$wrong_name <$wrong_address>");
523                                                   519 
524             my $wrong_email = format_email($wr    520             my $wrong_email = format_email($wrong_name, $wrong_address, 1);
525             $mailmap->{names}->{$wrong_email}     521             $mailmap->{names}->{$wrong_email} = $real_name;
526             $mailmap->{addresses}->{$wrong_ema    522             $mailmap->{addresses}->{$wrong_email} = $real_address;
527         }                                         523         }
528     }                                             524     }
529     close($mailmap_file);                         525     close($mailmap_file);
530 }                                                 526 }
531                                                   527 
532 ## use the filenames on the command line or fi    528 ## use the filenames on the command line or find the filenames in the patchfiles
533                                                   529 
534 if (!@ARGV) {                                     530 if (!@ARGV) {
535     push(@ARGV, "&STDIN");                        531     push(@ARGV, "&STDIN");
536 }                                                 532 }
537                                                   533 
538 foreach my $file (@ARGV) {                        534 foreach my $file (@ARGV) {
539     if ($file ne "&STDIN") {                      535     if ($file ne "&STDIN") {
540         $file = canonpath($file);                 536         $file = canonpath($file);
541         ##if $file is a directory and it lacks    537         ##if $file is a directory and it lacks a trailing slash, add one
542         if ((-d $file)) {                         538         if ((-d $file)) {
543             $file =~ s@([^/])$@$1/@;              539             $file =~ s@([^/])$@$1/@;
544         } elsif (!(-f $file)) {                   540         } elsif (!(-f $file)) {
545             die "$P: file '${file}' not found\    541             die "$P: file '${file}' not found\n";
546         }                                         542         }
547     }                                             543     }
548     if ($from_filename && (vcs_exists() && !vc    544     if ($from_filename && (vcs_exists() && !vcs_file_exists($file))) {
549         warn "$P: file '$file' not found in ve    545         warn "$P: file '$file' not found in version control $!\n";
550     }                                             546     }
551     if ($from_filename || ($file ne "&STDIN" &    547     if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
552         $file =~ s/^\Q${cur_path}\E//;  #strip    548         $file =~ s/^\Q${cur_path}\E//;  #strip any absolute path
553         $file =~ s/^\Q${lk_path}\E//;   #or th    549         $file =~ s/^\Q${lk_path}\E//;   #or the path to the lk tree
554         push(@files, $file);                      550         push(@files, $file);
555         if ($file ne "MAINTAINERS" && -f $file !! 551         if ($file ne "MAINTAINERS" && -f $file && $keywords) {
556             open(my $f, '<', $file)               552             open(my $f, '<', $file)
557                 or die "$P: Can't open $file:     553                 or die "$P: Can't open $file: $!\n";
558             my $text = do { local($/) ; <$f> }    554             my $text = do { local($/) ; <$f> };
559             close($f);                            555             close($f);
560             foreach my $line (keys %keyword_ha !! 556             if ($keywords) {
561                 if ($text =~ m/$keyword_hash{$ !! 557                 foreach my $line (keys %keyword_hash) {
562                     push(@keyword_tvi, $line); !! 558                     if ($text =~ m/$keyword_hash{$line}/x) {
                                                   >> 559                         push(@keyword_tvi, $line);
                                                   >> 560                     }
563                 }                                 561                 }
564             }                                     562             }
565         }                                         563         }
566     } else {                                      564     } else {
567         my $file_cnt = @files;                    565         my $file_cnt = @files;
568         my $lastfile;                             566         my $lastfile;
569                                                   567 
570         open(my $patch, "< $file")                568         open(my $patch, "< $file")
571             or die "$P: Can't open $file: $!\n    569             or die "$P: Can't open $file: $!\n";
572                                                   570 
573         # We can check arbitrary information b    571         # We can check arbitrary information before the patch
574         # like the commit message, mail header    572         # like the commit message, mail headers, etc...
575         # This allows us to match arbitrary ke    573         # This allows us to match arbitrary keywords against any part
576         # of a git format-patch generated file    574         # of a git format-patch generated file (subject tags, etc...)
577                                                   575 
578         my $patch_prefix = "";                    576         my $patch_prefix = "";                  #Parsing the intro
579                                                   577 
580         while (<$patch>) {                        578         while (<$patch>) {
581             my $patch_line = $_;                  579             my $patch_line = $_;
582             if (m/^ mode change [0-7]+ => [0-7    580             if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) {
583                 my $filename = $1;                581                 my $filename = $1;
584                 push(@files, $filename);          582                 push(@files, $filename);
585             } elsif (m/^rename (?:from|to) (\S    583             } elsif (m/^rename (?:from|to) (\S+)\s*$/) {
586                 my $filename = $1;                584                 my $filename = $1;
587                 push(@files, $filename);          585                 push(@files, $filename);
588             } elsif (m/^diff --git a\/(\S+) b\    586             } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) {
589                 my $filename1 = $1;               587                 my $filename1 = $1;
590                 my $filename2 = $2;               588                 my $filename2 = $2;
591                 push(@files, $filename1);         589                 push(@files, $filename1);
592                 push(@files, $filename2);         590                 push(@files, $filename2);
593             } elsif (m/^Fixes:\s+([0-9a-fA-F]{    591             } elsif (m/^Fixes:\s+([0-9a-fA-F]{6,40})/) {
594                 push(@fixes, $1) if ($email_fi    592                 push(@fixes, $1) if ($email_fixes);
595             } elsif (m/^\+\+\+\s+(\S+)/ or m/^    593             } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
596                 my $filename = $1;                594                 my $filename = $1;
597                 $filename =~ s@^[^/]*/@@;         595                 $filename =~ s@^[^/]*/@@;
598                 $filename =~ s@\n@@;              596                 $filename =~ s@\n@@;
599                 $lastfile = $filename;            597                 $lastfile = $filename;
600                 push(@files, $filename);          598                 push(@files, $filename);
601                 $patch_prefix = "^[+-].*";        599                 $patch_prefix = "^[+-].*";      #Now parsing the actual patch
602             } elsif (m/^\@\@ -(\d+),(\d+)/) {     600             } elsif (m/^\@\@ -(\d+),(\d+)/) {
603                 if ($email_git_blame) {           601                 if ($email_git_blame) {
604                     push(@range, "$lastfile:$1    602                     push(@range, "$lastfile:$1:$2");
605                 }                                 603                 }
606             } elsif ($keywords) {                 604             } elsif ($keywords) {
607                 foreach my $line (keys %keywor    605                 foreach my $line (keys %keyword_hash) {
608                     if ($patch_line =~ m/${pat    606                     if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
609                         push(@keyword_tvi, $li    607                         push(@keyword_tvi, $line);
610                     }                             608                     }
611                 }                                 609                 }
612             }                                     610             }
613         }                                         611         }
614         close($patch);                            612         close($patch);
615                                                   613 
616         if ($file_cnt == @files) {                614         if ($file_cnt == @files) {
617             warn "$P: file '${file}' doesn't a    615             warn "$P: file '${file}' doesn't appear to be a patch.  "
618                 . "Add -f to options?\n";         616                 . "Add -f to options?\n";
619         }                                         617         }
620         @files = sort_and_uniq(@files);           618         @files = sort_and_uniq(@files);
621     }                                             619     }
622 }                                                 620 }
623                                                   621 
624 @file_emails = uniq(@file_emails);                622 @file_emails = uniq(@file_emails);
625 @fixes = uniq(@fixes);                            623 @fixes = uniq(@fixes);
626                                                   624 
627 my %email_hash_name;                              625 my %email_hash_name;
628 my %email_hash_address;                           626 my %email_hash_address;
629 my @email_to = ();                                627 my @email_to = ();
630 my %hash_list_to;                                 628 my %hash_list_to;
631 my @list_to = ();                                 629 my @list_to = ();
632 my @scm = ();                                     630 my @scm = ();
633 my @web = ();                                     631 my @web = ();
634 my @subsystem = ();                               632 my @subsystem = ();
635 my @status = ();                                  633 my @status = ();
636 my %deduplicate_name_hash = ();                   634 my %deduplicate_name_hash = ();
637 my %deduplicate_address_hash = ();                635 my %deduplicate_address_hash = ();
638                                                   636 
639 my @maintainers = get_maintainers();              637 my @maintainers = get_maintainers();
640 if (@maintainers) {                               638 if (@maintainers) {
641     @maintainers = merge_email(@maintainers);     639     @maintainers = merge_email(@maintainers);
642     output(@maintainers);                         640     output(@maintainers);
643 }                                                 641 }
644                                                   642 
645 if ($scm) {                                       643 if ($scm) {
646     @scm = uniq(@scm);                            644     @scm = uniq(@scm);
647     output(@scm);                                 645     output(@scm);
648 }                                                 646 }
649                                                   647 
650 if ($status) {                                    648 if ($status) {
651     @status = uniq(@status);                      649     @status = uniq(@status);
652     output(@status);                              650     output(@status);
653 }                                                 651 }
654                                                   652 
655 if ($subsystem) {                                 653 if ($subsystem) {
656     @subsystem = uniq(@subsystem);                654     @subsystem = uniq(@subsystem);
657     output(@subsystem);                           655     output(@subsystem);
658 }                                                 656 }
659                                                   657 
660 if ($web) {                                       658 if ($web) {
661     @web = uniq(@web);                            659     @web = uniq(@web);
662     output(@web);                                 660     output(@web);
663 }                                                 661 }
664                                                   662 
665 exit($exit);                                      663 exit($exit);
666                                                   664 
667 sub self_test {                                   665 sub self_test {
668     my @lsfiles = ();                             666     my @lsfiles = ();
669     my @good_links = ();                          667     my @good_links = ();
670     my @bad_links = ();                           668     my @bad_links = ();
671     my @section_headers = ();                     669     my @section_headers = ();
672     my $index = 0;                                670     my $index = 0;
673                                                   671 
674     @lsfiles = vcs_list_files($lk_path);          672     @lsfiles = vcs_list_files($lk_path);
675                                                   673 
676     for my $x (@self_test_info) {                 674     for my $x (@self_test_info) {
677         $index++;                                 675         $index++;
678                                                   676 
679         ## Section header duplication and miss    677         ## Section header duplication and missing section content
680         if (($self_test eq "" || $self_test =~    678         if (($self_test eq "" || $self_test =~ /\bsections\b/) &&
681             $x->{line} =~ /^\S[^:]/ &&            679             $x->{line} =~ /^\S[^:]/ &&
682             defined $self_test_info[$index] &&    680             defined $self_test_info[$index] &&
683             $self_test_info[$index]->{line} =~    681             $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) {
684             my $has_S = 0;                        682             my $has_S = 0;
685             my $has_F = 0;                        683             my $has_F = 0;
686             my $has_ML = 0;                       684             my $has_ML = 0;
687             my $status = "";                      685             my $status = "";
688             if (grep(m@^\Q$x->{line}\E@, @sect    686             if (grep(m@^\Q$x->{line}\E@, @section_headers)) {
689                 print("$x->{file}:$x->{linenr}    687                 print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n");
690             } else {                              688             } else {
691                 push(@section_headers, $x->{li    689                 push(@section_headers, $x->{line});
692             }                                     690             }
693             my $nextline = $index;                691             my $nextline = $index;
694             while (defined $self_test_info[$ne    692             while (defined $self_test_info[$nextline] &&
695                    $self_test_info[$nextline]-    693                    $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) {
696                 my $type = $1;                    694                 my $type = $1;
697                 my $value = $2;                   695                 my $value = $2;
698                 if ($type eq "S") {               696                 if ($type eq "S") {
699                     $has_S = 1;                   697                     $has_S = 1;
700                     $status = $value;             698                     $status = $value;
701                 } elsif ($type eq "F" || $type    699                 } elsif ($type eq "F" || $type eq "N") {
702                     $has_F = 1;                   700                     $has_F = 1;
703                 } elsif ($type eq "M" || $type    701                 } elsif ($type eq "M" || $type eq "R" || $type eq "L") {
704                     $has_ML = 1;                  702                     $has_ML = 1;
705                 }                                 703                 }
706                 $nextline++;                      704                 $nextline++;
707             }                                     705             }
708             if (!$has_ML && $status !~ /orphan    706             if (!$has_ML && $status !~ /orphan|obsolete/i) {
709                 print("$x->{file}:$x->{linenr}    707                 print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n");
710             }                                     708             }
711             if (!$has_S) {                        709             if (!$has_S) {
712                 print("$x->{file}:$x->{linenr}    710                 print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n");
713             }                                     711             }
714             if (!$has_F) {                        712             if (!$has_F) {
715                 print("$x->{file}:$x->{linenr}    713                 print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n");
716             }                                     714             }
717         }                                         715         }
718                                                   716 
719         next if ($x->{line} !~ /^([A-Z]):\s*(.    717         next if ($x->{line} !~ /^([A-Z]):\s*(.*)/);
720                                                   718 
721         my $type = $1;                            719         my $type = $1;
722         my $value = $2;                           720         my $value = $2;
723                                                   721 
724         ## Filename pattern matching              722         ## Filename pattern matching
725         if (($type eq "F" || $type eq "X") &&     723         if (($type eq "F" || $type eq "X") &&
726             ($self_test eq "" || $self_test =~    724             ($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
727             $value =~ s@\.@\\\.@g;       ##Con    725             $value =~ s@\.@\\\.@g;       ##Convert . to \.
728             $value =~ s/\*/\.\*/g;       ##Con    726             $value =~ s/\*/\.\*/g;       ##Convert * to .*
729             $value =~ s/\?/\./g;         ##Con    727             $value =~ s/\?/\./g;         ##Convert ? to .
730             ##if pattern is a directory and it    728             ##if pattern is a directory and it lacks a trailing slash, add one
731             if ((-d $value)) {                    729             if ((-d $value)) {
732                 $value =~ s@([^/])$@$1/@;         730                 $value =~ s@([^/])$@$1/@;
733             }                                     731             }
734             if (!grep(m@^$value@, @lsfiles)) {    732             if (!grep(m@^$value@, @lsfiles)) {
735                 print("$x->{file}:$x->{linenr}    733                 print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n");
736             }                                     734             }
737                                                   735 
738         ## Link reachability                      736         ## Link reachability
739         } elsif (($type eq "W" || $type eq "Q"    737         } elsif (($type eq "W" || $type eq "Q" || $type eq "B") &&
740                  $value =~ /^https?:/ &&          738                  $value =~ /^https?:/ &&
741                  ($self_test eq "" || $self_te    739                  ($self_test eq "" || $self_test =~ /\blinks\b/)) {
742             next if (grep(m@^\Q$value\E$@, @go    740             next if (grep(m@^\Q$value\E$@, @good_links));
743             my $isbad = 0;                        741             my $isbad = 0;
744             if (grep(m@^\Q$value\E$@, @bad_lin    742             if (grep(m@^\Q$value\E$@, @bad_links)) {
745                 $isbad = 1;                       743                 $isbad = 1;
746             } else {                              744             } else {
747                 my $output = `wget --spider -q    745                 my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`;
748                 if ($? == 0) {                    746                 if ($? == 0) {
749                     push(@good_links, $value);    747                     push(@good_links, $value);
750                 } else {                          748                 } else {
751                     push(@bad_links, $value);     749                     push(@bad_links, $value);
752                     $isbad = 1;                   750                     $isbad = 1;
753                 }                                 751                 }
754             }                                     752             }
755             if ($isbad) {                         753             if ($isbad) {
756                 print("$x->{file}:$x->{linenr}    754                 print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
757             }                                     755             }
758                                                   756 
759         ## SCM reachability                       757         ## SCM reachability
760         } elsif ($type eq "T" &&                  758         } elsif ($type eq "T" &&
761                  ($self_test eq "" || $self_te    759                  ($self_test eq "" || $self_test =~ /\bscm\b/)) {
762             next if (grep(m@^\Q$value\E$@, @go    760             next if (grep(m@^\Q$value\E$@, @good_links));
763             my $isbad = 0;                        761             my $isbad = 0;
764             if (grep(m@^\Q$value\E$@, @bad_lin    762             if (grep(m@^\Q$value\E$@, @bad_links)) {
765                 $isbad = 1;                       763                 $isbad = 1;
766             } elsif ($value !~ /^(?:git|quilt|    764             } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) {
767                 print("$x->{file}:$x->{linenr}    765                 print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n");
768             } elsif ($value =~ /^git\s+(\S+)(\    766             } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) {
769                 my $url = $1;                     767                 my $url = $1;
770                 my $branch = "";                  768                 my $branch = "";
771                 $branch = $3 if $3;               769                 $branch = $3 if $3;
772                 my $output = `git ls-remote --    770                 my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`;
773                 if ($? == 0) {                    771                 if ($? == 0) {
774                     push(@good_links, $value);    772                     push(@good_links, $value);
775                 } else {                          773                 } else {
776                     push(@bad_links, $value);     774                     push(@bad_links, $value);
777                     $isbad = 1;                   775                     $isbad = 1;
778                 }                                 776                 }
779             } elsif ($value =~ /^(?:quilt|hg)\    777             } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) {
780                 my $url = $1;                     778                 my $url = $1;
781                 my $output = `wget --spider -q    779                 my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`;
782                 if ($? == 0) {                    780                 if ($? == 0) {
783                     push(@good_links, $value);    781                     push(@good_links, $value);
784                 } else {                          782                 } else {
785                     push(@bad_links, $value);     783                     push(@bad_links, $value);
786                     $isbad = 1;                   784                     $isbad = 1;
787                 }                                 785                 }
788             }                                     786             }
789             if ($isbad) {                         787             if ($isbad) {
790                 print("$x->{file}:$x->{linenr}    788                 print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
791             }                                     789             }
792         }                                         790         }
793     }                                             791     }
794 }                                                 792 }
795                                                   793 
796 sub ignore_email_address {                        794 sub ignore_email_address {
797     my ($address) = @_;                           795     my ($address) = @_;
798                                                   796 
799     foreach my $ignore (@ignore_emails) {         797     foreach my $ignore (@ignore_emails) {
800         return 1 if ($ignore eq $address);        798         return 1 if ($ignore eq $address);
801     }                                             799     }
802                                                   800 
803     return 0;                                     801     return 0;
804 }                                                 802 }
805                                                   803 
806 sub range_is_maintained {                         804 sub range_is_maintained {
807     my ($start, $end) = @_;                       805     my ($start, $end) = @_;
808                                                   806 
809     for (my $i = $start; $i < $end; $i++) {       807     for (my $i = $start; $i < $end; $i++) {
810         my $line = $typevalue[$i];                808         my $line = $typevalue[$i];
811         if ($line =~ m/^([A-Z]):\s*(.*)/) {       809         if ($line =~ m/^([A-Z]):\s*(.*)/) {
812             my $type = $1;                        810             my $type = $1;
813             my $value = $2;                       811             my $value = $2;
814             if ($type eq 'S') {                   812             if ($type eq 'S') {
815                 if ($value =~ /(maintain|suppo    813                 if ($value =~ /(maintain|support)/i) {
816                     return 1;                     814                     return 1;
817                 }                                 815                 }
818             }                                     816             }
819         }                                         817         }
820     }                                             818     }
821     return 0;                                     819     return 0;
822 }                                                 820 }
823                                                   821 
824 sub range_has_maintainer {                        822 sub range_has_maintainer {
825     my ($start, $end) = @_;                       823     my ($start, $end) = @_;
826                                                   824 
827     for (my $i = $start; $i < $end; $i++) {       825     for (my $i = $start; $i < $end; $i++) {
828         my $line = $typevalue[$i];                826         my $line = $typevalue[$i];
829         if ($line =~ m/^([A-Z]):\s*(.*)/) {       827         if ($line =~ m/^([A-Z]):\s*(.*)/) {
830             my $type = $1;                        828             my $type = $1;
831             my $value = $2;                       829             my $value = $2;
832             if ($type eq 'M') {                   830             if ($type eq 'M') {
833                 return 1;                         831                 return 1;
834             }                                     832             }
835         }                                         833         }
836     }                                             834     }
837     return 0;                                     835     return 0;
838 }                                                 836 }
839                                                   837 
840 sub get_maintainers {                             838 sub get_maintainers {
841     %email_hash_name = ();                        839     %email_hash_name = ();
842     %email_hash_address = ();                     840     %email_hash_address = ();
843     %commit_author_hash = ();                     841     %commit_author_hash = ();
844     %commit_signer_hash = ();                     842     %commit_signer_hash = ();
845     @email_to = ();                               843     @email_to = ();
846     %hash_list_to = ();                           844     %hash_list_to = ();
847     @list_to = ();                                845     @list_to = ();
848     @scm = ();                                    846     @scm = ();
849     @web = ();                                    847     @web = ();
850     @subsystem = ();                              848     @subsystem = ();
851     @status = ();                                 849     @status = ();
852     %deduplicate_name_hash = ();                  850     %deduplicate_name_hash = ();
853     %deduplicate_address_hash = ();               851     %deduplicate_address_hash = ();
854     if ($email_git_all_signature_types) {         852     if ($email_git_all_signature_types) {
855         $signature_pattern = "(.+?)[Bb][Yy]:";    853         $signature_pattern = "(.+?)[Bb][Yy]:";
856     } else {                                      854     } else {
857         $signature_pattern = "\(" . join("|",     855         $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
858     }                                             856     }
859                                                   857 
860     # Find responsible parties                    858     # Find responsible parties
861                                                   859 
862     my %exact_pattern_match_hash = ();            860     my %exact_pattern_match_hash = ();
863                                                   861 
864     foreach my $file (@files) {                   862     foreach my $file (@files) {
865                                                   863 
866         my %hash;                                 864         my %hash;
867         my $tvi = find_first_section();           865         my $tvi = find_first_section();
868         while ($tvi < @typevalue) {               866         while ($tvi < @typevalue) {
869             my $start = find_starting_index($t    867             my $start = find_starting_index($tvi);
870             my $end = find_ending_index($tvi);    868             my $end = find_ending_index($tvi);
871             my $exclude = 0;                      869             my $exclude = 0;
872             my $i;                                870             my $i;
873                                                   871 
874             #Do not match excluded file patter    872             #Do not match excluded file patterns
875                                                   873 
876             for ($i = $start; $i < $end; $i++)    874             for ($i = $start; $i < $end; $i++) {
877                 my $line = $typevalue[$i];        875                 my $line = $typevalue[$i];
878                 if ($line =~ m/^([A-Z]):\s*(.*    876                 if ($line =~ m/^([A-Z]):\s*(.*)/) {
879                     my $type = $1;                877                     my $type = $1;
880                     my $value = $2;               878                     my $value = $2;
881                     if ($type eq 'X') {           879                     if ($type eq 'X') {
882                         if (file_match_pattern    880                         if (file_match_pattern($file, $value)) {
883                             $exclude = 1;         881                             $exclude = 1;
884                             last;                 882                             last;
885                         }                         883                         }
886                     }                             884                     }
887                 }                                 885                 }
888             }                                     886             }
889                                                   887 
890             if (!$exclude) {                      888             if (!$exclude) {
891                 for ($i = $start; $i < $end; $    889                 for ($i = $start; $i < $end; $i++) {
892                     my $line = $typevalue[$i];    890                     my $line = $typevalue[$i];
893                     if ($line =~ m/^([A-Z]):\s    891                     if ($line =~ m/^([A-Z]):\s*(.*)/) {
894                         my $type = $1;            892                         my $type = $1;
895                         my $value = $2;           893                         my $value = $2;
896                         if ($type eq 'F') {       894                         if ($type eq 'F') {
897                             if (file_match_pat    895                             if (file_match_pattern($file, $value)) {
898                                 my $value_pd =    896                                 my $value_pd = ($value =~ tr@/@@);
899                                 my $file_pd =     897                                 my $file_pd = ($file  =~ tr@/@@);
900                                 $value_pd++ if    898                                 $value_pd++ if (substr($value,-1,1) ne "/");
901                                 $value_pd = -1    899                                 $value_pd = -1 if ($value =~ /^\.\*/);
902                                 if ($value_pd     900                                 if ($value_pd >= $file_pd &&
903                                     range_is_m    901                                     range_is_maintained($start, $end) &&
904                                     range_has_    902                                     range_has_maintainer($start, $end)) {
905                                     $exact_pat    903                                     $exact_pattern_match_hash{$file} = 1;
906                                 }                 904                                 }
907                                 if ($pattern_d    905                                 if ($pattern_depth == 0 ||
908                                     (($file_pd    906                                     (($file_pd - $value_pd) < $pattern_depth)) {
909                                     $hash{$tvi    907                                     $hash{$tvi} = $value_pd;
910                                 }                 908                                 }
911                             }                     909                             }
912                         } elsif ($type eq 'N')    910                         } elsif ($type eq 'N') {
913                             if ($file =~ m/$va    911                             if ($file =~ m/$value/x) {
914                                 $hash{$tvi} =     912                                 $hash{$tvi} = 0;
915                             }                     913                             }
916                         }                         914                         }
917                     }                             915                     }
918                 }                                 916                 }
919             }                                     917             }
920             $tvi = $end + 1;                      918             $tvi = $end + 1;
921         }                                         919         }
922                                                   920 
923         foreach my $line (sort {$hash{$b} <=>     921         foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
924             add_categories($line, "");         !! 922             add_categories($line);
925             if ($sections) {                      923             if ($sections) {
926                 my $i;                            924                 my $i;
927                 my $start = find_starting_inde    925                 my $start = find_starting_index($line);
928                 my $end = find_ending_index($l    926                 my $end = find_ending_index($line);
929                 for ($i = $start; $i < $end; $    927                 for ($i = $start; $i < $end; $i++) {
930                     my $line = $typevalue[$i];    928                     my $line = $typevalue[$i];
931                     if ($line =~ /^[FX]:/) {      929                     if ($line =~ /^[FX]:/) {            ##Restore file patterns
932                         $line =~ s/([^\\])\.([    930                         $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
933                         $line =~ s/([^\\])\.$/    931                         $line =~ s/([^\\])\.$/$1\?/g;   ##Convert . back to ?
934                         $line =~ s/\\\./\./g;     932                         $line =~ s/\\\./\./g;           ##Convert \. to .
935                         $line =~ s/\.\*/\*/g;     933                         $line =~ s/\.\*/\*/g;           ##Convert .* to *
936                     }                             934                     }
937                     my $count = $line =~ s/^([    935                     my $count = $line =~ s/^([A-Z]):/$1:\t/g;
938                     if ($letters eq "" || (!$c    936                     if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
939                         print("$line\n");         937                         print("$line\n");
940                     }                             938                     }
941                 }                                 939                 }
942                 print("\n");                      940                 print("\n");
943             }                                     941             }
944         }                                         942         }
945                                                   943 
946         maintainers_in_file($file);               944         maintainers_in_file($file);
947     }                                             945     }
948                                                   946 
949     if ($keywords) {                              947     if ($keywords) {
950         @keyword_tvi = sort_and_uniq(@keyword_    948         @keyword_tvi = sort_and_uniq(@keyword_tvi);
951         foreach my $line (@keyword_tvi) {         949         foreach my $line (@keyword_tvi) {
952             add_categories($line, ":Keyword:$k !! 950             add_categories($line);
953         }                                         951         }
954     }                                             952     }
955                                                   953 
956     foreach my $email (@email_to, @list_to) {     954     foreach my $email (@email_to, @list_to) {
957         $email->[0] = deduplicate_email($email    955         $email->[0] = deduplicate_email($email->[0]);
958     }                                             956     }
959                                                   957 
960     foreach my $file (@files) {                   958     foreach my $file (@files) {
961         if ($email &&                             959         if ($email &&
962             ($email_git ||                        960             ($email_git ||
963              ($email_git_fallback &&              961              ($email_git_fallback &&
964               $file !~ /MAINTAINERS$/ &&          962               $file !~ /MAINTAINERS$/ &&
965               !$exact_pattern_match_hash{$file    963               !$exact_pattern_match_hash{$file}))) {
966             vcs_file_signoffs($file);             964             vcs_file_signoffs($file);
967         }                                         965         }
968         if ($email && $email_git_blame) {         966         if ($email && $email_git_blame) {
969             vcs_file_blame($file);                967             vcs_file_blame($file);
970         }                                         968         }
971     }                                             969     }
972                                                   970 
973     if ($email) {                                 971     if ($email) {
974         foreach my $chief (@penguin_chief) {      972         foreach my $chief (@penguin_chief) {
975             if ($chief =~ m/^(.*):(.*)/) {        973             if ($chief =~ m/^(.*):(.*)/) {
976                 my $email_address;                974                 my $email_address;
977                                                   975 
978                 $email_address = format_email(    976                 $email_address = format_email($1, $2, $email_usename);
979                 if ($email_git_penguin_chiefs)    977                 if ($email_git_penguin_chiefs) {
980                     push(@email_to, [$email_ad    978                     push(@email_to, [$email_address, 'chief penguin']);
981                 } else {                          979                 } else {
982                     @email_to = grep($_->[0] !    980                     @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
983                 }                                 981                 }
984             }                                     982             }
985         }                                         983         }
986                                                   984 
987         foreach my $email (@file_emails) {        985         foreach my $email (@file_emails) {
988             $email = mailmap_email($email);       986             $email = mailmap_email($email);
989             my ($name, $address) = parse_email    987             my ($name, $address) = parse_email($email);
990                                                   988 
991             my $tmp_email = format_email($name    989             my $tmp_email = format_email($name, $address, $email_usename);
992             push_email_address($tmp_email, '')    990             push_email_address($tmp_email, '');
993             add_role($tmp_email, 'in file');      991             add_role($tmp_email, 'in file');
994         }                                         992         }
995     }                                             993     }
996                                                   994 
997     foreach my $fix (@fixes) {                    995     foreach my $fix (@fixes) {
998         vcs_add_commit_signers($fix, "blamed_f    996         vcs_add_commit_signers($fix, "blamed_fixes");
999     }                                             997     }
1000                                                  998 
1001     my @to = ();                                 999     my @to = ();
1002     if ($email || $email_list) {                 1000     if ($email || $email_list) {
1003         if ($email) {                            1001         if ($email) {
1004             @to = (@to, @email_to);              1002             @to = (@to, @email_to);
1005         }                                        1003         }
1006         if ($email_list) {                       1004         if ($email_list) {
1007             @to = (@to, @list_to);               1005             @to = (@to, @list_to);
1008         }                                        1006         }
1009     }                                            1007     }
1010                                                  1008 
1011     if ($interactive) {                          1009     if ($interactive) {
1012         @to = interactive_get_maintainers(\@t    1010         @to = interactive_get_maintainers(\@to);
1013     }                                            1011     }
1014                                                  1012 
1015     return @to;                                  1013     return @to;
1016 }                                                1014 }
1017                                                  1015 
1018 sub file_match_pattern {                         1016 sub file_match_pattern {
1019     my ($file, $pattern) = @_;                   1017     my ($file, $pattern) = @_;
1020     if (substr($pattern, -1) eq "/") {           1018     if (substr($pattern, -1) eq "/") {
1021         if ($file =~ m@^$pattern@) {             1019         if ($file =~ m@^$pattern@) {
1022             return 1;                            1020             return 1;
1023         }                                        1021         }
1024     } else {                                     1022     } else {
1025         if ($file =~ m@^$pattern@) {             1023         if ($file =~ m@^$pattern@) {
1026             my $s1 = ($file =~ tr@/@@);          1024             my $s1 = ($file =~ tr@/@@);
1027             my $s2 = ($pattern =~ tr@/@@);       1025             my $s2 = ($pattern =~ tr@/@@);
1028             if ($s1 == $s2) {                    1026             if ($s1 == $s2) {
1029                 return 1;                        1027                 return 1;
1030             }                                    1028             }
1031         }                                        1029         }
1032     }                                            1030     }
1033     return 0;                                    1031     return 0;
1034 }                                                1032 }
1035                                                  1033 
1036 sub usage {                                      1034 sub usage {
1037     print <<EOT;                                 1035     print <<EOT;
1038 usage: $P [options] patchfile                    1036 usage: $P [options] patchfile
1039        $P [options] -f file|directory            1037        $P [options] -f file|directory
1040 version: $V                                      1038 version: $V
1041                                                  1039 
1042 MAINTAINER field selection options:              1040 MAINTAINER field selection options:
1043   --email => print email address(es) if any      1041   --email => print email address(es) if any
1044     --git => include recent git \*-by: signer    1042     --git => include recent git \*-by: signers
1045     --git-all-signature-types => include sign    1043     --git-all-signature-types => include signers regardless of signature type
1046         or use only ${signature_pattern} sign    1044         or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
1047     --git-fallback => use git when no exact M    1045     --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
1048     --git-chief-penguins => include ${penguin    1046     --git-chief-penguins => include ${penguin_chiefs}
1049     --git-min-signatures => number of signatu    1047     --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
1050     --git-max-maintainers => maximum maintain    1048     --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
1051     --git-min-percent => minimum percentage o    1049     --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
1052     --git-blame => use git blame to find modi    1050     --git-blame => use git blame to find modified commits for patch or file
1053     --git-blame-signatures => when used with     1051     --git-blame-signatures => when used with --git-blame, also include all commit signers
1054     --git-since => git history to use (defaul    1052     --git-since => git history to use (default: $email_git_since)
1055     --hg-since => hg history to use (default:    1053     --hg-since => hg history to use (default: $email_hg_since)
1056     --interactive => display a menu (mostly u    1054     --interactive => display a menu (mostly useful if used with the --git option)
1057     --m => include maintainer(s) if any          1055     --m => include maintainer(s) if any
1058     --r => include reviewer(s) if any            1056     --r => include reviewer(s) if any
1059     --n => include name 'Full Name <addr\@doma    1057     --n => include name 'Full Name <addr\@domain.tld>'
1060     --l => include list(s) if any                1058     --l => include list(s) if any
1061     --moderated => include moderated lists(s)    1059     --moderated => include moderated lists(s) if any (default: true)
1062     --s => include subscriber only list(s) if    1060     --s => include subscriber only list(s) if any (default: false)
1063     --remove-duplicates => minimize duplicate    1061     --remove-duplicates => minimize duplicate email names/addresses
1064     --roles => show roles (status:subsystem,     1062     --roles => show roles (status:subsystem, git-signer, list, etc...)
1065     --rolestats => show roles and statistics     1063     --rolestats => show roles and statistics (commits/total_commits, %)
1066     --file-emails => add email addresses foun    1064     --file-emails => add email addresses found in -f file (default: 0 (off))
1067     --fixes => for patches, add signatures of    1065     --fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on))
1068   --scm => print SCM tree(s) if any              1066   --scm => print SCM tree(s) if any
1069   --status => print status if any                1067   --status => print status if any
1070   --subsystem => print subsystem name if any     1068   --subsystem => print subsystem name if any
1071   --web => print website(s) if any               1069   --web => print website(s) if any
1072                                                  1070 
1073 Output type options:                             1071 Output type options:
1074   --separator [, ] => separator for multiple     1072   --separator [, ] => separator for multiple entries on 1 line
1075     using --separator also sets --nomultiline    1073     using --separator also sets --nomultiline if --separator is not [, ]
1076   --multiline => print 1 entry per line          1074   --multiline => print 1 entry per line
1077                                                  1075 
1078 Other options:                                   1076 Other options:
1079   --pattern-depth => Number of pattern direct    1077   --pattern-depth => Number of pattern directory traversals (default: 0 (all))
1080   --keywords => scan patch for keywords (defa    1078   --keywords => scan patch for keywords (default: $keywords)
1081   --keywords-in-file => scan file for keyword << 
1082   --sections => print all of the subsystem se    1079   --sections => print all of the subsystem sections with pattern matches
1083   --letters => print all matching 'letter' ty    1080   --letters => print all matching 'letter' types from all matching sections
1084   --mailmap => use .mailmap file (default: $e    1081   --mailmap => use .mailmap file (default: $email_use_mailmap)
1085   --no-tree => run without a kernel tree         1082   --no-tree => run without a kernel tree
1086   --self-test => show potential issues with M    1083   --self-test => show potential issues with MAINTAINERS file content
1087   --version => show version                      1084   --version => show version
1088   --help => show this help information           1085   --help => show this help information
1089                                                  1086 
1090 Default options:                                 1087 Default options:
1091   [--email --tree --nogit --git-fallback --m     1088   [--email --tree --nogit --git-fallback --m --r --n --l --multiline
1092    --pattern-depth=0 --remove-duplicates --ro !! 1089    --pattern-depth=0 --remove-duplicates --rolestats]
1093                                                  1090 
1094 Notes:                                           1091 Notes:
1095   Using "-f directory" may give unexpected re    1092   Using "-f directory" may give unexpected results:
1096       Used with "--git", git signators for _a    1093       Used with "--git", git signators for _all_ files in and below
1097           directory are examined as git recur    1094           directory are examined as git recurses directories.
1098           Any specified X: (exclude) pattern     1095           Any specified X: (exclude) pattern matches are _not_ ignored.
1099       Used with "--nogit", directory is used     1096       Used with "--nogit", directory is used as a pattern match,
1100           no individual file within the direc    1097           no individual file within the directory or subdirectory
1101           is matched.                            1098           is matched.
1102       Used with "--git-blame", does not itera    1099       Used with "--git-blame", does not iterate all files in directory
1103   Using "--git-blame" is slow and may add old    1100   Using "--git-blame" is slow and may add old committers and authors
1104       that are no longer active maintainers t    1101       that are no longer active maintainers to the output.
1105   Using "--roles" or "--rolestats" with git s    1102   Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
1106       other automated tools that expect only     1103       other automated tools that expect only ["name"] <email address>
1107       may not work because of additional outp    1104       may not work because of additional output after <email address>.
1108   Using "--rolestats" and "--git-blame" shows    1105   Using "--rolestats" and "--git-blame" shows the #/total=% commits,
1109       not the percentage of the entire file a    1106       not the percentage of the entire file authored.  # of commits is
1110       not a good measure of amount of code au    1107       not a good measure of amount of code authored.  1 major commit may
1111       contain a thousand lines, 5 trivial com    1108       contain a thousand lines, 5 trivial commits may modify a single line.
1112   If git is not installed, but mercurial (hg)    1109   If git is not installed, but mercurial (hg) is installed and an .hg
1113       repository exists, the following option    1110       repository exists, the following options apply to mercurial:
1114           --git,                                 1111           --git,
1115           --git-min-signatures, --git-max-mai    1112           --git-min-signatures, --git-max-maintainers, --git-min-percent, and
1116           --git-blame                            1113           --git-blame
1117       Use --hg-since not --git-since to contr    1114       Use --hg-since not --git-since to control date selection
1118   File ".get_maintainer.conf", if it exists i    1115   File ".get_maintainer.conf", if it exists in the linux kernel source root
1119       directory, can change whatever get_main    1116       directory, can change whatever get_maintainer defaults are desired.
1120       Entries in this file can be any command    1117       Entries in this file can be any command line argument.
1121       This file is prepended to any additiona    1118       This file is prepended to any additional command line arguments.
1122       Multiple lines and # comments are allow    1119       Multiple lines and # comments are allowed.
1123   Most options have both positive and negativ    1120   Most options have both positive and negative forms.
1124       The negative forms for --<foo> are --no    1121       The negative forms for --<foo> are --no<foo> and --no-<foo>.
1125                                                  1122 
1126 EOT                                              1123 EOT
1127 }                                                1124 }
1128                                                  1125 
1129 sub top_of_kernel_tree {                         1126 sub top_of_kernel_tree {
1130     my ($lk_path) = @_;                          1127     my ($lk_path) = @_;
1131                                                  1128 
1132     if ($lk_path ne "" && substr($lk_path,len    1129     if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
1133         $lk_path .= "/";                         1130         $lk_path .= "/";
1134     }                                            1131     }
1135     if (   (-f "${lk_path}COPYING")              1132     if (   (-f "${lk_path}COPYING")
1136         && (-f "${lk_path}CREDITS")              1133         && (-f "${lk_path}CREDITS")
1137         && (-f "${lk_path}Kbuild")               1134         && (-f "${lk_path}Kbuild")
1138         && (-e "${lk_path}MAINTAINERS")          1135         && (-e "${lk_path}MAINTAINERS")
1139         && (-f "${lk_path}Makefile")             1136         && (-f "${lk_path}Makefile")
1140         && (-f "${lk_path}README")               1137         && (-f "${lk_path}README")
1141         && (-d "${lk_path}Documentation")        1138         && (-d "${lk_path}Documentation")
1142         && (-d "${lk_path}arch")                 1139         && (-d "${lk_path}arch")
1143         && (-d "${lk_path}include")              1140         && (-d "${lk_path}include")
1144         && (-d "${lk_path}drivers")              1141         && (-d "${lk_path}drivers")
1145         && (-d "${lk_path}fs")                   1142         && (-d "${lk_path}fs")
1146         && (-d "${lk_path}init")                 1143         && (-d "${lk_path}init")
1147         && (-d "${lk_path}ipc")                  1144         && (-d "${lk_path}ipc")
1148         && (-d "${lk_path}kernel")               1145         && (-d "${lk_path}kernel")
1149         && (-d "${lk_path}lib")                  1146         && (-d "${lk_path}lib")
1150         && (-d "${lk_path}scripts")) {           1147         && (-d "${lk_path}scripts")) {
1151         return 1;                                1148         return 1;
1152     }                                            1149     }
1153     return 0;                                    1150     return 0;
1154 }                                                1151 }
1155                                                  1152 
1156 sub escape_name {                             << 
1157     my ($name) = @_;                          << 
1158                                               << 
1159     if ($name =~ /[^\w \-]/ai) {         ##ha << 
1160         $name =~ s/(?<!\\)"/\\"/g;       ##es << 
1161         $name = "\"$name\"";                  << 
1162     }                                         << 
1163                                               << 
1164     return $name;                             << 
1165 }                                             << 
1166                                               << 
1167 sub parse_email {                                1153 sub parse_email {
1168     my ($formatted_email) = @_;                  1154     my ($formatted_email) = @_;
1169                                                  1155 
1170     my $name = "";                               1156     my $name = "";
1171     my $address = "";                            1157     my $address = "";
1172                                                  1158 
1173     if ($formatted_email =~ /^([^<]+)<(.+\@.*)    1159     if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
1174         $name = $1;                              1160         $name = $1;
1175         $address = $2;                           1161         $address = $2;
1176     } elsif ($formatted_email =~ /^\s*<(.+\@\S    1162     } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
1177         $address = $1;                           1163         $address = $1;
1178     } elsif ($formatted_email =~ /^(.+\@\S*).    1164     } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
1179         $address = $1;                           1165         $address = $1;
1180     }                                            1166     }
1181                                                  1167 
1182     $name =~ s/^\s+|\s+$//g;                     1168     $name =~ s/^\s+|\s+$//g;
1183     $name =~ s/^\"|\"$//g;                       1169     $name =~ s/^\"|\"$//g;
1184     $name = escape_name($name);               << 
1185     $address =~ s/^\s+|\s+$//g;                  1170     $address =~ s/^\s+|\s+$//g;
1186                                                  1171 
                                                   >> 1172     if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
                                                   >> 1173         $name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
                                                   >> 1174         $name = "\"$name\"";
                                                   >> 1175     }
                                                   >> 1176 
1187     return ($name, $address);                    1177     return ($name, $address);
1188 }                                                1178 }
1189                                                  1179 
1190 sub format_email {                               1180 sub format_email {
1191     my ($name, $address, $usename) = @_;         1181     my ($name, $address, $usename) = @_;
1192                                                  1182 
1193     my $formatted_email;                         1183     my $formatted_email;
1194                                                  1184 
1195     $name =~ s/^\s+|\s+$//g;                     1185     $name =~ s/^\s+|\s+$//g;
1196     $name =~ s/^\"|\"$//g;                       1186     $name =~ s/^\"|\"$//g;
1197     $name = escape_name($name);               << 
1198     $address =~ s/^\s+|\s+$//g;                  1187     $address =~ s/^\s+|\s+$//g;
1199                                                  1188 
                                                   >> 1189     if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
                                                   >> 1190         $name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
                                                   >> 1191         $name = "\"$name\"";
                                                   >> 1192     }
                                                   >> 1193 
1200     if ($usename) {                              1194     if ($usename) {
1201         if ("$name" eq "") {                     1195         if ("$name" eq "") {
1202             $formatted_email = "$address";       1196             $formatted_email = "$address";
1203         } else {                                 1197         } else {
1204             $formatted_email = "$name <$addre    1198             $formatted_email = "$name <$address>";
1205         }                                        1199         }
1206     } else {                                     1200     } else {
1207         $formatted_email = $address;             1201         $formatted_email = $address;
1208     }                                            1202     }
1209                                                  1203 
1210     return $formatted_email;                     1204     return $formatted_email;
1211 }                                                1205 }
1212                                                  1206 
1213 sub find_first_section {                         1207 sub find_first_section {
1214     my $index = 0;                               1208     my $index = 0;
1215                                                  1209 
1216     while ($index < @typevalue) {                1210     while ($index < @typevalue) {
1217         my $tv = $typevalue[$index];             1211         my $tv = $typevalue[$index];
1218         if (($tv =~ m/^([A-Z]):\s*(.*)/)) {      1212         if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
1219             last;                                1213             last;
1220         }                                        1214         }
1221         $index++;                                1215         $index++;
1222     }                                            1216     }
1223                                                  1217 
1224     return $index;                               1218     return $index;
1225 }                                                1219 }
1226                                                  1220 
1227 sub find_starting_index {                        1221 sub find_starting_index {
1228     my ($index) = @_;                            1222     my ($index) = @_;
1229                                                  1223 
1230     while ($index > 0) {                         1224     while ($index > 0) {
1231         my $tv = $typevalue[$index];             1225         my $tv = $typevalue[$index];
1232         if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {     1226         if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
1233             last;                                1227             last;
1234         }                                        1228         }
1235         $index--;                                1229         $index--;
1236     }                                            1230     }
1237                                                  1231 
1238     return $index;                               1232     return $index;
1239 }                                                1233 }
1240                                                  1234 
1241 sub find_ending_index {                          1235 sub find_ending_index {
1242     my ($index) = @_;                            1236     my ($index) = @_;
1243                                                  1237 
1244     while ($index < @typevalue) {                1238     while ($index < @typevalue) {
1245         my $tv = $typevalue[$index];             1239         my $tv = $typevalue[$index];
1246         if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {     1240         if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
1247             last;                                1241             last;
1248         }                                        1242         }
1249         $index++;                                1243         $index++;
1250     }                                            1244     }
1251                                                  1245 
1252     return $index;                               1246     return $index;
1253 }                                                1247 }
1254                                                  1248 
1255 sub get_subsystem_name {                         1249 sub get_subsystem_name {
1256     my ($index) = @_;                            1250     my ($index) = @_;
1257                                                  1251 
1258     my $start = find_starting_index($index);     1252     my $start = find_starting_index($index);
1259                                                  1253 
1260     my $subsystem = $typevalue[$start];          1254     my $subsystem = $typevalue[$start];
1261     if ($output_section_maxlen && length($sub    1255     if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1262         $subsystem = substr($subsystem, 0, $o    1256         $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
1263         $subsystem =~ s/\s*$//;                  1257         $subsystem =~ s/\s*$//;
1264         $subsystem = $subsystem . "...";         1258         $subsystem = $subsystem . "...";
1265     }                                            1259     }
1266     return $subsystem;                           1260     return $subsystem;
1267 }                                                1261 }
1268                                                  1262 
1269 sub get_maintainer_role {                        1263 sub get_maintainer_role {
1270     my ($index) = @_;                            1264     my ($index) = @_;
1271                                                  1265 
1272     my $i;                                       1266     my $i;
1273     my $start = find_starting_index($index);     1267     my $start = find_starting_index($index);
1274     my $end = find_ending_index($index);         1268     my $end = find_ending_index($index);
1275                                                  1269 
1276     my $role = "unknown";                        1270     my $role = "unknown";
1277     my $subsystem = get_subsystem_name($index    1271     my $subsystem = get_subsystem_name($index);
1278                                                  1272 
1279     for ($i = $start + 1; $i < $end; $i++) {     1273     for ($i = $start + 1; $i < $end; $i++) {
1280         my $tv = $typevalue[$i];                 1274         my $tv = $typevalue[$i];
1281         if ($tv =~ m/^([A-Z]):\s*(.*)/) {        1275         if ($tv =~ m/^([A-Z]):\s*(.*)/) {
1282             my $ptype = $1;                      1276             my $ptype = $1;
1283             my $pvalue = $2;                     1277             my $pvalue = $2;
1284             if ($ptype eq "S") {                 1278             if ($ptype eq "S") {
1285                 $role = $pvalue;                 1279                 $role = $pvalue;
1286             }                                    1280             }
1287         }                                        1281         }
1288     }                                            1282     }
1289                                                  1283 
1290     $role = lc($role);                           1284     $role = lc($role);
1291     if      ($role eq "supported") {             1285     if      ($role eq "supported") {
1292         $role = "supporter";                     1286         $role = "supporter";
1293     } elsif ($role eq "maintained") {            1287     } elsif ($role eq "maintained") {
1294         $role = "maintainer";                    1288         $role = "maintainer";
1295     } elsif ($role eq "odd fixes") {             1289     } elsif ($role eq "odd fixes") {
1296         $role = "odd fixer";                     1290         $role = "odd fixer";
1297     } elsif ($role eq "orphan") {                1291     } elsif ($role eq "orphan") {
1298         $role = "orphan minder";                 1292         $role = "orphan minder";
1299     } elsif ($role eq "obsolete") {              1293     } elsif ($role eq "obsolete") {
1300         $role = "obsolete minder";               1294         $role = "obsolete minder";
1301     } elsif ($role eq "buried alive in report    1295     } elsif ($role eq "buried alive in reporters") {
1302         $role = "chief penguin";                 1296         $role = "chief penguin";
1303     }                                            1297     }
1304                                                  1298 
1305     return $role . ":" . $subsystem;             1299     return $role . ":" . $subsystem;
1306 }                                                1300 }
1307                                                  1301 
1308 sub get_list_role {                              1302 sub get_list_role {
1309     my ($index) = @_;                            1303     my ($index) = @_;
1310                                                  1304 
1311     my $subsystem = get_subsystem_name($index    1305     my $subsystem = get_subsystem_name($index);
1312                                                  1306 
1313     if ($subsystem eq "THE REST") {              1307     if ($subsystem eq "THE REST") {
1314         $subsystem = "";                         1308         $subsystem = "";
1315     }                                            1309     }
1316                                                  1310 
1317     return $subsystem;                           1311     return $subsystem;
1318 }                                                1312 }
1319                                                  1313 
1320 sub add_categories {                             1314 sub add_categories {
1321     my ($index, $suffix) = @_;                !! 1315     my ($index) = @_;
1322                                                  1316 
1323     my $i;                                       1317     my $i;
1324     my $start = find_starting_index($index);     1318     my $start = find_starting_index($index);
1325     my $end = find_ending_index($index);         1319     my $end = find_ending_index($index);
1326                                                  1320 
1327     push(@subsystem, $typevalue[$start]);        1321     push(@subsystem, $typevalue[$start]);
1328                                                  1322 
1329     for ($i = $start + 1; $i < $end; $i++) {     1323     for ($i = $start + 1; $i < $end; $i++) {
1330         my $tv = $typevalue[$i];                 1324         my $tv = $typevalue[$i];
1331         if ($tv =~ m/^([A-Z]):\s*(.*)/) {        1325         if ($tv =~ m/^([A-Z]):\s*(.*)/) {
1332             my $ptype = $1;                      1326             my $ptype = $1;
1333             my $pvalue = $2;                     1327             my $pvalue = $2;
1334             if ($ptype eq "L") {                 1328             if ($ptype eq "L") {
1335                 my $list_address = $pvalue;      1329                 my $list_address = $pvalue;
1336                 my $list_additional = "";        1330                 my $list_additional = "";
1337                 my $list_role = get_list_role    1331                 my $list_role = get_list_role($i);
1338                                                  1332 
1339                 if ($list_role ne "") {          1333                 if ($list_role ne "") {
1340                     $list_role = ":" . $list_    1334                     $list_role = ":" . $list_role;
1341                 }                                1335                 }
1342                 if ($list_address =~ m/([^\s]    1336                 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1343                     $list_address = $1;          1337                     $list_address = $1;
1344                     $list_additional = $2;       1338                     $list_additional = $2;
1345                 }                                1339                 }
1346                 if ($list_additional =~ m/sub    1340                 if ($list_additional =~ m/subscribers-only/) {
1347                     if ($email_subscriber_lis    1341                     if ($email_subscriber_list) {
1348                         if (!$hash_list_to{lc    1342                         if (!$hash_list_to{lc($list_address)}) {
1349                             $hash_list_to{lc(    1343                             $hash_list_to{lc($list_address)} = 1;
1350                             push(@list_to, [$    1344                             push(@list_to, [$list_address,
1351                                             " !! 1345                                             "subscriber list${list_role}"]);
1352                         }                        1346                         }
1353                     }                            1347                     }
1354                 } else {                         1348                 } else {
1355                     if ($email_list) {           1349                     if ($email_list) {
1356                         if (!$hash_list_to{lc    1350                         if (!$hash_list_to{lc($list_address)}) {
1357                             if ($list_additio    1351                             if ($list_additional =~ m/moderated/) {
1358                                 if ($email_mo    1352                                 if ($email_moderated_list) {
1359                                     $hash_lis    1353                                     $hash_list_to{lc($list_address)} = 1;
1360                                     push(@lis    1354                                     push(@list_to, [$list_address,
1361                                               !! 1355                                                     "moderated list${list_role}"]);
1362                                 }                1356                                 }
1363                             } else {             1357                             } else {
1364                                 $hash_list_to    1358                                 $hash_list_to{lc($list_address)} = 1;
1365                                 push(@list_to    1359                                 push(@list_to, [$list_address,
1366                                               !! 1360                                                 "open list${list_role}"]);
1367                             }                    1361                             }
1368                         }                        1362                         }
1369                     }                            1363                     }
1370                 }                                1364                 }
1371             } elsif ($ptype eq "M") {            1365             } elsif ($ptype eq "M") {
1372                 if ($email_maintainer) {         1366                 if ($email_maintainer) {
1373                     my $role = get_maintainer    1367                     my $role = get_maintainer_role($i);
1374                     push_email_addresses($pva !! 1368                     push_email_addresses($pvalue, $role);
1375                 }                                1369                 }
1376             } elsif ($ptype eq "R") {            1370             } elsif ($ptype eq "R") {
1377                 if ($email_reviewer) {           1371                 if ($email_reviewer) {
1378                     my $subsystem = get_subsy    1372                     my $subsystem = get_subsystem_name($i);
1379                     push_email_addresses($pva !! 1373                     push_email_addresses($pvalue, "reviewer:$subsystem");
1380                 }                                1374                 }
1381             } elsif ($ptype eq "T") {            1375             } elsif ($ptype eq "T") {
1382                 push(@scm, $pvalue . $suffix) !! 1376                 push(@scm, $pvalue);
1383             } elsif ($ptype eq "W") {            1377             } elsif ($ptype eq "W") {
1384                 push(@web, $pvalue . $suffix) !! 1378                 push(@web, $pvalue);
1385             } elsif ($ptype eq "S") {            1379             } elsif ($ptype eq "S") {
1386                 push(@status, $pvalue . $suff !! 1380                 push(@status, $pvalue);
1387             }                                    1381             }
1388         }                                        1382         }
1389     }                                            1383     }
1390 }                                                1384 }
1391                                                  1385 
1392 sub email_inuse {                                1386 sub email_inuse {
1393     my ($name, $address) = @_;                   1387     my ($name, $address) = @_;
1394                                                  1388 
1395     return 1 if (($name eq "") && ($address e    1389     return 1 if (($name eq "") && ($address eq ""));
1396     return 1 if (($name ne "") && exists($ema    1390     return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1397     return 1 if (($address ne "") && exists($    1391     return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
1398                                                  1392 
1399     return 0;                                    1393     return 0;
1400 }                                                1394 }
1401                                                  1395 
1402 sub push_email_address {                         1396 sub push_email_address {
1403     my ($line, $role) = @_;                      1397     my ($line, $role) = @_;
1404                                                  1398 
1405     my ($name, $address) = parse_email($line)    1399     my ($name, $address) = parse_email($line);
1406                                                  1400 
1407     if ($address eq "") {                        1401     if ($address eq "") {
1408         return 0;                                1402         return 0;
1409     }                                            1403     }
1410                                                  1404 
1411     if (!$email_remove_duplicates) {             1405     if (!$email_remove_duplicates) {
1412         push(@email_to, [format_email($name,     1406         push(@email_to, [format_email($name, $address, $email_usename), $role]);
1413     } elsif (!email_inuse($name, $address)) {    1407     } elsif (!email_inuse($name, $address)) {
1414         push(@email_to, [format_email($name,     1408         push(@email_to, [format_email($name, $address, $email_usename), $role]);
1415         $email_hash_name{lc($name)}++ if ($na    1409         $email_hash_name{lc($name)}++ if ($name ne "");
1416         $email_hash_address{lc($address)}++;     1410         $email_hash_address{lc($address)}++;
1417     }                                            1411     }
1418                                                  1412 
1419     return 1;                                    1413     return 1;
1420 }                                                1414 }
1421                                                  1415 
1422 sub push_email_addresses {                       1416 sub push_email_addresses {
1423     my ($address, $role) = @_;                   1417     my ($address, $role) = @_;
1424                                                  1418 
1425     my @address_list = ();                       1419     my @address_list = ();
1426                                                  1420 
1427     if (rfc822_valid($address)) {                1421     if (rfc822_valid($address)) {
1428         push_email_address($address, $role);     1422         push_email_address($address, $role);
1429     } elsif (@address_list = rfc822_validlist    1423     } elsif (@address_list = rfc822_validlist($address)) {
1430         my $array_count = shift(@address_list    1424         my $array_count = shift(@address_list);
1431         while (my $entry = shift(@address_lis    1425         while (my $entry = shift(@address_list)) {
1432             push_email_address($entry, $role)    1426             push_email_address($entry, $role);
1433         }                                        1427         }
1434     } else {                                     1428     } else {
1435         if (!push_email_address($address, $ro    1429         if (!push_email_address($address, $role)) {
1436             warn("Invalid MAINTAINERS address    1430             warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1437         }                                        1431         }
1438     }                                            1432     }
1439 }                                                1433 }
1440                                                  1434 
1441 sub add_role {                                   1435 sub add_role {
1442     my ($line, $role) = @_;                      1436     my ($line, $role) = @_;
1443                                                  1437 
1444     my ($name, $address) = parse_email($line)    1438     my ($name, $address) = parse_email($line);
1445     my $email = format_email($name, $address,    1439     my $email = format_email($name, $address, $email_usename);
1446                                                  1440 
1447     foreach my $entry (@email_to) {              1441     foreach my $entry (@email_to) {
1448         if ($email_remove_duplicates) {          1442         if ($email_remove_duplicates) {
1449             my ($entry_name, $entry_address)     1443             my ($entry_name, $entry_address) = parse_email($entry->[0]);
1450             if (($name eq $entry_name || $add    1444             if (($name eq $entry_name || $address eq $entry_address)
1451                 && ($role eq "" || !($entry->    1445                 && ($role eq "" || !($entry->[1] =~ m/$role/))
1452             ) {                                  1446             ) {
1453                 if ($entry->[1] eq "") {         1447                 if ($entry->[1] eq "") {
1454                     $entry->[1] = "$role";       1448                     $entry->[1] = "$role";
1455                 } else {                         1449                 } else {
1456                     $entry->[1] = "$entry->[1    1450                     $entry->[1] = "$entry->[1],$role";
1457                 }                                1451                 }
1458             }                                    1452             }
1459         } else {                                 1453         } else {
1460             if ($email eq $entry->[0]            1454             if ($email eq $entry->[0]
1461                 && ($role eq "" || !($entry->    1455                 && ($role eq "" || !($entry->[1] =~ m/$role/))
1462             ) {                                  1456             ) {
1463                 if ($entry->[1] eq "") {         1457                 if ($entry->[1] eq "") {
1464                     $entry->[1] = "$role";       1458                     $entry->[1] = "$role";
1465                 } else {                         1459                 } else {
1466                     $entry->[1] = "$entry->[1    1460                     $entry->[1] = "$entry->[1],$role";
1467                 }                                1461                 }
1468             }                                    1462             }
1469         }                                        1463         }
1470     }                                            1464     }
1471 }                                                1465 }
1472                                                  1466 
1473 sub which {                                      1467 sub which {
1474     my ($bin) = @_;                              1468     my ($bin) = @_;
1475                                                  1469 
1476     foreach my $path (split(/:/, $ENV{PATH}))    1470     foreach my $path (split(/:/, $ENV{PATH})) {
1477         if (-e "$path/$bin") {                   1471         if (-e "$path/$bin") {
1478             return "$path/$bin";                 1472             return "$path/$bin";
1479         }                                        1473         }
1480     }                                            1474     }
1481                                                  1475 
1482     return "";                                   1476     return "";
1483 }                                                1477 }
1484                                                  1478 
1485 sub which_conf {                                 1479 sub which_conf {
1486     my ($conf) = @_;                             1480     my ($conf) = @_;
1487                                                  1481 
1488     foreach my $path (split(/:/, ".:$ENV{HOME    1482     foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1489         if (-e "$path/$conf") {                  1483         if (-e "$path/$conf") {
1490             return "$path/$conf";                1484             return "$path/$conf";
1491         }                                        1485         }
1492     }                                            1486     }
1493                                                  1487 
1494     return "";                                   1488     return "";
1495 }                                                1489 }
1496                                                  1490 
1497 sub mailmap_email {                              1491 sub mailmap_email {
1498     my ($line) = @_;                             1492     my ($line) = @_;
1499                                                  1493 
1500     my ($name, $address) = parse_email($line)    1494     my ($name, $address) = parse_email($line);
1501     my $email = format_email($name, $address,    1495     my $email = format_email($name, $address, 1);
1502     my $real_name = $name;                       1496     my $real_name = $name;
1503     my $real_address = $address;                 1497     my $real_address = $address;
1504                                                  1498 
1505     if (exists $mailmap->{names}->{$email} ||    1499     if (exists $mailmap->{names}->{$email} ||
1506         exists $mailmap->{addresses}->{$email    1500         exists $mailmap->{addresses}->{$email}) {
1507         if (exists $mailmap->{names}->{$email    1501         if (exists $mailmap->{names}->{$email}) {
1508             $real_name = $mailmap->{names}->{    1502             $real_name = $mailmap->{names}->{$email};
1509         }                                        1503         }
1510         if (exists $mailmap->{addresses}->{$e    1504         if (exists $mailmap->{addresses}->{$email}) {
1511             $real_address = $mailmap->{addres    1505             $real_address = $mailmap->{addresses}->{$email};
1512         }                                        1506         }
1513     } else {                                     1507     } else {
1514         if (exists $mailmap->{names}->{$addre    1508         if (exists $mailmap->{names}->{$address}) {
1515             $real_name = $mailmap->{names}->{    1509             $real_name = $mailmap->{names}->{$address};
1516         }                                        1510         }
1517         if (exists $mailmap->{addresses}->{$a    1511         if (exists $mailmap->{addresses}->{$address}) {
1518             $real_address = $mailmap->{addres    1512             $real_address = $mailmap->{addresses}->{$address};
1519         }                                        1513         }
1520     }                                            1514     }
1521     return format_email($real_name, $real_add    1515     return format_email($real_name, $real_address, 1);
1522 }                                                1516 }
1523                                                  1517 
1524 sub mailmap {                                    1518 sub mailmap {
1525     my (@addresses) = @_;                        1519     my (@addresses) = @_;
1526                                                  1520 
1527     my @mapped_emails = ();                      1521     my @mapped_emails = ();
1528     foreach my $line (@addresses) {              1522     foreach my $line (@addresses) {
1529         push(@mapped_emails, mailmap_email($l    1523         push(@mapped_emails, mailmap_email($line));
1530     }                                            1524     }
1531     merge_by_realname(@mapped_emails) if ($em    1525     merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1532     return @mapped_emails;                       1526     return @mapped_emails;
1533 }                                                1527 }
1534                                                  1528 
1535 sub merge_by_realname {                          1529 sub merge_by_realname {
1536     my %address_map;                             1530     my %address_map;
1537     my (@emails) = @_;                           1531     my (@emails) = @_;
1538                                                  1532 
1539     foreach my $email (@emails) {                1533     foreach my $email (@emails) {
1540         my ($name, $address) = parse_email($e    1534         my ($name, $address) = parse_email($email);
1541         if (exists $address_map{$name}) {        1535         if (exists $address_map{$name}) {
1542             $address = $address_map{$name};      1536             $address = $address_map{$name};
1543             $email = format_email($name, $add    1537             $email = format_email($name, $address, 1);
1544         } else {                                 1538         } else {
1545             $address_map{$name} = $address;      1539             $address_map{$name} = $address;
1546         }                                        1540         }
1547     }                                            1541     }
1548 }                                                1542 }
1549                                                  1543 
1550 sub git_execute_cmd {                            1544 sub git_execute_cmd {
1551     my ($cmd) = @_;                              1545     my ($cmd) = @_;
1552     my @lines = ();                              1546     my @lines = ();
1553                                                  1547 
1554     my $output = `$cmd`;                         1548     my $output = `$cmd`;
1555     $output =~ s/^\s*//gm;                       1549     $output =~ s/^\s*//gm;
1556     @lines = split("\n", $output);               1550     @lines = split("\n", $output);
1557                                                  1551 
1558     return @lines;                               1552     return @lines;
1559 }                                                1553 }
1560                                                  1554 
1561 sub hg_execute_cmd {                             1555 sub hg_execute_cmd {
1562     my ($cmd) = @_;                              1556     my ($cmd) = @_;
1563     my @lines = ();                              1557     my @lines = ();
1564                                                  1558 
1565     my $output = `$cmd`;                         1559     my $output = `$cmd`;
1566     @lines = split("\n", $output);               1560     @lines = split("\n", $output);
1567                                                  1561 
1568     return @lines;                               1562     return @lines;
1569 }                                                1563 }
1570                                                  1564 
1571 sub extract_formatted_signatures {               1565 sub extract_formatted_signatures {
1572     my (@signature_lines) = @_;                  1566     my (@signature_lines) = @_;
1573                                                  1567 
1574     my @type = @signature_lines;                 1568     my @type = @signature_lines;
1575                                                  1569 
1576     s/\s*(.*):.*/$1/ for (@type);                1570     s/\s*(.*):.*/$1/ for (@type);
1577                                                  1571 
1578     # cut -f2- -d":"                             1572     # cut -f2- -d":"
1579     s/\s*.*:\s*(.+)\s*/$1/ for (@signature_li    1573     s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1580                                                  1574 
1581 ## Reformat email addresses (with names) to a    1575 ## Reformat email addresses (with names) to avoid badly written signatures
1582                                                  1576 
1583     foreach my $signer (@signature_lines) {      1577     foreach my $signer (@signature_lines) {
1584         $signer = deduplicate_email($signer);    1578         $signer = deduplicate_email($signer);
1585     }                                            1579     }
1586                                                  1580 
1587     return (\@type, \@signature_lines);          1581     return (\@type, \@signature_lines);
1588 }                                                1582 }
1589                                                  1583 
1590 sub vcs_find_signers {                           1584 sub vcs_find_signers {
1591     my ($cmd, $file) = @_;                       1585     my ($cmd, $file) = @_;
1592     my $commits;                                 1586     my $commits;
1593     my @lines = ();                              1587     my @lines = ();
1594     my @signatures = ();                         1588     my @signatures = ();
1595     my @authors = ();                            1589     my @authors = ();
1596     my @stats = ();                              1590     my @stats = ();
1597                                                  1591 
1598     @lines = &{$VCS_cmds{"execute_cmd"}}($cmd    1592     @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1599                                                  1593 
1600     my $pattern = $VCS_cmds{"commit_pattern"}    1594     my $pattern = $VCS_cmds{"commit_pattern"};
1601     my $author_pattern = $VCS_cmds{"author_pa    1595     my $author_pattern = $VCS_cmds{"author_pattern"};
1602     my $stat_pattern = $VCS_cmds{"stat_patter    1596     my $stat_pattern = $VCS_cmds{"stat_pattern"};
1603                                                  1597 
1604     $stat_pattern =~ s/(\$\w+)/$1/eeg;           1598     $stat_pattern =~ s/(\$\w+)/$1/eeg;          #interpolate $stat_pattern
1605                                                  1599 
1606     $commits = grep(/$pattern/, @lines);         1600     $commits = grep(/$pattern/, @lines);        # of commits
1607                                                  1601 
1608     @authors = grep(/$author_pattern/, @lines    1602     @authors = grep(/$author_pattern/, @lines);
1609     @signatures = grep(/^[ \t]*${signature_pa    1603     @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1610     @stats = grep(/$stat_pattern/, @lines);      1604     @stats = grep(/$stat_pattern/, @lines);
1611                                                  1605 
1612 #    print("stats: <@stats>\n");                  1606 #    print("stats: <@stats>\n");
1613                                                  1607 
1614     return (0, \@signatures, \@authors, \@sta    1608     return (0, \@signatures, \@authors, \@stats) if !@signatures;
1615                                                  1609 
1616     save_commits_by_author(@lines) if ($inter    1610     save_commits_by_author(@lines) if ($interactive);
1617     save_commits_by_signer(@lines) if ($inter    1611     save_commits_by_signer(@lines) if ($interactive);
1618                                                  1612 
1619     if (!$email_git_penguin_chiefs) {            1613     if (!$email_git_penguin_chiefs) {
1620         @signatures = grep(!/${penguin_chiefs    1614         @signatures = grep(!/${penguin_chiefs}/i, @signatures);
1621     }                                            1615     }
1622                                                  1616 
1623     my ($author_ref, $authors_ref) = extract_    1617     my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
1624     my ($types_ref, $signers_ref) = extract_f    1618     my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1625                                                  1619 
1626     return ($commits, $signers_ref, $authors_    1620     return ($commits, $signers_ref, $authors_ref, \@stats);
1627 }                                                1621 }
1628                                                  1622 
1629 sub vcs_find_author {                            1623 sub vcs_find_author {
1630     my ($cmd) = @_;                              1624     my ($cmd) = @_;
1631     my @lines = ();                              1625     my @lines = ();
1632                                                  1626 
1633     @lines = &{$VCS_cmds{"execute_cmd"}}($cmd    1627     @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1634                                                  1628 
1635     if (!$email_git_penguin_chiefs) {            1629     if (!$email_git_penguin_chiefs) {
1636         @lines = grep(!/${penguin_chiefs}/i,     1630         @lines = grep(!/${penguin_chiefs}/i, @lines);
1637     }                                            1631     }
1638                                                  1632 
1639     return @lines if !@lines;                    1633     return @lines if !@lines;
1640                                                  1634 
1641     my @authors = ();                            1635     my @authors = ();
1642     foreach my $line (@lines) {                  1636     foreach my $line (@lines) {
1643         if ($line =~ m/$VCS_cmds{"author_patt    1637         if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1644             my $author = $1;                     1638             my $author = $1;
1645             my ($name, $address) = parse_emai    1639             my ($name, $address) = parse_email($author);
1646             $author = format_email($name, $ad    1640             $author = format_email($name, $address, 1);
1647             push(@authors, $author);             1641             push(@authors, $author);
1648         }                                        1642         }
1649     }                                            1643     }
1650                                                  1644 
1651     save_commits_by_author(@lines) if ($inter    1645     save_commits_by_author(@lines) if ($interactive);
1652     save_commits_by_signer(@lines) if ($inter    1646     save_commits_by_signer(@lines) if ($interactive);
1653                                                  1647 
1654     return @authors;                             1648     return @authors;
1655 }                                                1649 }
1656                                                  1650 
1657 sub vcs_save_commits {                           1651 sub vcs_save_commits {
1658     my ($cmd) = @_;                              1652     my ($cmd) = @_;
1659     my @lines = ();                              1653     my @lines = ();
1660     my @commits = ();                            1654     my @commits = ();
1661                                                  1655 
1662     @lines = &{$VCS_cmds{"execute_cmd"}}($cmd    1656     @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1663                                                  1657 
1664     foreach my $line (@lines) {                  1658     foreach my $line (@lines) {
1665         if ($line =~ m/$VCS_cmds{"blame_commi    1659         if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1666             push(@commits, $1);                  1660             push(@commits, $1);
1667         }                                        1661         }
1668     }                                            1662     }
1669                                                  1663 
1670     return @commits;                             1664     return @commits;
1671 }                                                1665 }
1672                                                  1666 
1673 sub vcs_blame {                                  1667 sub vcs_blame {
1674     my ($file) = @_;                             1668     my ($file) = @_;
1675     my $cmd;                                     1669     my $cmd;
1676     my @commits = ();                            1670     my @commits = ();
1677                                                  1671 
1678     return @commits if (!(-f $file));            1672     return @commits if (!(-f $file));
1679                                                  1673 
1680     if (@range && $VCS_cmds{"blame_range_cmd"    1674     if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1681         my @all_commits = ();                    1675         my @all_commits = ();
1682                                                  1676 
1683         $cmd = $VCS_cmds{"blame_file_cmd"};      1677         $cmd = $VCS_cmds{"blame_file_cmd"};
1684         $cmd =~ s/(\$\w+)/$1/eeg;                1678         $cmd =~ s/(\$\w+)/$1/eeg;               #interpolate $cmd
1685         @all_commits = vcs_save_commits($cmd)    1679         @all_commits = vcs_save_commits($cmd);
1686                                                  1680 
1687         foreach my $file_range_diff (@range)     1681         foreach my $file_range_diff (@range) {
1688             next if (!($file_range_diff =~ m/    1682             next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1689             my $diff_file = $1;                  1683             my $diff_file = $1;
1690             my $diff_start = $2;                 1684             my $diff_start = $2;
1691             my $diff_length = $3;                1685             my $diff_length = $3;
1692             next if ("$file" ne "$diff_file")    1686             next if ("$file" ne "$diff_file");
1693             for (my $i = $diff_start; $i < $d    1687             for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1694                 push(@commits, $all_commits[$    1688                 push(@commits, $all_commits[$i]);
1695             }                                    1689             }
1696         }                                        1690         }
1697     } elsif (@range) {                           1691     } elsif (@range) {
1698         foreach my $file_range_diff (@range)     1692         foreach my $file_range_diff (@range) {
1699             next if (!($file_range_diff =~ m/    1693             next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1700             my $diff_file = $1;                  1694             my $diff_file = $1;
1701             my $diff_start = $2;                 1695             my $diff_start = $2;
1702             my $diff_length = $3;                1696             my $diff_length = $3;
1703             next if ("$file" ne "$diff_file")    1697             next if ("$file" ne "$diff_file");
1704             $cmd = $VCS_cmds{"blame_range_cmd    1698             $cmd = $VCS_cmds{"blame_range_cmd"};
1705             $cmd =~ s/(\$\w+)/$1/eeg;            1699             $cmd =~ s/(\$\w+)/$1/eeg;           #interpolate $cmd
1706             push(@commits, vcs_save_commits($    1700             push(@commits, vcs_save_commits($cmd));
1707         }                                        1701         }
1708     } else {                                     1702     } else {
1709         $cmd = $VCS_cmds{"blame_file_cmd"};      1703         $cmd = $VCS_cmds{"blame_file_cmd"};
1710         $cmd =~ s/(\$\w+)/$1/eeg;                1704         $cmd =~ s/(\$\w+)/$1/eeg;               #interpolate $cmd
1711         @commits = vcs_save_commits($cmd);       1705         @commits = vcs_save_commits($cmd);
1712     }                                            1706     }
1713                                                  1707 
1714     foreach my $commit (@commits) {              1708     foreach my $commit (@commits) {
1715         $commit =~ s/^\^//g;                     1709         $commit =~ s/^\^//g;
1716     }                                            1710     }
1717                                                  1711 
1718     return @commits;                             1712     return @commits;
1719 }                                                1713 }
1720                                                  1714 
1721 my $printed_novcs = 0;                           1715 my $printed_novcs = 0;
1722 sub vcs_exists {                                 1716 sub vcs_exists {
1723     %VCS_cmds = %VCS_cmds_git;                   1717     %VCS_cmds = %VCS_cmds_git;
1724     return 1 if eval $VCS_cmds{"available"};     1718     return 1 if eval $VCS_cmds{"available"};
1725     %VCS_cmds = %VCS_cmds_hg;                    1719     %VCS_cmds = %VCS_cmds_hg;
1726     return 2 if eval $VCS_cmds{"available"};     1720     return 2 if eval $VCS_cmds{"available"};
1727     %VCS_cmds = ();                              1721     %VCS_cmds = ();
1728     if (!$printed_novcs && $email_git) {         1722     if (!$printed_novcs && $email_git) {
1729         warn("$P: No supported VCS found.  Ad    1723         warn("$P: No supported VCS found.  Add --nogit to options?\n");
1730         warn("Using a git repository produces    1724         warn("Using a git repository produces better results.\n");
1731         warn("Try Linus Torvalds' latest git     1725         warn("Try Linus Torvalds' latest git repository using:\n");
1732         warn("git clone git://git.kernel.org/    1726         warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
1733         $printed_novcs = 1;                      1727         $printed_novcs = 1;
1734     }                                            1728     }
1735     return 0;                                    1729     return 0;
1736 }                                                1730 }
1737                                                  1731 
1738 sub vcs_is_git {                                 1732 sub vcs_is_git {
1739     vcs_exists();                                1733     vcs_exists();
1740     return $vcs_used == 1;                       1734     return $vcs_used == 1;
1741 }                                                1735 }
1742                                                  1736 
1743 sub vcs_is_hg {                                  1737 sub vcs_is_hg {
1744     return $vcs_used == 2;                       1738     return $vcs_used == 2;
1745 }                                                1739 }
1746                                                  1740 
1747 sub vcs_add_commit_signers {                     1741 sub vcs_add_commit_signers {
1748     return if (!vcs_exists());                   1742     return if (!vcs_exists());
1749                                                  1743 
1750     my ($commit, $desc) = @_;                    1744     my ($commit, $desc) = @_;
1751     my $commit_count = 0;                        1745     my $commit_count = 0;
1752     my $commit_authors_ref;                      1746     my $commit_authors_ref;
1753     my $commit_signers_ref;                      1747     my $commit_signers_ref;
1754     my $stats_ref;                               1748     my $stats_ref;
1755     my @commit_authors = ();                     1749     my @commit_authors = ();
1756     my @commit_signers = ();                     1750     my @commit_signers = ();
1757     my $cmd;                                     1751     my $cmd;
1758                                                  1752 
1759     $cmd = $VCS_cmds{"find_commit_signers_cmd    1753     $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1760     $cmd =~ s/(\$\w+)/$1/eeg;   #substitute v    1754     $cmd =~ s/(\$\w+)/$1/eeg;   #substitute variables in $cmd
1761                                                  1755 
1762     ($commit_count, $commit_signers_ref, $com    1756     ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, "");
1763     @commit_authors = @{$commit_authors_ref}     1757     @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
1764     @commit_signers = @{$commit_signers_ref}     1758     @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
1765                                                  1759 
1766     foreach my $signer (@commit_signers) {       1760     foreach my $signer (@commit_signers) {
1767         $signer = deduplicate_email($signer);    1761         $signer = deduplicate_email($signer);
1768     }                                            1762     }
1769                                                  1763 
1770     vcs_assign($desc, 1, @commit_signers);       1764     vcs_assign($desc, 1, @commit_signers);
1771 }                                                1765 }
1772                                                  1766 
1773 sub interactive_get_maintainers {                1767 sub interactive_get_maintainers {
1774     my ($list_ref) = @_;                         1768     my ($list_ref) = @_;
1775     my @list = @$list_ref;                       1769     my @list = @$list_ref;
1776                                                  1770 
1777     vcs_exists();                                1771     vcs_exists();
1778                                                  1772 
1779     my %selected;                                1773     my %selected;
1780     my %authored;                                1774     my %authored;
1781     my %signed;                                  1775     my %signed;
1782     my $count = 0;                               1776     my $count = 0;
1783     my $maintained = 0;                          1777     my $maintained = 0;
1784     foreach my $entry (@list) {                  1778     foreach my $entry (@list) {
1785         $maintained = 1 if ($entry->[1] =~ /^    1779         $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1786         $selected{$count} = 1;                   1780         $selected{$count} = 1;
1787         $authored{$count} = 0;                   1781         $authored{$count} = 0;
1788         $signed{$count} = 0;                     1782         $signed{$count} = 0;
1789         $count++;                                1783         $count++;
1790     }                                            1784     }
1791                                                  1785 
1792     #menu loop                                   1786     #menu loop
1793     my $done = 0;                                1787     my $done = 0;
1794     my $print_options = 0;                       1788     my $print_options = 0;
1795     my $redraw = 1;                              1789     my $redraw = 1;
1796     while (!$done) {                             1790     while (!$done) {
1797         $count = 0;                              1791         $count = 0;
1798         if ($redraw) {                           1792         if ($redraw) {
1799             printf STDERR "\n%1s %2s %-65s",     1793             printf STDERR "\n%1s %2s %-65s",
1800                           "*", "#", "email/li    1794                           "*", "#", "email/list and role:stats";
1801             if ($email_git ||                    1795             if ($email_git ||
1802                 ($email_git_fallback && !$mai    1796                 ($email_git_fallback && !$maintained) ||
1803                 $email_git_blame) {              1797                 $email_git_blame) {
1804                 print STDERR "auth sign";        1798                 print STDERR "auth sign";
1805             }                                    1799             }
1806             print STDERR "\n";                   1800             print STDERR "\n";
1807             foreach my $entry (@list) {          1801             foreach my $entry (@list) {
1808                 my $email = $entry->[0];         1802                 my $email = $entry->[0];
1809                 my $role = $entry->[1];          1803                 my $role = $entry->[1];
1810                 my $sel = "";                    1804                 my $sel = "";
1811                 $sel = "*" if ($selected{$cou    1805                 $sel = "*" if ($selected{$count});
1812                 my $commit_author = $commit_a    1806                 my $commit_author = $commit_author_hash{$email};
1813                 my $commit_signer = $commit_s    1807                 my $commit_signer = $commit_signer_hash{$email};
1814                 my $authored = 0;                1808                 my $authored = 0;
1815                 my $signed = 0;                  1809                 my $signed = 0;
1816                 $authored++ for (@{$commit_au    1810                 $authored++ for (@{$commit_author});
1817                 $signed++ for (@{$commit_sign    1811                 $signed++ for (@{$commit_signer});
1818                 printf STDERR "%1s %2d %-65s"    1812                 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1819                 printf STDERR "%4d %4d", $aut    1813                 printf STDERR "%4d %4d", $authored, $signed
1820                     if ($authored > 0 || $sig    1814                     if ($authored > 0 || $signed > 0);
1821                 printf STDERR "\n     %s\n",     1815                 printf STDERR "\n     %s\n", $role;
1822                 if ($authored{$count}) {         1816                 if ($authored{$count}) {
1823                     my $commit_author = $comm    1817                     my $commit_author = $commit_author_hash{$email};
1824                     foreach my $ref (@{$commi    1818                     foreach my $ref (@{$commit_author}) {
1825                         print STDERR "     Au    1819                         print STDERR "     Author: @{$ref}[1]\n";
1826                     }                            1820                     }
1827                 }                                1821                 }
1828                 if ($signed{$count}) {           1822                 if ($signed{$count}) {
1829                     my $commit_signer = $comm    1823                     my $commit_signer = $commit_signer_hash{$email};
1830                     foreach my $ref (@{$commi    1824                     foreach my $ref (@{$commit_signer}) {
1831                         print STDERR "     @{    1825                         print STDERR "     @{$ref}[2]: @{$ref}[1]\n";
1832                     }                            1826                     }
1833                 }                                1827                 }
1834                                                  1828 
1835                 $count++;                        1829                 $count++;
1836             }                                    1830             }
1837         }                                        1831         }
1838         my $date_ref = \$email_git_since;        1832         my $date_ref = \$email_git_since;
1839         $date_ref = \$email_hg_since if (vcs_    1833         $date_ref = \$email_hg_since if (vcs_is_hg());
1840         if ($print_options) {                    1834         if ($print_options) {
1841             $print_options = 0;                  1835             $print_options = 0;
1842             if (vcs_exists()) {                  1836             if (vcs_exists()) {
1843                 print STDERR <<EOT               1837                 print STDERR <<EOT
1844                                                  1838 
1845 Version Control options:                         1839 Version Control options:
1846 g  use git history      [$email_git]             1840 g  use git history      [$email_git]
1847 gf use git-fallback     [$email_git_fallback]    1841 gf use git-fallback     [$email_git_fallback]
1848 b  use git blame        [$email_git_blame]       1842 b  use git blame        [$email_git_blame]
1849 bs use blame signatures [$email_git_blame_sig    1843 bs use blame signatures [$email_git_blame_signatures]
1850 c# minimum commits      [$email_git_min_signa    1844 c# minimum commits      [$email_git_min_signatures]
1851 %# min percent          [$email_git_min_perce    1845 %# min percent          [$email_git_min_percent]
1852 d# history to use       [$$date_ref]             1846 d# history to use       [$$date_ref]
1853 x# max maintainers      [$email_git_max_maint    1847 x# max maintainers      [$email_git_max_maintainers]
1854 t  all signature types  [$email_git_all_signa    1848 t  all signature types  [$email_git_all_signature_types]
1855 m  use .mailmap         [$email_use_mailmap]     1849 m  use .mailmap         [$email_use_mailmap]
1856 EOT                                              1850 EOT
1857             }                                    1851             }
1858             print STDERR <<EOT                   1852             print STDERR <<EOT
1859                                                  1853 
1860 Additional options:                              1854 Additional options:
1861 0  toggle all                                    1855 0  toggle all
1862 tm toggle maintainers                            1856 tm toggle maintainers
1863 tg toggle git entries                            1857 tg toggle git entries
1864 tl toggle open list entries                      1858 tl toggle open list entries
1865 ts toggle subscriber list entries                1859 ts toggle subscriber list entries
1866 f  emails in file       [$email_file_emails]     1860 f  emails in file       [$email_file_emails]
1867 k  keywords in file     [$keywords]              1861 k  keywords in file     [$keywords]
1868 r  remove duplicates    [$email_remove_duplic    1862 r  remove duplicates    [$email_remove_duplicates]
1869 p# pattern match depth  [$pattern_depth]         1863 p# pattern match depth  [$pattern_depth]
1870 EOT                                              1864 EOT
1871         }                                        1865         }
1872         print STDERR                             1866         print STDERR
1873 "\n#(toggle), A#(author), S#(signed) *(all),     1867 "\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1874                                                  1868 
1875         my $input = <STDIN>;                     1869         my $input = <STDIN>;
1876         chomp($input);                           1870         chomp($input);
1877                                                  1871 
1878         $redraw = 1;                             1872         $redraw = 1;
1879         my $rerun = 0;                           1873         my $rerun = 0;
1880         my @wish = split(/[, ]+/, $input);       1874         my @wish = split(/[, ]+/, $input);
1881         foreach my $nr (@wish) {                 1875         foreach my $nr (@wish) {
1882             $nr = lc($nr);                       1876             $nr = lc($nr);
1883             my $sel = substr($nr, 0, 1);         1877             my $sel = substr($nr, 0, 1);
1884             my $str = substr($nr, 1);            1878             my $str = substr($nr, 1);
1885             my $val = 0;                         1879             my $val = 0;
1886             $val = $1 if $str =~ /^(\d+)$/;      1880             $val = $1 if $str =~ /^(\d+)$/;
1887                                                  1881 
1888             if ($sel eq "y") {                   1882             if ($sel eq "y") {
1889                 $interactive = 0;                1883                 $interactive = 0;
1890                 $done = 1;                       1884                 $done = 1;
1891                 $output_rolestats = 0;           1885                 $output_rolestats = 0;
1892                 $output_roles = 0;               1886                 $output_roles = 0;
1893                 last;                            1887                 last;
1894             } elsif ($nr =~ /^\d+$/ && $nr >     1888             } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1895                 $selected{$nr - 1} = !$select    1889                 $selected{$nr - 1} = !$selected{$nr - 1};
1896             } elsif ($sel eq "*" || $sel eq '    1890             } elsif ($sel eq "*" || $sel eq '^') {
1897                 my $toggle = 0;                  1891                 my $toggle = 0;
1898                 $toggle = 1 if ($sel eq '*');    1892                 $toggle = 1 if ($sel eq '*');
1899                 for (my $i = 0; $i < $count;     1893                 for (my $i = 0; $i < $count; $i++) {
1900                     $selected{$i} = $toggle;     1894                     $selected{$i} = $toggle;
1901                 }                                1895                 }
1902             } elsif ($sel eq "0") {              1896             } elsif ($sel eq "0") {
1903                 for (my $i = 0; $i < $count;     1897                 for (my $i = 0; $i < $count; $i++) {
1904                     $selected{$i} = !$selecte    1898                     $selected{$i} = !$selected{$i};
1905                 }                                1899                 }
1906             } elsif ($sel eq "t") {              1900             } elsif ($sel eq "t") {
1907                 if (lc($str) eq "m") {           1901                 if (lc($str) eq "m") {
1908                     for (my $i = 0; $i < $cou    1902                     for (my $i = 0; $i < $count; $i++) {
1909                         $selected{$i} = !$sel    1903                         $selected{$i} = !$selected{$i}
1910                             if ($list[$i]->[1    1904                             if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1911                     }                            1905                     }
1912                 } elsif (lc($str) eq "g") {      1906                 } elsif (lc($str) eq "g") {
1913                     for (my $i = 0; $i < $cou    1907                     for (my $i = 0; $i < $count; $i++) {
1914                         $selected{$i} = !$sel    1908                         $selected{$i} = !$selected{$i}
1915                             if ($list[$i]->[1    1909                             if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1916                     }                            1910                     }
1917                 } elsif (lc($str) eq "l") {      1911                 } elsif (lc($str) eq "l") {
1918                     for (my $i = 0; $i < $cou    1912                     for (my $i = 0; $i < $count; $i++) {
1919                         $selected{$i} = !$sel    1913                         $selected{$i} = !$selected{$i}
1920                             if ($list[$i]->[1    1914                             if ($list[$i]->[1] =~ /^(open list)/i);
1921                     }                            1915                     }
1922                 } elsif (lc($str) eq "s") {      1916                 } elsif (lc($str) eq "s") {
1923                     for (my $i = 0; $i < $cou    1917                     for (my $i = 0; $i < $count; $i++) {
1924                         $selected{$i} = !$sel    1918                         $selected{$i} = !$selected{$i}
1925                             if ($list[$i]->[1    1919                             if ($list[$i]->[1] =~ /^(subscriber list)/i);
1926                     }                            1920                     }
1927                 }                                1921                 }
1928             } elsif ($sel eq "a") {              1922             } elsif ($sel eq "a") {
1929                 if ($val > 0 && $val <= $coun    1923                 if ($val > 0 && $val <= $count) {
1930                     $authored{$val - 1} = !$a    1924                     $authored{$val - 1} = !$authored{$val - 1};
1931                 } elsif ($str eq '*' || $str     1925                 } elsif ($str eq '*' || $str eq '^') {
1932                     my $toggle = 0;              1926                     my $toggle = 0;
1933                     $toggle = 1 if ($str eq '    1927                     $toggle = 1 if ($str eq '*');
1934                     for (my $i = 0; $i < $cou    1928                     for (my $i = 0; $i < $count; $i++) {
1935                         $authored{$i} = $togg    1929                         $authored{$i} = $toggle;
1936                     }                            1930                     }
1937                 }                                1931                 }
1938             } elsif ($sel eq "s") {              1932             } elsif ($sel eq "s") {
1939                 if ($val > 0 && $val <= $coun    1933                 if ($val > 0 && $val <= $count) {
1940                     $signed{$val - 1} = !$sig    1934                     $signed{$val - 1} = !$signed{$val - 1};
1941                 } elsif ($str eq '*' || $str     1935                 } elsif ($str eq '*' || $str eq '^') {
1942                     my $toggle = 0;              1936                     my $toggle = 0;
1943                     $toggle = 1 if ($str eq '    1937                     $toggle = 1 if ($str eq '*');
1944                     for (my $i = 0; $i < $cou    1938                     for (my $i = 0; $i < $count; $i++) {
1945                         $signed{$i} = $toggle    1939                         $signed{$i} = $toggle;
1946                     }                            1940                     }
1947                 }                                1941                 }
1948             } elsif ($sel eq "o") {              1942             } elsif ($sel eq "o") {
1949                 $print_options = 1;              1943                 $print_options = 1;
1950                 $redraw = 1;                     1944                 $redraw = 1;
1951             } elsif ($sel eq "g") {              1945             } elsif ($sel eq "g") {
1952                 if ($str eq "f") {               1946                 if ($str eq "f") {
1953                     bool_invert(\$email_git_f    1947                     bool_invert(\$email_git_fallback);
1954                 } else {                         1948                 } else {
1955                     bool_invert(\$email_git);    1949                     bool_invert(\$email_git);
1956                 }                                1950                 }
1957                 $rerun = 1;                      1951                 $rerun = 1;
1958             } elsif ($sel eq "b") {              1952             } elsif ($sel eq "b") {
1959                 if ($str eq "s") {               1953                 if ($str eq "s") {
1960                     bool_invert(\$email_git_b    1954                     bool_invert(\$email_git_blame_signatures);
1961                 } else {                         1955                 } else {
1962                     bool_invert(\$email_git_b    1956                     bool_invert(\$email_git_blame);
1963                 }                                1957                 }
1964                 $rerun = 1;                      1958                 $rerun = 1;
1965             } elsif ($sel eq "c") {              1959             } elsif ($sel eq "c") {
1966                 if ($val > 0) {                  1960                 if ($val > 0) {
1967                     $email_git_min_signatures    1961                     $email_git_min_signatures = $val;
1968                     $rerun = 1;                  1962                     $rerun = 1;
1969                 }                                1963                 }
1970             } elsif ($sel eq "x") {              1964             } elsif ($sel eq "x") {
1971                 if ($val > 0) {                  1965                 if ($val > 0) {
1972                     $email_git_max_maintainer    1966                     $email_git_max_maintainers = $val;
1973                     $rerun = 1;                  1967                     $rerun = 1;
1974                 }                                1968                 }
1975             } elsif ($sel eq "%") {              1969             } elsif ($sel eq "%") {
1976                 if ($str ne "" && $val >= 0)     1970                 if ($str ne "" && $val >= 0) {
1977                     $email_git_min_percent =     1971                     $email_git_min_percent = $val;
1978                     $rerun = 1;                  1972                     $rerun = 1;
1979                 }                                1973                 }
1980             } elsif ($sel eq "d") {              1974             } elsif ($sel eq "d") {
1981                 if (vcs_is_git()) {              1975                 if (vcs_is_git()) {
1982                     $email_git_since = $str;     1976                     $email_git_since = $str;
1983                 } elsif (vcs_is_hg()) {          1977                 } elsif (vcs_is_hg()) {
1984                     $email_hg_since = $str;      1978                     $email_hg_since = $str;
1985                 }                                1979                 }
1986                 $rerun = 1;                      1980                 $rerun = 1;
1987             } elsif ($sel eq "t") {              1981             } elsif ($sel eq "t") {
1988                 bool_invert(\$email_git_all_s    1982                 bool_invert(\$email_git_all_signature_types);
1989                 $rerun = 1;                      1983                 $rerun = 1;
1990             } elsif ($sel eq "f") {              1984             } elsif ($sel eq "f") {
1991                 bool_invert(\$email_file_emai    1985                 bool_invert(\$email_file_emails);
1992                 $rerun = 1;                      1986                 $rerun = 1;
1993             } elsif ($sel eq "r") {              1987             } elsif ($sel eq "r") {
1994                 bool_invert(\$email_remove_du    1988                 bool_invert(\$email_remove_duplicates);
1995                 $rerun = 1;                      1989                 $rerun = 1;
1996             } elsif ($sel eq "m") {              1990             } elsif ($sel eq "m") {
1997                 bool_invert(\$email_use_mailm    1991                 bool_invert(\$email_use_mailmap);
1998                 read_mailmap();                  1992                 read_mailmap();
1999                 $rerun = 1;                      1993                 $rerun = 1;
2000             } elsif ($sel eq "k") {              1994             } elsif ($sel eq "k") {
2001                 bool_invert(\$keywords);         1995                 bool_invert(\$keywords);
2002                 $rerun = 1;                      1996                 $rerun = 1;
2003             } elsif ($sel eq "p") {              1997             } elsif ($sel eq "p") {
2004                 if ($str ne "" && $val >= 0)     1998                 if ($str ne "" && $val >= 0) {
2005                     $pattern_depth = $val;       1999                     $pattern_depth = $val;
2006                     $rerun = 1;                  2000                     $rerun = 1;
2007                 }                                2001                 }
2008             } elsif ($sel eq "h" || $sel eq "    2002             } elsif ($sel eq "h" || $sel eq "?") {
2009                 print STDERR <<EOT               2003                 print STDERR <<EOT
2010                                                  2004 
2011 Interactive mode allows you to select the var    2005 Interactive mode allows you to select the various maintainers, submitters,
2012 commit signers and mailing lists that could b    2006 commit signers and mailing lists that could be CC'd on a patch.
2013                                                  2007 
2014 Any *'d entry is selected.                       2008 Any *'d entry is selected.
2015                                                  2009 
2016 If you have git or hg installed, you can choo    2010 If you have git or hg installed, you can choose to summarize the commit
2017 history of files in the patch.  Also, each li    2011 history of files in the patch.  Also, each line of the current file can
2018 be matched to its commit author and that comm    2012 be matched to its commit author and that commits signers with blame.
2019                                                  2013 
2020 Various knobs exist to control the length of     2014 Various knobs exist to control the length of time for active commit
2021 tracking, the maximum number of commit author    2015 tracking, the maximum number of commit authors and signers to add,
2022 and such.                                        2016 and such.
2023                                                  2017 
2024 Enter selections at the prompt until you are     2018 Enter selections at the prompt until you are satisfied that the selected
2025 maintainers are appropriate.  You may enter m    2019 maintainers are appropriate.  You may enter multiple selections separated
2026 by either commas or spaces.                      2020 by either commas or spaces.
2027                                                  2021 
2028 EOT                                              2022 EOT
2029             } else {                             2023             } else {
2030                 print STDERR "invalid option:    2024                 print STDERR "invalid option: '$nr'\n";
2031                 $redraw = 0;                     2025                 $redraw = 0;
2032             }                                    2026             }
2033         }                                        2027         }
2034         if ($rerun) {                            2028         if ($rerun) {
2035             print STDERR "git-blame can be ve    2029             print STDERR "git-blame can be very slow, please have patience..."
2036                 if ($email_git_blame);           2030                 if ($email_git_blame);
2037             goto &get_maintainers;               2031             goto &get_maintainers;
2038         }                                        2032         }
2039     }                                            2033     }
2040                                                  2034 
2041     #drop not selected entries                   2035     #drop not selected entries
2042     $count = 0;                                  2036     $count = 0;
2043     my @new_emailto = ();                        2037     my @new_emailto = ();
2044     foreach my $entry (@list) {                  2038     foreach my $entry (@list) {
2045         if ($selected{$count}) {                 2039         if ($selected{$count}) {
2046             push(@new_emailto, $list[$count])    2040             push(@new_emailto, $list[$count]);
2047         }                                        2041         }
2048         $count++;                                2042         $count++;
2049     }                                            2043     }
2050     return @new_emailto;                         2044     return @new_emailto;
2051 }                                                2045 }
2052                                                  2046 
2053 sub bool_invert {                                2047 sub bool_invert {
2054     my ($bool_ref) = @_;                         2048     my ($bool_ref) = @_;
2055                                                  2049 
2056     if ($$bool_ref) {                            2050     if ($$bool_ref) {
2057         $$bool_ref = 0;                          2051         $$bool_ref = 0;
2058     } else {                                     2052     } else {
2059         $$bool_ref = 1;                          2053         $$bool_ref = 1;
2060     }                                            2054     }
2061 }                                                2055 }
2062                                                  2056 
2063 sub deduplicate_email {                          2057 sub deduplicate_email {
2064     my ($email) = @_;                            2058     my ($email) = @_;
2065                                                  2059 
2066     my $matched = 0;                             2060     my $matched = 0;
2067     my ($name, $address) = parse_email($email    2061     my ($name, $address) = parse_email($email);
2068     $email = format_email($name, $address, 1)    2062     $email = format_email($name, $address, 1);
2069     $email = mailmap_email($email);              2063     $email = mailmap_email($email);
2070                                                  2064 
2071     return $email if (!$email_remove_duplicat    2065     return $email if (!$email_remove_duplicates);
2072                                                  2066 
2073     ($name, $address) = parse_email($email);     2067     ($name, $address) = parse_email($email);
2074                                                  2068 
2075     if ($name ne "" && $deduplicate_name_hash    2069     if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
2076         $name = $deduplicate_name_hash{lc($na    2070         $name = $deduplicate_name_hash{lc($name)}->[0];
2077         $address = $deduplicate_name_hash{lc(    2071         $address = $deduplicate_name_hash{lc($name)}->[1];
2078         $matched = 1;                            2072         $matched = 1;
2079     } elsif ($deduplicate_address_hash{lc($ad    2073     } elsif ($deduplicate_address_hash{lc($address)}) {
2080         $name = $deduplicate_address_hash{lc(    2074         $name = $deduplicate_address_hash{lc($address)}->[0];
2081         $address = $deduplicate_address_hash{    2075         $address = $deduplicate_address_hash{lc($address)}->[1];
2082         $matched = 1;                            2076         $matched = 1;
2083     }                                            2077     }
2084     if (!$matched) {                             2078     if (!$matched) {
2085         $deduplicate_name_hash{lc($name)} = [    2079         $deduplicate_name_hash{lc($name)} = [ $name, $address ];
2086         $deduplicate_address_hash{lc($address    2080         $deduplicate_address_hash{lc($address)} = [ $name, $address ];
2087     }                                            2081     }
2088     $email = format_email($name, $address, 1)    2082     $email = format_email($name, $address, 1);
2089     $email = mailmap_email($email);              2083     $email = mailmap_email($email);
2090     return $email;                               2084     return $email;
2091 }                                                2085 }
2092                                                  2086 
2093 sub save_commits_by_author {                     2087 sub save_commits_by_author {
2094     my (@lines) = @_;                            2088     my (@lines) = @_;
2095                                                  2089 
2096     my @authors = ();                            2090     my @authors = ();
2097     my @commits = ();                            2091     my @commits = ();
2098     my @subjects = ();                           2092     my @subjects = ();
2099                                                  2093 
2100     foreach my $line (@lines) {                  2094     foreach my $line (@lines) {
2101         if ($line =~ m/$VCS_cmds{"author_patt    2095         if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2102             my $author = $1;                     2096             my $author = $1;
2103             $author = deduplicate_email($auth    2097             $author = deduplicate_email($author);
2104             push(@authors, $author);             2098             push(@authors, $author);
2105         }                                        2099         }
2106         push(@commits, $1) if ($line =~ m/$VC    2100         push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
2107         push(@subjects, $1) if ($line =~ m/$V    2101         push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
2108     }                                            2102     }
2109                                                  2103 
2110     for (my $i = 0; $i < @authors; $i++) {       2104     for (my $i = 0; $i < @authors; $i++) {
2111         my $exists = 0;                          2105         my $exists = 0;
2112         foreach my $ref(@{$commit_author_hash    2106         foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
2113             if (@{$ref}[0] eq $commits[$i] &&    2107             if (@{$ref}[0] eq $commits[$i] &&
2114                 @{$ref}[1] eq $subjects[$i])     2108                 @{$ref}[1] eq $subjects[$i]) {
2115                 $exists = 1;                     2109                 $exists = 1;
2116                 last;                            2110                 last;
2117             }                                    2111             }
2118         }                                        2112         }
2119         if (!$exists) {                          2113         if (!$exists) {
2120             push(@{$commit_author_hash{$autho    2114             push(@{$commit_author_hash{$authors[$i]}},
2121                  [ ($commits[$i], $subjects[$    2115                  [ ($commits[$i], $subjects[$i]) ]);
2122         }                                        2116         }
2123     }                                            2117     }
2124 }                                                2118 }
2125                                                  2119 
2126 sub save_commits_by_signer {                     2120 sub save_commits_by_signer {
2127     my (@lines) = @_;                            2121     my (@lines) = @_;
2128                                                  2122 
2129     my $commit = "";                             2123     my $commit = "";
2130     my $subject = "";                            2124     my $subject = "";
2131                                                  2125 
2132     foreach my $line (@lines) {                  2126     foreach my $line (@lines) {
2133         $commit = $1 if ($line =~ m/$VCS_cmds    2127         $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
2134         $subject = $1 if ($line =~ m/$VCS_cmd    2128         $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
2135         if ($line =~ /^[ \t]*${signature_patt    2129         if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
2136             my @signatures = ($line);            2130             my @signatures = ($line);
2137             my ($types_ref, $signers_ref) = e    2131             my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
2138             my @types = @$types_ref;             2132             my @types = @$types_ref;
2139             my @signers = @$signers_ref;         2133             my @signers = @$signers_ref;
2140                                                  2134 
2141             my $type = $types[0];                2135             my $type = $types[0];
2142             my $signer = $signers[0];            2136             my $signer = $signers[0];
2143                                                  2137 
2144             $signer = deduplicate_email($sign    2138             $signer = deduplicate_email($signer);
2145                                                  2139 
2146             my $exists = 0;                      2140             my $exists = 0;
2147             foreach my $ref(@{$commit_signer_    2141             foreach my $ref(@{$commit_signer_hash{$signer}}) {
2148                 if (@{$ref}[0] eq $commit &&     2142                 if (@{$ref}[0] eq $commit &&
2149                     @{$ref}[1] eq $subject &&    2143                     @{$ref}[1] eq $subject &&
2150                     @{$ref}[2] eq $type) {       2144                     @{$ref}[2] eq $type) {
2151                     $exists = 1;                 2145                     $exists = 1;
2152                     last;                        2146                     last;
2153                 }                                2147                 }
2154             }                                    2148             }
2155             if (!$exists) {                      2149             if (!$exists) {
2156                 push(@{$commit_signer_hash{$s    2150                 push(@{$commit_signer_hash{$signer}},
2157                      [ ($commit, $subject, $t    2151                      [ ($commit, $subject, $type) ]);
2158             }                                    2152             }
2159         }                                        2153         }
2160     }                                            2154     }
2161 }                                                2155 }
2162                                                  2156 
2163 sub vcs_assign {                                 2157 sub vcs_assign {
2164     my ($role, $divisor, @lines) = @_;           2158     my ($role, $divisor, @lines) = @_;
2165                                                  2159 
2166     my %hash;                                    2160     my %hash;
2167     my $count = 0;                               2161     my $count = 0;
2168                                                  2162 
2169     return if (@lines <= 0);                     2163     return if (@lines <= 0);
2170                                                  2164 
2171     if ($divisor <= 0) {                         2165     if ($divisor <= 0) {
2172         warn("Bad divisor in " . (caller(0))[    2166         warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
2173         $divisor = 1;                            2167         $divisor = 1;
2174     }                                            2168     }
2175                                                  2169 
2176     @lines = mailmap(@lines);                    2170     @lines = mailmap(@lines);
2177                                                  2171 
2178     return if (@lines <= 0);                     2172     return if (@lines <= 0);
2179                                                  2173 
2180     @lines = sort(@lines);                       2174     @lines = sort(@lines);
2181                                                  2175 
2182     # uniq -c                                    2176     # uniq -c
2183     $hash{$_}++ for @lines;                      2177     $hash{$_}++ for @lines;
2184                                                  2178 
2185     # sort -rn                                   2179     # sort -rn
2186     foreach my $line (sort {$hash{$b} <=> $ha    2180     foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
2187         my $sign_offs = $hash{$line};            2181         my $sign_offs = $hash{$line};
2188         my $percent = $sign_offs * 100 / $div    2182         my $percent = $sign_offs * 100 / $divisor;
2189                                                  2183 
2190         $percent = 100 if ($percent > 100);      2184         $percent = 100 if ($percent > 100);
2191         next if (ignore_email_address($line))    2185         next if (ignore_email_address($line));
2192         $count++;                                2186         $count++;
2193         last if ($sign_offs < $email_git_min_    2187         last if ($sign_offs < $email_git_min_signatures ||
2194                  $count > $email_git_max_main    2188                  $count > $email_git_max_maintainers ||
2195                  $percent < $email_git_min_pe    2189                  $percent < $email_git_min_percent);
2196         push_email_address($line, '');           2190         push_email_address($line, '');
2197         if ($output_rolestats) {                 2191         if ($output_rolestats) {
2198             my $fmt_percent = sprintf("%.0f",    2192             my $fmt_percent = sprintf("%.0f", $percent);
2199             add_role($line, "$role:$sign_offs    2193             add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
2200         } else {                                 2194         } else {
2201             add_role($line, $role);              2195             add_role($line, $role);
2202         }                                        2196         }
2203     }                                            2197     }
2204 }                                                2198 }
2205                                                  2199 
2206 sub vcs_file_signoffs {                          2200 sub vcs_file_signoffs {
2207     my ($file) = @_;                             2201     my ($file) = @_;
2208                                                  2202 
2209     my $authors_ref;                             2203     my $authors_ref;
2210     my $signers_ref;                             2204     my $signers_ref;
2211     my $stats_ref;                               2205     my $stats_ref;
2212     my @authors = ();                            2206     my @authors = ();
2213     my @signers = ();                            2207     my @signers = ();
2214     my @stats = ();                              2208     my @stats = ();
2215     my $commits;                                 2209     my $commits;
2216                                                  2210 
2217     $vcs_used = vcs_exists();                    2211     $vcs_used = vcs_exists();
2218     return if (!$vcs_used);                      2212     return if (!$vcs_used);
2219                                                  2213 
2220     my $cmd = $VCS_cmds{"find_signers_cmd"};     2214     my $cmd = $VCS_cmds{"find_signers_cmd"};
2221     $cmd =~ s/(\$\w+)/$1/eeg;           # int    2215     $cmd =~ s/(\$\w+)/$1/eeg;           # interpolate $cmd
2222                                                  2216 
2223     ($commits, $signers_ref, $authors_ref, $s    2217     ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2224                                                  2218 
2225     @signers = @{$signers_ref} if defined $si    2219     @signers = @{$signers_ref} if defined $signers_ref;
2226     @authors = @{$authors_ref} if defined $au    2220     @authors = @{$authors_ref} if defined $authors_ref;
2227     @stats = @{$stats_ref} if defined $stats_    2221     @stats = @{$stats_ref} if defined $stats_ref;
2228                                                  2222 
2229 #    print("commits: <$commits>\nsigners:<@sig    2223 #    print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
2230                                                  2224 
2231     foreach my $signer (@signers) {              2225     foreach my $signer (@signers) {
2232         $signer = deduplicate_email($signer);    2226         $signer = deduplicate_email($signer);
2233     }                                            2227     }
2234                                                  2228 
2235     vcs_assign("commit_signer", $commits, @si    2229     vcs_assign("commit_signer", $commits, @signers);
2236     vcs_assign("authored", $commits, @authors    2230     vcs_assign("authored", $commits, @authors);
2237     if ($#authors == $#stats) {                  2231     if ($#authors == $#stats) {
2238         my $stat_pattern = $VCS_cmds{"stat_pa    2232         my $stat_pattern = $VCS_cmds{"stat_pattern"};
2239         $stat_pattern =~ s/(\$\w+)/$1/eeg;       2233         $stat_pattern =~ s/(\$\w+)/$1/eeg;      #interpolate $stat_pattern
2240                                                  2234 
2241         my $added = 0;                           2235         my $added = 0;
2242         my $deleted = 0;                         2236         my $deleted = 0;
2243         for (my $i = 0; $i <= $#stats; $i++)     2237         for (my $i = 0; $i <= $#stats; $i++) {
2244             if ($stats[$i] =~ /$stat_pattern/    2238             if ($stats[$i] =~ /$stat_pattern/) {
2245                 $added += $1;                    2239                 $added += $1;
2246                 $deleted += $2;                  2240                 $deleted += $2;
2247             }                                    2241             }
2248         }                                        2242         }
2249         my @tmp_authors = uniq(@authors);        2243         my @tmp_authors = uniq(@authors);
2250         foreach my $author (@tmp_authors) {      2244         foreach my $author (@tmp_authors) {
2251             $author = deduplicate_email($auth    2245             $author = deduplicate_email($author);
2252         }                                        2246         }
2253         @tmp_authors = uniq(@tmp_authors);       2247         @tmp_authors = uniq(@tmp_authors);
2254         my @list_added = ();                     2248         my @list_added = ();
2255         my @list_deleted = ();                   2249         my @list_deleted = ();
2256         foreach my $author (@tmp_authors) {      2250         foreach my $author (@tmp_authors) {
2257             my $auth_added = 0;                  2251             my $auth_added = 0;
2258             my $auth_deleted = 0;                2252             my $auth_deleted = 0;
2259             for (my $i = 0; $i <= $#stats; $i    2253             for (my $i = 0; $i <= $#stats; $i++) {
2260                 if ($author eq deduplicate_em    2254                 if ($author eq deduplicate_email($authors[$i]) &&
2261                     $stats[$i] =~ /$stat_patt    2255                     $stats[$i] =~ /$stat_pattern/) {
2262                     $auth_added += $1;           2256                     $auth_added += $1;
2263                     $auth_deleted += $2;         2257                     $auth_deleted += $2;
2264                 }                                2258                 }
2265             }                                    2259             }
2266             for (my $i = 0; $i < $auth_added;    2260             for (my $i = 0; $i < $auth_added; $i++) {
2267                 push(@list_added, $author);      2261                 push(@list_added, $author);
2268             }                                    2262             }
2269             for (my $i = 0; $i < $auth_delete    2263             for (my $i = 0; $i < $auth_deleted; $i++) {
2270                 push(@list_deleted, $author);    2264                 push(@list_deleted, $author);
2271             }                                    2265             }
2272         }                                        2266         }
2273         vcs_assign("added_lines", $added, @li    2267         vcs_assign("added_lines", $added, @list_added);
2274         vcs_assign("removed_lines", $deleted,    2268         vcs_assign("removed_lines", $deleted, @list_deleted);
2275     }                                            2269     }
2276 }                                                2270 }
2277                                                  2271 
2278 sub vcs_file_blame {                             2272 sub vcs_file_blame {
2279     my ($file) = @_;                             2273     my ($file) = @_;
2280                                                  2274 
2281     my @signers = ();                            2275     my @signers = ();
2282     my @all_commits = ();                        2276     my @all_commits = ();
2283     my @commits = ();                            2277     my @commits = ();
2284     my $total_commits;                           2278     my $total_commits;
2285     my $total_lines;                             2279     my $total_lines;
2286                                                  2280 
2287     $vcs_used = vcs_exists();                    2281     $vcs_used = vcs_exists();
2288     return if (!$vcs_used);                      2282     return if (!$vcs_used);
2289                                                  2283 
2290     @all_commits = vcs_blame($file);             2284     @all_commits = vcs_blame($file);
2291     @commits = uniq(@all_commits);               2285     @commits = uniq(@all_commits);
2292     $total_commits = @commits;                   2286     $total_commits = @commits;
2293     $total_lines = @all_commits;                 2287     $total_lines = @all_commits;
2294                                                  2288 
2295     if ($email_git_blame_signatures) {           2289     if ($email_git_blame_signatures) {
2296         if (vcs_is_hg()) {                       2290         if (vcs_is_hg()) {
2297             my $commit_count;                    2291             my $commit_count;
2298             my $commit_authors_ref;              2292             my $commit_authors_ref;
2299             my $commit_signers_ref;              2293             my $commit_signers_ref;
2300             my $stats_ref;                       2294             my $stats_ref;
2301             my @commit_authors = ();             2295             my @commit_authors = ();
2302             my @commit_signers = ();             2296             my @commit_signers = ();
2303             my $commit = join(" -r ", @commit    2297             my $commit = join(" -r ", @commits);
2304             my $cmd;                             2298             my $cmd;
2305                                                  2299 
2306             $cmd = $VCS_cmds{"find_commit_sig    2300             $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2307             $cmd =~ s/(\$\w+)/$1/eeg;   #subs    2301             $cmd =~ s/(\$\w+)/$1/eeg;   #substitute variables in $cmd
2308                                                  2302 
2309             ($commit_count, $commit_signers_r    2303             ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2310             @commit_authors = @{$commit_autho    2304             @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2311             @commit_signers = @{$commit_signe    2305             @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
2312                                                  2306 
2313             push(@signers, @commit_signers);     2307             push(@signers, @commit_signers);
2314         } else {                                 2308         } else {
2315             foreach my $commit (@commits) {      2309             foreach my $commit (@commits) {
2316                 my $commit_count;                2310                 my $commit_count;
2317                 my $commit_authors_ref;          2311                 my $commit_authors_ref;
2318                 my $commit_signers_ref;          2312                 my $commit_signers_ref;
2319                 my $stats_ref;                   2313                 my $stats_ref;
2320                 my @commit_authors = ();         2314                 my @commit_authors = ();
2321                 my @commit_signers = ();         2315                 my @commit_signers = ();
2322                 my $cmd;                         2316                 my $cmd;
2323                                                  2317 
2324                 $cmd = $VCS_cmds{"find_commit    2318                 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2325                 $cmd =~ s/(\$\w+)/$1/eeg;        2319                 $cmd =~ s/(\$\w+)/$1/eeg;       #substitute variables in $cmd
2326                                                  2320 
2327                 ($commit_count, $commit_signe    2321                 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2328                 @commit_authors = @{$commit_a    2322                 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2329                 @commit_signers = @{$commit_s    2323                 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
2330                                                  2324 
2331                 push(@signers, @commit_signer    2325                 push(@signers, @commit_signers);
2332             }                                    2326             }
2333         }                                        2327         }
2334     }                                            2328     }
2335                                                  2329 
2336     if ($from_filename) {                        2330     if ($from_filename) {
2337         if ($output_rolestats) {                 2331         if ($output_rolestats) {
2338             my @blame_signers;                   2332             my @blame_signers;
2339             if (vcs_is_hg()) {{         # Dou    2333             if (vcs_is_hg()) {{         # Double brace for last exit
2340                 my $commit_count;                2334                 my $commit_count;
2341                 my @commit_signers = ();         2335                 my @commit_signers = ();
2342                 @commits = uniq(@commits);       2336                 @commits = uniq(@commits);
2343                 @commits = sort(@commits);       2337                 @commits = sort(@commits);
2344                 my $commit = join(" -r ", @co    2338                 my $commit = join(" -r ", @commits);
2345                 my $cmd;                         2339                 my $cmd;
2346                                                  2340 
2347                 $cmd = $VCS_cmds{"find_commit    2341                 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2348                 $cmd =~ s/(\$\w+)/$1/eeg;        2342                 $cmd =~ s/(\$\w+)/$1/eeg;       #substitute variables in $cmd
2349                                                  2343 
2350                 my @lines = ();                  2344                 my @lines = ();
2351                                                  2345 
2352                 @lines = &{$VCS_cmds{"execute    2346                 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2353                                                  2347 
2354                 if (!$email_git_penguin_chief    2348                 if (!$email_git_penguin_chiefs) {
2355                     @lines = grep(!/${penguin    2349                     @lines = grep(!/${penguin_chiefs}/i, @lines);
2356                 }                                2350                 }
2357                                                  2351 
2358                 last if !@lines;                 2352                 last if !@lines;
2359                                                  2353 
2360                 my @authors = ();                2354                 my @authors = ();
2361                 foreach my $line (@lines) {      2355                 foreach my $line (@lines) {
2362                     if ($line =~ m/$VCS_cmds{    2356                     if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2363                         my $author = $1;         2357                         my $author = $1;
2364                         $author = deduplicate    2358                         $author = deduplicate_email($author);
2365                         push(@authors, $autho    2359                         push(@authors, $author);
2366                     }                            2360                     }
2367                 }                                2361                 }
2368                                                  2362 
2369                 save_commits_by_author(@lines    2363                 save_commits_by_author(@lines) if ($interactive);
2370                 save_commits_by_signer(@lines    2364                 save_commits_by_signer(@lines) if ($interactive);
2371                                                  2365 
2372                 push(@signers, @authors);        2366                 push(@signers, @authors);
2373             }}                                   2367             }}
2374             else {                               2368             else {
2375                 foreach my $commit (@commits)    2369                 foreach my $commit (@commits) {
2376                     my $i;                       2370                     my $i;
2377                     my $cmd = $VCS_cmds{"find    2371                     my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2378                     $cmd =~ s/(\$\w+)/$1/eeg;    2372                     $cmd =~ s/(\$\w+)/$1/eeg;   #interpolate $cmd
2379                     my @author = vcs_find_aut    2373                     my @author = vcs_find_author($cmd);
2380                     next if !@author;            2374                     next if !@author;
2381                                                  2375 
2382                     my $formatted_author = de    2376                     my $formatted_author = deduplicate_email($author[0]);
2383                                                  2377 
2384                     my $count = grep(/$commit    2378                     my $count = grep(/$commit/, @all_commits);
2385                     for ($i = 0; $i < $count     2379                     for ($i = 0; $i < $count ; $i++) {
2386                         push(@blame_signers,     2380                         push(@blame_signers, $formatted_author);
2387                     }                            2381                     }
2388                 }                                2382                 }
2389             }                                    2383             }
2390             if (@blame_signers) {                2384             if (@blame_signers) {
2391                 vcs_assign("authored lines",     2385                 vcs_assign("authored lines", $total_lines, @blame_signers);
2392             }                                    2386             }
2393         }                                        2387         }
2394         foreach my $signer (@signers) {          2388         foreach my $signer (@signers) {
2395             $signer = deduplicate_email($sign    2389             $signer = deduplicate_email($signer);
2396         }                                        2390         }
2397         vcs_assign("commits", $total_commits,    2391         vcs_assign("commits", $total_commits, @signers);
2398     } else {                                     2392     } else {
2399         foreach my $signer (@signers) {          2393         foreach my $signer (@signers) {
2400             $signer = deduplicate_email($sign    2394             $signer = deduplicate_email($signer);
2401         }                                        2395         }
2402         vcs_assign("modified commits", $total    2396         vcs_assign("modified commits", $total_commits, @signers);
2403     }                                            2397     }
2404 }                                                2398 }
2405                                                  2399 
2406 sub vcs_file_exists {                            2400 sub vcs_file_exists {
2407     my ($file) = @_;                             2401     my ($file) = @_;
2408                                                  2402 
2409     my $exists;                                  2403     my $exists;
2410                                                  2404 
2411     my $vcs_used = vcs_exists();                 2405     my $vcs_used = vcs_exists();
2412     return 0 if (!$vcs_used);                    2406     return 0 if (!$vcs_used);
2413                                                  2407 
2414     my $cmd = $VCS_cmds{"file_exists_cmd"};      2408     my $cmd = $VCS_cmds{"file_exists_cmd"};
2415     $cmd =~ s/(\$\w+)/$1/eeg;           # int    2409     $cmd =~ s/(\$\w+)/$1/eeg;           # interpolate $cmd
2416     $cmd .= " 2>&1";                             2410     $cmd .= " 2>&1";
2417     $exists = &{$VCS_cmds{"execute_cmd"}}($cm    2411     $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
2418                                                  2412 
2419     return 0 if ($? != 0);                       2413     return 0 if ($? != 0);
2420                                                  2414 
2421     return $exists;                              2415     return $exists;
2422 }                                                2416 }
2423                                                  2417 
2424 sub vcs_list_files {                             2418 sub vcs_list_files {
2425     my ($file) = @_;                             2419     my ($file) = @_;
2426                                                  2420 
2427     my @lsfiles = ();                            2421     my @lsfiles = ();
2428                                                  2422 
2429     my $vcs_used = vcs_exists();                 2423     my $vcs_used = vcs_exists();
2430     return 0 if (!$vcs_used);                    2424     return 0 if (!$vcs_used);
2431                                                  2425 
2432     my $cmd = $VCS_cmds{"list_files_cmd"};       2426     my $cmd = $VCS_cmds{"list_files_cmd"};
2433     $cmd =~ s/(\$\w+)/$1/eeg;   # interpolate    2427     $cmd =~ s/(\$\w+)/$1/eeg;   # interpolate $cmd
2434     @lsfiles = &{$VCS_cmds{"execute_cmd"}}($c    2428     @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
2435                                                  2429 
2436     return () if ($? != 0);                      2430     return () if ($? != 0);
2437                                                  2431 
2438     return @lsfiles;                             2432     return @lsfiles;
2439 }                                                2433 }
2440                                                  2434 
2441 sub uniq {                                       2435 sub uniq {
2442     my (@parms) = @_;                            2436     my (@parms) = @_;
2443                                                  2437 
2444     my %saw;                                     2438     my %saw;
2445     @parms = grep(!$saw{$_}++, @parms);          2439     @parms = grep(!$saw{$_}++, @parms);
2446     return @parms;                               2440     return @parms;
2447 }                                                2441 }
2448                                                  2442 
2449 sub sort_and_uniq {                              2443 sub sort_and_uniq {
2450     my (@parms) = @_;                            2444     my (@parms) = @_;
2451                                                  2445 
2452     my %saw;                                     2446     my %saw;
2453     @parms = sort @parms;                        2447     @parms = sort @parms;
2454     @parms = grep(!$saw{$_}++, @parms);          2448     @parms = grep(!$saw{$_}++, @parms);
2455     return @parms;                               2449     return @parms;
2456 }                                                2450 }
2457                                                  2451 
2458 sub clean_file_emails {                          2452 sub clean_file_emails {
2459     my (@file_emails) = @_;                      2453     my (@file_emails) = @_;
2460     my @fmt_emails = ();                         2454     my @fmt_emails = ();
2461                                                  2455 
2462     foreach my $email (@file_emails) {           2456     foreach my $email (@file_emails) {
2463         $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\    2457         $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2464         my ($name, $address) = parse_email($e    2458         my ($name, $address) = parse_email($email);
                                                   >> 2459         if ($name eq '"[,\.]"') {
                                                   >> 2460             $name = "";
                                                   >> 2461         }
2465                                                  2462 
2466         # Strip quotes for easier processing, !! 2463         my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2467         $name =~ s/^"(.*)"$/$1/;              << 
2468                                               << 
2469         # Split into name-like parts and remo << 
2470         my @nw = split(/[^\p{L}\'\,\.\+-]/, $ << 
2471         @nw = grep(!/^[\'\,\.\+-]$/, @nw);    << 
2472                                               << 
2473         # Make a best effort to extract the n << 
2474         # only the last two names, or in the  << 
2475         # three names.                        << 
2476         if (@nw > 2) {                           2464         if (@nw > 2) {
2477             my $first = $nw[@nw - 3];            2465             my $first = $nw[@nw - 3];
2478             my $middle = $nw[@nw - 2];           2466             my $middle = $nw[@nw - 2];
2479             my $last = $nw[@nw - 1];             2467             my $last = $nw[@nw - 1];
2480                                                  2468 
2481             if (((length($first) == 1 && $fir !! 2469             if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2482                  (length($first) == 2 && subs    2470                  (length($first) == 2 && substr($first, -1) eq ".")) ||
2483                 (length($middle) == 1 ||         2471                 (length($middle) == 1 ||
2484                  (length($middle) == 2 && sub    2472                  (length($middle) == 2 && substr($middle, -1) eq "."))) {
2485                 $name = "$first $middle $last    2473                 $name = "$first $middle $last";
2486             } else {                             2474             } else {
2487                 $name = "$middle $last";         2475                 $name = "$middle $last";
2488             }                                    2476             }
2489         } else {                              << 
2490             $name = "@nw";                    << 
2491         }                                        2477         }
2492                                                  2478 
2493         if (substr($name, -1) =~ /[,\.]/) {      2479         if (substr($name, -1) =~ /[,\.]/) {
2494             $name = substr($name, 0, length($    2480             $name = substr($name, 0, length($name) - 1);
                                                   >> 2481         } elsif (substr($name, -2) =~ /[,\.]"/) {
                                                   >> 2482             $name = substr($name, 0, length($name) - 2) . '"';
2495         }                                        2483         }
2496                                                  2484 
2497         if (substr($name, 0, 1) =~ /[,\.]/) {    2485         if (substr($name, 0, 1) =~ /[,\.]/) {
2498             $name = substr($name, 1, length($    2486             $name = substr($name, 1, length($name) - 1);
                                                   >> 2487         } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
                                                   >> 2488             $name = '"' . substr($name, 2, length($name) - 2);
2499         }                                        2489         }
2500                                                  2490 
2501         my $fmt_email = format_email($name, $    2491         my $fmt_email = format_email($name, $address, $email_usename);
2502         push(@fmt_emails, $fmt_email);           2492         push(@fmt_emails, $fmt_email);
2503     }                                            2493     }
2504     return @fmt_emails;                          2494     return @fmt_emails;
2505 }                                                2495 }
2506                                                  2496 
2507 sub merge_email {                                2497 sub merge_email {
2508     my @lines;                                   2498     my @lines;
2509     my %saw;                                     2499     my %saw;
2510                                                  2500 
2511     for (@_) {                                   2501     for (@_) {
2512         my ($address, $role) = @$_;              2502         my ($address, $role) = @$_;
2513         if (!$saw{$address}) {                   2503         if (!$saw{$address}) {
2514             if ($output_roles) {                 2504             if ($output_roles) {
2515                 push(@lines, "$address ($role    2505                 push(@lines, "$address ($role)");
2516             } else {                             2506             } else {
2517                 push(@lines, $address);          2507                 push(@lines, $address);
2518             }                                    2508             }
2519             $saw{$address} = 1;                  2509             $saw{$address} = 1;
2520         }                                        2510         }
2521     }                                            2511     }
2522                                                  2512 
2523     return @lines;                               2513     return @lines;
2524 }                                                2514 }
2525                                                  2515 
2526 sub output {                                     2516 sub output {
2527     my (@parms) = @_;                            2517     my (@parms) = @_;
2528                                                  2518 
2529     if ($output_multiline) {                     2519     if ($output_multiline) {
2530         foreach my $line (@parms) {              2520         foreach my $line (@parms) {
2531             print("${line}\n");                  2521             print("${line}\n");
2532         }                                        2522         }
2533     } else {                                     2523     } else {
2534         print(join($output_separator, @parms)    2524         print(join($output_separator, @parms));
2535         print("\n");                             2525         print("\n");
2536     }                                            2526     }
2537 }                                                2527 }
2538                                                  2528 
2539 my $rfc822re;                                    2529 my $rfc822re;
2540                                                  2530 
2541 sub make_rfc822re {                              2531 sub make_rfc822re {
2542 #   Basic lexical tokens are specials, domain    2532 #   Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2543 #   comment.  We must allow for rfc822_lwsp (    2533 #   comment.  We must allow for rfc822_lwsp (or comments) after each of these.
2544 #   This regexp will only work on addresses w    2534 #   This regexp will only work on addresses which have had comments stripped
2545 #   and replaced with rfc822_lwsp.               2535 #   and replaced with rfc822_lwsp.
2546                                                  2536 
2547     my $specials = '()<>@,;:\\\\".\\[\\]';       2537     my $specials = '()<>@,;:\\\\".\\[\\]';
2548     my $controls = '\\000-\\037\\177';           2538     my $controls = '\\000-\\037\\177';
2549                                                  2539 
2550     my $dtext = "[^\\[\\]\\r\\\\]";              2540     my $dtext = "[^\\[\\]\\r\\\\]";
2551     my $domain_literal = "\\[(?:$dtext|\\\\.)    2541     my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2552                                                  2542 
2553     my $quoted_string = "\"(?:[^\\\"\\r\\\\]|    2543     my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2554                                                  2544 
2555 #   Use zero-width assertion to spot the limi    2545 #   Use zero-width assertion to spot the limit of an atom.  A simple
2556 #   $rfc822_lwsp* causes the regexp engine to    2546 #   $rfc822_lwsp* causes the regexp engine to hang occasionally.
2557     my $atom = "[^$specials $controls]+(?:$rf    2547     my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2558     my $word = "(?:$atom|$quoted_string)";       2548     my $word = "(?:$atom|$quoted_string)";
2559     my $localpart = "$word(?:\\.$rfc822_lwsp*    2549     my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2560                                                  2550 
2561     my $sub_domain = "(?:$atom|$domain_litera    2551     my $sub_domain = "(?:$atom|$domain_literal)";
2562     my $domain = "$sub_domain(?:\\.$rfc822_lw    2552     my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2563                                                  2553 
2564     my $addr_spec = "$localpart\@$rfc822_lwsp    2554     my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2565                                                  2555 
2566     my $phrase = "$word*";                       2556     my $phrase = "$word*";
2567     my $route = "(?:\@$domain(?:,\@$rfc822_lw    2557     my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2568     my $route_addr = "\\<$rfc822_lwsp*$route?    2558     my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2569     my $mailbox = "(?:$addr_spec|$phrase$rout    2559     my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2570                                                  2560 
2571     my $group = "$phrase:$rfc822_lwsp*(?:$mai    2561     my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2572     my $address = "(?:$mailbox|$group)";         2562     my $address = "(?:$mailbox|$group)";
2573                                                  2563 
2574     return "$rfc822_lwsp*$address";              2564     return "$rfc822_lwsp*$address";
2575 }                                                2565 }
2576                                                  2566 
2577 sub rfc822_strip_comments {                      2567 sub rfc822_strip_comments {
2578     my $s = shift;                               2568     my $s = shift;
2579 #   Recursively remove comments, and replace     2569 #   Recursively remove comments, and replace with a single space.  The simpler
2580 #   regexps in the Email Addressing FAQ are i    2570 #   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2581 #   chars in atoms, for example.                 2571 #   chars in atoms, for example.
2582                                                  2572 
2583     while ($s =~ s/^((?:[^"\\]|\\.)*             2573     while ($s =~ s/^((?:[^"\\]|\\.)*
2584                     (?:"(?:[^"\\]|\\.)*"(?:[^    2574                     (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2585                     \((?:[^()\\]|\\.)*\)/$1 /    2575                     \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2586     return $s;                                   2576     return $s;
2587 }                                                2577 }
2588                                                  2578 
2589 #   valid: returns true if the parameter is a    2579 #   valid: returns true if the parameter is an RFC822 valid address
2590 #                                                2580 #
2591 sub rfc822_valid {                               2581 sub rfc822_valid {
2592     my $s = rfc822_strip_comments(shift);        2582     my $s = rfc822_strip_comments(shift);
2593                                                  2583 
2594     if (!$rfc822re) {                            2584     if (!$rfc822re) {
2595         $rfc822re = make_rfc822re();             2585         $rfc822re = make_rfc822re();
2596     }                                            2586     }
2597                                                  2587 
2598     return $s =~ m/^$rfc822re$/so && $s =~ m/    2588     return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2599 }                                                2589 }
2600                                                  2590 
2601 #   validlist: In scalar context, returns tru    2591 #   validlist: In scalar context, returns true if the parameter is an RFC822
2602 #              valid list of addresses.          2592 #              valid list of addresses.
2603 #                                                2593 #
2604 #              In list context, returns an em    2594 #              In list context, returns an empty list on failure (an invalid
2605 #              address was found); otherwise     2595 #              address was found); otherwise a list whose first element is the
2606 #              number of addresses found and     2596 #              number of addresses found and whose remaining elements are the
2607 #              addresses.  This is needed to     2597 #              addresses.  This is needed to disambiguate failure (invalid)
2608 #              from success with no addresses    2598 #              from success with no addresses found, because an empty string is
2609 #              a valid list.                     2599 #              a valid list.
2610                                                  2600 
2611 sub rfc822_validlist {                           2601 sub rfc822_validlist {
2612     my $s = rfc822_strip_comments(shift);        2602     my $s = rfc822_strip_comments(shift);
2613                                                  2603 
2614     if (!$rfc822re) {                            2604     if (!$rfc822re) {
2615         $rfc822re = make_rfc822re();             2605         $rfc822re = make_rfc822re();
2616     }                                            2606     }
2617     # * null list items are valid according t    2607     # * null list items are valid according to the RFC
2618     # * the '1' business is to aid in disting    2608     # * the '1' business is to aid in distinguishing failure from no results
2619                                                  2609 
2620     my @r;                                       2610     my @r;
2621     if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822    2611     if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2622         $s =~ m/^$rfc822_char*$/) {              2612         $s =~ m/^$rfc822_char*$/) {
2623         while ($s =~ m/(?:^|,$rfc822_lwsp*)($    2613         while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
2624             push(@r, $1);                        2614             push(@r, $1);
2625         }                                        2615         }
2626         return wantarray ? (scalar(@r), @r) :    2616         return wantarray ? (scalar(@r), @r) : 1;
2627     }                                            2617     }
2628     return wantarray ? () : 0;                   2618     return wantarray ? () : 0;
2629 }                                                2619 }
                                                      

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php