blob: 82a482f858ee046537b9ad00305c79833455996a [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 Perchescb7301c2009-04-07 20:40:12 -070065
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 Perches083bf9c2017-11-17 15:27:46 -0800268 'self-test:s' => \$self_test,
Joe Perchescb7301c2009-04-07 20:40:12 -0700269 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800270 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700271 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800272 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700273}
274
275if ($help != 0) {
276 usage();
277 exit 0;
278}
279
280if ($version != 0) {
281 print("${P} ${V}\n");
282 exit 0;
283}
284
Joe Perches083bf9c2017-11-17 15:27:46 -0800285if (defined $self_test) {
Tom Saegere1f75902017-11-17 15:27:42 -0800286 read_all_maintainer_files();
Joe Perches083bf9c2017-11-17 15:27:46 -0800287 self_test();
Tom Saegere1f75902017-11-17 15:27:42 -0800288 exit 0;
289}
290
Joe Perches64f77f32010-03-05 13:43:04 -0800291if (-t STDIN && !@ARGV) {
292 # We're talking to a terminal, but have no command line arguments.
293 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700294}
295
Joe Perches683c6f82010-10-26 14:22:55 -0700296$output_multiline = 0 if ($output_separator ne ", ");
297$output_rolestats = 1 if ($interactive);
298$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800299
Joe Perches03aed212016-12-12 16:45:59 -0800300if ($sections || $letters ne "") {
301 $sections = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800302 $email = 0;
303 $email_list = 0;
304 $scm = 0;
305 $status = 0;
306 $subsystem = 0;
307 $web = 0;
308 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700309 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800310} else {
311 my $selections = $email + $scm + $status + $subsystem + $web;
312 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800313 die "$P: Missing required option: email, scm, status, subsystem or web\n";
314 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700315}
316
Joe Perchesf5492662009-09-21 17:04:13 -0700317if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700318 ($email_maintainer + $email_reviewer +
319 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700320 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700321 die "$P: Please select at least 1 email option\n";
322}
323
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -0700324if ($tree && !top_of_kernel_tree($lk_path)) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700325 die "$P: The current directory does not appear to be "
326 . "a linux kernel source tree.\n";
327}
328
329## Read MAINTAINERS for type/value pairs
330
331my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700332my %keyword_hash;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700333my @mfiles = ();
Joe Perches083bf9c2017-11-17 15:27:46 -0800334my @self_test_info = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700335
Joe Perches6f7d98e2017-08-04 21:45:48 -0700336sub read_maintainer_file {
337 my ($file) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700338
Joe Perches6f7d98e2017-08-04 21:45:48 -0700339 open (my $maint, '<', "$file")
340 or die "$P: Can't open MAINTAINERS file '$file': $!\n";
Tom Saegere1f75902017-11-17 15:27:42 -0800341 my $i = 1;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700342 while (<$maint>) {
343 my $line = $_;
Joe Perches083bf9c2017-11-17 15:27:46 -0800344 chomp $line;
Joe Perchescb7301c2009-04-07 20:40:12 -0700345
Joe Perches6f7d98e2017-08-04 21:45:48 -0700346 if ($line =~ m/^([A-Z]):\s*(.*)/) {
347 my $type = $1;
348 my $value = $2;
349
350 ##Filename pattern matching
351 if ($type eq "F" || $type eq "X") {
352 $value =~ s@\.@\\\.@g; ##Convert . to \.
353 $value =~ s/\*/\.\*/g; ##Convert * to .*
354 $value =~ s/\?/\./g; ##Convert ? to .
355 ##if pattern is a directory and it lacks a trailing slash, add one
356 if ((-d $value)) {
357 $value =~ s@([^/])$@$1/@;
358 }
359 } elsif ($type eq "K") {
360 $keyword_hash{@typevalue} = $value;
Joe Perches870020f2009-07-29 15:04:28 -0700361 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700362 push(@typevalue, "$type:$value");
363 } elsif (!(/^\s*$/ || /^\s*\#/)) {
Joe Perches6f7d98e2017-08-04 21:45:48 -0700364 push(@typevalue, $line);
Joe Perchescb7301c2009-04-07 20:40:12 -0700365 }
Joe Perches083bf9c2017-11-17 15:27:46 -0800366 if (defined $self_test) {
367 push(@self_test_info, {file=>$file, linenr=>$i, line=>$line});
368 }
Tom Saegere1f75902017-11-17 15:27:42 -0800369 $i++;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700370 }
371 close($maint);
372}
373
374sub find_is_maintainer_file {
375 my ($file) = $_;
376 return if ($file !~ m@/MAINTAINERS$@);
377 $file = $File::Find::name;
378 return if (! -f $file);
379 push(@mfiles, $file);
380}
381
382sub find_ignore_git {
383 return grep { $_ !~ /^\.git$/; } @_;
384}
385
Tom Saegere1f75902017-11-17 15:27:42 -0800386read_all_maintainer_files();
387
388sub read_all_maintainer_files {
389 if (-d "${lk_path}MAINTAINERS") {
390 opendir(DIR, "${lk_path}MAINTAINERS") or die $!;
391 my @files = readdir(DIR);
392 closedir(DIR);
393 foreach my $file (@files) {
394 push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./);
395 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700396 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700397
Tom Saegere1f75902017-11-17 15:27:42 -0800398 if ($find_maintainer_files) {
399 find( { wanted => \&find_is_maintainer_file,
400 preprocess => \&find_ignore_git,
401 no_chdir => 1,
402 }, "${lk_path}");
403 } else {
404 push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS";
405 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700406
Tom Saegere1f75902017-11-17 15:27:42 -0800407 foreach my $file (@mfiles) {
408 read_maintainer_file("$file");
409 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700410}
Joe Perches8cbb3a72009-09-21 17:04:21 -0700411
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700412#
413# Read mail address map
414#
415
Joe Perchesb9e23312010-10-26 14:22:58 -0700416my $mailmap;
417
418read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700419
420sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700421 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700422 names => {},
423 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700424 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700425
Joe Perchesb9e23312010-10-26 14:22:58 -0700426 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700427
428 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800429 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700430
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700431 while (<$mailmap_file>) {
432 s/#.*$//; #strip comments
433 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700434
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700435 next if (/^\s*$/); #skip empty lines
436 #entries have one of the following formats:
437 # name1 <mail1>
438 # <mail1> <mail2>
439 # name1 <mail1> <mail2>
440 # name1 <mail1> name2 <mail2>
441 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700442
443 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700444 my $real_name = $1;
445 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700446
Joe Perches47abc722010-10-26 14:22:57 -0700447 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700448 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700449 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700450
Joe Perches0334b382011-07-25 17:13:13 -0700451 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700452 my $real_address = $1;
453 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700454
Joe Perches47abc722010-10-26 14:22:57 -0700455 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700456
Joe Perches0334b382011-07-25 17:13:13 -0700457 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700458 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700459 my $real_address = $2;
460 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700461
Joe Perches47abc722010-10-26 14:22:57 -0700462 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700463 ($real_name, $real_address) =
464 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700465 $mailmap->{names}->{$wrong_address} = $real_name;
466 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700467
Joe Perches0334b382011-07-25 17:13:13 -0700468 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700469 my $real_name = $1;
470 my $real_address = $2;
471 my $wrong_name = $3;
472 my $wrong_address = $4;
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>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700477
Joe Perchesb9e23312010-10-26 14:22:58 -0700478 $wrong_name =~ s/\s+$//;
479 ($wrong_name, $wrong_address) =
480 parse_email("$wrong_name <$wrong_address>");
481
482 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
483 $mailmap->{names}->{$wrong_email} = $real_name;
484 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700485 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700486 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700487 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700488}
489
Joe Perches4a7fdb52009-04-10 12:28:57 -0700490## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700491
492my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700493my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700494my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800495my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700496
Joe Perches64f77f32010-03-05 13:43:04 -0800497if (!@ARGV) {
498 push(@ARGV, "&STDIN");
499}
500
Joe Perches4a7fdb52009-04-10 12:28:57 -0700501foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800502 if ($file ne "&STDIN") {
503 ##if $file is a directory and it lacks a trailing slash, add one
504 if ((-d $file)) {
505 $file =~ s@([^/])$@$1/@;
506 } elsif (!(-f $file)) {
507 die "$P: file '${file}' not found\n";
508 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700509 }
Joe Perchesaec742e2016-08-10 08:45:11 -0700510 if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
Joe Perchesbe17bdd2016-01-20 14:58:24 -0800511 $file =~ s/^\Q${cur_path}\E//; #strip any absolute path
512 $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
Joe Perches4a7fdb52009-04-10 12:28:57 -0700513 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700514 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800515 open(my $f, '<', $file)
516 or die "$P: Can't open $file: $!\n";
517 my $text = do { local($/) ; <$f> };
518 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800519 if ($keywords) {
520 foreach my $line (keys %keyword_hash) {
521 if ($text =~ m/$keyword_hash{$line}/x) {
522 push(@keyword_tvi, $line);
523 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700524 }
525 }
Joe Perches03372db2010-03-05 13:43:00 -0800526 if ($file_emails) {
527 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;
528 push(@file_emails, clean_file_emails(@poss_addr));
529 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700530 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700531 } else {
532 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700533 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800534
Wolfram Sang3a4df132010-03-23 13:35:18 -0700535 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800536 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700537
538 # We can check arbitrary information before the patch
539 # like the commit message, mail headers, etc...
540 # This allows us to match arbitrary keywords against any part
541 # of a git format-patch generated file (subject tags, etc...)
542
543 my $patch_prefix = ""; #Parsing the intro
544
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800545 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700546 my $patch_line = $_;
Joe Perches0455c742018-06-07 17:10:38 -0700547 if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) {
548 my $filename = $1;
549 push(@files, $filename);
550 } elsif (m/^rename (?:from|to) (\S+)\s*$/) {
551 my $filename = $1;
552 push(@files, $filename);
553 } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) {
554 my $filename1 = $1;
555 my $filename2 = $2;
556 push(@files, $filename1);
557 push(@files, $filename2);
558 } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700559 my $filename = $1;
560 $filename =~ s@^[^/]*/@@;
561 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700562 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700563 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700564 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700565 } elsif (m/^\@\@ -(\d+),(\d+)/) {
566 if ($email_git_blame) {
567 push(@range, "$lastfile:$1:$2");
568 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700569 } elsif ($keywords) {
570 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700571 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700572 push(@keyword_tvi, $line);
573 }
574 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700575 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700576 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800577 close($patch);
578
Joe Perches4a7fdb52009-04-10 12:28:57 -0700579 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700580 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700581 . "Add -f to options?\n";
582 }
583 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700584 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700585}
586
Joe Perches03372db2010-03-05 13:43:00 -0800587@file_emails = uniq(@file_emails);
588
Joe Perches683c6f82010-10-26 14:22:55 -0700589my %email_hash_name;
590my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700591my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700592my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700593my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700594my @scm = ();
595my @web = ();
596my @subsystem = ();
597my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700598my %deduplicate_name_hash = ();
599my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700600
Joe Perches6ef1c522010-10-26 14:22:56 -0700601my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700602
Joe Perches6ef1c522010-10-26 14:22:56 -0700603if (@maintainers) {
604 @maintainers = merge_email(@maintainers);
605 output(@maintainers);
606}
Joe Perchescb7301c2009-04-07 20:40:12 -0700607
608if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700609 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700610 output(@scm);
611}
Joe Perches683c6f82010-10-26 14:22:55 -0700612
Joe Perchescb7301c2009-04-07 20:40:12 -0700613if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700614 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700615 output(@status);
616}
617
618if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700619 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700620 output(@subsystem);
621}
622
623if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700624 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700625 output(@web);
626}
627
628exit($exit);
629
Joe Perches083bf9c2017-11-17 15:27:46 -0800630sub self_test {
Tom Saegere1f75902017-11-17 15:27:42 -0800631 my @lsfiles = ();
Joe Perches083bf9c2017-11-17 15:27:46 -0800632 my @good_links = ();
633 my @bad_links = ();
634 my @section_headers = ();
635 my $index = 0;
Tom Saegere1f75902017-11-17 15:27:42 -0800636
637 @lsfiles = vcs_list_files($lk_path);
638
Joe Perches083bf9c2017-11-17 15:27:46 -0800639 for my $x (@self_test_info) {
640 $index++;
641
642 ## Section header duplication and missing section content
643 if (($self_test eq "" || $self_test =~ /\bsections\b/) &&
644 $x->{line} =~ /^\S[^:]/ &&
645 defined $self_test_info[$index] &&
646 $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) {
647 my $has_S = 0;
648 my $has_F = 0;
649 my $has_ML = 0;
650 my $status = "";
651 if (grep(m@^\Q$x->{line}\E@, @section_headers)) {
652 print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n");
653 } else {
654 push(@section_headers, $x->{line});
655 }
656 my $nextline = $index;
657 while (defined $self_test_info[$nextline] &&
658 $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) {
659 my $type = $1;
660 my $value = $2;
661 if ($type eq "S") {
662 $has_S = 1;
663 $status = $value;
664 } elsif ($type eq "F" || $type eq "N") {
665 $has_F = 1;
666 } elsif ($type eq "M" || $type eq "R" || $type eq "L") {
667 $has_ML = 1;
668 }
669 $nextline++;
670 }
671 if (!$has_ML && $status !~ /orphan|obsolete/i) {
672 print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n");
673 }
674 if (!$has_S) {
675 print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n");
676 }
677 if (!$has_F) {
678 print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n");
679 }
680 }
681
682 next if ($x->{line} !~ /^([A-Z]):\s*(.*)/);
683
684 my $type = $1;
685 my $value = $2;
686
687 ## Filename pattern matching
688 if (($type eq "F" || $type eq "X") &&
689 ($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
690 $value =~ s@\.@\\\.@g; ##Convert . to \.
691 $value =~ s/\*/\.\*/g; ##Convert * to .*
692 $value =~ s/\?/\./g; ##Convert ? to .
693 ##if pattern is a directory and it lacks a trailing slash, add one
694 if ((-d $value)) {
695 $value =~ s@([^/])$@$1/@;
696 }
697 if (!grep(m@^$value@, @lsfiles)) {
698 print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n");
699 }
700
701 ## Link reachability
702 } elsif (($type eq "W" || $type eq "Q" || $type eq "B") &&
703 $value =~ /^https?:/ &&
704 ($self_test eq "" || $self_test =~ /\blinks\b/)) {
705 next if (grep(m@^\Q$value\E$@, @good_links));
706 my $isbad = 0;
707 if (grep(m@^\Q$value\E$@, @bad_links)) {
708 $isbad = 1;
709 } else {
710 my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`;
711 if ($? == 0) {
712 push(@good_links, $value);
713 } else {
714 push(@bad_links, $value);
715 $isbad = 1;
716 }
717 }
718 if ($isbad) {
719 print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
720 }
721
722 ## SCM reachability
723 } elsif ($type eq "T" &&
724 ($self_test eq "" || $self_test =~ /\bscm\b/)) {
725 next if (grep(m@^\Q$value\E$@, @good_links));
726 my $isbad = 0;
727 if (grep(m@^\Q$value\E$@, @bad_links)) {
728 $isbad = 1;
729 } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) {
730 print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n");
731 } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) {
732 my $url = $1;
733 my $branch = "";
734 $branch = $3 if $3;
735 my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`;
736 if ($? == 0) {
737 push(@good_links, $value);
738 } else {
739 push(@bad_links, $value);
740 $isbad = 1;
741 }
742 } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) {
743 my $url = $1;
744 my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`;
745 if ($? == 0) {
746 push(@good_links, $value);
747 } else {
748 push(@bad_links, $value);
749 $isbad = 1;
750 }
751 }
752 if ($isbad) {
753 print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
754 }
755 }
Tom Saegere1f75902017-11-17 15:27:42 -0800756 }
757}
758
Joe Perches435de072015-06-25 15:01:50 -0700759sub ignore_email_address {
760 my ($address) = @_;
761
762 foreach my $ignore (@ignore_emails) {
763 return 1 if ($ignore eq $address);
764 }
765
766 return 0;
767}
768
Joe Perchesab6c9372011-01-12 16:59:50 -0800769sub range_is_maintained {
770 my ($start, $end) = @_;
771
772 for (my $i = $start; $i < $end; $i++) {
773 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700774 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800775 my $type = $1;
776 my $value = $2;
777 if ($type eq 'S') {
778 if ($value =~ /(maintain|support)/i) {
779 return 1;
780 }
781 }
782 }
783 }
784 return 0;
785}
786
787sub range_has_maintainer {
788 my ($start, $end) = @_;
789
790 for (my $i = $start; $i < $end; $i++) {
791 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700792 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800793 my $type = $1;
794 my $value = $2;
795 if ($type eq 'M') {
796 return 1;
797 }
798 }
799 }
800 return 0;
801}
802
Joe Perches6ef1c522010-10-26 14:22:56 -0700803sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700804 %email_hash_name = ();
805 %email_hash_address = ();
806 %commit_author_hash = ();
807 %commit_signer_hash = ();
808 @email_to = ();
809 %hash_list_to = ();
810 @list_to = ();
811 @scm = ();
812 @web = ();
813 @subsystem = ();
814 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700815 %deduplicate_name_hash = ();
816 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700817 if ($email_git_all_signature_types) {
818 $signature_pattern = "(.+?)[Bb][Yy]:";
819 } else {
820 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
821 }
822
823 # Find responsible parties
824
Joe Perchesb9e23312010-10-26 14:22:58 -0700825 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700826
Joe Perches683c6f82010-10-26 14:22:55 -0700827 foreach my $file (@files) {
828
829 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700830 my $tvi = find_first_section();
831 while ($tvi < @typevalue) {
832 my $start = find_starting_index($tvi);
833 my $end = find_ending_index($tvi);
834 my $exclude = 0;
835 my $i;
836
837 #Do not match excluded file patterns
838
839 for ($i = $start; $i < $end; $i++) {
840 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700841 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700842 my $type = $1;
843 my $value = $2;
844 if ($type eq 'X') {
845 if (file_match_pattern($file, $value)) {
846 $exclude = 1;
847 last;
848 }
849 }
850 }
851 }
852
853 if (!$exclude) {
854 for ($i = $start; $i < $end; $i++) {
855 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700856 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700857 my $type = $1;
858 my $value = $2;
859 if ($type eq 'F') {
860 if (file_match_pattern($file, $value)) {
861 my $value_pd = ($value =~ tr@/@@);
862 my $file_pd = ($file =~ tr@/@@);
863 $value_pd++ if (substr($value,-1,1) ne "/");
864 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800865 if ($value_pd >= $file_pd &&
866 range_is_maintained($start, $end) &&
867 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700868 $exact_pattern_match_hash{$file} = 1;
869 }
Joe Perches683c6f82010-10-26 14:22:55 -0700870 if ($pattern_depth == 0 ||
871 (($file_pd - $value_pd) < $pattern_depth)) {
872 $hash{$tvi} = $value_pd;
873 }
874 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700875 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800876 if ($file =~ m/$value/x) {
877 $hash{$tvi} = 0;
878 }
Joe Perches683c6f82010-10-26 14:22:55 -0700879 }
880 }
881 }
882 }
883 $tvi = $end + 1;
884 }
885
886 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
887 add_categories($line);
888 if ($sections) {
889 my $i;
890 my $start = find_starting_index($line);
891 my $end = find_ending_index($line);
892 for ($i = $start; $i < $end; $i++) {
893 my $line = $typevalue[$i];
894 if ($line =~ /^[FX]:/) { ##Restore file patterns
895 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
896 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
897 $line =~ s/\\\./\./g; ##Convert \. to .
898 $line =~ s/\.\*/\*/g; ##Convert .* to *
899 }
Joe Perches03aed212016-12-12 16:45:59 -0800900 my $count = $line =~ s/^([A-Z]):/$1:\t/g;
901 if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
902 print("$line\n");
903 }
Joe Perches683c6f82010-10-26 14:22:55 -0700904 }
905 print("\n");
906 }
907 }
Joe Perches683c6f82010-10-26 14:22:55 -0700908 }
909
910 if ($keywords) {
911 @keyword_tvi = sort_and_uniq(@keyword_tvi);
912 foreach my $line (@keyword_tvi) {
913 add_categories($line);
914 }
915 }
916
Joe Perchesb9e23312010-10-26 14:22:58 -0700917 foreach my $email (@email_to, @list_to) {
918 $email->[0] = deduplicate_email($email->[0]);
919 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700920
921 foreach my $file (@files) {
922 if ($email &&
923 ($email_git || ($email_git_fallback &&
924 !$exact_pattern_match_hash{$file}))) {
925 vcs_file_signoffs($file);
926 }
927 if ($email && $email_git_blame) {
928 vcs_file_blame($file);
929 }
930 }
931
Joe Perches683c6f82010-10-26 14:22:55 -0700932 if ($email) {
933 foreach my $chief (@penguin_chief) {
934 if ($chief =~ m/^(.*):(.*)/) {
935 my $email_address;
936
937 $email_address = format_email($1, $2, $email_usename);
938 if ($email_git_penguin_chiefs) {
939 push(@email_to, [$email_address, 'chief penguin']);
940 } else {
941 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
942 }
943 }
944 }
945
946 foreach my $email (@file_emails) {
947 my ($name, $address) = parse_email($email);
948
949 my $tmp_email = format_email($name, $address, $email_usename);
950 push_email_address($tmp_email, '');
951 add_role($tmp_email, 'in file');
952 }
953 }
954
955 my @to = ();
956 if ($email || $email_list) {
957 if ($email) {
958 @to = (@to, @email_to);
959 }
960 if ($email_list) {
961 @to = (@to, @list_to);
962 }
963 }
964
Joe Perches6ef1c522010-10-26 14:22:56 -0700965 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700966 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700967 }
Joe Perches683c6f82010-10-26 14:22:55 -0700968
969 return @to;
970}
971
Joe Perchescb7301c2009-04-07 20:40:12 -0700972sub file_match_pattern {
973 my ($file, $pattern) = @_;
974 if (substr($pattern, -1) eq "/") {
975 if ($file =~ m@^$pattern@) {
976 return 1;
977 }
978 } else {
979 if ($file =~ m@^$pattern@) {
980 my $s1 = ($file =~ tr@/@@);
981 my $s2 = ($pattern =~ tr@/@@);
982 if ($s1 == $s2) {
983 return 1;
984 }
985 }
986 }
987 return 0;
988}
989
990sub usage {
991 print <<EOT;
992usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700993 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700994version: $V
995
996MAINTAINER field selection options:
997 --email => print email address(es) if any
998 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700999 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -07001000 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -07001001 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -07001002 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -07001003 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
1004 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
1005 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -07001006 --git-blame => use git blame to find modified commits for patch or file
Brian Norris3cbcca82015-11-06 16:30:41 -08001007 --git-blame-signatures => when used with --git-blame, also include all commit signers
Joe Perchese4d26b02010-05-24 14:33:17 -07001008 --git-since => git history to use (default: $email_git_since)
1009 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -07001010 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -07001011 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001012 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -07001013 --n => include name 'Full Name <addr\@domain.tld>'
1014 --l => include list(s) if any
1015 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -07001016 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -08001017 --roles => show roles (status:subsystem, git-signer, list, etc...)
1018 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -08001019 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -07001020 --scm => print SCM tree(s) if any
1021 --status => print status if any
1022 --subsystem => print subsystem name if any
1023 --web => print website(s) if any
1024
1025Output type options:
1026 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -07001027 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -07001028 --multiline => print 1 entry per line
1029
Joe Perchescb7301c2009-04-07 20:40:12 -07001030Other options:
Joe Perches3fb55652009-09-21 17:04:17 -07001031 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -07001032 --keywords => scan patch for keywords (default: $keywords)
1033 --sections => print all of the subsystem sections with pattern matches
Joe Perches03aed212016-12-12 16:45:59 -08001034 --letters => print all matching 'letter' types from all matching sections
Joe Perchesb9e23312010-10-26 14:22:58 -07001035 --mailmap => use .mailmap file (default: $email_use_mailmap)
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -07001036 --no-tree => run without a kernel tree
Tom Saegere1f75902017-11-17 15:27:42 -08001037 --self-test => show potential issues with MAINTAINERS file content
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001038 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -07001039 --help => show this help information
1040
Joe Perches3fb55652009-09-21 17:04:17 -07001041Default options:
Antonio Nino Diaz31bb82c2018-08-21 21:56:48 -07001042 [--email --tree --nogit --git-fallback --m --r --n --l --multiline
1043 --pattern-depth=0 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -07001044
Joe Perches870020f2009-07-29 15:04:28 -07001045Notes:
1046 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -07001047 Used with "--git", git signators for _all_ files in and below
1048 directory are examined as git recurses directories.
1049 Any specified X: (exclude) pattern matches are _not_ ignored.
1050 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -08001051 no individual file within the directory or subdirectory
1052 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -07001053 Used with "--git-blame", does not iterate all files in directory
1054 Using "--git-blame" is slow and may add old committers and authors
1055 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -08001056 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
1057 other automated tools that expect only ["name"] <email address>
1058 may not work because of additional output after <email address>.
1059 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
1060 not the percentage of the entire file authored. # of commits is
1061 not a good measure of amount of code authored. 1 major commit may
1062 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -08001063 If git is not installed, but mercurial (hg) is installed and an .hg
1064 repository exists, the following options apply to mercurial:
1065 --git,
1066 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
1067 --git-blame
1068 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -07001069 File ".get_maintainer.conf", if it exists in the linux kernel source root
1070 directory, can change whatever get_maintainer defaults are desired.
1071 Entries in this file can be any command line argument.
1072 This file is prepended to any additional command line arguments.
1073 Multiple lines and # comments are allowed.
Brian Norrisb1312bf2015-11-06 16:30:46 -08001074 Most options have both positive and negative forms.
1075 The negative forms for --<foo> are --no<foo> and --no-<foo>.
1076
Joe Perchescb7301c2009-04-07 20:40:12 -07001077EOT
1078}
1079
1080sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -07001081 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001082
Joe Perches47abc722010-10-26 14:22:57 -07001083 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
1084 $lk_path .= "/";
1085 }
1086 if ( (-f "${lk_path}COPYING")
1087 && (-f "${lk_path}CREDITS")
1088 && (-f "${lk_path}Kbuild")
Joe Perches6f7d98e2017-08-04 21:45:48 -07001089 && (-e "${lk_path}MAINTAINERS")
Joe Perches47abc722010-10-26 14:22:57 -07001090 && (-f "${lk_path}Makefile")
1091 && (-f "${lk_path}README")
1092 && (-d "${lk_path}Documentation")
1093 && (-d "${lk_path}arch")
1094 && (-d "${lk_path}include")
1095 && (-d "${lk_path}drivers")
1096 && (-d "${lk_path}fs")
1097 && (-d "${lk_path}init")
1098 && (-d "${lk_path}ipc")
1099 && (-d "${lk_path}kernel")
1100 && (-d "${lk_path}lib")
1101 && (-d "${lk_path}scripts")) {
1102 return 1;
1103 }
1104 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -07001105}
1106
Joe Perches0e70e832009-09-21 17:04:20 -07001107sub parse_email {
1108 my ($formatted_email) = @_;
1109
1110 my $name = "";
1111 my $address = "";
1112
Joe Perches11ecf532009-09-21 17:04:22 -07001113 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001114 $name = $1;
1115 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -07001116 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001117 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -07001118 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001119 $address = $1;
1120 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001121
1122 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -07001123 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -07001124 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -07001125
Stephen Hemmingera63ceb42010-03-05 13:43:06 -08001126 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -07001127 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -07001128 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -07001129 }
Joe Perches0e70e832009-09-21 17:04:20 -07001130
1131 return ($name, $address);
1132}
1133
1134sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -08001135 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001136
1137 my $formatted_email;
1138
1139 $name =~ s/^\s+|\s+$//g;
1140 $name =~ s/^\"|\"$//g;
1141 $address =~ s/^\s+|\s+$//g;
1142
Stephen Hemmingera63ceb42010-03-05 13:43:06 -08001143 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -07001144 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
1145 $name = "\"$name\"";
1146 }
1147
Joe Perchesa8af2432009-12-14 18:00:49 -08001148 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -07001149 if ("$name" eq "") {
1150 $formatted_email = "$address";
1151 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -08001152 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -07001153 }
1154 } else {
1155 $formatted_email = $address;
1156 }
1157
Joe Perchescb7301c2009-04-07 20:40:12 -07001158 return $formatted_email;
1159}
1160
Joe Perches272a8972010-01-08 14:42:48 -08001161sub find_first_section {
1162 my $index = 0;
1163
1164 while ($index < @typevalue) {
1165 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001166 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -08001167 last;
1168 }
1169 $index++;
1170 }
1171
1172 return $index;
1173}
1174
Joe Perchesb7816552009-09-21 17:04:24 -07001175sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -07001176 my ($index) = @_;
1177
1178 while ($index > 0) {
1179 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001180 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001181 last;
1182 }
1183 $index--;
1184 }
1185
1186 return $index;
1187}
1188
1189sub find_ending_index {
1190 my ($index) = @_;
1191
1192 while ($index < @typevalue) {
1193 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001194 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001195 last;
1196 }
1197 $index++;
1198 }
1199
1200 return $index;
1201}
1202
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001203sub get_subsystem_name {
1204 my ($index) = @_;
1205
1206 my $start = find_starting_index($index);
1207
1208 my $subsystem = $typevalue[$start];
1209 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1210 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
1211 $subsystem =~ s/\s*$//;
1212 $subsystem = $subsystem . "...";
1213 }
1214 return $subsystem;
1215}
1216
Joe Perches3c7385b2009-12-14 18:00:46 -08001217sub get_maintainer_role {
1218 my ($index) = @_;
1219
1220 my $i;
1221 my $start = find_starting_index($index);
1222 my $end = find_ending_index($index);
1223
Joe Perches0ede2742012-03-23 15:01:56 -07001224 my $role = "unknown";
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001225 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001226
1227 for ($i = $start + 1; $i < $end; $i++) {
1228 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001229 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001230 my $ptype = $1;
1231 my $pvalue = $2;
1232 if ($ptype eq "S") {
1233 $role = $pvalue;
1234 }
1235 }
1236 }
1237
1238 $role = lc($role);
1239 if ($role eq "supported") {
1240 $role = "supporter";
1241 } elsif ($role eq "maintained") {
1242 $role = "maintainer";
1243 } elsif ($role eq "odd fixes") {
1244 $role = "odd fixer";
1245 } elsif ($role eq "orphan") {
1246 $role = "orphan minder";
1247 } elsif ($role eq "obsolete") {
1248 $role = "obsolete minder";
1249 } elsif ($role eq "buried alive in reporters") {
1250 $role = "chief penguin";
1251 }
1252
1253 return $role . ":" . $subsystem;
1254}
1255
1256sub get_list_role {
1257 my ($index) = @_;
1258
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001259 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001260
1261 if ($subsystem eq "THE REST") {
1262 $subsystem = "";
1263 }
1264
1265 return $subsystem;
1266}
1267
Joe Perchescb7301c2009-04-07 20:40:12 -07001268sub add_categories {
1269 my ($index) = @_;
1270
Joe Perchesb7816552009-09-21 17:04:24 -07001271 my $i;
1272 my $start = find_starting_index($index);
1273 my $end = find_ending_index($index);
1274
1275 push(@subsystem, $typevalue[$start]);
1276
1277 for ($i = $start + 1; $i < $end; $i++) {
1278 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001279 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001280 my $ptype = $1;
1281 my $pvalue = $2;
1282 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001283 my $list_address = $pvalue;
1284 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001285 my $list_role = get_list_role($i);
1286
1287 if ($list_role ne "") {
1288 $list_role = ":" . $list_role;
1289 }
Joe Perches290603c2009-06-16 15:33:58 -07001290 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1291 $list_address = $1;
1292 $list_additional = $2;
1293 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001294 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001295 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001296 if (!$hash_list_to{lc($list_address)}) {
1297 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001298 push(@list_to, [$list_address,
1299 "subscriber list${list_role}"]);
1300 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001301 }
1302 } else {
1303 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001304 if (!$hash_list_to{lc($list_address)}) {
1305 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001306 if ($list_additional =~ m/moderated/) {
1307 push(@list_to, [$list_address,
1308 "moderated list${list_role}"]);
1309 } else {
1310 push(@list_to, [$list_address,
1311 "open list${list_role}"]);
1312 }
Joe Perches683c6f82010-10-26 14:22:55 -07001313 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001314 }
1315 }
1316 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001317 my ($name, $address) = parse_email($pvalue);
1318 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001319 if ($i > 0) {
1320 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001321 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001322 if ($1 eq "P") {
1323 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001324 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001325 }
1326 }
1327 }
1328 }
Joe Perches0e70e832009-09-21 17:04:20 -07001329 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001330 my $role = get_maintainer_role($i);
1331 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001332 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001333 } elsif ($ptype eq "R") {
1334 my ($name, $address) = parse_email($pvalue);
1335 if ($name eq "") {
1336 if ($i > 0) {
1337 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001338 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001339 if ($1 eq "P") {
1340 $name = $2;
1341 $pvalue = format_email($name, $address, $email_usename);
1342 }
1343 }
1344 }
1345 }
1346 if ($email_reviewer) {
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001347 my $subsystem = get_subsystem_name($i);
1348 push_email_addresses($pvalue, "reviewer:$subsystem");
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001349 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001350 } elsif ($ptype eq "T") {
1351 push(@scm, $pvalue);
1352 } elsif ($ptype eq "W") {
1353 push(@web, $pvalue);
1354 } elsif ($ptype eq "S") {
1355 push(@status, $pvalue);
1356 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001357 }
1358 }
1359}
1360
Joe Perches11ecf532009-09-21 17:04:22 -07001361sub email_inuse {
1362 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001363
Joe Perches11ecf532009-09-21 17:04:22 -07001364 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001365 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1366 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001367
Joe Perches0e70e832009-09-21 17:04:20 -07001368 return 0;
1369}
1370
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001371sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001372 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001373
Joe Perches0e70e832009-09-21 17:04:20 -07001374 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001375
Joe Perchesb7816552009-09-21 17:04:24 -07001376 if ($address eq "") {
1377 return 0;
1378 }
1379
Joe Perches11ecf532009-09-21 17:04:22 -07001380 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001381 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001382 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001383 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001384 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001385 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001386 }
Joe Perchesb7816552009-09-21 17:04:24 -07001387
1388 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001389}
1390
1391sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001392 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001393
1394 my @address_list = ();
1395
Joe Perches5f2441e2009-06-16 15:34:02 -07001396 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001397 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001398 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001399 my $array_count = shift(@address_list);
1400 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001401 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001402 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001403 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001404 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001405 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1406 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001407 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001408}
1409
Joe Perches3c7385b2009-12-14 18:00:46 -08001410sub add_role {
1411 my ($line, $role) = @_;
1412
1413 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001414 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001415
1416 foreach my $entry (@email_to) {
1417 if ($email_remove_duplicates) {
1418 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001419 if (($name eq $entry_name || $address eq $entry_address)
1420 && ($role eq "" || !($entry->[1] =~ m/$role/))
1421 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001422 if ($entry->[1] eq "") {
1423 $entry->[1] = "$role";
1424 } else {
1425 $entry->[1] = "$entry->[1],$role";
1426 }
1427 }
1428 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001429 if ($email eq $entry->[0]
1430 && ($role eq "" || !($entry->[1] =~ m/$role/))
1431 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001432 if ($entry->[1] eq "") {
1433 $entry->[1] = "$role";
1434 } else {
1435 $entry->[1] = "$entry->[1],$role";
1436 }
1437 }
1438 }
1439 }
1440}
1441
Joe Perchescb7301c2009-04-07 20:40:12 -07001442sub which {
1443 my ($bin) = @_;
1444
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001445 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001446 if (-e "$path/$bin") {
1447 return "$path/$bin";
1448 }
1449 }
1450
1451 return "";
1452}
1453
Joe Perchesbcde44e2010-10-26 14:22:53 -07001454sub which_conf {
1455 my ($conf) = @_;
1456
1457 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1458 if (-e "$path/$conf") {
1459 return "$path/$conf";
1460 }
1461 }
1462
1463 return "";
1464}
1465
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001466sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001467 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001468
Joe Perches47abc722010-10-26 14:22:57 -07001469 my ($name, $address) = parse_email($line);
1470 my $email = format_email($name, $address, 1);
1471 my $real_name = $name;
1472 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001473
Joe Perches47abc722010-10-26 14:22:57 -07001474 if (exists $mailmap->{names}->{$email} ||
1475 exists $mailmap->{addresses}->{$email}) {
1476 if (exists $mailmap->{names}->{$email}) {
1477 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001478 }
Joe Perches47abc722010-10-26 14:22:57 -07001479 if (exists $mailmap->{addresses}->{$email}) {
1480 $real_address = $mailmap->{addresses}->{$email};
1481 }
1482 } else {
1483 if (exists $mailmap->{names}->{$address}) {
1484 $real_name = $mailmap->{names}->{$address};
1485 }
1486 if (exists $mailmap->{addresses}->{$address}) {
1487 $real_address = $mailmap->{addresses}->{$address};
1488 }
1489 }
1490 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001491}
1492
1493sub mailmap {
1494 my (@addresses) = @_;
1495
Joe Perchesb9e23312010-10-26 14:22:58 -07001496 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001497 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001498 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001499 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001500 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1501 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001502}
1503
1504sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001505 my %address_map;
1506 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001507
Joe Perches47abc722010-10-26 14:22:57 -07001508 foreach my $email (@emails) {
1509 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001510 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001511 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001512 $email = format_email($name, $address, 1);
1513 } else {
1514 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001515 }
Joe Perches47abc722010-10-26 14:22:57 -07001516 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001517}
1518
Joe Perches60db31a2009-12-14 18:00:50 -08001519sub git_execute_cmd {
1520 my ($cmd) = @_;
1521 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001522
Joe Perches60db31a2009-12-14 18:00:50 -08001523 my $output = `$cmd`;
1524 $output =~ s/^\s*//gm;
1525 @lines = split("\n", $output);
1526
1527 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001528}
1529
Joe Perches60db31a2009-12-14 18:00:50 -08001530sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001531 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001532 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001533
Joe Perches60db31a2009-12-14 18:00:50 -08001534 my $output = `$cmd`;
1535 @lines = split("\n", $output);
1536
1537 return @lines;
1538}
1539
Joe Perches683c6f82010-10-26 14:22:55 -07001540sub extract_formatted_signatures {
1541 my (@signature_lines) = @_;
1542
1543 my @type = @signature_lines;
1544
1545 s/\s*(.*):.*/$1/ for (@type);
1546
1547 # cut -f2- -d":"
1548 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1549
1550## Reformat email addresses (with names) to avoid badly written signatures
1551
1552 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001553 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001554 }
1555
1556 return (\@type, \@signature_lines);
1557}
1558
Joe Perches60db31a2009-12-14 18:00:50 -08001559sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001560 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001561 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001562 my @lines = ();
1563 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001564 my @authors = ();
1565 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001566
Joe Perches60db31a2009-12-14 18:00:50 -08001567 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001568
Joe Perches60db31a2009-12-14 18:00:50 -08001569 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001570 my $author_pattern = $VCS_cmds{"author_pattern"};
1571 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1572
1573 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001574
Joe Perches60db31a2009-12-14 18:00:50 -08001575 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001576
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001577 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001578 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001579 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001580
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001581# print("stats: <@stats>\n");
1582
1583 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001584
1585 save_commits_by_author(@lines) if ($interactive);
1586 save_commits_by_signer(@lines) if ($interactive);
1587
Joe Perches0e70e832009-09-21 17:04:20 -07001588 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001589 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001590 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001591
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001592 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001593 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001594
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001595 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001596}
1597
Joe Perches63ab52d2010-10-26 14:22:51 -07001598sub vcs_find_author {
1599 my ($cmd) = @_;
1600 my @lines = ();
1601
1602 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1603
1604 if (!$email_git_penguin_chiefs) {
1605 @lines = grep(!/${penguin_chiefs}/i, @lines);
1606 }
1607
1608 return @lines if !@lines;
1609
Joe Perches683c6f82010-10-26 14:22:55 -07001610 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001611 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001612 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1613 my $author = $1;
1614 my ($name, $address) = parse_email($author);
1615 $author = format_email($name, $address, 1);
1616 push(@authors, $author);
1617 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001618 }
1619
Joe Perches683c6f82010-10-26 14:22:55 -07001620 save_commits_by_author(@lines) if ($interactive);
1621 save_commits_by_signer(@lines) if ($interactive);
1622
1623 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001624}
1625
Joe Perches60db31a2009-12-14 18:00:50 -08001626sub vcs_save_commits {
1627 my ($cmd) = @_;
1628 my @lines = ();
1629 my @commits = ();
1630
1631 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1632
1633 foreach my $line (@lines) {
1634 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1635 push(@commits, $1);
1636 }
1637 }
1638
1639 return @commits;
1640}
1641
1642sub vcs_blame {
1643 my ($file) = @_;
1644 my $cmd;
1645 my @commits = ();
1646
1647 return @commits if (!(-f $file));
1648
1649 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1650 my @all_commits = ();
1651
1652 $cmd = $VCS_cmds{"blame_file_cmd"};
1653 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1654 @all_commits = vcs_save_commits($cmd);
1655
1656 foreach my $file_range_diff (@range) {
1657 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1658 my $diff_file = $1;
1659 my $diff_start = $2;
1660 my $diff_length = $3;
1661 next if ("$file" ne "$diff_file");
1662 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1663 push(@commits, $all_commits[$i]);
1664 }
1665 }
1666 } elsif (@range) {
1667 foreach my $file_range_diff (@range) {
1668 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1669 my $diff_file = $1;
1670 my $diff_start = $2;
1671 my $diff_length = $3;
1672 next if ("$file" ne "$diff_file");
1673 $cmd = $VCS_cmds{"blame_range_cmd"};
1674 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1675 push(@commits, vcs_save_commits($cmd));
1676 }
1677 } else {
1678 $cmd = $VCS_cmds{"blame_file_cmd"};
1679 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1680 @commits = vcs_save_commits($cmd);
1681 }
1682
Joe Perches63ab52d2010-10-26 14:22:51 -07001683 foreach my $commit (@commits) {
1684 $commit =~ s/^\^//g;
1685 }
1686
Joe Perches60db31a2009-12-14 18:00:50 -08001687 return @commits;
1688}
1689
1690my $printed_novcs = 0;
1691sub vcs_exists {
1692 %VCS_cmds = %VCS_cmds_git;
1693 return 1 if eval $VCS_cmds{"available"};
1694 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001695 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001696 %VCS_cmds = ();
1697 if (!$printed_novcs) {
1698 warn("$P: No supported VCS found. Add --nogit to options?\n");
1699 warn("Using a git repository produces better results.\n");
1700 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001701 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001702 $printed_novcs = 1;
1703 }
1704 return 0;
1705}
1706
Joe Perches683c6f82010-10-26 14:22:55 -07001707sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001708 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001709 return $vcs_used == 1;
1710}
1711
1712sub vcs_is_hg {
1713 return $vcs_used == 2;
1714}
1715
Joe Perches6ef1c522010-10-26 14:22:56 -07001716sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001717 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001718 my @list = @$list_ref;
1719
Joe Perches683c6f82010-10-26 14:22:55 -07001720 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001721
1722 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001723 my %authored;
1724 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001725 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001726 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001727 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001728 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1729 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001730 $authored{$count} = 0;
1731 $signed{$count} = 0;
1732 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001733 }
1734
1735 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001736 my $done = 0;
1737 my $print_options = 0;
1738 my $redraw = 1;
1739 while (!$done) {
1740 $count = 0;
1741 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001742 printf STDERR "\n%1s %2s %-65s",
1743 "*", "#", "email/list and role:stats";
1744 if ($email_git ||
1745 ($email_git_fallback && !$maintained) ||
1746 $email_git_blame) {
1747 print STDERR "auth sign";
1748 }
1749 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001750 foreach my $entry (@list) {
1751 my $email = $entry->[0];
1752 my $role = $entry->[1];
1753 my $sel = "";
1754 $sel = "*" if ($selected{$count});
1755 my $commit_author = $commit_author_hash{$email};
1756 my $commit_signer = $commit_signer_hash{$email};
1757 my $authored = 0;
1758 my $signed = 0;
1759 $authored++ for (@{$commit_author});
1760 $signed++ for (@{$commit_signer});
1761 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1762 printf STDERR "%4d %4d", $authored, $signed
1763 if ($authored > 0 || $signed > 0);
1764 printf STDERR "\n %s\n", $role;
1765 if ($authored{$count}) {
1766 my $commit_author = $commit_author_hash{$email};
1767 foreach my $ref (@{$commit_author}) {
1768 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001769 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001770 }
Joe Perches683c6f82010-10-26 14:22:55 -07001771 if ($signed{$count}) {
1772 my $commit_signer = $commit_signer_hash{$email};
1773 foreach my $ref (@{$commit_signer}) {
1774 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1775 }
1776 }
1777
1778 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001779 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001780 }
Joe Perches683c6f82010-10-26 14:22:55 -07001781 my $date_ref = \$email_git_since;
1782 $date_ref = \$email_hg_since if (vcs_is_hg());
1783 if ($print_options) {
1784 $print_options = 0;
1785 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001786 print STDERR <<EOT
1787
1788Version Control options:
1789g use git history [$email_git]
1790gf use git-fallback [$email_git_fallback]
1791b use git blame [$email_git_blame]
1792bs use blame signatures [$email_git_blame_signatures]
1793c# minimum commits [$email_git_min_signatures]
1794%# min percent [$email_git_min_percent]
1795d# history to use [$$date_ref]
1796x# max maintainers [$email_git_max_maintainers]
1797t all signature types [$email_git_all_signature_types]
1798m use .mailmap [$email_use_mailmap]
1799EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001800 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001801 print STDERR <<EOT
1802
1803Additional options:
18040 toggle all
1805tm toggle maintainers
1806tg toggle git entries
1807tl toggle open list entries
1808ts toggle subscriber list entries
1809f emails in file [$file_emails]
1810k keywords in file [$keywords]
1811r remove duplicates [$email_remove_duplicates]
1812p# pattern match depth [$pattern_depth]
1813EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001814 }
1815 print STDERR
1816"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1817
1818 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001819 chomp($input);
1820
Joe Perches683c6f82010-10-26 14:22:55 -07001821 $redraw = 1;
1822 my $rerun = 0;
1823 my @wish = split(/[, ]+/, $input);
1824 foreach my $nr (@wish) {
1825 $nr = lc($nr);
1826 my $sel = substr($nr, 0, 1);
1827 my $str = substr($nr, 1);
1828 my $val = 0;
1829 $val = $1 if $str =~ /^(\d+)$/;
1830
1831 if ($sel eq "y") {
1832 $interactive = 0;
1833 $done = 1;
1834 $output_rolestats = 0;
1835 $output_roles = 0;
1836 last;
1837 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1838 $selected{$nr - 1} = !$selected{$nr - 1};
1839 } elsif ($sel eq "*" || $sel eq '^') {
1840 my $toggle = 0;
1841 $toggle = 1 if ($sel eq '*');
1842 for (my $i = 0; $i < $count; $i++) {
1843 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001844 }
Joe Perches683c6f82010-10-26 14:22:55 -07001845 } elsif ($sel eq "0") {
1846 for (my $i = 0; $i < $count; $i++) {
1847 $selected{$i} = !$selected{$i};
1848 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001849 } elsif ($sel eq "t") {
1850 if (lc($str) eq "m") {
1851 for (my $i = 0; $i < $count; $i++) {
1852 $selected{$i} = !$selected{$i}
1853 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1854 }
1855 } elsif (lc($str) eq "g") {
1856 for (my $i = 0; $i < $count; $i++) {
1857 $selected{$i} = !$selected{$i}
1858 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1859 }
1860 } elsif (lc($str) eq "l") {
1861 for (my $i = 0; $i < $count; $i++) {
1862 $selected{$i} = !$selected{$i}
1863 if ($list[$i]->[1] =~ /^(open list)/i);
1864 }
1865 } elsif (lc($str) eq "s") {
1866 for (my $i = 0; $i < $count; $i++) {
1867 $selected{$i} = !$selected{$i}
1868 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1869 }
1870 }
Joe Perches683c6f82010-10-26 14:22:55 -07001871 } elsif ($sel eq "a") {
1872 if ($val > 0 && $val <= $count) {
1873 $authored{$val - 1} = !$authored{$val - 1};
1874 } elsif ($str eq '*' || $str eq '^') {
1875 my $toggle = 0;
1876 $toggle = 1 if ($str eq '*');
1877 for (my $i = 0; $i < $count; $i++) {
1878 $authored{$i} = $toggle;
1879 }
1880 }
1881 } elsif ($sel eq "s") {
1882 if ($val > 0 && $val <= $count) {
1883 $signed{$val - 1} = !$signed{$val - 1};
1884 } elsif ($str eq '*' || $str eq '^') {
1885 my $toggle = 0;
1886 $toggle = 1 if ($str eq '*');
1887 for (my $i = 0; $i < $count; $i++) {
1888 $signed{$i} = $toggle;
1889 }
1890 }
1891 } elsif ($sel eq "o") {
1892 $print_options = 1;
1893 $redraw = 1;
1894 } elsif ($sel eq "g") {
1895 if ($str eq "f") {
1896 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001897 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001898 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001899 }
Joe Perches683c6f82010-10-26 14:22:55 -07001900 $rerun = 1;
1901 } elsif ($sel eq "b") {
1902 if ($str eq "s") {
1903 bool_invert(\$email_git_blame_signatures);
1904 } else {
1905 bool_invert(\$email_git_blame);
1906 }
1907 $rerun = 1;
1908 } elsif ($sel eq "c") {
1909 if ($val > 0) {
1910 $email_git_min_signatures = $val;
1911 $rerun = 1;
1912 }
1913 } elsif ($sel eq "x") {
1914 if ($val > 0) {
1915 $email_git_max_maintainers = $val;
1916 $rerun = 1;
1917 }
1918 } elsif ($sel eq "%") {
1919 if ($str ne "" && $val >= 0) {
1920 $email_git_min_percent = $val;
1921 $rerun = 1;
1922 }
1923 } elsif ($sel eq "d") {
1924 if (vcs_is_git()) {
1925 $email_git_since = $str;
1926 } elsif (vcs_is_hg()) {
1927 $email_hg_since = $str;
1928 }
1929 $rerun = 1;
1930 } elsif ($sel eq "t") {
1931 bool_invert(\$email_git_all_signature_types);
1932 $rerun = 1;
1933 } elsif ($sel eq "f") {
1934 bool_invert(\$file_emails);
1935 $rerun = 1;
1936 } elsif ($sel eq "r") {
1937 bool_invert(\$email_remove_duplicates);
1938 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001939 } elsif ($sel eq "m") {
1940 bool_invert(\$email_use_mailmap);
1941 read_mailmap();
1942 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001943 } elsif ($sel eq "k") {
1944 bool_invert(\$keywords);
1945 $rerun = 1;
1946 } elsif ($sel eq "p") {
1947 if ($str ne "" && $val >= 0) {
1948 $pattern_depth = $val;
1949 $rerun = 1;
1950 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001951 } elsif ($sel eq "h" || $sel eq "?") {
1952 print STDERR <<EOT
1953
1954Interactive mode allows you to select the various maintainers, submitters,
1955commit signers and mailing lists that could be CC'd on a patch.
1956
1957Any *'d entry is selected.
1958
Joe Perches47abc722010-10-26 14:22:57 -07001959If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001960history of files in the patch. Also, each line of the current file can
1961be matched to its commit author and that commits signers with blame.
1962
1963Various knobs exist to control the length of time for active commit
1964tracking, the maximum number of commit authors and signers to add,
1965and such.
1966
1967Enter selections at the prompt until you are satisfied that the selected
1968maintainers are appropriate. You may enter multiple selections separated
1969by either commas or spaces.
1970
1971EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001972 } else {
1973 print STDERR "invalid option: '$nr'\n";
1974 $redraw = 0;
1975 }
1976 }
1977 if ($rerun) {
1978 print STDERR "git-blame can be very slow, please have patience..."
1979 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001980 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001981 }
1982 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001983
1984 #drop not selected entries
1985 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001986 my @new_emailto = ();
1987 foreach my $entry (@list) {
1988 if ($selected{$count}) {
1989 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001990 }
1991 $count++;
1992 }
Joe Perches683c6f82010-10-26 14:22:55 -07001993 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001994}
1995
Joe Perches683c6f82010-10-26 14:22:55 -07001996sub bool_invert {
1997 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001998
Joe Perches683c6f82010-10-26 14:22:55 -07001999 if ($$bool_ref) {
2000 $$bool_ref = 0;
2001 } else {
2002 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07002003 }
Florian Micklerdace8e32010-10-26 14:22:54 -07002004}
2005
Joe Perchesb9e23312010-10-26 14:22:58 -07002006sub deduplicate_email {
2007 my ($email) = @_;
2008
2009 my $matched = 0;
2010 my ($name, $address) = parse_email($email);
2011 $email = format_email($name, $address, 1);
2012 $email = mailmap_email($email);
2013
2014 return $email if (!$email_remove_duplicates);
2015
2016 ($name, $address) = parse_email($email);
2017
Joe Perchesfae99202010-10-26 14:22:58 -07002018 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002019 $name = $deduplicate_name_hash{lc($name)}->[0];
2020 $address = $deduplicate_name_hash{lc($name)}->[1];
2021 $matched = 1;
2022 } elsif ($deduplicate_address_hash{lc($address)}) {
2023 $name = $deduplicate_address_hash{lc($address)}->[0];
2024 $address = $deduplicate_address_hash{lc($address)}->[1];
2025 $matched = 1;
2026 }
2027 if (!$matched) {
2028 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
2029 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
2030 }
2031 $email = format_email($name, $address, 1);
2032 $email = mailmap_email($email);
2033 return $email;
2034}
2035
Joe Perches683c6f82010-10-26 14:22:55 -07002036sub save_commits_by_author {
2037 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07002038
Joe Perches683c6f82010-10-26 14:22:55 -07002039 my @authors = ();
2040 my @commits = ();
2041 my @subjects = ();
2042
2043 foreach my $line (@lines) {
2044 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2045 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002046 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07002047 push(@authors, $author);
2048 }
2049 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
2050 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
2051 }
2052
2053 for (my $i = 0; $i < @authors; $i++) {
2054 my $exists = 0;
2055 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
2056 if (@{$ref}[0] eq $commits[$i] &&
2057 @{$ref}[1] eq $subjects[$i]) {
2058 $exists = 1;
2059 last;
2060 }
2061 }
2062 if (!$exists) {
2063 push(@{$commit_author_hash{$authors[$i]}},
2064 [ ($commits[$i], $subjects[$i]) ]);
2065 }
2066 }
2067}
2068
2069sub save_commits_by_signer {
2070 my (@lines) = @_;
2071
2072 my $commit = "";
2073 my $subject = "";
2074
2075 foreach my $line (@lines) {
2076 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
2077 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
2078 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
2079 my @signatures = ($line);
2080 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
2081 my @types = @$types_ref;
2082 my @signers = @$signers_ref;
2083
2084 my $type = $types[0];
2085 my $signer = $signers[0];
2086
Joe Perchesb9e23312010-10-26 14:22:58 -07002087 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07002088
Joe Perches683c6f82010-10-26 14:22:55 -07002089 my $exists = 0;
2090 foreach my $ref(@{$commit_signer_hash{$signer}}) {
2091 if (@{$ref}[0] eq $commit &&
2092 @{$ref}[1] eq $subject &&
2093 @{$ref}[2] eq $type) {
2094 $exists = 1;
2095 last;
2096 }
2097 }
2098 if (!$exists) {
2099 push(@{$commit_signer_hash{$signer}},
2100 [ ($commit, $subject, $type) ]);
2101 }
2102 }
2103 }
Florian Micklerdace8e32010-10-26 14:22:54 -07002104}
2105
Joe Perches60db31a2009-12-14 18:00:50 -08002106sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08002107 my ($role, $divisor, @lines) = @_;
2108
2109 my %hash;
2110 my $count = 0;
2111
Joe Perchesa8af2432009-12-14 18:00:49 -08002112 return if (@lines <= 0);
2113
2114 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08002115 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08002116 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08002117 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07002118
Florian Mickler7fa8ff22010-10-26 14:22:56 -07002119 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07002120
Joe Perches63ab52d2010-10-26 14:22:51 -07002121 return if (@lines <= 0);
2122
Joe Perches0e70e832009-09-21 17:04:20 -07002123 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07002124
Joe Perches11ecf532009-09-21 17:04:22 -07002125 # uniq -c
2126 $hash{$_}++ for @lines;
2127
2128 # sort -rn
2129 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
2130 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08002131 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08002132
Joe Perchesa8af2432009-12-14 18:00:49 -08002133 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07002134 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07002135 $count++;
2136 last if ($sign_offs < $email_git_min_signatures ||
2137 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08002138 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08002139 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08002140 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08002141 my $fmt_percent = sprintf("%.0f", $percent);
2142 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
2143 } else {
2144 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08002145 }
Joe Perchesf5492662009-09-21 17:04:13 -07002146 }
2147}
2148
Joe Perches60db31a2009-12-14 18:00:50 -08002149sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08002150 my ($file) = @_;
2151
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002152 my $authors_ref;
2153 my $signers_ref;
2154 my $stats_ref;
2155 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002156 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002157 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08002158 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002159
Joe Perches683c6f82010-10-26 14:22:55 -07002160 $vcs_used = vcs_exists();
2161 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08002162
Joe Perches60db31a2009-12-14 18:00:50 -08002163 my $cmd = $VCS_cmds{"find_signers_cmd"};
2164 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2165
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002166 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2167
2168 @signers = @{$signers_ref} if defined $signers_ref;
2169 @authors = @{$authors_ref} if defined $authors_ref;
2170 @stats = @{$stats_ref} if defined $stats_ref;
2171
2172# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07002173
2174 foreach my $signer (@signers) {
2175 $signer = deduplicate_email($signer);
2176 }
2177
Joe Perches60db31a2009-12-14 18:00:50 -08002178 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002179 vcs_assign("authored", $commits, @authors);
2180 if ($#authors == $#stats) {
2181 my $stat_pattern = $VCS_cmds{"stat_pattern"};
2182 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
2183
2184 my $added = 0;
2185 my $deleted = 0;
2186 for (my $i = 0; $i <= $#stats; $i++) {
2187 if ($stats[$i] =~ /$stat_pattern/) {
2188 $added += $1;
2189 $deleted += $2;
2190 }
2191 }
2192 my @tmp_authors = uniq(@authors);
2193 foreach my $author (@tmp_authors) {
2194 $author = deduplicate_email($author);
2195 }
2196 @tmp_authors = uniq(@tmp_authors);
2197 my @list_added = ();
2198 my @list_deleted = ();
2199 foreach my $author (@tmp_authors) {
2200 my $auth_added = 0;
2201 my $auth_deleted = 0;
2202 for (my $i = 0; $i <= $#stats; $i++) {
2203 if ($author eq deduplicate_email($authors[$i]) &&
2204 $stats[$i] =~ /$stat_pattern/) {
2205 $auth_added += $1;
2206 $auth_deleted += $2;
2207 }
2208 }
2209 for (my $i = 0; $i < $auth_added; $i++) {
2210 push(@list_added, $author);
2211 }
2212 for (my $i = 0; $i < $auth_deleted; $i++) {
2213 push(@list_deleted, $author);
2214 }
2215 }
2216 vcs_assign("added_lines", $added, @list_added);
2217 vcs_assign("removed_lines", $deleted, @list_deleted);
2218 }
Joe Perchesa8af2432009-12-14 18:00:49 -08002219}
2220
Joe Perches60db31a2009-12-14 18:00:50 -08002221sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07002222 my ($file) = @_;
2223
Joe Perches60db31a2009-12-14 18:00:50 -08002224 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07002225 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002226 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002227 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002228 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002229
Joe Perches683c6f82010-10-26 14:22:55 -07002230 $vcs_used = vcs_exists();
2231 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002232
Joe Perches63ab52d2010-10-26 14:22:51 -07002233 @all_commits = vcs_blame($file);
2234 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002235 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002236 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002237
Joe Perches683c6f82010-10-26 14:22:55 -07002238 if ($email_git_blame_signatures) {
2239 if (vcs_is_hg()) {
2240 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002241 my $commit_authors_ref;
2242 my $commit_signers_ref;
2243 my $stats_ref;
2244 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002245 my @commit_signers = ();
2246 my $commit = join(" -r ", @commits);
2247 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002248
Joe Perches683c6f82010-10-26 14:22:55 -07002249 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2250 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002251
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002252 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2253 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2254 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002255
Joe Perches683c6f82010-10-26 14:22:55 -07002256 push(@signers, @commit_signers);
2257 } else {
2258 foreach my $commit (@commits) {
2259 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002260 my $commit_authors_ref;
2261 my $commit_signers_ref;
2262 my $stats_ref;
2263 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002264 my @commit_signers = ();
2265 my $cmd;
2266
2267 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2268 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2269
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002270 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2271 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2272 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002273
2274 push(@signers, @commit_signers);
2275 }
2276 }
Joe Perchesf5492662009-09-21 17:04:13 -07002277 }
2278
Joe Perchesa8af2432009-12-14 18:00:49 -08002279 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002280 if ($output_rolestats) {
2281 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002282 if (vcs_is_hg()) {{ # Double brace for last exit
2283 my $commit_count;
2284 my @commit_signers = ();
2285 @commits = uniq(@commits);
2286 @commits = sort(@commits);
2287 my $commit = join(" -r ", @commits);
2288 my $cmd;
2289
2290 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2291 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2292
2293 my @lines = ();
2294
2295 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2296
2297 if (!$email_git_penguin_chiefs) {
2298 @lines = grep(!/${penguin_chiefs}/i, @lines);
2299 }
2300
2301 last if !@lines;
2302
2303 my @authors = ();
2304 foreach my $line (@lines) {
2305 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2306 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002307 $author = deduplicate_email($author);
2308 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002309 }
2310 }
2311
2312 save_commits_by_author(@lines) if ($interactive);
2313 save_commits_by_signer(@lines) if ($interactive);
2314
2315 push(@signers, @authors);
2316 }}
2317 else {
2318 foreach my $commit (@commits) {
2319 my $i;
2320 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2321 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2322 my @author = vcs_find_author($cmd);
2323 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002324
2325 my $formatted_author = deduplicate_email($author[0]);
2326
Joe Perches683c6f82010-10-26 14:22:55 -07002327 my $count = grep(/$commit/, @all_commits);
2328 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002329 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002330 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002331 }
2332 }
2333 if (@blame_signers) {
2334 vcs_assign("authored lines", $total_lines, @blame_signers);
2335 }
2336 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002337 foreach my $signer (@signers) {
2338 $signer = deduplicate_email($signer);
2339 }
Joe Perches60db31a2009-12-14 18:00:50 -08002340 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002341 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002342 foreach my $signer (@signers) {
2343 $signer = deduplicate_email($signer);
2344 }
Joe Perches60db31a2009-12-14 18:00:50 -08002345 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002346 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002347}
2348
Joe Perches4cad35a2016-08-02 14:04:10 -07002349sub vcs_file_exists {
2350 my ($file) = @_;
2351
2352 my $exists;
2353
2354 my $vcs_used = vcs_exists();
2355 return 0 if (!$vcs_used);
2356
2357 my $cmd = $VCS_cmds{"file_exists_cmd"};
2358 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
Joe Perches8582fb52016-08-25 15:16:48 -07002359 $cmd .= " 2>&1";
Joe Perches4cad35a2016-08-02 14:04:10 -07002360 $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
2361
Joe Perches8582fb52016-08-25 15:16:48 -07002362 return 0 if ($? != 0);
2363
Joe Perches4cad35a2016-08-02 14:04:10 -07002364 return $exists;
2365}
2366
Tom Saegere1f75902017-11-17 15:27:42 -08002367sub vcs_list_files {
2368 my ($file) = @_;
2369
2370 my @lsfiles = ();
2371
2372 my $vcs_used = vcs_exists();
2373 return 0 if (!$vcs_used);
2374
2375 my $cmd = $VCS_cmds{"list_files_cmd"};
2376 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2377 @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
2378
2379 return () if ($? != 0);
2380
2381 return @lsfiles;
2382}
2383
Joe Perchescb7301c2009-04-07 20:40:12 -07002384sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002385 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002386
2387 my %saw;
2388 @parms = grep(!$saw{$_}++, @parms);
2389 return @parms;
2390}
2391
2392sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002393 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002394
2395 my %saw;
2396 @parms = sort @parms;
2397 @parms = grep(!$saw{$_}++, @parms);
2398 return @parms;
2399}
2400
Joe Perches03372db2010-03-05 13:43:00 -08002401sub clean_file_emails {
2402 my (@file_emails) = @_;
2403 my @fmt_emails = ();
2404
2405 foreach my $email (@file_emails) {
2406 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2407 my ($name, $address) = parse_email($email);
2408 if ($name eq '"[,\.]"') {
2409 $name = "";
2410 }
2411
2412 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2413 if (@nw > 2) {
2414 my $first = $nw[@nw - 3];
2415 my $middle = $nw[@nw - 2];
2416 my $last = $nw[@nw - 1];
2417
2418 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2419 (length($first) == 2 && substr($first, -1) eq ".")) ||
2420 (length($middle) == 1 ||
2421 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2422 $name = "$first $middle $last";
2423 } else {
2424 $name = "$middle $last";
2425 }
2426 }
2427
2428 if (substr($name, -1) =~ /[,\.]/) {
2429 $name = substr($name, 0, length($name) - 1);
2430 } elsif (substr($name, -2) =~ /[,\.]"/) {
2431 $name = substr($name, 0, length($name) - 2) . '"';
2432 }
2433
2434 if (substr($name, 0, 1) =~ /[,\.]/) {
2435 $name = substr($name, 1, length($name) - 1);
2436 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2437 $name = '"' . substr($name, 2, length($name) - 2);
2438 }
2439
2440 my $fmt_email = format_email($name, $address, $email_usename);
2441 push(@fmt_emails, $fmt_email);
2442 }
2443 return @fmt_emails;
2444}
2445
Joe Perches3c7385b2009-12-14 18:00:46 -08002446sub merge_email {
2447 my @lines;
2448 my %saw;
2449
2450 for (@_) {
2451 my ($address, $role) = @$_;
2452 if (!$saw{$address}) {
2453 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002454 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002455 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002456 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002457 }
2458 $saw{$address} = 1;
2459 }
2460 }
2461
2462 return @lines;
2463}
2464
Joe Perchescb7301c2009-04-07 20:40:12 -07002465sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002466 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002467
2468 if ($output_multiline) {
2469 foreach my $line (@parms) {
2470 print("${line}\n");
2471 }
2472 } else {
2473 print(join($output_separator, @parms));
2474 print("\n");
2475 }
2476}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002477
2478my $rfc822re;
2479
2480sub make_rfc822re {
2481# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2482# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2483# This regexp will only work on addresses which have had comments stripped
2484# and replaced with rfc822_lwsp.
2485
2486 my $specials = '()<>@,;:\\\\".\\[\\]';
2487 my $controls = '\\000-\\037\\177';
2488
2489 my $dtext = "[^\\[\\]\\r\\\\]";
2490 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2491
2492 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2493
2494# Use zero-width assertion to spot the limit of an atom. A simple
2495# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2496 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2497 my $word = "(?:$atom|$quoted_string)";
2498 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2499
2500 my $sub_domain = "(?:$atom|$domain_literal)";
2501 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2502
2503 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2504
2505 my $phrase = "$word*";
2506 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2507 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2508 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2509
2510 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2511 my $address = "(?:$mailbox|$group)";
2512
2513 return "$rfc822_lwsp*$address";
2514}
2515
2516sub rfc822_strip_comments {
2517 my $s = shift;
2518# Recursively remove comments, and replace with a single space. The simpler
2519# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2520# chars in atoms, for example.
2521
2522 while ($s =~ s/^((?:[^"\\]|\\.)*
2523 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2524 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2525 return $s;
2526}
2527
2528# valid: returns true if the parameter is an RFC822 valid address
2529#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002530sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002531 my $s = rfc822_strip_comments(shift);
2532
2533 if (!$rfc822re) {
2534 $rfc822re = make_rfc822re();
2535 }
2536
2537 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2538}
2539
2540# validlist: In scalar context, returns true if the parameter is an RFC822
2541# valid list of addresses.
2542#
2543# In list context, returns an empty list on failure (an invalid
2544# address was found); otherwise a list whose first element is the
2545# number of addresses found and whose remaining elements are the
2546# addresses. This is needed to disambiguate failure (invalid)
2547# from success with no addresses found, because an empty string is
2548# a valid list.
2549
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002550sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002551 my $s = rfc822_strip_comments(shift);
2552
2553 if (!$rfc822re) {
2554 $rfc822re = make_rfc822re();
2555 }
2556 # * null list items are valid according to the RFC
2557 # * the '1' business is to aid in distinguishing failure from no results
2558
2559 my @r;
2560 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2561 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002562 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002563 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002564 }
2565 return wantarray ? (scalar(@r), @r) : 1;
2566 }
Joe Perches60db31a2009-12-14 18:00:50 -08002567 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002568}