blob: 0ebdefe74d5b1a0e98deefe4643a0131468fda07 [file] [log] [blame]
Kamil Rytarowskicb77f0d2017-05-07 23:25:26 +02001#!/usr/bin/env perl
Joe Perches882ea1d2018-06-07 17:04:33 -07002# SPDX-License-Identifier: GPL-2.0
3#
Joe Perchescb7301c2009-04-07 20:40:12 -07004# (c) 2007, Joe Perches <joe@perches.com>
5# created from checkpatch.pl
6#
7# Print selected MAINTAINERS information for
8# the files modified in a patch or for a file
9#
Roel Kluin3bd7bf52009-11-11 14:26:13 -080010# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
11# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
Joe Perchescb7301c2009-04-07 20:40:12 -070012
Kamil Rytarowskicb77f0d2017-05-07 23:25:26 +020013use warnings;
Joe Perchescb7301c2009-04-07 20:40:12 -070014use strict;
15
16my $P = $0;
Joe Perches7e1863a2011-01-12 16:59:49 -080017my $V = '0.26';
Joe Perchescb7301c2009-04-07 20:40:12 -070018
19use Getopt::Long qw(:config no_auto_abbrev);
Joe Perchesbe17bdd2016-01-20 14:58:24 -080020use Cwd;
Joe Perches6f7d98e2017-08-04 21:45:48 -070021use File::Find;
Joe Perchescb7301c2009-04-07 20:40:12 -070022
Joe Perchesbe17bdd2016-01-20 14:58:24 -080023my $cur_path = fastgetcwd() . '/';
Joe Perchescb7301c2009-04-07 20:40:12 -070024my $lk_path = "./";
25my $email = 1;
26my $email_usename = 1;
27my $email_maintainer = 1;
Joe Perchesc1c3f2c2014-06-02 12:05:17 -070028my $email_reviewer = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070029my $email_list = 1;
30my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070031my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070032my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070033my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080034my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070035my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070036my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070037my $email_git_min_signatures = 1;
38my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070039my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070040my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080041my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070042my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070043my $email_remove_duplicates = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -070044my $email_use_mailmap = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070045my $output_multiline = 1;
46my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080047my $output_roles = 0;
Joe Perches7e1863a2011-01-12 16:59:49 -080048my $output_rolestats = 1;
Joe Perches364f68d2015-06-25 15:01:52 -070049my $output_section_maxlen = 50;
Joe Perchescb7301c2009-04-07 20:40:12 -070050my $scm = 0;
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -070051my $tree = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070052my $web = 0;
53my $subsystem = 0;
54my $status = 0;
Joe Perches03aed212016-12-12 16:45:59 -080055my $letters = "";
Joe Perchesdcf36a92009-10-26 16:49:47 -070056my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080057my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080058my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070059my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070060my $pattern_depth = 0;
Joe Perches083bf9c2017-11-17 15:27:46 -080061my $self_test = undef;
Joe Perchescb7301c2009-04-07 20:40:12 -070062my $version = 0;
63my $help = 0;
Joe Perches6f7d98e2017-08-04 21:45:48 -070064my $find_maintainer_files = 0;
Joe Perches5f0baf92018-08-21 21:56:52 -070065my $maintainer_path;
Joe Perches683c6f82010-10-26 14:22:55 -070066my $vcs_used = 0;
67
Joe Perchescb7301c2009-04-07 20:40:12 -070068my $exit = 0;
69
Joe Perches683c6f82010-10-26 14:22:55 -070070my %commit_author_hash;
71my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070072
Joe Perchescb7301c2009-04-07 20:40:12 -070073my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070074push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070075#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070076#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070077
78my @penguin_chief_names = ();
79foreach my $chief (@penguin_chief) {
80 if ($chief =~ m/^(.*):(.*)/) {
81 my $chief_name = $1;
82 my $chief_addr = $2;
83 push(@penguin_chief_names, $chief_name);
84 }
85}
Joe Perchese4d26b02010-05-24 14:33:17 -070086my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
87
88# Signature types of people who are either
89# a) responsible for the code in question, or
90# b) familiar enough with it to give relevant feedback
91my @signature_tags = ();
92push(@signature_tags, "Signed-off-by:");
93push(@signature_tags, "Reviewed-by:");
94push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070095
Joe Perches7dea2682012-06-20 12:53:02 -070096my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
97
Joe Perches5f2441e2009-06-16 15:34:02 -070098# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070099my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -0700100my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700101
Joe Perches60db31a2009-12-14 18:00:50 -0800102# VCS command support: class-like functions and strings
103
104my %VCS_cmds;
105
106my %VCS_cmds_git = (
107 "execute_cmd" => \&git_execute_cmd,
Richard Genoudec83b612014-02-10 14:25:31 -0800108 "available" => '(which("git") ne "") && (-e ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -0700109 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800110 "git log --no-color --follow --since=\$email_git_since " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800111 '--numstat --no-merges ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700112 '--format="GitCommit: %H%n' .
113 'GitAuthor: %an <%ae>%n' .
114 'GitDate: %aD%n' .
115 'GitSubject: %s%n' .
116 '%b%n"' .
117 " -- \$file",
118 "find_commit_signers_cmd" =>
119 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800120 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700121 '--format="GitCommit: %H%n' .
122 'GitAuthor: %an <%ae>%n' .
123 'GitDate: %aD%n' .
124 'GitSubject: %s%n' .
125 '%b%n"' .
126 " -1 \$commit",
127 "find_commit_author_cmd" =>
128 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800129 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700130 '--format="GitCommit: %H%n' .
131 'GitAuthor: %an <%ae>%n' .
132 'GitDate: %aD%n' .
133 'GitSubject: %s%n"' .
134 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800135 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
136 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700137 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700138 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700139 "author_pattern" => "^GitAuthor: (.*)",
140 "subject_pattern" => "^GitSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800141 "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700142 "file_exists_cmd" => "git ls-files \$file",
Tom Saegere1f75902017-11-17 15:27:42 -0800143 "list_files_cmd" => "git ls-files \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800144);
145
146my %VCS_cmds_hg = (
147 "execute_cmd" => \&hg_execute_cmd,
148 "available" => '(which("hg") ne "") && (-d ".hg")',
149 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700150 "hg log --date=\$email_hg_since " .
151 "--template='HgCommit: {node}\\n" .
152 "HgAuthor: {author}\\n" .
153 "HgSubject: {desc}\\n'" .
154 " -- \$file",
155 "find_commit_signers_cmd" =>
156 "hg log " .
157 "--template='HgSubject: {desc}\\n'" .
158 " -r \$commit",
159 "find_commit_author_cmd" =>
160 "hg log " .
161 "--template='HgCommit: {node}\\n" .
162 "HgAuthor: {author}\\n" .
163 "HgSubject: {desc|firstline}\\n'" .
164 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800165 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700166 "blame_file_cmd" => "hg blame -n \$file",
167 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
168 "blame_commit_pattern" => "^([ 0-9a-f]+):",
169 "author_pattern" => "^HgAuthor: (.*)",
170 "subject_pattern" => "^HgSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800171 "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700172 "file_exists_cmd" => "hg files \$file",
Tom Saegere1f75902017-11-17 15:27:42 -0800173 "list_files_cmd" => "hg manifest -R \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800174);
175
Joe Perchesbcde44e2010-10-26 14:22:53 -0700176my $conf = which_conf(".get_maintainer.conf");
177if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700178 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700179 open(my $conffile, '<', "$conf")
180 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
181
Joe Perches368669d2010-05-24 14:33:19 -0700182 while (<$conffile>) {
183 my $line = $_;
184
185 $line =~ s/\s*\n?$//g;
186 $line =~ s/^\s*//g;
187 $line =~ s/\s+/ /g;
188
189 next if ($line =~ m/^\s*#/);
190 next if ($line =~ m/^\s*$/);
191
192 my @words = split(" ", $line);
193 foreach my $word (@words) {
194 last if ($word =~ m/^#/);
195 push (@conf_args, $word);
196 }
197 }
198 close($conffile);
199 unshift(@ARGV, @conf_args) if @conf_args;
200}
201
Joe Perches435de072015-06-25 15:01:50 -0700202my @ignore_emails = ();
203my $ignore_file = which_conf(".get_maintainer.ignore");
204if (-f $ignore_file) {
205 open(my $ignore, '<', "$ignore_file")
206 or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
207 while (<$ignore>) {
208 my $line = $_;
209
210 $line =~ s/\s*\n?$//;
211 $line =~ s/^\s*//;
212 $line =~ s/\s+$//;
213 $line =~ s/#.*$//;
214
215 next if ($line =~ m/^\s*$/);
216 if (rfc822_valid($line)) {
217 push(@ignore_emails, $line);
218 }
219 }
220 close($ignore);
221}
222
Tom Saegere1f75902017-11-17 15:27:42 -0800223if ($#ARGV > 0) {
224 foreach (@ARGV) {
Joe Perches083bf9c2017-11-17 15:27:46 -0800225 if ($_ =~ /^-{1,2}self-test(?:=|$)/) {
Tom Saegere1f75902017-11-17 15:27:42 -0800226 die "$P: using --self-test does not allow any other option or argument\n";
227 }
228 }
229}
230
Joe Perchescb7301c2009-04-07 20:40:12 -0700231if (!GetOptions(
232 'email!' => \$email,
233 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700234 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800235 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700236 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700237 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700238 'git-chief-penguins!' => \$email_git_penguin_chiefs,
239 'git-min-signatures=i' => \$email_git_min_signatures,
240 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700241 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700242 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800243 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700244 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700245 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700246 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700247 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700248 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700249 'n!' => \$email_usename,
250 'l!' => \$email_list,
251 's!' => \$email_subscriber_list,
252 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800253 'roles!' => \$output_roles,
254 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700255 'separator=s' => \$output_separator,
256 'subsystem!' => \$subsystem,
257 'status!' => \$status,
258 'scm!' => \$scm,
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -0700259 'tree!' => \$tree,
Joe Perchescb7301c2009-04-07 20:40:12 -0700260 'web!' => \$web,
Joe Perches03aed212016-12-12 16:45:59 -0800261 'letters=s' => \$letters,
Joe Perches3fb55652009-09-21 17:04:17 -0700262 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700263 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800264 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800265 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700266 'f|file' => \$from_filename,
Joe Perches6f7d98e2017-08-04 21:45:48 -0700267 'find-maintainer-files' => \$find_maintainer_files,
Joe Perches5f0baf92018-08-21 21:56:52 -0700268 'mpath|maintainer-path=s' => \$maintainer_path,
Joe Perches083bf9c2017-11-17 15:27:46 -0800269 'self-test:s' => \$self_test,
Joe Perchescb7301c2009-04-07 20:40:12 -0700270 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800271 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700272 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800273 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700274}
275
276if ($help != 0) {
277 usage();
278 exit 0;
279}
280
281if ($version != 0) {
282 print("${P} ${V}\n");
283 exit 0;
284}
285
Joe Perches083bf9c2017-11-17 15:27:46 -0800286if (defined $self_test) {
Tom Saegere1f75902017-11-17 15:27:42 -0800287 read_all_maintainer_files();
Joe Perches083bf9c2017-11-17 15:27:46 -0800288 self_test();
Tom Saegere1f75902017-11-17 15:27:42 -0800289 exit 0;
290}
291
Joe Perches64f77f32010-03-05 13:43:04 -0800292if (-t STDIN && !@ARGV) {
293 # We're talking to a terminal, but have no command line arguments.
294 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700295}
296
Joe Perches683c6f82010-10-26 14:22:55 -0700297$output_multiline = 0 if ($output_separator ne ", ");
298$output_rolestats = 1 if ($interactive);
299$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800300
Joe Perches03aed212016-12-12 16:45:59 -0800301if ($sections || $letters ne "") {
302 $sections = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800303 $email = 0;
304 $email_list = 0;
305 $scm = 0;
306 $status = 0;
307 $subsystem = 0;
308 $web = 0;
309 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700310 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800311} else {
312 my $selections = $email + $scm + $status + $subsystem + $web;
313 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800314 die "$P: Missing required option: email, scm, status, subsystem or web\n";
315 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700316}
317
Joe Perchesf5492662009-09-21 17:04:13 -0700318if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700319 ($email_maintainer + $email_reviewer +
320 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700321 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700322 die "$P: Please select at least 1 email option\n";
323}
324
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -0700325if ($tree && !top_of_kernel_tree($lk_path)) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700326 die "$P: The current directory does not appear to be "
327 . "a linux kernel source tree.\n";
328}
329
330## Read MAINTAINERS for type/value pairs
331
332my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700333my %keyword_hash;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700334my @mfiles = ();
Joe Perches083bf9c2017-11-17 15:27:46 -0800335my @self_test_info = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700336
Joe Perches6f7d98e2017-08-04 21:45:48 -0700337sub read_maintainer_file {
338 my ($file) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700339
Joe Perches6f7d98e2017-08-04 21:45:48 -0700340 open (my $maint, '<', "$file")
341 or die "$P: Can't open MAINTAINERS file '$file': $!\n";
Tom Saegere1f75902017-11-17 15:27:42 -0800342 my $i = 1;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700343 while (<$maint>) {
344 my $line = $_;
Joe Perches083bf9c2017-11-17 15:27:46 -0800345 chomp $line;
Joe Perchescb7301c2009-04-07 20:40:12 -0700346
Joe Perches6f7d98e2017-08-04 21:45:48 -0700347 if ($line =~ m/^([A-Z]):\s*(.*)/) {
348 my $type = $1;
349 my $value = $2;
350
351 ##Filename pattern matching
352 if ($type eq "F" || $type eq "X") {
353 $value =~ s@\.@\\\.@g; ##Convert . to \.
354 $value =~ s/\*/\.\*/g; ##Convert * to .*
355 $value =~ s/\?/\./g; ##Convert ? to .
356 ##if pattern is a directory and it lacks a trailing slash, add one
357 if ((-d $value)) {
358 $value =~ s@([^/])$@$1/@;
359 }
360 } elsif ($type eq "K") {
361 $keyword_hash{@typevalue} = $value;
Joe Perches870020f2009-07-29 15:04:28 -0700362 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700363 push(@typevalue, "$type:$value");
364 } elsif (!(/^\s*$/ || /^\s*\#/)) {
Joe Perches6f7d98e2017-08-04 21:45:48 -0700365 push(@typevalue, $line);
Joe Perchescb7301c2009-04-07 20:40:12 -0700366 }
Joe Perches083bf9c2017-11-17 15:27:46 -0800367 if (defined $self_test) {
368 push(@self_test_info, {file=>$file, linenr=>$i, line=>$line});
369 }
Tom Saegere1f75902017-11-17 15:27:42 -0800370 $i++;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700371 }
372 close($maint);
373}
374
375sub find_is_maintainer_file {
376 my ($file) = $_;
377 return if ($file !~ m@/MAINTAINERS$@);
378 $file = $File::Find::name;
379 return if (! -f $file);
380 push(@mfiles, $file);
381}
382
383sub find_ignore_git {
384 return grep { $_ !~ /^\.git$/; } @_;
385}
386
Tom Saegere1f75902017-11-17 15:27:42 -0800387read_all_maintainer_files();
388
389sub read_all_maintainer_files {
Joe Perches5f0baf92018-08-21 21:56:52 -0700390 my $path = "${lk_path}MAINTAINERS";
391 if (defined $maintainer_path) {
392 $path = $maintainer_path;
393 # Perl Cookbook tilde expansion if necessary
394 $path =~ s@^~([^/]*)@ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7])@ex;
Joe Perchescb7301c2009-04-07 20:40:12 -0700395 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700396
Joe Perches5f0baf92018-08-21 21:56:52 -0700397 if (-d $path) {
398 $path .= '/' if ($path !~ m@/$@);
399 if ($path eq "${lk_path}MAINTAINERS/") {
400 opendir(DIR, "$path") or die $!;
401 my @files = readdir(DIR);
402 closedir(DIR);
403 foreach my $file (@files) {
404 push(@mfiles, "$path$file") if ($file !~ /^\./);
405 }
406 }
407 if ($find_maintainer_files) {
408 find( { wanted => \&find_is_maintainer_file,
409 preprocess => \&find_ignore_git,
410 no_chdir => 1,
411 }, "$path");
412 }
413 } elsif (-f "$path") {
414 push(@mfiles, "$path");
Tom Saegere1f75902017-11-17 15:27:42 -0800415 } else {
Joe Perches5f0baf92018-08-21 21:56:52 -0700416 die "$P: MAINTAINER file not found '$path'\n";
Tom Saegere1f75902017-11-17 15:27:42 -0800417 }
Joe Perches5f0baf92018-08-21 21:56:52 -0700418 die "$P: No MAINTAINER files found in '$path'\n" if (scalar(@mfiles) == 0);
Tom Saegere1f75902017-11-17 15:27:42 -0800419 foreach my $file (@mfiles) {
Joe Perches5f0baf92018-08-21 21:56:52 -0700420 read_maintainer_file("$file");
Tom Saegere1f75902017-11-17 15:27:42 -0800421 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700422}
Joe Perches8cbb3a72009-09-21 17:04:21 -0700423
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700424#
425# Read mail address map
426#
427
Joe Perchesb9e23312010-10-26 14:22:58 -0700428my $mailmap;
429
430read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700431
432sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700433 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700434 names => {},
435 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700436 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700437
Joe Perchesb9e23312010-10-26 14:22:58 -0700438 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700439
440 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800441 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700442
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700443 while (<$mailmap_file>) {
444 s/#.*$//; #strip comments
445 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700446
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700447 next if (/^\s*$/); #skip empty lines
448 #entries have one of the following formats:
449 # name1 <mail1>
450 # <mail1> <mail2>
451 # name1 <mail1> <mail2>
452 # name1 <mail1> name2 <mail2>
453 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700454
455 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700456 my $real_name = $1;
457 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700458
Joe Perches47abc722010-10-26 14:22:57 -0700459 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700460 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700461 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700462
Joe Perches0334b382011-07-25 17:13:13 -0700463 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700464 my $real_address = $1;
465 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700466
Joe Perches47abc722010-10-26 14:22:57 -0700467 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700468
Joe Perches0334b382011-07-25 17:13:13 -0700469 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700470 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700471 my $real_address = $2;
472 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700473
Joe Perches47abc722010-10-26 14:22:57 -0700474 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700475 ($real_name, $real_address) =
476 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700477 $mailmap->{names}->{$wrong_address} = $real_name;
478 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700479
Joe Perches0334b382011-07-25 17:13:13 -0700480 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700481 my $real_name = $1;
482 my $real_address = $2;
483 my $wrong_name = $3;
484 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700485
Joe Perches47abc722010-10-26 14:22:57 -0700486 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700487 ($real_name, $real_address) =
488 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700489
Joe Perchesb9e23312010-10-26 14:22:58 -0700490 $wrong_name =~ s/\s+$//;
491 ($wrong_name, $wrong_address) =
492 parse_email("$wrong_name <$wrong_address>");
493
494 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
495 $mailmap->{names}->{$wrong_email} = $real_name;
496 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700497 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700498 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700499 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700500}
501
Joe Perches4a7fdb52009-04-10 12:28:57 -0700502## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700503
504my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700505my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700506my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800507my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700508
Joe Perches64f77f32010-03-05 13:43:04 -0800509if (!@ARGV) {
510 push(@ARGV, "&STDIN");
511}
512
Joe Perches4a7fdb52009-04-10 12:28:57 -0700513foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800514 if ($file ne "&STDIN") {
515 ##if $file is a directory and it lacks a trailing slash, add one
516 if ((-d $file)) {
517 $file =~ s@([^/])$@$1/@;
518 } elsif (!(-f $file)) {
519 die "$P: file '${file}' not found\n";
520 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700521 }
Joe Perchesaec742e2016-08-10 08:45:11 -0700522 if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
Joe Perchesbe17bdd2016-01-20 14:58:24 -0800523 $file =~ s/^\Q${cur_path}\E//; #strip any absolute path
524 $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
Joe Perches4a7fdb52009-04-10 12:28:57 -0700525 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700526 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800527 open(my $f, '<', $file)
528 or die "$P: Can't open $file: $!\n";
529 my $text = do { local($/) ; <$f> };
530 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800531 if ($keywords) {
532 foreach my $line (keys %keyword_hash) {
533 if ($text =~ m/$keyword_hash{$line}/x) {
534 push(@keyword_tvi, $line);
535 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700536 }
537 }
Joe Perches03372db2010-03-05 13:43:00 -0800538 if ($file_emails) {
539 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;
540 push(@file_emails, clean_file_emails(@poss_addr));
541 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700542 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700543 } else {
544 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700545 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800546
Wolfram Sang3a4df132010-03-23 13:35:18 -0700547 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800548 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700549
550 # We can check arbitrary information before the patch
551 # like the commit message, mail headers, etc...
552 # This allows us to match arbitrary keywords against any part
553 # of a git format-patch generated file (subject tags, etc...)
554
555 my $patch_prefix = ""; #Parsing the intro
556
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800557 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700558 my $patch_line = $_;
Joe Perches0455c742018-06-07 17:10:38 -0700559 if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) {
560 my $filename = $1;
561 push(@files, $filename);
562 } elsif (m/^rename (?:from|to) (\S+)\s*$/) {
563 my $filename = $1;
564 push(@files, $filename);
565 } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) {
566 my $filename1 = $1;
567 my $filename2 = $2;
568 push(@files, $filename1);
569 push(@files, $filename2);
570 } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700571 my $filename = $1;
572 $filename =~ s@^[^/]*/@@;
573 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700574 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700575 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700576 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700577 } elsif (m/^\@\@ -(\d+),(\d+)/) {
578 if ($email_git_blame) {
579 push(@range, "$lastfile:$1:$2");
580 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700581 } elsif ($keywords) {
582 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700583 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700584 push(@keyword_tvi, $line);
585 }
586 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700587 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700588 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800589 close($patch);
590
Joe Perches4a7fdb52009-04-10 12:28:57 -0700591 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700592 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700593 . "Add -f to options?\n";
594 }
595 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700596 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700597}
598
Joe Perches03372db2010-03-05 13:43:00 -0800599@file_emails = uniq(@file_emails);
600
Joe Perches683c6f82010-10-26 14:22:55 -0700601my %email_hash_name;
602my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700603my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700604my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700605my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700606my @scm = ();
607my @web = ();
608my @subsystem = ();
609my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700610my %deduplicate_name_hash = ();
611my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700612
Joe Perches6ef1c522010-10-26 14:22:56 -0700613my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700614
Joe Perches6ef1c522010-10-26 14:22:56 -0700615if (@maintainers) {
616 @maintainers = merge_email(@maintainers);
617 output(@maintainers);
618}
Joe Perchescb7301c2009-04-07 20:40:12 -0700619
620if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700621 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700622 output(@scm);
623}
Joe Perches683c6f82010-10-26 14:22:55 -0700624
Joe Perchescb7301c2009-04-07 20:40:12 -0700625if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700626 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700627 output(@status);
628}
629
630if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700631 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700632 output(@subsystem);
633}
634
635if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700636 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700637 output(@web);
638}
639
640exit($exit);
641
Joe Perches083bf9c2017-11-17 15:27:46 -0800642sub self_test {
Tom Saegere1f75902017-11-17 15:27:42 -0800643 my @lsfiles = ();
Joe Perches083bf9c2017-11-17 15:27:46 -0800644 my @good_links = ();
645 my @bad_links = ();
646 my @section_headers = ();
647 my $index = 0;
Tom Saegere1f75902017-11-17 15:27:42 -0800648
649 @lsfiles = vcs_list_files($lk_path);
650
Joe Perches083bf9c2017-11-17 15:27:46 -0800651 for my $x (@self_test_info) {
652 $index++;
653
654 ## Section header duplication and missing section content
655 if (($self_test eq "" || $self_test =~ /\bsections\b/) &&
656 $x->{line} =~ /^\S[^:]/ &&
657 defined $self_test_info[$index] &&
658 $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) {
659 my $has_S = 0;
660 my $has_F = 0;
661 my $has_ML = 0;
662 my $status = "";
663 if (grep(m@^\Q$x->{line}\E@, @section_headers)) {
664 print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n");
665 } else {
666 push(@section_headers, $x->{line});
667 }
668 my $nextline = $index;
669 while (defined $self_test_info[$nextline] &&
670 $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) {
671 my $type = $1;
672 my $value = $2;
673 if ($type eq "S") {
674 $has_S = 1;
675 $status = $value;
676 } elsif ($type eq "F" || $type eq "N") {
677 $has_F = 1;
678 } elsif ($type eq "M" || $type eq "R" || $type eq "L") {
679 $has_ML = 1;
680 }
681 $nextline++;
682 }
683 if (!$has_ML && $status !~ /orphan|obsolete/i) {
684 print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n");
685 }
686 if (!$has_S) {
687 print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n");
688 }
689 if (!$has_F) {
690 print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n");
691 }
692 }
693
694 next if ($x->{line} !~ /^([A-Z]):\s*(.*)/);
695
696 my $type = $1;
697 my $value = $2;
698
699 ## Filename pattern matching
700 if (($type eq "F" || $type eq "X") &&
701 ($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
702 $value =~ s@\.@\\\.@g; ##Convert . to \.
703 $value =~ s/\*/\.\*/g; ##Convert * to .*
704 $value =~ s/\?/\./g; ##Convert ? to .
705 ##if pattern is a directory and it lacks a trailing slash, add one
706 if ((-d $value)) {
707 $value =~ s@([^/])$@$1/@;
708 }
709 if (!grep(m@^$value@, @lsfiles)) {
710 print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n");
711 }
712
713 ## Link reachability
714 } elsif (($type eq "W" || $type eq "Q" || $type eq "B") &&
715 $value =~ /^https?:/ &&
716 ($self_test eq "" || $self_test =~ /\blinks\b/)) {
717 next if (grep(m@^\Q$value\E$@, @good_links));
718 my $isbad = 0;
719 if (grep(m@^\Q$value\E$@, @bad_links)) {
720 $isbad = 1;
721 } else {
722 my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`;
723 if ($? == 0) {
724 push(@good_links, $value);
725 } else {
726 push(@bad_links, $value);
727 $isbad = 1;
728 }
729 }
730 if ($isbad) {
731 print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
732 }
733
734 ## SCM reachability
735 } elsif ($type eq "T" &&
736 ($self_test eq "" || $self_test =~ /\bscm\b/)) {
737 next if (grep(m@^\Q$value\E$@, @good_links));
738 my $isbad = 0;
739 if (grep(m@^\Q$value\E$@, @bad_links)) {
740 $isbad = 1;
741 } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) {
742 print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n");
743 } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) {
744 my $url = $1;
745 my $branch = "";
746 $branch = $3 if $3;
747 my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`;
748 if ($? == 0) {
749 push(@good_links, $value);
750 } else {
751 push(@bad_links, $value);
752 $isbad = 1;
753 }
754 } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) {
755 my $url = $1;
756 my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`;
757 if ($? == 0) {
758 push(@good_links, $value);
759 } else {
760 push(@bad_links, $value);
761 $isbad = 1;
762 }
763 }
764 if ($isbad) {
765 print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
766 }
767 }
Tom Saegere1f75902017-11-17 15:27:42 -0800768 }
769}
770
Joe Perches435de072015-06-25 15:01:50 -0700771sub ignore_email_address {
772 my ($address) = @_;
773
774 foreach my $ignore (@ignore_emails) {
775 return 1 if ($ignore eq $address);
776 }
777
778 return 0;
779}
780
Joe Perchesab6c9372011-01-12 16:59:50 -0800781sub range_is_maintained {
782 my ($start, $end) = @_;
783
784 for (my $i = $start; $i < $end; $i++) {
785 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700786 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800787 my $type = $1;
788 my $value = $2;
789 if ($type eq 'S') {
790 if ($value =~ /(maintain|support)/i) {
791 return 1;
792 }
793 }
794 }
795 }
796 return 0;
797}
798
799sub range_has_maintainer {
800 my ($start, $end) = @_;
801
802 for (my $i = $start; $i < $end; $i++) {
803 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700804 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800805 my $type = $1;
806 my $value = $2;
807 if ($type eq 'M') {
808 return 1;
809 }
810 }
811 }
812 return 0;
813}
814
Joe Perches6ef1c522010-10-26 14:22:56 -0700815sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700816 %email_hash_name = ();
817 %email_hash_address = ();
818 %commit_author_hash = ();
819 %commit_signer_hash = ();
820 @email_to = ();
821 %hash_list_to = ();
822 @list_to = ();
823 @scm = ();
824 @web = ();
825 @subsystem = ();
826 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700827 %deduplicate_name_hash = ();
828 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700829 if ($email_git_all_signature_types) {
830 $signature_pattern = "(.+?)[Bb][Yy]:";
831 } else {
832 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
833 }
834
835 # Find responsible parties
836
Joe Perchesb9e23312010-10-26 14:22:58 -0700837 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700838
Joe Perches683c6f82010-10-26 14:22:55 -0700839 foreach my $file (@files) {
840
841 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700842 my $tvi = find_first_section();
843 while ($tvi < @typevalue) {
844 my $start = find_starting_index($tvi);
845 my $end = find_ending_index($tvi);
846 my $exclude = 0;
847 my $i;
848
849 #Do not match excluded file patterns
850
851 for ($i = $start; $i < $end; $i++) {
852 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700853 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700854 my $type = $1;
855 my $value = $2;
856 if ($type eq 'X') {
857 if (file_match_pattern($file, $value)) {
858 $exclude = 1;
859 last;
860 }
861 }
862 }
863 }
864
865 if (!$exclude) {
866 for ($i = $start; $i < $end; $i++) {
867 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700868 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700869 my $type = $1;
870 my $value = $2;
871 if ($type eq 'F') {
872 if (file_match_pattern($file, $value)) {
873 my $value_pd = ($value =~ tr@/@@);
874 my $file_pd = ($file =~ tr@/@@);
875 $value_pd++ if (substr($value,-1,1) ne "/");
876 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800877 if ($value_pd >= $file_pd &&
878 range_is_maintained($start, $end) &&
879 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700880 $exact_pattern_match_hash{$file} = 1;
881 }
Joe Perches683c6f82010-10-26 14:22:55 -0700882 if ($pattern_depth == 0 ||
883 (($file_pd - $value_pd) < $pattern_depth)) {
884 $hash{$tvi} = $value_pd;
885 }
886 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700887 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800888 if ($file =~ m/$value/x) {
889 $hash{$tvi} = 0;
890 }
Joe Perches683c6f82010-10-26 14:22:55 -0700891 }
892 }
893 }
894 }
895 $tvi = $end + 1;
896 }
897
898 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
899 add_categories($line);
900 if ($sections) {
901 my $i;
902 my $start = find_starting_index($line);
903 my $end = find_ending_index($line);
904 for ($i = $start; $i < $end; $i++) {
905 my $line = $typevalue[$i];
906 if ($line =~ /^[FX]:/) { ##Restore file patterns
907 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
908 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
909 $line =~ s/\\\./\./g; ##Convert \. to .
910 $line =~ s/\.\*/\*/g; ##Convert .* to *
911 }
Joe Perches03aed212016-12-12 16:45:59 -0800912 my $count = $line =~ s/^([A-Z]):/$1:\t/g;
913 if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
914 print("$line\n");
915 }
Joe Perches683c6f82010-10-26 14:22:55 -0700916 }
917 print("\n");
918 }
919 }
Joe Perches683c6f82010-10-26 14:22:55 -0700920 }
921
922 if ($keywords) {
923 @keyword_tvi = sort_and_uniq(@keyword_tvi);
924 foreach my $line (@keyword_tvi) {
925 add_categories($line);
926 }
927 }
928
Joe Perchesb9e23312010-10-26 14:22:58 -0700929 foreach my $email (@email_to, @list_to) {
930 $email->[0] = deduplicate_email($email->[0]);
931 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700932
933 foreach my $file (@files) {
934 if ($email &&
935 ($email_git || ($email_git_fallback &&
936 !$exact_pattern_match_hash{$file}))) {
937 vcs_file_signoffs($file);
938 }
939 if ($email && $email_git_blame) {
940 vcs_file_blame($file);
941 }
942 }
943
Joe Perches683c6f82010-10-26 14:22:55 -0700944 if ($email) {
945 foreach my $chief (@penguin_chief) {
946 if ($chief =~ m/^(.*):(.*)/) {
947 my $email_address;
948
949 $email_address = format_email($1, $2, $email_usename);
950 if ($email_git_penguin_chiefs) {
951 push(@email_to, [$email_address, 'chief penguin']);
952 } else {
953 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
954 }
955 }
956 }
957
958 foreach my $email (@file_emails) {
959 my ($name, $address) = parse_email($email);
960
961 my $tmp_email = format_email($name, $address, $email_usename);
962 push_email_address($tmp_email, '');
963 add_role($tmp_email, 'in file');
964 }
965 }
966
967 my @to = ();
968 if ($email || $email_list) {
969 if ($email) {
970 @to = (@to, @email_to);
971 }
972 if ($email_list) {
973 @to = (@to, @list_to);
974 }
975 }
976
Joe Perches6ef1c522010-10-26 14:22:56 -0700977 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700978 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700979 }
Joe Perches683c6f82010-10-26 14:22:55 -0700980
981 return @to;
982}
983
Joe Perchescb7301c2009-04-07 20:40:12 -0700984sub file_match_pattern {
985 my ($file, $pattern) = @_;
986 if (substr($pattern, -1) eq "/") {
987 if ($file =~ m@^$pattern@) {
988 return 1;
989 }
990 } else {
991 if ($file =~ m@^$pattern@) {
992 my $s1 = ($file =~ tr@/@@);
993 my $s2 = ($pattern =~ tr@/@@);
994 if ($s1 == $s2) {
995 return 1;
996 }
997 }
998 }
999 return 0;
1000}
1001
1002sub usage {
1003 print <<EOT;
1004usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -07001005 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -07001006version: $V
1007
1008MAINTAINER field selection options:
1009 --email => print email address(es) if any
1010 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -07001011 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -07001012 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -07001013 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -07001014 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -07001015 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
1016 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
1017 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -07001018 --git-blame => use git blame to find modified commits for patch or file
Brian Norris3cbcca82015-11-06 16:30:41 -08001019 --git-blame-signatures => when used with --git-blame, also include all commit signers
Joe Perchese4d26b02010-05-24 14:33:17 -07001020 --git-since => git history to use (default: $email_git_since)
1021 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -07001022 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -07001023 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001024 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -07001025 --n => include name 'Full Name <addr\@domain.tld>'
1026 --l => include list(s) if any
1027 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -07001028 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -08001029 --roles => show roles (status:subsystem, git-signer, list, etc...)
1030 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -08001031 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -07001032 --scm => print SCM tree(s) if any
1033 --status => print status if any
1034 --subsystem => print subsystem name if any
1035 --web => print website(s) if any
1036
1037Output type options:
1038 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -07001039 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -07001040 --multiline => print 1 entry per line
1041
Joe Perchescb7301c2009-04-07 20:40:12 -07001042Other options:
Joe Perches3fb55652009-09-21 17:04:17 -07001043 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -07001044 --keywords => scan patch for keywords (default: $keywords)
1045 --sections => print all of the subsystem sections with pattern matches
Joe Perches03aed212016-12-12 16:45:59 -08001046 --letters => print all matching 'letter' types from all matching sections
Joe Perchesb9e23312010-10-26 14:22:58 -07001047 --mailmap => use .mailmap file (default: $email_use_mailmap)
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -07001048 --no-tree => run without a kernel tree
Tom Saegere1f75902017-11-17 15:27:42 -08001049 --self-test => show potential issues with MAINTAINERS file content
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001050 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -07001051 --help => show this help information
1052
Joe Perches3fb55652009-09-21 17:04:17 -07001053Default options:
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -07001054 [--email --tree --nogit --git-fallback --m --r --n --l --multiline
1055 --pattern-depth=0 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -07001056
Joe Perches870020f2009-07-29 15:04:28 -07001057Notes:
1058 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -07001059 Used with "--git", git signators for _all_ files in and below
1060 directory are examined as git recurses directories.
1061 Any specified X: (exclude) pattern matches are _not_ ignored.
1062 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -08001063 no individual file within the directory or subdirectory
1064 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -07001065 Used with "--git-blame", does not iterate all files in directory
1066 Using "--git-blame" is slow and may add old committers and authors
1067 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -08001068 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
1069 other automated tools that expect only ["name"] <email address>
1070 may not work because of additional output after <email address>.
1071 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
1072 not the percentage of the entire file authored. # of commits is
1073 not a good measure of amount of code authored. 1 major commit may
1074 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -08001075 If git is not installed, but mercurial (hg) is installed and an .hg
1076 repository exists, the following options apply to mercurial:
1077 --git,
1078 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
1079 --git-blame
1080 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -07001081 File ".get_maintainer.conf", if it exists in the linux kernel source root
1082 directory, can change whatever get_maintainer defaults are desired.
1083 Entries in this file can be any command line argument.
1084 This file is prepended to any additional command line arguments.
1085 Multiple lines and # comments are allowed.
Brian Norrisb1312bf2015-11-06 16:30:46 -08001086 Most options have both positive and negative forms.
1087 The negative forms for --<foo> are --no<foo> and --no-<foo>.
1088
Joe Perchescb7301c2009-04-07 20:40:12 -07001089EOT
1090}
1091
1092sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -07001093 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001094
Joe Perches47abc722010-10-26 14:22:57 -07001095 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
1096 $lk_path .= "/";
1097 }
1098 if ( (-f "${lk_path}COPYING")
1099 && (-f "${lk_path}CREDITS")
1100 && (-f "${lk_path}Kbuild")
Joe Perches6f7d98e2017-08-04 21:45:48 -07001101 && (-e "${lk_path}MAINTAINERS")
Joe Perches47abc722010-10-26 14:22:57 -07001102 && (-f "${lk_path}Makefile")
1103 && (-f "${lk_path}README")
1104 && (-d "${lk_path}Documentation")
1105 && (-d "${lk_path}arch")
1106 && (-d "${lk_path}include")
1107 && (-d "${lk_path}drivers")
1108 && (-d "${lk_path}fs")
1109 && (-d "${lk_path}init")
1110 && (-d "${lk_path}ipc")
1111 && (-d "${lk_path}kernel")
1112 && (-d "${lk_path}lib")
1113 && (-d "${lk_path}scripts")) {
1114 return 1;
1115 }
1116 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -07001117}
1118
Joe Perches0e70e832009-09-21 17:04:20 -07001119sub parse_email {
1120 my ($formatted_email) = @_;
1121
1122 my $name = "";
1123 my $address = "";
1124
Joe Perches11ecf532009-09-21 17:04:22 -07001125 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001126 $name = $1;
1127 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -07001128 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001129 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -07001130 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001131 $address = $1;
1132 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001133
1134 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -07001135 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -07001136 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -07001137
Stephen Hemmingera63ceb42010-03-05 13:43:06 -08001138 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -07001139 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -07001140 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -07001141 }
Joe Perches0e70e832009-09-21 17:04:20 -07001142
1143 return ($name, $address);
1144}
1145
1146sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -08001147 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001148
1149 my $formatted_email;
1150
1151 $name =~ s/^\s+|\s+$//g;
1152 $name =~ s/^\"|\"$//g;
1153 $address =~ s/^\s+|\s+$//g;
1154
Stephen Hemmingera63ceb42010-03-05 13:43:06 -08001155 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -07001156 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
1157 $name = "\"$name\"";
1158 }
1159
Joe Perchesa8af2432009-12-14 18:00:49 -08001160 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -07001161 if ("$name" eq "") {
1162 $formatted_email = "$address";
1163 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -08001164 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -07001165 }
1166 } else {
1167 $formatted_email = $address;
1168 }
1169
Joe Perchescb7301c2009-04-07 20:40:12 -07001170 return $formatted_email;
1171}
1172
Joe Perches272a8972010-01-08 14:42:48 -08001173sub find_first_section {
1174 my $index = 0;
1175
1176 while ($index < @typevalue) {
1177 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001178 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -08001179 last;
1180 }
1181 $index++;
1182 }
1183
1184 return $index;
1185}
1186
Joe Perchesb7816552009-09-21 17:04:24 -07001187sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -07001188 my ($index) = @_;
1189
1190 while ($index > 0) {
1191 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001192 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001193 last;
1194 }
1195 $index--;
1196 }
1197
1198 return $index;
1199}
1200
1201sub find_ending_index {
1202 my ($index) = @_;
1203
1204 while ($index < @typevalue) {
1205 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001206 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001207 last;
1208 }
1209 $index++;
1210 }
1211
1212 return $index;
1213}
1214
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001215sub get_subsystem_name {
1216 my ($index) = @_;
1217
1218 my $start = find_starting_index($index);
1219
1220 my $subsystem = $typevalue[$start];
1221 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1222 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
1223 $subsystem =~ s/\s*$//;
1224 $subsystem = $subsystem . "...";
1225 }
1226 return $subsystem;
1227}
1228
Joe Perches3c7385b2009-12-14 18:00:46 -08001229sub get_maintainer_role {
1230 my ($index) = @_;
1231
1232 my $i;
1233 my $start = find_starting_index($index);
1234 my $end = find_ending_index($index);
1235
Joe Perches0ede2742012-03-23 15:01:56 -07001236 my $role = "unknown";
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001237 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001238
1239 for ($i = $start + 1; $i < $end; $i++) {
1240 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001241 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001242 my $ptype = $1;
1243 my $pvalue = $2;
1244 if ($ptype eq "S") {
1245 $role = $pvalue;
1246 }
1247 }
1248 }
1249
1250 $role = lc($role);
1251 if ($role eq "supported") {
1252 $role = "supporter";
1253 } elsif ($role eq "maintained") {
1254 $role = "maintainer";
1255 } elsif ($role eq "odd fixes") {
1256 $role = "odd fixer";
1257 } elsif ($role eq "orphan") {
1258 $role = "orphan minder";
1259 } elsif ($role eq "obsolete") {
1260 $role = "obsolete minder";
1261 } elsif ($role eq "buried alive in reporters") {
1262 $role = "chief penguin";
1263 }
1264
1265 return $role . ":" . $subsystem;
1266}
1267
1268sub get_list_role {
1269 my ($index) = @_;
1270
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001271 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001272
1273 if ($subsystem eq "THE REST") {
1274 $subsystem = "";
1275 }
1276
1277 return $subsystem;
1278}
1279
Joe Perchescb7301c2009-04-07 20:40:12 -07001280sub add_categories {
1281 my ($index) = @_;
1282
Joe Perchesb7816552009-09-21 17:04:24 -07001283 my $i;
1284 my $start = find_starting_index($index);
1285 my $end = find_ending_index($index);
1286
1287 push(@subsystem, $typevalue[$start]);
1288
1289 for ($i = $start + 1; $i < $end; $i++) {
1290 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001291 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001292 my $ptype = $1;
1293 my $pvalue = $2;
1294 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001295 my $list_address = $pvalue;
1296 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001297 my $list_role = get_list_role($i);
1298
1299 if ($list_role ne "") {
1300 $list_role = ":" . $list_role;
1301 }
Joe Perches290603c2009-06-16 15:33:58 -07001302 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1303 $list_address = $1;
1304 $list_additional = $2;
1305 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001306 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001307 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001308 if (!$hash_list_to{lc($list_address)}) {
1309 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001310 push(@list_to, [$list_address,
1311 "subscriber list${list_role}"]);
1312 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001313 }
1314 } else {
1315 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001316 if (!$hash_list_to{lc($list_address)}) {
1317 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001318 if ($list_additional =~ m/moderated/) {
1319 push(@list_to, [$list_address,
1320 "moderated list${list_role}"]);
1321 } else {
1322 push(@list_to, [$list_address,
1323 "open list${list_role}"]);
1324 }
Joe Perches683c6f82010-10-26 14:22:55 -07001325 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001326 }
1327 }
1328 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001329 my ($name, $address) = parse_email($pvalue);
1330 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001331 if ($i > 0) {
1332 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001333 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001334 if ($1 eq "P") {
1335 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001336 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001337 }
1338 }
1339 }
1340 }
Joe Perches0e70e832009-09-21 17:04:20 -07001341 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001342 my $role = get_maintainer_role($i);
1343 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001344 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001345 } elsif ($ptype eq "R") {
1346 my ($name, $address) = parse_email($pvalue);
1347 if ($name eq "") {
1348 if ($i > 0) {
1349 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001350 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001351 if ($1 eq "P") {
1352 $name = $2;
1353 $pvalue = format_email($name, $address, $email_usename);
1354 }
1355 }
1356 }
1357 }
1358 if ($email_reviewer) {
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001359 my $subsystem = get_subsystem_name($i);
1360 push_email_addresses($pvalue, "reviewer:$subsystem");
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001361 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001362 } elsif ($ptype eq "T") {
1363 push(@scm, $pvalue);
1364 } elsif ($ptype eq "W") {
1365 push(@web, $pvalue);
1366 } elsif ($ptype eq "S") {
1367 push(@status, $pvalue);
1368 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001369 }
1370 }
1371}
1372
Joe Perches11ecf532009-09-21 17:04:22 -07001373sub email_inuse {
1374 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001375
Joe Perches11ecf532009-09-21 17:04:22 -07001376 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001377 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1378 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001379
Joe Perches0e70e832009-09-21 17:04:20 -07001380 return 0;
1381}
1382
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001383sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001384 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001385
Joe Perches0e70e832009-09-21 17:04:20 -07001386 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001387
Joe Perchesb7816552009-09-21 17:04:24 -07001388 if ($address eq "") {
1389 return 0;
1390 }
1391
Joe Perches11ecf532009-09-21 17:04:22 -07001392 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001393 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001394 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001395 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001396 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001397 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001398 }
Joe Perchesb7816552009-09-21 17:04:24 -07001399
1400 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001401}
1402
1403sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001404 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001405
1406 my @address_list = ();
1407
Joe Perches5f2441e2009-06-16 15:34:02 -07001408 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001409 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001410 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001411 my $array_count = shift(@address_list);
1412 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001413 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001414 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001415 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001416 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001417 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1418 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001419 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001420}
1421
Joe Perches3c7385b2009-12-14 18:00:46 -08001422sub add_role {
1423 my ($line, $role) = @_;
1424
1425 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001426 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001427
1428 foreach my $entry (@email_to) {
1429 if ($email_remove_duplicates) {
1430 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001431 if (($name eq $entry_name || $address eq $entry_address)
1432 && ($role eq "" || !($entry->[1] =~ m/$role/))
1433 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001434 if ($entry->[1] eq "") {
1435 $entry->[1] = "$role";
1436 } else {
1437 $entry->[1] = "$entry->[1],$role";
1438 }
1439 }
1440 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001441 if ($email eq $entry->[0]
1442 && ($role eq "" || !($entry->[1] =~ m/$role/))
1443 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001444 if ($entry->[1] eq "") {
1445 $entry->[1] = "$role";
1446 } else {
1447 $entry->[1] = "$entry->[1],$role";
1448 }
1449 }
1450 }
1451 }
1452}
1453
Joe Perchescb7301c2009-04-07 20:40:12 -07001454sub which {
1455 my ($bin) = @_;
1456
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001457 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001458 if (-e "$path/$bin") {
1459 return "$path/$bin";
1460 }
1461 }
1462
1463 return "";
1464}
1465
Joe Perchesbcde44e2010-10-26 14:22:53 -07001466sub which_conf {
1467 my ($conf) = @_;
1468
1469 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1470 if (-e "$path/$conf") {
1471 return "$path/$conf";
1472 }
1473 }
1474
1475 return "";
1476}
1477
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001478sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001479 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001480
Joe Perches47abc722010-10-26 14:22:57 -07001481 my ($name, $address) = parse_email($line);
1482 my $email = format_email($name, $address, 1);
1483 my $real_name = $name;
1484 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001485
Joe Perches47abc722010-10-26 14:22:57 -07001486 if (exists $mailmap->{names}->{$email} ||
1487 exists $mailmap->{addresses}->{$email}) {
1488 if (exists $mailmap->{names}->{$email}) {
1489 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001490 }
Joe Perches47abc722010-10-26 14:22:57 -07001491 if (exists $mailmap->{addresses}->{$email}) {
1492 $real_address = $mailmap->{addresses}->{$email};
1493 }
1494 } else {
1495 if (exists $mailmap->{names}->{$address}) {
1496 $real_name = $mailmap->{names}->{$address};
1497 }
1498 if (exists $mailmap->{addresses}->{$address}) {
1499 $real_address = $mailmap->{addresses}->{$address};
1500 }
1501 }
1502 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001503}
1504
1505sub mailmap {
1506 my (@addresses) = @_;
1507
Joe Perchesb9e23312010-10-26 14:22:58 -07001508 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001509 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001510 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001511 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001512 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1513 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001514}
1515
1516sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001517 my %address_map;
1518 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001519
Joe Perches47abc722010-10-26 14:22:57 -07001520 foreach my $email (@emails) {
1521 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001522 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001523 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001524 $email = format_email($name, $address, 1);
1525 } else {
1526 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001527 }
Joe Perches47abc722010-10-26 14:22:57 -07001528 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001529}
1530
Joe Perches60db31a2009-12-14 18:00:50 -08001531sub git_execute_cmd {
1532 my ($cmd) = @_;
1533 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001534
Joe Perches60db31a2009-12-14 18:00:50 -08001535 my $output = `$cmd`;
1536 $output =~ s/^\s*//gm;
1537 @lines = split("\n", $output);
1538
1539 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001540}
1541
Joe Perches60db31a2009-12-14 18:00:50 -08001542sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001543 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001544 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001545
Joe Perches60db31a2009-12-14 18:00:50 -08001546 my $output = `$cmd`;
1547 @lines = split("\n", $output);
1548
1549 return @lines;
1550}
1551
Joe Perches683c6f82010-10-26 14:22:55 -07001552sub extract_formatted_signatures {
1553 my (@signature_lines) = @_;
1554
1555 my @type = @signature_lines;
1556
1557 s/\s*(.*):.*/$1/ for (@type);
1558
1559 # cut -f2- -d":"
1560 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1561
1562## Reformat email addresses (with names) to avoid badly written signatures
1563
1564 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001565 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001566 }
1567
1568 return (\@type, \@signature_lines);
1569}
1570
Joe Perches60db31a2009-12-14 18:00:50 -08001571sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001572 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001573 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001574 my @lines = ();
1575 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001576 my @authors = ();
1577 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001578
Joe Perches60db31a2009-12-14 18:00:50 -08001579 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001580
Joe Perches60db31a2009-12-14 18:00:50 -08001581 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001582 my $author_pattern = $VCS_cmds{"author_pattern"};
1583 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1584
1585 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001586
Joe Perches60db31a2009-12-14 18:00:50 -08001587 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001588
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001589 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001590 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001591 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001592
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001593# print("stats: <@stats>\n");
1594
1595 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001596
1597 save_commits_by_author(@lines) if ($interactive);
1598 save_commits_by_signer(@lines) if ($interactive);
1599
Joe Perches0e70e832009-09-21 17:04:20 -07001600 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001601 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001602 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001603
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001604 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001605 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001606
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001607 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001608}
1609
Joe Perches63ab52d2010-10-26 14:22:51 -07001610sub vcs_find_author {
1611 my ($cmd) = @_;
1612 my @lines = ();
1613
1614 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1615
1616 if (!$email_git_penguin_chiefs) {
1617 @lines = grep(!/${penguin_chiefs}/i, @lines);
1618 }
1619
1620 return @lines if !@lines;
1621
Joe Perches683c6f82010-10-26 14:22:55 -07001622 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001623 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001624 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1625 my $author = $1;
1626 my ($name, $address) = parse_email($author);
1627 $author = format_email($name, $address, 1);
1628 push(@authors, $author);
1629 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001630 }
1631
Joe Perches683c6f82010-10-26 14:22:55 -07001632 save_commits_by_author(@lines) if ($interactive);
1633 save_commits_by_signer(@lines) if ($interactive);
1634
1635 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001636}
1637
Joe Perches60db31a2009-12-14 18:00:50 -08001638sub vcs_save_commits {
1639 my ($cmd) = @_;
1640 my @lines = ();
1641 my @commits = ();
1642
1643 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1644
1645 foreach my $line (@lines) {
1646 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1647 push(@commits, $1);
1648 }
1649 }
1650
1651 return @commits;
1652}
1653
1654sub vcs_blame {
1655 my ($file) = @_;
1656 my $cmd;
1657 my @commits = ();
1658
1659 return @commits if (!(-f $file));
1660
1661 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1662 my @all_commits = ();
1663
1664 $cmd = $VCS_cmds{"blame_file_cmd"};
1665 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1666 @all_commits = vcs_save_commits($cmd);
1667
1668 foreach my $file_range_diff (@range) {
1669 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1670 my $diff_file = $1;
1671 my $diff_start = $2;
1672 my $diff_length = $3;
1673 next if ("$file" ne "$diff_file");
1674 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1675 push(@commits, $all_commits[$i]);
1676 }
1677 }
1678 } elsif (@range) {
1679 foreach my $file_range_diff (@range) {
1680 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1681 my $diff_file = $1;
1682 my $diff_start = $2;
1683 my $diff_length = $3;
1684 next if ("$file" ne "$diff_file");
1685 $cmd = $VCS_cmds{"blame_range_cmd"};
1686 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1687 push(@commits, vcs_save_commits($cmd));
1688 }
1689 } else {
1690 $cmd = $VCS_cmds{"blame_file_cmd"};
1691 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1692 @commits = vcs_save_commits($cmd);
1693 }
1694
Joe Perches63ab52d2010-10-26 14:22:51 -07001695 foreach my $commit (@commits) {
1696 $commit =~ s/^\^//g;
1697 }
1698
Joe Perches60db31a2009-12-14 18:00:50 -08001699 return @commits;
1700}
1701
1702my $printed_novcs = 0;
1703sub vcs_exists {
1704 %VCS_cmds = %VCS_cmds_git;
1705 return 1 if eval $VCS_cmds{"available"};
1706 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001707 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001708 %VCS_cmds = ();
1709 if (!$printed_novcs) {
1710 warn("$P: No supported VCS found. Add --nogit to options?\n");
1711 warn("Using a git repository produces better results.\n");
1712 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001713 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001714 $printed_novcs = 1;
1715 }
1716 return 0;
1717}
1718
Joe Perches683c6f82010-10-26 14:22:55 -07001719sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001720 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001721 return $vcs_used == 1;
1722}
1723
1724sub vcs_is_hg {
1725 return $vcs_used == 2;
1726}
1727
Joe Perches6ef1c522010-10-26 14:22:56 -07001728sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001729 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001730 my @list = @$list_ref;
1731
Joe Perches683c6f82010-10-26 14:22:55 -07001732 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001733
1734 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001735 my %authored;
1736 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001737 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001738 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001739 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001740 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1741 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001742 $authored{$count} = 0;
1743 $signed{$count} = 0;
1744 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001745 }
1746
1747 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001748 my $done = 0;
1749 my $print_options = 0;
1750 my $redraw = 1;
1751 while (!$done) {
1752 $count = 0;
1753 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001754 printf STDERR "\n%1s %2s %-65s",
1755 "*", "#", "email/list and role:stats";
1756 if ($email_git ||
1757 ($email_git_fallback && !$maintained) ||
1758 $email_git_blame) {
1759 print STDERR "auth sign";
1760 }
1761 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001762 foreach my $entry (@list) {
1763 my $email = $entry->[0];
1764 my $role = $entry->[1];
1765 my $sel = "";
1766 $sel = "*" if ($selected{$count});
1767 my $commit_author = $commit_author_hash{$email};
1768 my $commit_signer = $commit_signer_hash{$email};
1769 my $authored = 0;
1770 my $signed = 0;
1771 $authored++ for (@{$commit_author});
1772 $signed++ for (@{$commit_signer});
1773 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1774 printf STDERR "%4d %4d", $authored, $signed
1775 if ($authored > 0 || $signed > 0);
1776 printf STDERR "\n %s\n", $role;
1777 if ($authored{$count}) {
1778 my $commit_author = $commit_author_hash{$email};
1779 foreach my $ref (@{$commit_author}) {
1780 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001781 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001782 }
Joe Perches683c6f82010-10-26 14:22:55 -07001783 if ($signed{$count}) {
1784 my $commit_signer = $commit_signer_hash{$email};
1785 foreach my $ref (@{$commit_signer}) {
1786 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1787 }
1788 }
1789
1790 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001791 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001792 }
Joe Perches683c6f82010-10-26 14:22:55 -07001793 my $date_ref = \$email_git_since;
1794 $date_ref = \$email_hg_since if (vcs_is_hg());
1795 if ($print_options) {
1796 $print_options = 0;
1797 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001798 print STDERR <<EOT
1799
1800Version Control options:
1801g use git history [$email_git]
1802gf use git-fallback [$email_git_fallback]
1803b use git blame [$email_git_blame]
1804bs use blame signatures [$email_git_blame_signatures]
1805c# minimum commits [$email_git_min_signatures]
1806%# min percent [$email_git_min_percent]
1807d# history to use [$$date_ref]
1808x# max maintainers [$email_git_max_maintainers]
1809t all signature types [$email_git_all_signature_types]
1810m use .mailmap [$email_use_mailmap]
1811EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001812 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001813 print STDERR <<EOT
1814
1815Additional options:
18160 toggle all
1817tm toggle maintainers
1818tg toggle git entries
1819tl toggle open list entries
1820ts toggle subscriber list entries
1821f emails in file [$file_emails]
1822k keywords in file [$keywords]
1823r remove duplicates [$email_remove_duplicates]
1824p# pattern match depth [$pattern_depth]
1825EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001826 }
1827 print STDERR
1828"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1829
1830 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001831 chomp($input);
1832
Joe Perches683c6f82010-10-26 14:22:55 -07001833 $redraw = 1;
1834 my $rerun = 0;
1835 my @wish = split(/[, ]+/, $input);
1836 foreach my $nr (@wish) {
1837 $nr = lc($nr);
1838 my $sel = substr($nr, 0, 1);
1839 my $str = substr($nr, 1);
1840 my $val = 0;
1841 $val = $1 if $str =~ /^(\d+)$/;
1842
1843 if ($sel eq "y") {
1844 $interactive = 0;
1845 $done = 1;
1846 $output_rolestats = 0;
1847 $output_roles = 0;
1848 last;
1849 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1850 $selected{$nr - 1} = !$selected{$nr - 1};
1851 } elsif ($sel eq "*" || $sel eq '^') {
1852 my $toggle = 0;
1853 $toggle = 1 if ($sel eq '*');
1854 for (my $i = 0; $i < $count; $i++) {
1855 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001856 }
Joe Perches683c6f82010-10-26 14:22:55 -07001857 } elsif ($sel eq "0") {
1858 for (my $i = 0; $i < $count; $i++) {
1859 $selected{$i} = !$selected{$i};
1860 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001861 } elsif ($sel eq "t") {
1862 if (lc($str) eq "m") {
1863 for (my $i = 0; $i < $count; $i++) {
1864 $selected{$i} = !$selected{$i}
1865 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1866 }
1867 } elsif (lc($str) eq "g") {
1868 for (my $i = 0; $i < $count; $i++) {
1869 $selected{$i} = !$selected{$i}
1870 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1871 }
1872 } elsif (lc($str) eq "l") {
1873 for (my $i = 0; $i < $count; $i++) {
1874 $selected{$i} = !$selected{$i}
1875 if ($list[$i]->[1] =~ /^(open list)/i);
1876 }
1877 } elsif (lc($str) eq "s") {
1878 for (my $i = 0; $i < $count; $i++) {
1879 $selected{$i} = !$selected{$i}
1880 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1881 }
1882 }
Joe Perches683c6f82010-10-26 14:22:55 -07001883 } elsif ($sel eq "a") {
1884 if ($val > 0 && $val <= $count) {
1885 $authored{$val - 1} = !$authored{$val - 1};
1886 } elsif ($str eq '*' || $str eq '^') {
1887 my $toggle = 0;
1888 $toggle = 1 if ($str eq '*');
1889 for (my $i = 0; $i < $count; $i++) {
1890 $authored{$i} = $toggle;
1891 }
1892 }
1893 } elsif ($sel eq "s") {
1894 if ($val > 0 && $val <= $count) {
1895 $signed{$val - 1} = !$signed{$val - 1};
1896 } elsif ($str eq '*' || $str eq '^') {
1897 my $toggle = 0;
1898 $toggle = 1 if ($str eq '*');
1899 for (my $i = 0; $i < $count; $i++) {
1900 $signed{$i} = $toggle;
1901 }
1902 }
1903 } elsif ($sel eq "o") {
1904 $print_options = 1;
1905 $redraw = 1;
1906 } elsif ($sel eq "g") {
1907 if ($str eq "f") {
1908 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001909 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001910 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001911 }
Joe Perches683c6f82010-10-26 14:22:55 -07001912 $rerun = 1;
1913 } elsif ($sel eq "b") {
1914 if ($str eq "s") {
1915 bool_invert(\$email_git_blame_signatures);
1916 } else {
1917 bool_invert(\$email_git_blame);
1918 }
1919 $rerun = 1;
1920 } elsif ($sel eq "c") {
1921 if ($val > 0) {
1922 $email_git_min_signatures = $val;
1923 $rerun = 1;
1924 }
1925 } elsif ($sel eq "x") {
1926 if ($val > 0) {
1927 $email_git_max_maintainers = $val;
1928 $rerun = 1;
1929 }
1930 } elsif ($sel eq "%") {
1931 if ($str ne "" && $val >= 0) {
1932 $email_git_min_percent = $val;
1933 $rerun = 1;
1934 }
1935 } elsif ($sel eq "d") {
1936 if (vcs_is_git()) {
1937 $email_git_since = $str;
1938 } elsif (vcs_is_hg()) {
1939 $email_hg_since = $str;
1940 }
1941 $rerun = 1;
1942 } elsif ($sel eq "t") {
1943 bool_invert(\$email_git_all_signature_types);
1944 $rerun = 1;
1945 } elsif ($sel eq "f") {
1946 bool_invert(\$file_emails);
1947 $rerun = 1;
1948 } elsif ($sel eq "r") {
1949 bool_invert(\$email_remove_duplicates);
1950 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001951 } elsif ($sel eq "m") {
1952 bool_invert(\$email_use_mailmap);
1953 read_mailmap();
1954 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001955 } elsif ($sel eq "k") {
1956 bool_invert(\$keywords);
1957 $rerun = 1;
1958 } elsif ($sel eq "p") {
1959 if ($str ne "" && $val >= 0) {
1960 $pattern_depth = $val;
1961 $rerun = 1;
1962 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001963 } elsif ($sel eq "h" || $sel eq "?") {
1964 print STDERR <<EOT
1965
1966Interactive mode allows you to select the various maintainers, submitters,
1967commit signers and mailing lists that could be CC'd on a patch.
1968
1969Any *'d entry is selected.
1970
Joe Perches47abc722010-10-26 14:22:57 -07001971If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001972history of files in the patch. Also, each line of the current file can
1973be matched to its commit author and that commits signers with blame.
1974
1975Various knobs exist to control the length of time for active commit
1976tracking, the maximum number of commit authors and signers to add,
1977and such.
1978
1979Enter selections at the prompt until you are satisfied that the selected
1980maintainers are appropriate. You may enter multiple selections separated
1981by either commas or spaces.
1982
1983EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001984 } else {
1985 print STDERR "invalid option: '$nr'\n";
1986 $redraw = 0;
1987 }
1988 }
1989 if ($rerun) {
1990 print STDERR "git-blame can be very slow, please have patience..."
1991 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001992 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001993 }
1994 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001995
1996 #drop not selected entries
1997 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001998 my @new_emailto = ();
1999 foreach my $entry (@list) {
2000 if ($selected{$count}) {
2001 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07002002 }
2003 $count++;
2004 }
Joe Perches683c6f82010-10-26 14:22:55 -07002005 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07002006}
2007
Joe Perches683c6f82010-10-26 14:22:55 -07002008sub bool_invert {
2009 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07002010
Joe Perches683c6f82010-10-26 14:22:55 -07002011 if ($$bool_ref) {
2012 $$bool_ref = 0;
2013 } else {
2014 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07002015 }
Florian Micklerdace8e32010-10-26 14:22:54 -07002016}
2017
Joe Perchesb9e23312010-10-26 14:22:58 -07002018sub deduplicate_email {
2019 my ($email) = @_;
2020
2021 my $matched = 0;
2022 my ($name, $address) = parse_email($email);
2023 $email = format_email($name, $address, 1);
2024 $email = mailmap_email($email);
2025
2026 return $email if (!$email_remove_duplicates);
2027
2028 ($name, $address) = parse_email($email);
2029
Joe Perchesfae99202010-10-26 14:22:58 -07002030 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002031 $name = $deduplicate_name_hash{lc($name)}->[0];
2032 $address = $deduplicate_name_hash{lc($name)}->[1];
2033 $matched = 1;
2034 } elsif ($deduplicate_address_hash{lc($address)}) {
2035 $name = $deduplicate_address_hash{lc($address)}->[0];
2036 $address = $deduplicate_address_hash{lc($address)}->[1];
2037 $matched = 1;
2038 }
2039 if (!$matched) {
2040 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
2041 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
2042 }
2043 $email = format_email($name, $address, 1);
2044 $email = mailmap_email($email);
2045 return $email;
2046}
2047
Joe Perches683c6f82010-10-26 14:22:55 -07002048sub save_commits_by_author {
2049 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07002050
Joe Perches683c6f82010-10-26 14:22:55 -07002051 my @authors = ();
2052 my @commits = ();
2053 my @subjects = ();
2054
2055 foreach my $line (@lines) {
2056 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2057 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002058 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07002059 push(@authors, $author);
2060 }
2061 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
2062 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
2063 }
2064
2065 for (my $i = 0; $i < @authors; $i++) {
2066 my $exists = 0;
2067 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
2068 if (@{$ref}[0] eq $commits[$i] &&
2069 @{$ref}[1] eq $subjects[$i]) {
2070 $exists = 1;
2071 last;
2072 }
2073 }
2074 if (!$exists) {
2075 push(@{$commit_author_hash{$authors[$i]}},
2076 [ ($commits[$i], $subjects[$i]) ]);
2077 }
2078 }
2079}
2080
2081sub save_commits_by_signer {
2082 my (@lines) = @_;
2083
2084 my $commit = "";
2085 my $subject = "";
2086
2087 foreach my $line (@lines) {
2088 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
2089 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
2090 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
2091 my @signatures = ($line);
2092 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
2093 my @types = @$types_ref;
2094 my @signers = @$signers_ref;
2095
2096 my $type = $types[0];
2097 my $signer = $signers[0];
2098
Joe Perchesb9e23312010-10-26 14:22:58 -07002099 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07002100
Joe Perches683c6f82010-10-26 14:22:55 -07002101 my $exists = 0;
2102 foreach my $ref(@{$commit_signer_hash{$signer}}) {
2103 if (@{$ref}[0] eq $commit &&
2104 @{$ref}[1] eq $subject &&
2105 @{$ref}[2] eq $type) {
2106 $exists = 1;
2107 last;
2108 }
2109 }
2110 if (!$exists) {
2111 push(@{$commit_signer_hash{$signer}},
2112 [ ($commit, $subject, $type) ]);
2113 }
2114 }
2115 }
Florian Micklerdace8e32010-10-26 14:22:54 -07002116}
2117
Joe Perches60db31a2009-12-14 18:00:50 -08002118sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08002119 my ($role, $divisor, @lines) = @_;
2120
2121 my %hash;
2122 my $count = 0;
2123
Joe Perchesa8af2432009-12-14 18:00:49 -08002124 return if (@lines <= 0);
2125
2126 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08002127 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08002128 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08002129 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07002130
Florian Mickler7fa8ff22010-10-26 14:22:56 -07002131 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07002132
Joe Perches63ab52d2010-10-26 14:22:51 -07002133 return if (@lines <= 0);
2134
Joe Perches0e70e832009-09-21 17:04:20 -07002135 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07002136
Joe Perches11ecf532009-09-21 17:04:22 -07002137 # uniq -c
2138 $hash{$_}++ for @lines;
2139
2140 # sort -rn
2141 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
2142 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08002143 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08002144
Joe Perchesa8af2432009-12-14 18:00:49 -08002145 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07002146 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07002147 $count++;
2148 last if ($sign_offs < $email_git_min_signatures ||
2149 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08002150 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08002151 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08002152 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08002153 my $fmt_percent = sprintf("%.0f", $percent);
2154 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
2155 } else {
2156 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08002157 }
Joe Perchesf5492662009-09-21 17:04:13 -07002158 }
2159}
2160
Joe Perches60db31a2009-12-14 18:00:50 -08002161sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08002162 my ($file) = @_;
2163
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002164 my $authors_ref;
2165 my $signers_ref;
2166 my $stats_ref;
2167 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002168 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002169 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08002170 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002171
Joe Perches683c6f82010-10-26 14:22:55 -07002172 $vcs_used = vcs_exists();
2173 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08002174
Joe Perches60db31a2009-12-14 18:00:50 -08002175 my $cmd = $VCS_cmds{"find_signers_cmd"};
2176 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2177
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002178 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2179
2180 @signers = @{$signers_ref} if defined $signers_ref;
2181 @authors = @{$authors_ref} if defined $authors_ref;
2182 @stats = @{$stats_ref} if defined $stats_ref;
2183
2184# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07002185
2186 foreach my $signer (@signers) {
2187 $signer = deduplicate_email($signer);
2188 }
2189
Joe Perches60db31a2009-12-14 18:00:50 -08002190 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002191 vcs_assign("authored", $commits, @authors);
2192 if ($#authors == $#stats) {
2193 my $stat_pattern = $VCS_cmds{"stat_pattern"};
2194 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
2195
2196 my $added = 0;
2197 my $deleted = 0;
2198 for (my $i = 0; $i <= $#stats; $i++) {
2199 if ($stats[$i] =~ /$stat_pattern/) {
2200 $added += $1;
2201 $deleted += $2;
2202 }
2203 }
2204 my @tmp_authors = uniq(@authors);
2205 foreach my $author (@tmp_authors) {
2206 $author = deduplicate_email($author);
2207 }
2208 @tmp_authors = uniq(@tmp_authors);
2209 my @list_added = ();
2210 my @list_deleted = ();
2211 foreach my $author (@tmp_authors) {
2212 my $auth_added = 0;
2213 my $auth_deleted = 0;
2214 for (my $i = 0; $i <= $#stats; $i++) {
2215 if ($author eq deduplicate_email($authors[$i]) &&
2216 $stats[$i] =~ /$stat_pattern/) {
2217 $auth_added += $1;
2218 $auth_deleted += $2;
2219 }
2220 }
2221 for (my $i = 0; $i < $auth_added; $i++) {
2222 push(@list_added, $author);
2223 }
2224 for (my $i = 0; $i < $auth_deleted; $i++) {
2225 push(@list_deleted, $author);
2226 }
2227 }
2228 vcs_assign("added_lines", $added, @list_added);
2229 vcs_assign("removed_lines", $deleted, @list_deleted);
2230 }
Joe Perchesa8af2432009-12-14 18:00:49 -08002231}
2232
Joe Perches60db31a2009-12-14 18:00:50 -08002233sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07002234 my ($file) = @_;
2235
Joe Perches60db31a2009-12-14 18:00:50 -08002236 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07002237 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002238 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002239 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002240 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002241
Joe Perches683c6f82010-10-26 14:22:55 -07002242 $vcs_used = vcs_exists();
2243 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002244
Joe Perches63ab52d2010-10-26 14:22:51 -07002245 @all_commits = vcs_blame($file);
2246 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002247 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002248 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002249
Joe Perches683c6f82010-10-26 14:22:55 -07002250 if ($email_git_blame_signatures) {
2251 if (vcs_is_hg()) {
2252 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002253 my $commit_authors_ref;
2254 my $commit_signers_ref;
2255 my $stats_ref;
2256 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002257 my @commit_signers = ();
2258 my $commit = join(" -r ", @commits);
2259 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002260
Joe Perches683c6f82010-10-26 14:22:55 -07002261 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2262 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002263
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002264 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2265 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2266 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002267
Joe Perches683c6f82010-10-26 14:22:55 -07002268 push(@signers, @commit_signers);
2269 } else {
2270 foreach my $commit (@commits) {
2271 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002272 my $commit_authors_ref;
2273 my $commit_signers_ref;
2274 my $stats_ref;
2275 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002276 my @commit_signers = ();
2277 my $cmd;
2278
2279 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2280 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2281
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002282 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2283 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2284 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002285
2286 push(@signers, @commit_signers);
2287 }
2288 }
Joe Perchesf5492662009-09-21 17:04:13 -07002289 }
2290
Joe Perchesa8af2432009-12-14 18:00:49 -08002291 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002292 if ($output_rolestats) {
2293 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002294 if (vcs_is_hg()) {{ # Double brace for last exit
2295 my $commit_count;
2296 my @commit_signers = ();
2297 @commits = uniq(@commits);
2298 @commits = sort(@commits);
2299 my $commit = join(" -r ", @commits);
2300 my $cmd;
2301
2302 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2303 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2304
2305 my @lines = ();
2306
2307 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2308
2309 if (!$email_git_penguin_chiefs) {
2310 @lines = grep(!/${penguin_chiefs}/i, @lines);
2311 }
2312
2313 last if !@lines;
2314
2315 my @authors = ();
2316 foreach my $line (@lines) {
2317 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2318 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002319 $author = deduplicate_email($author);
2320 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002321 }
2322 }
2323
2324 save_commits_by_author(@lines) if ($interactive);
2325 save_commits_by_signer(@lines) if ($interactive);
2326
2327 push(@signers, @authors);
2328 }}
2329 else {
2330 foreach my $commit (@commits) {
2331 my $i;
2332 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2333 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2334 my @author = vcs_find_author($cmd);
2335 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002336
2337 my $formatted_author = deduplicate_email($author[0]);
2338
Joe Perches683c6f82010-10-26 14:22:55 -07002339 my $count = grep(/$commit/, @all_commits);
2340 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002341 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002342 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002343 }
2344 }
2345 if (@blame_signers) {
2346 vcs_assign("authored lines", $total_lines, @blame_signers);
2347 }
2348 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002349 foreach my $signer (@signers) {
2350 $signer = deduplicate_email($signer);
2351 }
Joe Perches60db31a2009-12-14 18:00:50 -08002352 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002353 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002354 foreach my $signer (@signers) {
2355 $signer = deduplicate_email($signer);
2356 }
Joe Perches60db31a2009-12-14 18:00:50 -08002357 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002358 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002359}
2360
Joe Perches4cad35a2016-08-02 14:04:10 -07002361sub vcs_file_exists {
2362 my ($file) = @_;
2363
2364 my $exists;
2365
2366 my $vcs_used = vcs_exists();
2367 return 0 if (!$vcs_used);
2368
2369 my $cmd = $VCS_cmds{"file_exists_cmd"};
2370 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
Joe Perches8582fb52016-08-25 15:16:48 -07002371 $cmd .= " 2>&1";
Joe Perches4cad35a2016-08-02 14:04:10 -07002372 $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
2373
Joe Perches8582fb52016-08-25 15:16:48 -07002374 return 0 if ($? != 0);
2375
Joe Perches4cad35a2016-08-02 14:04:10 -07002376 return $exists;
2377}
2378
Tom Saegere1f75902017-11-17 15:27:42 -08002379sub vcs_list_files {
2380 my ($file) = @_;
2381
2382 my @lsfiles = ();
2383
2384 my $vcs_used = vcs_exists();
2385 return 0 if (!$vcs_used);
2386
2387 my $cmd = $VCS_cmds{"list_files_cmd"};
2388 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2389 @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
2390
2391 return () if ($? != 0);
2392
2393 return @lsfiles;
2394}
2395
Joe Perchescb7301c2009-04-07 20:40:12 -07002396sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002397 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002398
2399 my %saw;
2400 @parms = grep(!$saw{$_}++, @parms);
2401 return @parms;
2402}
2403
2404sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002405 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002406
2407 my %saw;
2408 @parms = sort @parms;
2409 @parms = grep(!$saw{$_}++, @parms);
2410 return @parms;
2411}
2412
Joe Perches03372db2010-03-05 13:43:00 -08002413sub clean_file_emails {
2414 my (@file_emails) = @_;
2415 my @fmt_emails = ();
2416
2417 foreach my $email (@file_emails) {
2418 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2419 my ($name, $address) = parse_email($email);
2420 if ($name eq '"[,\.]"') {
2421 $name = "";
2422 }
2423
2424 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2425 if (@nw > 2) {
2426 my $first = $nw[@nw - 3];
2427 my $middle = $nw[@nw - 2];
2428 my $last = $nw[@nw - 1];
2429
2430 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2431 (length($first) == 2 && substr($first, -1) eq ".")) ||
2432 (length($middle) == 1 ||
2433 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2434 $name = "$first $middle $last";
2435 } else {
2436 $name = "$middle $last";
2437 }
2438 }
2439
2440 if (substr($name, -1) =~ /[,\.]/) {
2441 $name = substr($name, 0, length($name) - 1);
2442 } elsif (substr($name, -2) =~ /[,\.]"/) {
2443 $name = substr($name, 0, length($name) - 2) . '"';
2444 }
2445
2446 if (substr($name, 0, 1) =~ /[,\.]/) {
2447 $name = substr($name, 1, length($name) - 1);
2448 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2449 $name = '"' . substr($name, 2, length($name) - 2);
2450 }
2451
2452 my $fmt_email = format_email($name, $address, $email_usename);
2453 push(@fmt_emails, $fmt_email);
2454 }
2455 return @fmt_emails;
2456}
2457
Joe Perches3c7385b2009-12-14 18:00:46 -08002458sub merge_email {
2459 my @lines;
2460 my %saw;
2461
2462 for (@_) {
2463 my ($address, $role) = @$_;
2464 if (!$saw{$address}) {
2465 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002466 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002467 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002468 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002469 }
2470 $saw{$address} = 1;
2471 }
2472 }
2473
2474 return @lines;
2475}
2476
Joe Perchescb7301c2009-04-07 20:40:12 -07002477sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002478 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002479
2480 if ($output_multiline) {
2481 foreach my $line (@parms) {
2482 print("${line}\n");
2483 }
2484 } else {
2485 print(join($output_separator, @parms));
2486 print("\n");
2487 }
2488}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002489
2490my $rfc822re;
2491
2492sub make_rfc822re {
2493# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2494# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2495# This regexp will only work on addresses which have had comments stripped
2496# and replaced with rfc822_lwsp.
2497
2498 my $specials = '()<>@,;:\\\\".\\[\\]';
2499 my $controls = '\\000-\\037\\177';
2500
2501 my $dtext = "[^\\[\\]\\r\\\\]";
2502 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2503
2504 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2505
2506# Use zero-width assertion to spot the limit of an atom. A simple
2507# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2508 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2509 my $word = "(?:$atom|$quoted_string)";
2510 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2511
2512 my $sub_domain = "(?:$atom|$domain_literal)";
2513 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2514
2515 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2516
2517 my $phrase = "$word*";
2518 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2519 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2520 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2521
2522 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2523 my $address = "(?:$mailbox|$group)";
2524
2525 return "$rfc822_lwsp*$address";
2526}
2527
2528sub rfc822_strip_comments {
2529 my $s = shift;
2530# Recursively remove comments, and replace with a single space. The simpler
2531# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2532# chars in atoms, for example.
2533
2534 while ($s =~ s/^((?:[^"\\]|\\.)*
2535 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2536 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2537 return $s;
2538}
2539
2540# valid: returns true if the parameter is an RFC822 valid address
2541#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002542sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002543 my $s = rfc822_strip_comments(shift);
2544
2545 if (!$rfc822re) {
2546 $rfc822re = make_rfc822re();
2547 }
2548
2549 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2550}
2551
2552# validlist: In scalar context, returns true if the parameter is an RFC822
2553# valid list of addresses.
2554#
2555# In list context, returns an empty list on failure (an invalid
2556# address was found); otherwise a list whose first element is the
2557# number of addresses found and whose remaining elements are the
2558# addresses. This is needed to disambiguate failure (invalid)
2559# from success with no addresses found, because an empty string is
2560# a valid list.
2561
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002562sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002563 my $s = rfc822_strip_comments(shift);
2564
2565 if (!$rfc822re) {
2566 $rfc822re = make_rfc822re();
2567 }
2568 # * null list items are valid according to the RFC
2569 # * the '1' business is to aid in distinguishing failure from no results
2570
2571 my @r;
2572 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2573 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002574 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002575 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002576 }
2577 return wantarray ? (scalar(@r), @r) : 1;
2578 }
Joe Perches60db31a2009-12-14 18:00:50 -08002579 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002580}