blob: c68a5d1ba709e7194c10d24cb447d55b4b5fa1cf [file] [log] [blame]
Kamil Rytarowskicb77f0d2017-05-07 23:25:26 +02001#!/usr/bin/env perl
Joe Perchescb7301c2009-04-07 20:40:12 -07002# (c) 2007, Joe Perches <joe@perches.com>
3# created from checkpatch.pl
4#
5# Print selected MAINTAINERS information for
6# the files modified in a patch or for a file
7#
Roel Kluin3bd7bf52009-11-11 14:26:13 -08008# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
9# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
Joe Perchescb7301c2009-04-07 20:40:12 -070010#
11# Licensed under the terms of the GNU GPL License version 2
12
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;
51my $web = 0;
52my $subsystem = 0;
53my $status = 0;
Joe Perches03aed212016-12-12 16:45:59 -080054my $letters = "";
Joe Perchesdcf36a92009-10-26 16:49:47 -070055my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080056my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080057my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070058my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070059my $pattern_depth = 0;
Tom Saegere1f75902017-11-17 15:27:42 -080060my $self_test = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070061my $version = 0;
62my $help = 0;
Joe Perches6f7d98e2017-08-04 21:45:48 -070063my $find_maintainer_files = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070064
Joe Perches683c6f82010-10-26 14:22:55 -070065my $vcs_used = 0;
66
Joe Perchescb7301c2009-04-07 20:40:12 -070067my $exit = 0;
68
Joe Perches683c6f82010-10-26 14:22:55 -070069my %commit_author_hash;
70my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070071
Joe Perchescb7301c2009-04-07 20:40:12 -070072my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070073push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070074#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070075#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070076
77my @penguin_chief_names = ();
78foreach my $chief (@penguin_chief) {
79 if ($chief =~ m/^(.*):(.*)/) {
80 my $chief_name = $1;
81 my $chief_addr = $2;
82 push(@penguin_chief_names, $chief_name);
83 }
84}
Joe Perchese4d26b02010-05-24 14:33:17 -070085my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
86
87# Signature types of people who are either
88# a) responsible for the code in question, or
89# b) familiar enough with it to give relevant feedback
90my @signature_tags = ();
91push(@signature_tags, "Signed-off-by:");
92push(@signature_tags, "Reviewed-by:");
93push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070094
Joe Perches7dea2682012-06-20 12:53:02 -070095my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
96
Joe Perches5f2441e2009-06-16 15:34:02 -070097# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070098my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070099my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700100
Joe Perches60db31a2009-12-14 18:00:50 -0800101# VCS command support: class-like functions and strings
102
103my %VCS_cmds;
104
105my %VCS_cmds_git = (
106 "execute_cmd" => \&git_execute_cmd,
Richard Genoudec83b612014-02-10 14:25:31 -0800107 "available" => '(which("git") ne "") && (-e ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -0700108 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800109 "git log --no-color --follow --since=\$email_git_since " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800110 '--numstat --no-merges ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700111 '--format="GitCommit: %H%n' .
112 'GitAuthor: %an <%ae>%n' .
113 'GitDate: %aD%n' .
114 'GitSubject: %s%n' .
115 '%b%n"' .
116 " -- \$file",
117 "find_commit_signers_cmd" =>
118 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800119 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700120 '--format="GitCommit: %H%n' .
121 'GitAuthor: %an <%ae>%n' .
122 'GitDate: %aD%n' .
123 'GitSubject: %s%n' .
124 '%b%n"' .
125 " -1 \$commit",
126 "find_commit_author_cmd" =>
127 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800128 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700129 '--format="GitCommit: %H%n' .
130 'GitAuthor: %an <%ae>%n' .
131 'GitDate: %aD%n' .
132 'GitSubject: %s%n"' .
133 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800134 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
135 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700136 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700137 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700138 "author_pattern" => "^GitAuthor: (.*)",
139 "subject_pattern" => "^GitSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800140 "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700141 "file_exists_cmd" => "git ls-files \$file",
Tom Saegere1f75902017-11-17 15:27:42 -0800142 "list_files_cmd" => "git ls-files \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800143);
144
145my %VCS_cmds_hg = (
146 "execute_cmd" => \&hg_execute_cmd,
147 "available" => '(which("hg") ne "") && (-d ".hg")',
148 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700149 "hg log --date=\$email_hg_since " .
150 "--template='HgCommit: {node}\\n" .
151 "HgAuthor: {author}\\n" .
152 "HgSubject: {desc}\\n'" .
153 " -- \$file",
154 "find_commit_signers_cmd" =>
155 "hg log " .
156 "--template='HgSubject: {desc}\\n'" .
157 " -r \$commit",
158 "find_commit_author_cmd" =>
159 "hg log " .
160 "--template='HgCommit: {node}\\n" .
161 "HgAuthor: {author}\\n" .
162 "HgSubject: {desc|firstline}\\n'" .
163 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800164 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700165 "blame_file_cmd" => "hg blame -n \$file",
166 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
167 "blame_commit_pattern" => "^([ 0-9a-f]+):",
168 "author_pattern" => "^HgAuthor: (.*)",
169 "subject_pattern" => "^HgSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800170 "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700171 "file_exists_cmd" => "hg files \$file",
Tom Saegere1f75902017-11-17 15:27:42 -0800172 "list_files_cmd" => "hg manifest -R \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800173);
174
Joe Perchesbcde44e2010-10-26 14:22:53 -0700175my $conf = which_conf(".get_maintainer.conf");
176if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700177 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700178 open(my $conffile, '<', "$conf")
179 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
180
Joe Perches368669d2010-05-24 14:33:19 -0700181 while (<$conffile>) {
182 my $line = $_;
183
184 $line =~ s/\s*\n?$//g;
185 $line =~ s/^\s*//g;
186 $line =~ s/\s+/ /g;
187
188 next if ($line =~ m/^\s*#/);
189 next if ($line =~ m/^\s*$/);
190
191 my @words = split(" ", $line);
192 foreach my $word (@words) {
193 last if ($word =~ m/^#/);
194 push (@conf_args, $word);
195 }
196 }
197 close($conffile);
198 unshift(@ARGV, @conf_args) if @conf_args;
199}
200
Joe Perches435de072015-06-25 15:01:50 -0700201my @ignore_emails = ();
202my $ignore_file = which_conf(".get_maintainer.ignore");
203if (-f $ignore_file) {
204 open(my $ignore, '<', "$ignore_file")
205 or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
206 while (<$ignore>) {
207 my $line = $_;
208
209 $line =~ s/\s*\n?$//;
210 $line =~ s/^\s*//;
211 $line =~ s/\s+$//;
212 $line =~ s/#.*$//;
213
214 next if ($line =~ m/^\s*$/);
215 if (rfc822_valid($line)) {
216 push(@ignore_emails, $line);
217 }
218 }
219 close($ignore);
220}
221
Tom Saegere1f75902017-11-17 15:27:42 -0800222if ($#ARGV > 0) {
223 foreach (@ARGV) {
224 if ($_ eq "-self-test" || $_ eq "--self-test") {
225 die "$P: using --self-test does not allow any other option or argument\n";
226 }
227 }
228}
229
Joe Perchescb7301c2009-04-07 20:40:12 -0700230if (!GetOptions(
231 'email!' => \$email,
232 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700233 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800234 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700235 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700236 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700237 'git-chief-penguins!' => \$email_git_penguin_chiefs,
238 'git-min-signatures=i' => \$email_git_min_signatures,
239 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700240 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700241 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800242 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700243 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700244 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700245 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700246 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700247 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700248 'n!' => \$email_usename,
249 'l!' => \$email_list,
250 's!' => \$email_subscriber_list,
251 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800252 'roles!' => \$output_roles,
253 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700254 'separator=s' => \$output_separator,
255 'subsystem!' => \$subsystem,
256 'status!' => \$status,
257 'scm!' => \$scm,
258 'web!' => \$web,
Joe Perches03aed212016-12-12 16:45:59 -0800259 'letters=s' => \$letters,
Joe Perches3fb55652009-09-21 17:04:17 -0700260 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700261 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800262 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800263 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700264 'f|file' => \$from_filename,
Joe Perches6f7d98e2017-08-04 21:45:48 -0700265 'find-maintainer-files' => \$find_maintainer_files,
Tom Saegere1f75902017-11-17 15:27:42 -0800266 'self-test' => \$self_test,
Joe Perchescb7301c2009-04-07 20:40:12 -0700267 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800268 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700269 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800270 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700271}
272
273if ($help != 0) {
274 usage();
275 exit 0;
276}
277
278if ($version != 0) {
279 print("${P} ${V}\n");
280 exit 0;
281}
282
Tom Saegere1f75902017-11-17 15:27:42 -0800283if ($self_test) {
284 read_all_maintainer_files();
285 check_maintainers_patterns();
286 exit 0;
287}
288
Joe Perches64f77f32010-03-05 13:43:04 -0800289if (-t STDIN && !@ARGV) {
290 # We're talking to a terminal, but have no command line arguments.
291 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700292}
293
Joe Perches683c6f82010-10-26 14:22:55 -0700294$output_multiline = 0 if ($output_separator ne ", ");
295$output_rolestats = 1 if ($interactive);
296$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800297
Joe Perches03aed212016-12-12 16:45:59 -0800298if ($sections || $letters ne "") {
299 $sections = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800300 $email = 0;
301 $email_list = 0;
302 $scm = 0;
303 $status = 0;
304 $subsystem = 0;
305 $web = 0;
306 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700307 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800308} else {
309 my $selections = $email + $scm + $status + $subsystem + $web;
310 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800311 die "$P: Missing required option: email, scm, status, subsystem or web\n";
312 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700313}
314
Joe Perchesf5492662009-09-21 17:04:13 -0700315if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700316 ($email_maintainer + $email_reviewer +
317 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700318 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700319 die "$P: Please select at least 1 email option\n";
320}
321
322if (!top_of_kernel_tree($lk_path)) {
323 die "$P: The current directory does not appear to be "
324 . "a linux kernel source tree.\n";
325}
326
327## Read MAINTAINERS for type/value pairs
328
329my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700330my %keyword_hash;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700331my @mfiles = ();
Tom Saegere1f75902017-11-17 15:27:42 -0800332my @self_test_pattern_info = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700333
Joe Perches6f7d98e2017-08-04 21:45:48 -0700334sub read_maintainer_file {
335 my ($file) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700336
Joe Perches6f7d98e2017-08-04 21:45:48 -0700337 open (my $maint, '<', "$file")
338 or die "$P: Can't open MAINTAINERS file '$file': $!\n";
Tom Saegere1f75902017-11-17 15:27:42 -0800339 my $i = 1;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700340 while (<$maint>) {
341 my $line = $_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700342
Joe Perches6f7d98e2017-08-04 21:45:48 -0700343 if ($line =~ m/^([A-Z]):\s*(.*)/) {
344 my $type = $1;
345 my $value = $2;
346
347 ##Filename pattern matching
348 if ($type eq "F" || $type eq "X") {
349 $value =~ s@\.@\\\.@g; ##Convert . to \.
350 $value =~ s/\*/\.\*/g; ##Convert * to .*
351 $value =~ s/\?/\./g; ##Convert ? to .
352 ##if pattern is a directory and it lacks a trailing slash, add one
353 if ((-d $value)) {
354 $value =~ s@([^/])$@$1/@;
355 }
Tom Saegere1f75902017-11-17 15:27:42 -0800356 if ($self_test) {
357 push(@self_test_pattern_info, {file=>$file, line=>$line, linenr=>$i, pat=>$value});
358 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700359 } 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*\#/)) {
364 $line =~ s/\n$//g;
365 push(@typevalue, $line);
Joe Perchescb7301c2009-04-07 20:40:12 -0700366 }
Tom Saegere1f75902017-11-17 15:27:42 -0800367 $i++;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700368 }
369 close($maint);
370}
371
372sub find_is_maintainer_file {
373 my ($file) = $_;
374 return if ($file !~ m@/MAINTAINERS$@);
375 $file = $File::Find::name;
376 return if (! -f $file);
377 push(@mfiles, $file);
378}
379
380sub find_ignore_git {
381 return grep { $_ !~ /^\.git$/; } @_;
382}
383
Tom Saegere1f75902017-11-17 15:27:42 -0800384read_all_maintainer_files();
385
386sub read_all_maintainer_files {
387 if (-d "${lk_path}MAINTAINERS") {
388 opendir(DIR, "${lk_path}MAINTAINERS") or die $!;
389 my @files = readdir(DIR);
390 closedir(DIR);
391 foreach my $file (@files) {
392 push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./);
393 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700394 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700395
Tom Saegere1f75902017-11-17 15:27:42 -0800396 if ($find_maintainer_files) {
397 find( { wanted => \&find_is_maintainer_file,
398 preprocess => \&find_ignore_git,
399 no_chdir => 1,
400 }, "${lk_path}");
401 } else {
402 push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS";
403 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700404
Tom Saegere1f75902017-11-17 15:27:42 -0800405 foreach my $file (@mfiles) {
406 read_maintainer_file("$file");
407 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700408}
Joe Perches8cbb3a72009-09-21 17:04:21 -0700409
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700410#
411# Read mail address map
412#
413
Joe Perchesb9e23312010-10-26 14:22:58 -0700414my $mailmap;
415
416read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700417
418sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700419 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700420 names => {},
421 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700422 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700423
Joe Perchesb9e23312010-10-26 14:22:58 -0700424 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700425
426 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800427 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700428
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700429 while (<$mailmap_file>) {
430 s/#.*$//; #strip comments
431 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700432
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700433 next if (/^\s*$/); #skip empty lines
434 #entries have one of the following formats:
435 # name1 <mail1>
436 # <mail1> <mail2>
437 # name1 <mail1> <mail2>
438 # name1 <mail1> name2 <mail2>
439 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700440
441 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700442 my $real_name = $1;
443 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700444
Joe Perches47abc722010-10-26 14:22:57 -0700445 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700446 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700447 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700448
Joe Perches0334b382011-07-25 17:13:13 -0700449 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700450 my $real_address = $1;
451 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700452
Joe Perches47abc722010-10-26 14:22:57 -0700453 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700454
Joe Perches0334b382011-07-25 17:13:13 -0700455 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700456 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700457 my $real_address = $2;
458 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700459
Joe Perches47abc722010-10-26 14:22:57 -0700460 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700461 ($real_name, $real_address) =
462 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700463 $mailmap->{names}->{$wrong_address} = $real_name;
464 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700465
Joe Perches0334b382011-07-25 17:13:13 -0700466 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700467 my $real_name = $1;
468 my $real_address = $2;
469 my $wrong_name = $3;
470 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700471
Joe Perches47abc722010-10-26 14:22:57 -0700472 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700473 ($real_name, $real_address) =
474 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700475
Joe Perchesb9e23312010-10-26 14:22:58 -0700476 $wrong_name =~ s/\s+$//;
477 ($wrong_name, $wrong_address) =
478 parse_email("$wrong_name <$wrong_address>");
479
480 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
481 $mailmap->{names}->{$wrong_email} = $real_name;
482 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700483 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700484 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700485 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700486}
487
Joe Perches4a7fdb52009-04-10 12:28:57 -0700488## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700489
490my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700491my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700492my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800493my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700494
Joe Perches64f77f32010-03-05 13:43:04 -0800495if (!@ARGV) {
496 push(@ARGV, "&STDIN");
497}
498
Joe Perches4a7fdb52009-04-10 12:28:57 -0700499foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800500 if ($file ne "&STDIN") {
501 ##if $file is a directory and it lacks a trailing slash, add one
502 if ((-d $file)) {
503 $file =~ s@([^/])$@$1/@;
504 } elsif (!(-f $file)) {
505 die "$P: file '${file}' not found\n";
506 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700507 }
Joe Perchesaec742e2016-08-10 08:45:11 -0700508 if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
Joe Perchesbe17bdd2016-01-20 14:58:24 -0800509 $file =~ s/^\Q${cur_path}\E//; #strip any absolute path
510 $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
Joe Perches4a7fdb52009-04-10 12:28:57 -0700511 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700512 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800513 open(my $f, '<', $file)
514 or die "$P: Can't open $file: $!\n";
515 my $text = do { local($/) ; <$f> };
516 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800517 if ($keywords) {
518 foreach my $line (keys %keyword_hash) {
519 if ($text =~ m/$keyword_hash{$line}/x) {
520 push(@keyword_tvi, $line);
521 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700522 }
523 }
Joe Perches03372db2010-03-05 13:43:00 -0800524 if ($file_emails) {
525 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;
526 push(@file_emails, clean_file_emails(@poss_addr));
527 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700528 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700529 } else {
530 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700531 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800532
Wolfram Sang3a4df132010-03-23 13:35:18 -0700533 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800534 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700535
536 # We can check arbitrary information before the patch
537 # like the commit message, mail headers, etc...
538 # This allows us to match arbitrary keywords against any part
539 # of a git format-patch generated file (subject tags, etc...)
540
541 my $patch_prefix = ""; #Parsing the intro
542
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800543 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700544 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800545 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700546 my $filename = $1;
547 $filename =~ s@^[^/]*/@@;
548 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700549 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700550 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700551 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700552 } elsif (m/^\@\@ -(\d+),(\d+)/) {
553 if ($email_git_blame) {
554 push(@range, "$lastfile:$1:$2");
555 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700556 } elsif ($keywords) {
557 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700558 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700559 push(@keyword_tvi, $line);
560 }
561 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700562 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700563 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800564 close($patch);
565
Joe Perches4a7fdb52009-04-10 12:28:57 -0700566 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700567 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700568 . "Add -f to options?\n";
569 }
570 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700571 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700572}
573
Joe Perches03372db2010-03-05 13:43:00 -0800574@file_emails = uniq(@file_emails);
575
Joe Perches683c6f82010-10-26 14:22:55 -0700576my %email_hash_name;
577my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700578my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700579my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700580my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700581my @scm = ();
582my @web = ();
583my @subsystem = ();
584my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700585my %deduplicate_name_hash = ();
586my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700587
Joe Perches6ef1c522010-10-26 14:22:56 -0700588my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700589
Joe Perches6ef1c522010-10-26 14:22:56 -0700590if (@maintainers) {
591 @maintainers = merge_email(@maintainers);
592 output(@maintainers);
593}
Joe Perchescb7301c2009-04-07 20:40:12 -0700594
595if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700596 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700597 output(@scm);
598}
Joe Perches683c6f82010-10-26 14:22:55 -0700599
Joe Perchescb7301c2009-04-07 20:40:12 -0700600if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700601 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700602 output(@status);
603}
604
605if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700606 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700607 output(@subsystem);
608}
609
610if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700611 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700612 output(@web);
613}
614
615exit($exit);
616
Tom Saegere1f75902017-11-17 15:27:42 -0800617sub check_maintainers_patterns {
618 my @lsfiles = ();
619
620 @lsfiles = vcs_list_files($lk_path);
621
622 for my $x (@self_test_pattern_info) {
623 if (!grep(m@^$x->{pat}@, @lsfiles)) {
624 my $line = $x->{line};
625 chomp($line);
626 print("$x->{file}:$x->{linenr}: warning: no matches $line\n");
627 }
628 }
629}
630
Joe Perches435de072015-06-25 15:01:50 -0700631sub ignore_email_address {
632 my ($address) = @_;
633
634 foreach my $ignore (@ignore_emails) {
635 return 1 if ($ignore eq $address);
636 }
637
638 return 0;
639}
640
Joe Perchesab6c9372011-01-12 16:59:50 -0800641sub range_is_maintained {
642 my ($start, $end) = @_;
643
644 for (my $i = $start; $i < $end; $i++) {
645 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700646 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800647 my $type = $1;
648 my $value = $2;
649 if ($type eq 'S') {
650 if ($value =~ /(maintain|support)/i) {
651 return 1;
652 }
653 }
654 }
655 }
656 return 0;
657}
658
659sub range_has_maintainer {
660 my ($start, $end) = @_;
661
662 for (my $i = $start; $i < $end; $i++) {
663 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700664 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800665 my $type = $1;
666 my $value = $2;
667 if ($type eq 'M') {
668 return 1;
669 }
670 }
671 }
672 return 0;
673}
674
Joe Perches6ef1c522010-10-26 14:22:56 -0700675sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700676 %email_hash_name = ();
677 %email_hash_address = ();
678 %commit_author_hash = ();
679 %commit_signer_hash = ();
680 @email_to = ();
681 %hash_list_to = ();
682 @list_to = ();
683 @scm = ();
684 @web = ();
685 @subsystem = ();
686 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700687 %deduplicate_name_hash = ();
688 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700689 if ($email_git_all_signature_types) {
690 $signature_pattern = "(.+?)[Bb][Yy]:";
691 } else {
692 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
693 }
694
695 # Find responsible parties
696
Joe Perchesb9e23312010-10-26 14:22:58 -0700697 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700698
Joe Perches683c6f82010-10-26 14:22:55 -0700699 foreach my $file (@files) {
700
701 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700702 my $tvi = find_first_section();
703 while ($tvi < @typevalue) {
704 my $start = find_starting_index($tvi);
705 my $end = find_ending_index($tvi);
706 my $exclude = 0;
707 my $i;
708
709 #Do not match excluded file patterns
710
711 for ($i = $start; $i < $end; $i++) {
712 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700713 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700714 my $type = $1;
715 my $value = $2;
716 if ($type eq 'X') {
717 if (file_match_pattern($file, $value)) {
718 $exclude = 1;
719 last;
720 }
721 }
722 }
723 }
724
725 if (!$exclude) {
726 for ($i = $start; $i < $end; $i++) {
727 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700728 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700729 my $type = $1;
730 my $value = $2;
731 if ($type eq 'F') {
732 if (file_match_pattern($file, $value)) {
733 my $value_pd = ($value =~ tr@/@@);
734 my $file_pd = ($file =~ tr@/@@);
735 $value_pd++ if (substr($value,-1,1) ne "/");
736 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800737 if ($value_pd >= $file_pd &&
738 range_is_maintained($start, $end) &&
739 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700740 $exact_pattern_match_hash{$file} = 1;
741 }
Joe Perches683c6f82010-10-26 14:22:55 -0700742 if ($pattern_depth == 0 ||
743 (($file_pd - $value_pd) < $pattern_depth)) {
744 $hash{$tvi} = $value_pd;
745 }
746 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700747 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800748 if ($file =~ m/$value/x) {
749 $hash{$tvi} = 0;
750 }
Joe Perches683c6f82010-10-26 14:22:55 -0700751 }
752 }
753 }
754 }
755 $tvi = $end + 1;
756 }
757
758 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
759 add_categories($line);
760 if ($sections) {
761 my $i;
762 my $start = find_starting_index($line);
763 my $end = find_ending_index($line);
764 for ($i = $start; $i < $end; $i++) {
765 my $line = $typevalue[$i];
766 if ($line =~ /^[FX]:/) { ##Restore file patterns
767 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
768 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
769 $line =~ s/\\\./\./g; ##Convert \. to .
770 $line =~ s/\.\*/\*/g; ##Convert .* to *
771 }
Joe Perches03aed212016-12-12 16:45:59 -0800772 my $count = $line =~ s/^([A-Z]):/$1:\t/g;
773 if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
774 print("$line\n");
775 }
Joe Perches683c6f82010-10-26 14:22:55 -0700776 }
777 print("\n");
778 }
779 }
Joe Perches683c6f82010-10-26 14:22:55 -0700780 }
781
782 if ($keywords) {
783 @keyword_tvi = sort_and_uniq(@keyword_tvi);
784 foreach my $line (@keyword_tvi) {
785 add_categories($line);
786 }
787 }
788
Joe Perchesb9e23312010-10-26 14:22:58 -0700789 foreach my $email (@email_to, @list_to) {
790 $email->[0] = deduplicate_email($email->[0]);
791 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700792
793 foreach my $file (@files) {
794 if ($email &&
795 ($email_git || ($email_git_fallback &&
796 !$exact_pattern_match_hash{$file}))) {
797 vcs_file_signoffs($file);
798 }
799 if ($email && $email_git_blame) {
800 vcs_file_blame($file);
801 }
802 }
803
Joe Perches683c6f82010-10-26 14:22:55 -0700804 if ($email) {
805 foreach my $chief (@penguin_chief) {
806 if ($chief =~ m/^(.*):(.*)/) {
807 my $email_address;
808
809 $email_address = format_email($1, $2, $email_usename);
810 if ($email_git_penguin_chiefs) {
811 push(@email_to, [$email_address, 'chief penguin']);
812 } else {
813 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
814 }
815 }
816 }
817
818 foreach my $email (@file_emails) {
819 my ($name, $address) = parse_email($email);
820
821 my $tmp_email = format_email($name, $address, $email_usename);
822 push_email_address($tmp_email, '');
823 add_role($tmp_email, 'in file');
824 }
825 }
826
827 my @to = ();
828 if ($email || $email_list) {
829 if ($email) {
830 @to = (@to, @email_to);
831 }
832 if ($email_list) {
833 @to = (@to, @list_to);
834 }
835 }
836
Joe Perches6ef1c522010-10-26 14:22:56 -0700837 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700838 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700839 }
Joe Perches683c6f82010-10-26 14:22:55 -0700840
841 return @to;
842}
843
Joe Perchescb7301c2009-04-07 20:40:12 -0700844sub file_match_pattern {
845 my ($file, $pattern) = @_;
846 if (substr($pattern, -1) eq "/") {
847 if ($file =~ m@^$pattern@) {
848 return 1;
849 }
850 } else {
851 if ($file =~ m@^$pattern@) {
852 my $s1 = ($file =~ tr@/@@);
853 my $s2 = ($pattern =~ tr@/@@);
854 if ($s1 == $s2) {
855 return 1;
856 }
857 }
858 }
859 return 0;
860}
861
862sub usage {
863 print <<EOT;
864usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700865 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700866version: $V
867
868MAINTAINER field selection options:
869 --email => print email address(es) if any
870 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700871 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700872 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700873 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700874 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700875 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
876 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
877 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700878 --git-blame => use git blame to find modified commits for patch or file
Brian Norris3cbcca82015-11-06 16:30:41 -0800879 --git-blame-signatures => when used with --git-blame, also include all commit signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700880 --git-since => git history to use (default: $email_git_since)
881 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700882 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700883 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700884 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700885 --n => include name 'Full Name <addr\@domain.tld>'
886 --l => include list(s) if any
887 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700888 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800889 --roles => show roles (status:subsystem, git-signer, list, etc...)
890 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800891 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700892 --scm => print SCM tree(s) if any
893 --status => print status if any
894 --subsystem => print subsystem name if any
895 --web => print website(s) if any
896
897Output type options:
898 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700899 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700900 --multiline => print 1 entry per line
901
Joe Perchescb7301c2009-04-07 20:40:12 -0700902Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700903 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700904 --keywords => scan patch for keywords (default: $keywords)
905 --sections => print all of the subsystem sections with pattern matches
Joe Perches03aed212016-12-12 16:45:59 -0800906 --letters => print all matching 'letter' types from all matching sections
Joe Perchesb9e23312010-10-26 14:22:58 -0700907 --mailmap => use .mailmap file (default: $email_use_mailmap)
Tom Saegere1f75902017-11-17 15:27:42 -0800908 --self-test => show potential issues with MAINTAINERS file content
Joe Perchesf5f5078d2009-06-16 15:34:00 -0700909 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700910 --help => show this help information
911
Joe Perches3fb55652009-09-21 17:04:17 -0700912Default options:
Brian Norris4f075102015-11-06 16:30:49 -0800913 [--email --nogit --git-fallback --m --r --n --l --multiline --pattern-depth=0
Joe Perches7e1863a2011-01-12 16:59:49 -0800914 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700915
Joe Perches870020f2009-07-29 15:04:28 -0700916Notes:
917 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700918 Used with "--git", git signators for _all_ files in and below
919 directory are examined as git recurses directories.
920 Any specified X: (exclude) pattern matches are _not_ ignored.
921 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800922 no individual file within the directory or subdirectory
923 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700924 Used with "--git-blame", does not iterate all files in directory
925 Using "--git-blame" is slow and may add old committers and authors
926 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800927 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
928 other automated tools that expect only ["name"] <email address>
929 may not work because of additional output after <email address>.
930 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
931 not the percentage of the entire file authored. # of commits is
932 not a good measure of amount of code authored. 1 major commit may
933 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800934 If git is not installed, but mercurial (hg) is installed and an .hg
935 repository exists, the following options apply to mercurial:
936 --git,
937 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
938 --git-blame
939 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700940 File ".get_maintainer.conf", if it exists in the linux kernel source root
941 directory, can change whatever get_maintainer defaults are desired.
942 Entries in this file can be any command line argument.
943 This file is prepended to any additional command line arguments.
944 Multiple lines and # comments are allowed.
Brian Norrisb1312bf2015-11-06 16:30:46 -0800945 Most options have both positive and negative forms.
946 The negative forms for --<foo> are --no<foo> and --no-<foo>.
947
Joe Perchescb7301c2009-04-07 20:40:12 -0700948EOT
949}
950
951sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700952 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700953
Joe Perches47abc722010-10-26 14:22:57 -0700954 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
955 $lk_path .= "/";
956 }
957 if ( (-f "${lk_path}COPYING")
958 && (-f "${lk_path}CREDITS")
959 && (-f "${lk_path}Kbuild")
Joe Perches6f7d98e2017-08-04 21:45:48 -0700960 && (-e "${lk_path}MAINTAINERS")
Joe Perches47abc722010-10-26 14:22:57 -0700961 && (-f "${lk_path}Makefile")
962 && (-f "${lk_path}README")
963 && (-d "${lk_path}Documentation")
964 && (-d "${lk_path}arch")
965 && (-d "${lk_path}include")
966 && (-d "${lk_path}drivers")
967 && (-d "${lk_path}fs")
968 && (-d "${lk_path}init")
969 && (-d "${lk_path}ipc")
970 && (-d "${lk_path}kernel")
971 && (-d "${lk_path}lib")
972 && (-d "${lk_path}scripts")) {
973 return 1;
974 }
975 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700976}
977
Joe Perches0e70e832009-09-21 17:04:20 -0700978sub parse_email {
979 my ($formatted_email) = @_;
980
981 my $name = "";
982 my $address = "";
983
Joe Perches11ecf532009-09-21 17:04:22 -0700984 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700985 $name = $1;
986 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700987 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700988 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700989 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700990 $address = $1;
991 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700992
993 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700994 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700995 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700996
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800997 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700998 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700999 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -07001000 }
Joe Perches0e70e832009-09-21 17:04:20 -07001001
1002 return ($name, $address);
1003}
1004
1005sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -08001006 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001007
1008 my $formatted_email;
1009
1010 $name =~ s/^\s+|\s+$//g;
1011 $name =~ s/^\"|\"$//g;
1012 $address =~ s/^\s+|\s+$//g;
1013
Stephen Hemmingera63ceb42010-03-05 13:43:06 -08001014 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -07001015 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
1016 $name = "\"$name\"";
1017 }
1018
Joe Perchesa8af2432009-12-14 18:00:49 -08001019 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -07001020 if ("$name" eq "") {
1021 $formatted_email = "$address";
1022 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -08001023 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -07001024 }
1025 } else {
1026 $formatted_email = $address;
1027 }
1028
Joe Perchescb7301c2009-04-07 20:40:12 -07001029 return $formatted_email;
1030}
1031
Joe Perches272a8972010-01-08 14:42:48 -08001032sub find_first_section {
1033 my $index = 0;
1034
1035 while ($index < @typevalue) {
1036 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001037 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -08001038 last;
1039 }
1040 $index++;
1041 }
1042
1043 return $index;
1044}
1045
Joe Perchesb7816552009-09-21 17:04:24 -07001046sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -07001047 my ($index) = @_;
1048
1049 while ($index > 0) {
1050 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001051 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001052 last;
1053 }
1054 $index--;
1055 }
1056
1057 return $index;
1058}
1059
1060sub find_ending_index {
1061 my ($index) = @_;
1062
1063 while ($index < @typevalue) {
1064 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001065 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001066 last;
1067 }
1068 $index++;
1069 }
1070
1071 return $index;
1072}
1073
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001074sub get_subsystem_name {
1075 my ($index) = @_;
1076
1077 my $start = find_starting_index($index);
1078
1079 my $subsystem = $typevalue[$start];
1080 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1081 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
1082 $subsystem =~ s/\s*$//;
1083 $subsystem = $subsystem . "...";
1084 }
1085 return $subsystem;
1086}
1087
Joe Perches3c7385b2009-12-14 18:00:46 -08001088sub get_maintainer_role {
1089 my ($index) = @_;
1090
1091 my $i;
1092 my $start = find_starting_index($index);
1093 my $end = find_ending_index($index);
1094
Joe Perches0ede2742012-03-23 15:01:56 -07001095 my $role = "unknown";
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001096 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001097
1098 for ($i = $start + 1; $i < $end; $i++) {
1099 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001100 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001101 my $ptype = $1;
1102 my $pvalue = $2;
1103 if ($ptype eq "S") {
1104 $role = $pvalue;
1105 }
1106 }
1107 }
1108
1109 $role = lc($role);
1110 if ($role eq "supported") {
1111 $role = "supporter";
1112 } elsif ($role eq "maintained") {
1113 $role = "maintainer";
1114 } elsif ($role eq "odd fixes") {
1115 $role = "odd fixer";
1116 } elsif ($role eq "orphan") {
1117 $role = "orphan minder";
1118 } elsif ($role eq "obsolete") {
1119 $role = "obsolete minder";
1120 } elsif ($role eq "buried alive in reporters") {
1121 $role = "chief penguin";
1122 }
1123
1124 return $role . ":" . $subsystem;
1125}
1126
1127sub get_list_role {
1128 my ($index) = @_;
1129
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001130 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001131
1132 if ($subsystem eq "THE REST") {
1133 $subsystem = "";
1134 }
1135
1136 return $subsystem;
1137}
1138
Joe Perchescb7301c2009-04-07 20:40:12 -07001139sub add_categories {
1140 my ($index) = @_;
1141
Joe Perchesb7816552009-09-21 17:04:24 -07001142 my $i;
1143 my $start = find_starting_index($index);
1144 my $end = find_ending_index($index);
1145
1146 push(@subsystem, $typevalue[$start]);
1147
1148 for ($i = $start + 1; $i < $end; $i++) {
1149 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001150 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001151 my $ptype = $1;
1152 my $pvalue = $2;
1153 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001154 my $list_address = $pvalue;
1155 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001156 my $list_role = get_list_role($i);
1157
1158 if ($list_role ne "") {
1159 $list_role = ":" . $list_role;
1160 }
Joe Perches290603c2009-06-16 15:33:58 -07001161 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1162 $list_address = $1;
1163 $list_additional = $2;
1164 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001165 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001166 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001167 if (!$hash_list_to{lc($list_address)}) {
1168 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001169 push(@list_to, [$list_address,
1170 "subscriber list${list_role}"]);
1171 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001172 }
1173 } else {
1174 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001175 if (!$hash_list_to{lc($list_address)}) {
1176 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001177 if ($list_additional =~ m/moderated/) {
1178 push(@list_to, [$list_address,
1179 "moderated list${list_role}"]);
1180 } else {
1181 push(@list_to, [$list_address,
1182 "open list${list_role}"]);
1183 }
Joe Perches683c6f82010-10-26 14:22:55 -07001184 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001185 }
1186 }
1187 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001188 my ($name, $address) = parse_email($pvalue);
1189 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001190 if ($i > 0) {
1191 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001192 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001193 if ($1 eq "P") {
1194 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001195 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001196 }
1197 }
1198 }
1199 }
Joe Perches0e70e832009-09-21 17:04:20 -07001200 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001201 my $role = get_maintainer_role($i);
1202 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001203 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001204 } elsif ($ptype eq "R") {
1205 my ($name, $address) = parse_email($pvalue);
1206 if ($name eq "") {
1207 if ($i > 0) {
1208 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001209 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001210 if ($1 eq "P") {
1211 $name = $2;
1212 $pvalue = format_email($name, $address, $email_usename);
1213 }
1214 }
1215 }
1216 }
1217 if ($email_reviewer) {
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001218 my $subsystem = get_subsystem_name($i);
1219 push_email_addresses($pvalue, "reviewer:$subsystem");
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001220 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001221 } elsif ($ptype eq "T") {
1222 push(@scm, $pvalue);
1223 } elsif ($ptype eq "W") {
1224 push(@web, $pvalue);
1225 } elsif ($ptype eq "S") {
1226 push(@status, $pvalue);
1227 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001228 }
1229 }
1230}
1231
Joe Perches11ecf532009-09-21 17:04:22 -07001232sub email_inuse {
1233 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001234
Joe Perches11ecf532009-09-21 17:04:22 -07001235 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001236 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1237 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001238
Joe Perches0e70e832009-09-21 17:04:20 -07001239 return 0;
1240}
1241
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001242sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001243 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001244
Joe Perches0e70e832009-09-21 17:04:20 -07001245 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001246
Joe Perchesb7816552009-09-21 17:04:24 -07001247 if ($address eq "") {
1248 return 0;
1249 }
1250
Joe Perches11ecf532009-09-21 17:04:22 -07001251 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001252 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001253 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001254 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001255 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001256 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001257 }
Joe Perchesb7816552009-09-21 17:04:24 -07001258
1259 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001260}
1261
1262sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001263 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001264
1265 my @address_list = ();
1266
Joe Perches5f2441e2009-06-16 15:34:02 -07001267 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001268 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001269 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001270 my $array_count = shift(@address_list);
1271 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001272 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001273 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001274 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001275 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001276 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1277 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001278 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001279}
1280
Joe Perches3c7385b2009-12-14 18:00:46 -08001281sub add_role {
1282 my ($line, $role) = @_;
1283
1284 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001285 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001286
1287 foreach my $entry (@email_to) {
1288 if ($email_remove_duplicates) {
1289 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001290 if (($name eq $entry_name || $address eq $entry_address)
1291 && ($role eq "" || !($entry->[1] =~ m/$role/))
1292 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001293 if ($entry->[1] eq "") {
1294 $entry->[1] = "$role";
1295 } else {
1296 $entry->[1] = "$entry->[1],$role";
1297 }
1298 }
1299 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001300 if ($email eq $entry->[0]
1301 && ($role eq "" || !($entry->[1] =~ m/$role/))
1302 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001303 if ($entry->[1] eq "") {
1304 $entry->[1] = "$role";
1305 } else {
1306 $entry->[1] = "$entry->[1],$role";
1307 }
1308 }
1309 }
1310 }
1311}
1312
Joe Perchescb7301c2009-04-07 20:40:12 -07001313sub which {
1314 my ($bin) = @_;
1315
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001316 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001317 if (-e "$path/$bin") {
1318 return "$path/$bin";
1319 }
1320 }
1321
1322 return "";
1323}
1324
Joe Perchesbcde44e2010-10-26 14:22:53 -07001325sub which_conf {
1326 my ($conf) = @_;
1327
1328 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1329 if (-e "$path/$conf") {
1330 return "$path/$conf";
1331 }
1332 }
1333
1334 return "";
1335}
1336
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001337sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001338 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001339
Joe Perches47abc722010-10-26 14:22:57 -07001340 my ($name, $address) = parse_email($line);
1341 my $email = format_email($name, $address, 1);
1342 my $real_name = $name;
1343 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001344
Joe Perches47abc722010-10-26 14:22:57 -07001345 if (exists $mailmap->{names}->{$email} ||
1346 exists $mailmap->{addresses}->{$email}) {
1347 if (exists $mailmap->{names}->{$email}) {
1348 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001349 }
Joe Perches47abc722010-10-26 14:22:57 -07001350 if (exists $mailmap->{addresses}->{$email}) {
1351 $real_address = $mailmap->{addresses}->{$email};
1352 }
1353 } else {
1354 if (exists $mailmap->{names}->{$address}) {
1355 $real_name = $mailmap->{names}->{$address};
1356 }
1357 if (exists $mailmap->{addresses}->{$address}) {
1358 $real_address = $mailmap->{addresses}->{$address};
1359 }
1360 }
1361 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001362}
1363
1364sub mailmap {
1365 my (@addresses) = @_;
1366
Joe Perchesb9e23312010-10-26 14:22:58 -07001367 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001368 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001369 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001370 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001371 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1372 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001373}
1374
1375sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001376 my %address_map;
1377 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001378
Joe Perches47abc722010-10-26 14:22:57 -07001379 foreach my $email (@emails) {
1380 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001381 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001382 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001383 $email = format_email($name, $address, 1);
1384 } else {
1385 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001386 }
Joe Perches47abc722010-10-26 14:22:57 -07001387 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001388}
1389
Joe Perches60db31a2009-12-14 18:00:50 -08001390sub git_execute_cmd {
1391 my ($cmd) = @_;
1392 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001393
Joe Perches60db31a2009-12-14 18:00:50 -08001394 my $output = `$cmd`;
1395 $output =~ s/^\s*//gm;
1396 @lines = split("\n", $output);
1397
1398 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001399}
1400
Joe Perches60db31a2009-12-14 18:00:50 -08001401sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001402 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001403 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001404
Joe Perches60db31a2009-12-14 18:00:50 -08001405 my $output = `$cmd`;
1406 @lines = split("\n", $output);
1407
1408 return @lines;
1409}
1410
Joe Perches683c6f82010-10-26 14:22:55 -07001411sub extract_formatted_signatures {
1412 my (@signature_lines) = @_;
1413
1414 my @type = @signature_lines;
1415
1416 s/\s*(.*):.*/$1/ for (@type);
1417
1418 # cut -f2- -d":"
1419 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1420
1421## Reformat email addresses (with names) to avoid badly written signatures
1422
1423 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001424 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001425 }
1426
1427 return (\@type, \@signature_lines);
1428}
1429
Joe Perches60db31a2009-12-14 18:00:50 -08001430sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001431 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001432 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001433 my @lines = ();
1434 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001435 my @authors = ();
1436 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001437
Joe Perches60db31a2009-12-14 18:00:50 -08001438 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001439
Joe Perches60db31a2009-12-14 18:00:50 -08001440 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001441 my $author_pattern = $VCS_cmds{"author_pattern"};
1442 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1443
1444 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001445
Joe Perches60db31a2009-12-14 18:00:50 -08001446 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001447
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001448 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001449 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001450 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001451
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001452# print("stats: <@stats>\n");
1453
1454 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001455
1456 save_commits_by_author(@lines) if ($interactive);
1457 save_commits_by_signer(@lines) if ($interactive);
1458
Joe Perches0e70e832009-09-21 17:04:20 -07001459 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001460 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001461 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001462
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001463 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001464 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001465
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001466 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001467}
1468
Joe Perches63ab52d2010-10-26 14:22:51 -07001469sub vcs_find_author {
1470 my ($cmd) = @_;
1471 my @lines = ();
1472
1473 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1474
1475 if (!$email_git_penguin_chiefs) {
1476 @lines = grep(!/${penguin_chiefs}/i, @lines);
1477 }
1478
1479 return @lines if !@lines;
1480
Joe Perches683c6f82010-10-26 14:22:55 -07001481 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001482 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001483 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1484 my $author = $1;
1485 my ($name, $address) = parse_email($author);
1486 $author = format_email($name, $address, 1);
1487 push(@authors, $author);
1488 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001489 }
1490
Joe Perches683c6f82010-10-26 14:22:55 -07001491 save_commits_by_author(@lines) if ($interactive);
1492 save_commits_by_signer(@lines) if ($interactive);
1493
1494 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001495}
1496
Joe Perches60db31a2009-12-14 18:00:50 -08001497sub vcs_save_commits {
1498 my ($cmd) = @_;
1499 my @lines = ();
1500 my @commits = ();
1501
1502 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1503
1504 foreach my $line (@lines) {
1505 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1506 push(@commits, $1);
1507 }
1508 }
1509
1510 return @commits;
1511}
1512
1513sub vcs_blame {
1514 my ($file) = @_;
1515 my $cmd;
1516 my @commits = ();
1517
1518 return @commits if (!(-f $file));
1519
1520 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1521 my @all_commits = ();
1522
1523 $cmd = $VCS_cmds{"blame_file_cmd"};
1524 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1525 @all_commits = vcs_save_commits($cmd);
1526
1527 foreach my $file_range_diff (@range) {
1528 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1529 my $diff_file = $1;
1530 my $diff_start = $2;
1531 my $diff_length = $3;
1532 next if ("$file" ne "$diff_file");
1533 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1534 push(@commits, $all_commits[$i]);
1535 }
1536 }
1537 } elsif (@range) {
1538 foreach my $file_range_diff (@range) {
1539 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1540 my $diff_file = $1;
1541 my $diff_start = $2;
1542 my $diff_length = $3;
1543 next if ("$file" ne "$diff_file");
1544 $cmd = $VCS_cmds{"blame_range_cmd"};
1545 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1546 push(@commits, vcs_save_commits($cmd));
1547 }
1548 } else {
1549 $cmd = $VCS_cmds{"blame_file_cmd"};
1550 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1551 @commits = vcs_save_commits($cmd);
1552 }
1553
Joe Perches63ab52d2010-10-26 14:22:51 -07001554 foreach my $commit (@commits) {
1555 $commit =~ s/^\^//g;
1556 }
1557
Joe Perches60db31a2009-12-14 18:00:50 -08001558 return @commits;
1559}
1560
1561my $printed_novcs = 0;
1562sub vcs_exists {
1563 %VCS_cmds = %VCS_cmds_git;
1564 return 1 if eval $VCS_cmds{"available"};
1565 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001566 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001567 %VCS_cmds = ();
1568 if (!$printed_novcs) {
1569 warn("$P: No supported VCS found. Add --nogit to options?\n");
1570 warn("Using a git repository produces better results.\n");
1571 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001572 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001573 $printed_novcs = 1;
1574 }
1575 return 0;
1576}
1577
Joe Perches683c6f82010-10-26 14:22:55 -07001578sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001579 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001580 return $vcs_used == 1;
1581}
1582
1583sub vcs_is_hg {
1584 return $vcs_used == 2;
1585}
1586
Joe Perches6ef1c522010-10-26 14:22:56 -07001587sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001588 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001589 my @list = @$list_ref;
1590
Joe Perches683c6f82010-10-26 14:22:55 -07001591 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001592
1593 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001594 my %authored;
1595 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001596 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001597 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001598 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001599 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1600 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001601 $authored{$count} = 0;
1602 $signed{$count} = 0;
1603 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001604 }
1605
1606 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001607 my $done = 0;
1608 my $print_options = 0;
1609 my $redraw = 1;
1610 while (!$done) {
1611 $count = 0;
1612 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001613 printf STDERR "\n%1s %2s %-65s",
1614 "*", "#", "email/list and role:stats";
1615 if ($email_git ||
1616 ($email_git_fallback && !$maintained) ||
1617 $email_git_blame) {
1618 print STDERR "auth sign";
1619 }
1620 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001621 foreach my $entry (@list) {
1622 my $email = $entry->[0];
1623 my $role = $entry->[1];
1624 my $sel = "";
1625 $sel = "*" if ($selected{$count});
1626 my $commit_author = $commit_author_hash{$email};
1627 my $commit_signer = $commit_signer_hash{$email};
1628 my $authored = 0;
1629 my $signed = 0;
1630 $authored++ for (@{$commit_author});
1631 $signed++ for (@{$commit_signer});
1632 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1633 printf STDERR "%4d %4d", $authored, $signed
1634 if ($authored > 0 || $signed > 0);
1635 printf STDERR "\n %s\n", $role;
1636 if ($authored{$count}) {
1637 my $commit_author = $commit_author_hash{$email};
1638 foreach my $ref (@{$commit_author}) {
1639 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001640 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001641 }
Joe Perches683c6f82010-10-26 14:22:55 -07001642 if ($signed{$count}) {
1643 my $commit_signer = $commit_signer_hash{$email};
1644 foreach my $ref (@{$commit_signer}) {
1645 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1646 }
1647 }
1648
1649 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001650 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001651 }
Joe Perches683c6f82010-10-26 14:22:55 -07001652 my $date_ref = \$email_git_since;
1653 $date_ref = \$email_hg_since if (vcs_is_hg());
1654 if ($print_options) {
1655 $print_options = 0;
1656 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001657 print STDERR <<EOT
1658
1659Version Control options:
1660g use git history [$email_git]
1661gf use git-fallback [$email_git_fallback]
1662b use git blame [$email_git_blame]
1663bs use blame signatures [$email_git_blame_signatures]
1664c# minimum commits [$email_git_min_signatures]
1665%# min percent [$email_git_min_percent]
1666d# history to use [$$date_ref]
1667x# max maintainers [$email_git_max_maintainers]
1668t all signature types [$email_git_all_signature_types]
1669m use .mailmap [$email_use_mailmap]
1670EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001671 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001672 print STDERR <<EOT
1673
1674Additional options:
16750 toggle all
1676tm toggle maintainers
1677tg toggle git entries
1678tl toggle open list entries
1679ts toggle subscriber list entries
1680f emails in file [$file_emails]
1681k keywords in file [$keywords]
1682r remove duplicates [$email_remove_duplicates]
1683p# pattern match depth [$pattern_depth]
1684EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001685 }
1686 print STDERR
1687"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1688
1689 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001690 chomp($input);
1691
Joe Perches683c6f82010-10-26 14:22:55 -07001692 $redraw = 1;
1693 my $rerun = 0;
1694 my @wish = split(/[, ]+/, $input);
1695 foreach my $nr (@wish) {
1696 $nr = lc($nr);
1697 my $sel = substr($nr, 0, 1);
1698 my $str = substr($nr, 1);
1699 my $val = 0;
1700 $val = $1 if $str =~ /^(\d+)$/;
1701
1702 if ($sel eq "y") {
1703 $interactive = 0;
1704 $done = 1;
1705 $output_rolestats = 0;
1706 $output_roles = 0;
1707 last;
1708 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1709 $selected{$nr - 1} = !$selected{$nr - 1};
1710 } elsif ($sel eq "*" || $sel eq '^') {
1711 my $toggle = 0;
1712 $toggle = 1 if ($sel eq '*');
1713 for (my $i = 0; $i < $count; $i++) {
1714 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001715 }
Joe Perches683c6f82010-10-26 14:22:55 -07001716 } elsif ($sel eq "0") {
1717 for (my $i = 0; $i < $count; $i++) {
1718 $selected{$i} = !$selected{$i};
1719 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001720 } elsif ($sel eq "t") {
1721 if (lc($str) eq "m") {
1722 for (my $i = 0; $i < $count; $i++) {
1723 $selected{$i} = !$selected{$i}
1724 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1725 }
1726 } elsif (lc($str) eq "g") {
1727 for (my $i = 0; $i < $count; $i++) {
1728 $selected{$i} = !$selected{$i}
1729 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1730 }
1731 } elsif (lc($str) eq "l") {
1732 for (my $i = 0; $i < $count; $i++) {
1733 $selected{$i} = !$selected{$i}
1734 if ($list[$i]->[1] =~ /^(open list)/i);
1735 }
1736 } elsif (lc($str) eq "s") {
1737 for (my $i = 0; $i < $count; $i++) {
1738 $selected{$i} = !$selected{$i}
1739 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1740 }
1741 }
Joe Perches683c6f82010-10-26 14:22:55 -07001742 } elsif ($sel eq "a") {
1743 if ($val > 0 && $val <= $count) {
1744 $authored{$val - 1} = !$authored{$val - 1};
1745 } elsif ($str eq '*' || $str eq '^') {
1746 my $toggle = 0;
1747 $toggle = 1 if ($str eq '*');
1748 for (my $i = 0; $i < $count; $i++) {
1749 $authored{$i} = $toggle;
1750 }
1751 }
1752 } elsif ($sel eq "s") {
1753 if ($val > 0 && $val <= $count) {
1754 $signed{$val - 1} = !$signed{$val - 1};
1755 } elsif ($str eq '*' || $str eq '^') {
1756 my $toggle = 0;
1757 $toggle = 1 if ($str eq '*');
1758 for (my $i = 0; $i < $count; $i++) {
1759 $signed{$i} = $toggle;
1760 }
1761 }
1762 } elsif ($sel eq "o") {
1763 $print_options = 1;
1764 $redraw = 1;
1765 } elsif ($sel eq "g") {
1766 if ($str eq "f") {
1767 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001768 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001769 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001770 }
Joe Perches683c6f82010-10-26 14:22:55 -07001771 $rerun = 1;
1772 } elsif ($sel eq "b") {
1773 if ($str eq "s") {
1774 bool_invert(\$email_git_blame_signatures);
1775 } else {
1776 bool_invert(\$email_git_blame);
1777 }
1778 $rerun = 1;
1779 } elsif ($sel eq "c") {
1780 if ($val > 0) {
1781 $email_git_min_signatures = $val;
1782 $rerun = 1;
1783 }
1784 } elsif ($sel eq "x") {
1785 if ($val > 0) {
1786 $email_git_max_maintainers = $val;
1787 $rerun = 1;
1788 }
1789 } elsif ($sel eq "%") {
1790 if ($str ne "" && $val >= 0) {
1791 $email_git_min_percent = $val;
1792 $rerun = 1;
1793 }
1794 } elsif ($sel eq "d") {
1795 if (vcs_is_git()) {
1796 $email_git_since = $str;
1797 } elsif (vcs_is_hg()) {
1798 $email_hg_since = $str;
1799 }
1800 $rerun = 1;
1801 } elsif ($sel eq "t") {
1802 bool_invert(\$email_git_all_signature_types);
1803 $rerun = 1;
1804 } elsif ($sel eq "f") {
1805 bool_invert(\$file_emails);
1806 $rerun = 1;
1807 } elsif ($sel eq "r") {
1808 bool_invert(\$email_remove_duplicates);
1809 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001810 } elsif ($sel eq "m") {
1811 bool_invert(\$email_use_mailmap);
1812 read_mailmap();
1813 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001814 } elsif ($sel eq "k") {
1815 bool_invert(\$keywords);
1816 $rerun = 1;
1817 } elsif ($sel eq "p") {
1818 if ($str ne "" && $val >= 0) {
1819 $pattern_depth = $val;
1820 $rerun = 1;
1821 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001822 } elsif ($sel eq "h" || $sel eq "?") {
1823 print STDERR <<EOT
1824
1825Interactive mode allows you to select the various maintainers, submitters,
1826commit signers and mailing lists that could be CC'd on a patch.
1827
1828Any *'d entry is selected.
1829
Joe Perches47abc722010-10-26 14:22:57 -07001830If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001831history of files in the patch. Also, each line of the current file can
1832be matched to its commit author and that commits signers with blame.
1833
1834Various knobs exist to control the length of time for active commit
1835tracking, the maximum number of commit authors and signers to add,
1836and such.
1837
1838Enter selections at the prompt until you are satisfied that the selected
1839maintainers are appropriate. You may enter multiple selections separated
1840by either commas or spaces.
1841
1842EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001843 } else {
1844 print STDERR "invalid option: '$nr'\n";
1845 $redraw = 0;
1846 }
1847 }
1848 if ($rerun) {
1849 print STDERR "git-blame can be very slow, please have patience..."
1850 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001851 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001852 }
1853 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001854
1855 #drop not selected entries
1856 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001857 my @new_emailto = ();
1858 foreach my $entry (@list) {
1859 if ($selected{$count}) {
1860 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001861 }
1862 $count++;
1863 }
Joe Perches683c6f82010-10-26 14:22:55 -07001864 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001865}
1866
Joe Perches683c6f82010-10-26 14:22:55 -07001867sub bool_invert {
1868 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001869
Joe Perches683c6f82010-10-26 14:22:55 -07001870 if ($$bool_ref) {
1871 $$bool_ref = 0;
1872 } else {
1873 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001874 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001875}
1876
Joe Perchesb9e23312010-10-26 14:22:58 -07001877sub deduplicate_email {
1878 my ($email) = @_;
1879
1880 my $matched = 0;
1881 my ($name, $address) = parse_email($email);
1882 $email = format_email($name, $address, 1);
1883 $email = mailmap_email($email);
1884
1885 return $email if (!$email_remove_duplicates);
1886
1887 ($name, $address) = parse_email($email);
1888
Joe Perchesfae99202010-10-26 14:22:58 -07001889 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001890 $name = $deduplicate_name_hash{lc($name)}->[0];
1891 $address = $deduplicate_name_hash{lc($name)}->[1];
1892 $matched = 1;
1893 } elsif ($deduplicate_address_hash{lc($address)}) {
1894 $name = $deduplicate_address_hash{lc($address)}->[0];
1895 $address = $deduplicate_address_hash{lc($address)}->[1];
1896 $matched = 1;
1897 }
1898 if (!$matched) {
1899 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1900 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1901 }
1902 $email = format_email($name, $address, 1);
1903 $email = mailmap_email($email);
1904 return $email;
1905}
1906
Joe Perches683c6f82010-10-26 14:22:55 -07001907sub save_commits_by_author {
1908 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001909
Joe Perches683c6f82010-10-26 14:22:55 -07001910 my @authors = ();
1911 my @commits = ();
1912 my @subjects = ();
1913
1914 foreach my $line (@lines) {
1915 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1916 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001917 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001918 push(@authors, $author);
1919 }
1920 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1921 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1922 }
1923
1924 for (my $i = 0; $i < @authors; $i++) {
1925 my $exists = 0;
1926 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1927 if (@{$ref}[0] eq $commits[$i] &&
1928 @{$ref}[1] eq $subjects[$i]) {
1929 $exists = 1;
1930 last;
1931 }
1932 }
1933 if (!$exists) {
1934 push(@{$commit_author_hash{$authors[$i]}},
1935 [ ($commits[$i], $subjects[$i]) ]);
1936 }
1937 }
1938}
1939
1940sub save_commits_by_signer {
1941 my (@lines) = @_;
1942
1943 my $commit = "";
1944 my $subject = "";
1945
1946 foreach my $line (@lines) {
1947 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1948 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1949 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1950 my @signatures = ($line);
1951 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1952 my @types = @$types_ref;
1953 my @signers = @$signers_ref;
1954
1955 my $type = $types[0];
1956 my $signer = $signers[0];
1957
Joe Perchesb9e23312010-10-26 14:22:58 -07001958 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001959
Joe Perches683c6f82010-10-26 14:22:55 -07001960 my $exists = 0;
1961 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1962 if (@{$ref}[0] eq $commit &&
1963 @{$ref}[1] eq $subject &&
1964 @{$ref}[2] eq $type) {
1965 $exists = 1;
1966 last;
1967 }
1968 }
1969 if (!$exists) {
1970 push(@{$commit_signer_hash{$signer}},
1971 [ ($commit, $subject, $type) ]);
1972 }
1973 }
1974 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001975}
1976
Joe Perches60db31a2009-12-14 18:00:50 -08001977sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001978 my ($role, $divisor, @lines) = @_;
1979
1980 my %hash;
1981 my $count = 0;
1982
Joe Perchesa8af2432009-12-14 18:00:49 -08001983 return if (@lines <= 0);
1984
1985 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001986 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001987 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001988 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001989
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001990 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001991
Joe Perches63ab52d2010-10-26 14:22:51 -07001992 return if (@lines <= 0);
1993
Joe Perches0e70e832009-09-21 17:04:20 -07001994 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001995
Joe Perches11ecf532009-09-21 17:04:22 -07001996 # uniq -c
1997 $hash{$_}++ for @lines;
1998
1999 # sort -rn
2000 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
2001 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08002002 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08002003
Joe Perchesa8af2432009-12-14 18:00:49 -08002004 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07002005 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07002006 $count++;
2007 last if ($sign_offs < $email_git_min_signatures ||
2008 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08002009 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08002010 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08002011 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08002012 my $fmt_percent = sprintf("%.0f", $percent);
2013 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
2014 } else {
2015 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08002016 }
Joe Perchesf5492662009-09-21 17:04:13 -07002017 }
2018}
2019
Joe Perches60db31a2009-12-14 18:00:50 -08002020sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08002021 my ($file) = @_;
2022
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002023 my $authors_ref;
2024 my $signers_ref;
2025 my $stats_ref;
2026 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002027 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002028 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08002029 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002030
Joe Perches683c6f82010-10-26 14:22:55 -07002031 $vcs_used = vcs_exists();
2032 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08002033
Joe Perches60db31a2009-12-14 18:00:50 -08002034 my $cmd = $VCS_cmds{"find_signers_cmd"};
2035 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2036
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002037 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2038
2039 @signers = @{$signers_ref} if defined $signers_ref;
2040 @authors = @{$authors_ref} if defined $authors_ref;
2041 @stats = @{$stats_ref} if defined $stats_ref;
2042
2043# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07002044
2045 foreach my $signer (@signers) {
2046 $signer = deduplicate_email($signer);
2047 }
2048
Joe Perches60db31a2009-12-14 18:00:50 -08002049 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002050 vcs_assign("authored", $commits, @authors);
2051 if ($#authors == $#stats) {
2052 my $stat_pattern = $VCS_cmds{"stat_pattern"};
2053 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
2054
2055 my $added = 0;
2056 my $deleted = 0;
2057 for (my $i = 0; $i <= $#stats; $i++) {
2058 if ($stats[$i] =~ /$stat_pattern/) {
2059 $added += $1;
2060 $deleted += $2;
2061 }
2062 }
2063 my @tmp_authors = uniq(@authors);
2064 foreach my $author (@tmp_authors) {
2065 $author = deduplicate_email($author);
2066 }
2067 @tmp_authors = uniq(@tmp_authors);
2068 my @list_added = ();
2069 my @list_deleted = ();
2070 foreach my $author (@tmp_authors) {
2071 my $auth_added = 0;
2072 my $auth_deleted = 0;
2073 for (my $i = 0; $i <= $#stats; $i++) {
2074 if ($author eq deduplicate_email($authors[$i]) &&
2075 $stats[$i] =~ /$stat_pattern/) {
2076 $auth_added += $1;
2077 $auth_deleted += $2;
2078 }
2079 }
2080 for (my $i = 0; $i < $auth_added; $i++) {
2081 push(@list_added, $author);
2082 }
2083 for (my $i = 0; $i < $auth_deleted; $i++) {
2084 push(@list_deleted, $author);
2085 }
2086 }
2087 vcs_assign("added_lines", $added, @list_added);
2088 vcs_assign("removed_lines", $deleted, @list_deleted);
2089 }
Joe Perchesa8af2432009-12-14 18:00:49 -08002090}
2091
Joe Perches60db31a2009-12-14 18:00:50 -08002092sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07002093 my ($file) = @_;
2094
Joe Perches60db31a2009-12-14 18:00:50 -08002095 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07002096 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002097 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002098 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002099 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002100
Joe Perches683c6f82010-10-26 14:22:55 -07002101 $vcs_used = vcs_exists();
2102 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002103
Joe Perches63ab52d2010-10-26 14:22:51 -07002104 @all_commits = vcs_blame($file);
2105 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002106 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002107 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002108
Joe Perches683c6f82010-10-26 14:22:55 -07002109 if ($email_git_blame_signatures) {
2110 if (vcs_is_hg()) {
2111 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002112 my $commit_authors_ref;
2113 my $commit_signers_ref;
2114 my $stats_ref;
2115 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002116 my @commit_signers = ();
2117 my $commit = join(" -r ", @commits);
2118 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002119
Joe Perches683c6f82010-10-26 14:22:55 -07002120 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2121 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002122
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002123 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2124 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2125 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002126
Joe Perches683c6f82010-10-26 14:22:55 -07002127 push(@signers, @commit_signers);
2128 } else {
2129 foreach my $commit (@commits) {
2130 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002131 my $commit_authors_ref;
2132 my $commit_signers_ref;
2133 my $stats_ref;
2134 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002135 my @commit_signers = ();
2136 my $cmd;
2137
2138 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2139 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2140
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002141 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2142 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2143 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002144
2145 push(@signers, @commit_signers);
2146 }
2147 }
Joe Perchesf5492662009-09-21 17:04:13 -07002148 }
2149
Joe Perchesa8af2432009-12-14 18:00:49 -08002150 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002151 if ($output_rolestats) {
2152 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002153 if (vcs_is_hg()) {{ # Double brace for last exit
2154 my $commit_count;
2155 my @commit_signers = ();
2156 @commits = uniq(@commits);
2157 @commits = sort(@commits);
2158 my $commit = join(" -r ", @commits);
2159 my $cmd;
2160
2161 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2162 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2163
2164 my @lines = ();
2165
2166 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2167
2168 if (!$email_git_penguin_chiefs) {
2169 @lines = grep(!/${penguin_chiefs}/i, @lines);
2170 }
2171
2172 last if !@lines;
2173
2174 my @authors = ();
2175 foreach my $line (@lines) {
2176 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2177 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002178 $author = deduplicate_email($author);
2179 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002180 }
2181 }
2182
2183 save_commits_by_author(@lines) if ($interactive);
2184 save_commits_by_signer(@lines) if ($interactive);
2185
2186 push(@signers, @authors);
2187 }}
2188 else {
2189 foreach my $commit (@commits) {
2190 my $i;
2191 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2192 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2193 my @author = vcs_find_author($cmd);
2194 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002195
2196 my $formatted_author = deduplicate_email($author[0]);
2197
Joe Perches683c6f82010-10-26 14:22:55 -07002198 my $count = grep(/$commit/, @all_commits);
2199 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002200 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002201 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002202 }
2203 }
2204 if (@blame_signers) {
2205 vcs_assign("authored lines", $total_lines, @blame_signers);
2206 }
2207 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002208 foreach my $signer (@signers) {
2209 $signer = deduplicate_email($signer);
2210 }
Joe Perches60db31a2009-12-14 18:00:50 -08002211 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002212 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002213 foreach my $signer (@signers) {
2214 $signer = deduplicate_email($signer);
2215 }
Joe Perches60db31a2009-12-14 18:00:50 -08002216 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002217 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002218}
2219
Joe Perches4cad35a2016-08-02 14:04:10 -07002220sub vcs_file_exists {
2221 my ($file) = @_;
2222
2223 my $exists;
2224
2225 my $vcs_used = vcs_exists();
2226 return 0 if (!$vcs_used);
2227
2228 my $cmd = $VCS_cmds{"file_exists_cmd"};
2229 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
Joe Perches8582fb52016-08-25 15:16:48 -07002230 $cmd .= " 2>&1";
Joe Perches4cad35a2016-08-02 14:04:10 -07002231 $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
2232
Joe Perches8582fb52016-08-25 15:16:48 -07002233 return 0 if ($? != 0);
2234
Joe Perches4cad35a2016-08-02 14:04:10 -07002235 return $exists;
2236}
2237
Tom Saegere1f75902017-11-17 15:27:42 -08002238sub vcs_list_files {
2239 my ($file) = @_;
2240
2241 my @lsfiles = ();
2242
2243 my $vcs_used = vcs_exists();
2244 return 0 if (!$vcs_used);
2245
2246 my $cmd = $VCS_cmds{"list_files_cmd"};
2247 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2248 @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
2249
2250 return () if ($? != 0);
2251
2252 return @lsfiles;
2253}
2254
Joe Perchescb7301c2009-04-07 20:40:12 -07002255sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002256 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002257
2258 my %saw;
2259 @parms = grep(!$saw{$_}++, @parms);
2260 return @parms;
2261}
2262
2263sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002264 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002265
2266 my %saw;
2267 @parms = sort @parms;
2268 @parms = grep(!$saw{$_}++, @parms);
2269 return @parms;
2270}
2271
Joe Perches03372db2010-03-05 13:43:00 -08002272sub clean_file_emails {
2273 my (@file_emails) = @_;
2274 my @fmt_emails = ();
2275
2276 foreach my $email (@file_emails) {
2277 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2278 my ($name, $address) = parse_email($email);
2279 if ($name eq '"[,\.]"') {
2280 $name = "";
2281 }
2282
2283 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2284 if (@nw > 2) {
2285 my $first = $nw[@nw - 3];
2286 my $middle = $nw[@nw - 2];
2287 my $last = $nw[@nw - 1];
2288
2289 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2290 (length($first) == 2 && substr($first, -1) eq ".")) ||
2291 (length($middle) == 1 ||
2292 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2293 $name = "$first $middle $last";
2294 } else {
2295 $name = "$middle $last";
2296 }
2297 }
2298
2299 if (substr($name, -1) =~ /[,\.]/) {
2300 $name = substr($name, 0, length($name) - 1);
2301 } elsif (substr($name, -2) =~ /[,\.]"/) {
2302 $name = substr($name, 0, length($name) - 2) . '"';
2303 }
2304
2305 if (substr($name, 0, 1) =~ /[,\.]/) {
2306 $name = substr($name, 1, length($name) - 1);
2307 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2308 $name = '"' . substr($name, 2, length($name) - 2);
2309 }
2310
2311 my $fmt_email = format_email($name, $address, $email_usename);
2312 push(@fmt_emails, $fmt_email);
2313 }
2314 return @fmt_emails;
2315}
2316
Joe Perches3c7385b2009-12-14 18:00:46 -08002317sub merge_email {
2318 my @lines;
2319 my %saw;
2320
2321 for (@_) {
2322 my ($address, $role) = @$_;
2323 if (!$saw{$address}) {
2324 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002325 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002326 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002327 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002328 }
2329 $saw{$address} = 1;
2330 }
2331 }
2332
2333 return @lines;
2334}
2335
Joe Perchescb7301c2009-04-07 20:40:12 -07002336sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002337 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002338
2339 if ($output_multiline) {
2340 foreach my $line (@parms) {
2341 print("${line}\n");
2342 }
2343 } else {
2344 print(join($output_separator, @parms));
2345 print("\n");
2346 }
2347}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002348
2349my $rfc822re;
2350
2351sub make_rfc822re {
2352# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2353# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2354# This regexp will only work on addresses which have had comments stripped
2355# and replaced with rfc822_lwsp.
2356
2357 my $specials = '()<>@,;:\\\\".\\[\\]';
2358 my $controls = '\\000-\\037\\177';
2359
2360 my $dtext = "[^\\[\\]\\r\\\\]";
2361 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2362
2363 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2364
2365# Use zero-width assertion to spot the limit of an atom. A simple
2366# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2367 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2368 my $word = "(?:$atom|$quoted_string)";
2369 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2370
2371 my $sub_domain = "(?:$atom|$domain_literal)";
2372 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2373
2374 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2375
2376 my $phrase = "$word*";
2377 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2378 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2379 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2380
2381 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2382 my $address = "(?:$mailbox|$group)";
2383
2384 return "$rfc822_lwsp*$address";
2385}
2386
2387sub rfc822_strip_comments {
2388 my $s = shift;
2389# Recursively remove comments, and replace with a single space. The simpler
2390# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2391# chars in atoms, for example.
2392
2393 while ($s =~ s/^((?:[^"\\]|\\.)*
2394 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2395 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2396 return $s;
2397}
2398
2399# valid: returns true if the parameter is an RFC822 valid address
2400#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002401sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002402 my $s = rfc822_strip_comments(shift);
2403
2404 if (!$rfc822re) {
2405 $rfc822re = make_rfc822re();
2406 }
2407
2408 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2409}
2410
2411# validlist: In scalar context, returns true if the parameter is an RFC822
2412# valid list of addresses.
2413#
2414# In list context, returns an empty list on failure (an invalid
2415# address was found); otherwise a list whose first element is the
2416# number of addresses found and whose remaining elements are the
2417# addresses. This is needed to disambiguate failure (invalid)
2418# from success with no addresses found, because an empty string is
2419# a valid list.
2420
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002421sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002422 my $s = rfc822_strip_comments(shift);
2423
2424 if (!$rfc822re) {
2425 $rfc822re = make_rfc822re();
2426 }
2427 # * null list items are valid according to the RFC
2428 # * the '1' business is to aid in distinguishing failure from no results
2429
2430 my @r;
2431 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2432 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002433 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002434 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002435 }
2436 return wantarray ? (scalar(@r), @r) : 1;
2437 }
Joe Perches60db31a2009-12-14 18:00:50 -08002438 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002439}