blob: bc443201d3ef00ac2b197da0896a186891cff188 [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;
Joe Perchescb7301c2009-04-07 20:40:12 -070060my $version = 0;
61my $help = 0;
Joe Perches6f7d98e2017-08-04 21:45:48 -070062my $find_maintainer_files = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070063
Joe Perches683c6f82010-10-26 14:22:55 -070064my $vcs_used = 0;
65
Joe Perchescb7301c2009-04-07 20:40:12 -070066my $exit = 0;
67
Joe Perches683c6f82010-10-26 14:22:55 -070068my %commit_author_hash;
69my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070070
Joe Perchescb7301c2009-04-07 20:40:12 -070071my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070072push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070073#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070074#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070075
76my @penguin_chief_names = ();
77foreach my $chief (@penguin_chief) {
78 if ($chief =~ m/^(.*):(.*)/) {
79 my $chief_name = $1;
80 my $chief_addr = $2;
81 push(@penguin_chief_names, $chief_name);
82 }
83}
Joe Perchese4d26b02010-05-24 14:33:17 -070084my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
85
86# Signature types of people who are either
87# a) responsible for the code in question, or
88# b) familiar enough with it to give relevant feedback
89my @signature_tags = ();
90push(@signature_tags, "Signed-off-by:");
91push(@signature_tags, "Reviewed-by:");
92push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070093
Joe Perches7dea2682012-06-20 12:53:02 -070094my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
95
Joe Perches5f2441e2009-06-16 15:34:02 -070096# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070097my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070098my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070099
Joe Perches60db31a2009-12-14 18:00:50 -0800100# VCS command support: class-like functions and strings
101
102my %VCS_cmds;
103
104my %VCS_cmds_git = (
105 "execute_cmd" => \&git_execute_cmd,
Richard Genoudec83b612014-02-10 14:25:31 -0800106 "available" => '(which("git") ne "") && (-e ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -0700107 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800108 "git log --no-color --follow --since=\$email_git_since " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800109 '--numstat --no-merges ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700110 '--format="GitCommit: %H%n' .
111 'GitAuthor: %an <%ae>%n' .
112 'GitDate: %aD%n' .
113 'GitSubject: %s%n' .
114 '%b%n"' .
115 " -- \$file",
116 "find_commit_signers_cmd" =>
117 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800118 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700119 '--format="GitCommit: %H%n' .
120 'GitAuthor: %an <%ae>%n' .
121 'GitDate: %aD%n' .
122 'GitSubject: %s%n' .
123 '%b%n"' .
124 " -1 \$commit",
125 "find_commit_author_cmd" =>
126 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800127 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700128 '--format="GitCommit: %H%n' .
129 'GitAuthor: %an <%ae>%n' .
130 'GitDate: %aD%n' .
131 'GitSubject: %s%n"' .
132 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800133 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
134 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700135 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700136 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700137 "author_pattern" => "^GitAuthor: (.*)",
138 "subject_pattern" => "^GitSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800139 "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700140 "file_exists_cmd" => "git ls-files \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800141);
142
143my %VCS_cmds_hg = (
144 "execute_cmd" => \&hg_execute_cmd,
145 "available" => '(which("hg") ne "") && (-d ".hg")',
146 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700147 "hg log --date=\$email_hg_since " .
148 "--template='HgCommit: {node}\\n" .
149 "HgAuthor: {author}\\n" .
150 "HgSubject: {desc}\\n'" .
151 " -- \$file",
152 "find_commit_signers_cmd" =>
153 "hg log " .
154 "--template='HgSubject: {desc}\\n'" .
155 " -r \$commit",
156 "find_commit_author_cmd" =>
157 "hg log " .
158 "--template='HgCommit: {node}\\n" .
159 "HgAuthor: {author}\\n" .
160 "HgSubject: {desc|firstline}\\n'" .
161 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800162 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700163 "blame_file_cmd" => "hg blame -n \$file",
164 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
165 "blame_commit_pattern" => "^([ 0-9a-f]+):",
166 "author_pattern" => "^HgAuthor: (.*)",
167 "subject_pattern" => "^HgSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800168 "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700169 "file_exists_cmd" => "hg files \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800170);
171
Joe Perchesbcde44e2010-10-26 14:22:53 -0700172my $conf = which_conf(".get_maintainer.conf");
173if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700174 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700175 open(my $conffile, '<', "$conf")
176 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
177
Joe Perches368669d2010-05-24 14:33:19 -0700178 while (<$conffile>) {
179 my $line = $_;
180
181 $line =~ s/\s*\n?$//g;
182 $line =~ s/^\s*//g;
183 $line =~ s/\s+/ /g;
184
185 next if ($line =~ m/^\s*#/);
186 next if ($line =~ m/^\s*$/);
187
188 my @words = split(" ", $line);
189 foreach my $word (@words) {
190 last if ($word =~ m/^#/);
191 push (@conf_args, $word);
192 }
193 }
194 close($conffile);
195 unshift(@ARGV, @conf_args) if @conf_args;
196}
197
Joe Perches435de072015-06-25 15:01:50 -0700198my @ignore_emails = ();
199my $ignore_file = which_conf(".get_maintainer.ignore");
200if (-f $ignore_file) {
201 open(my $ignore, '<', "$ignore_file")
202 or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
203 while (<$ignore>) {
204 my $line = $_;
205
206 $line =~ s/\s*\n?$//;
207 $line =~ s/^\s*//;
208 $line =~ s/\s+$//;
209 $line =~ s/#.*$//;
210
211 next if ($line =~ m/^\s*$/);
212 if (rfc822_valid($line)) {
213 push(@ignore_emails, $line);
214 }
215 }
216 close($ignore);
217}
218
Joe Perchescb7301c2009-04-07 20:40:12 -0700219if (!GetOptions(
220 'email!' => \$email,
221 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700222 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800223 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700224 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700225 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700226 'git-chief-penguins!' => \$email_git_penguin_chiefs,
227 'git-min-signatures=i' => \$email_git_min_signatures,
228 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700229 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700230 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800231 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700232 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700233 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700234 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700235 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700236 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700237 'n!' => \$email_usename,
238 'l!' => \$email_list,
239 's!' => \$email_subscriber_list,
240 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800241 'roles!' => \$output_roles,
242 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700243 'separator=s' => \$output_separator,
244 'subsystem!' => \$subsystem,
245 'status!' => \$status,
246 'scm!' => \$scm,
247 'web!' => \$web,
Joe Perches03aed212016-12-12 16:45:59 -0800248 'letters=s' => \$letters,
Joe Perches3fb55652009-09-21 17:04:17 -0700249 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700250 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800251 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800252 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700253 'f|file' => \$from_filename,
Joe Perches6f7d98e2017-08-04 21:45:48 -0700254 'find-maintainer-files' => \$find_maintainer_files,
Joe Perchescb7301c2009-04-07 20:40:12 -0700255 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800256 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700257 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800258 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700259}
260
261if ($help != 0) {
262 usage();
263 exit 0;
264}
265
266if ($version != 0) {
267 print("${P} ${V}\n");
268 exit 0;
269}
270
Joe Perches64f77f32010-03-05 13:43:04 -0800271if (-t STDIN && !@ARGV) {
272 # We're talking to a terminal, but have no command line arguments.
273 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700274}
275
Joe Perches683c6f82010-10-26 14:22:55 -0700276$output_multiline = 0 if ($output_separator ne ", ");
277$output_rolestats = 1 if ($interactive);
278$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800279
Joe Perches03aed212016-12-12 16:45:59 -0800280if ($sections || $letters ne "") {
281 $sections = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800282 $email = 0;
283 $email_list = 0;
284 $scm = 0;
285 $status = 0;
286 $subsystem = 0;
287 $web = 0;
288 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700289 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800290} else {
291 my $selections = $email + $scm + $status + $subsystem + $web;
292 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800293 die "$P: Missing required option: email, scm, status, subsystem or web\n";
294 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700295}
296
Joe Perchesf5492662009-09-21 17:04:13 -0700297if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700298 ($email_maintainer + $email_reviewer +
299 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700300 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700301 die "$P: Please select at least 1 email option\n";
302}
303
304if (!top_of_kernel_tree($lk_path)) {
305 die "$P: The current directory does not appear to be "
306 . "a linux kernel source tree.\n";
307}
308
309## Read MAINTAINERS for type/value pairs
310
311my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700312my %keyword_hash;
Joe Perches6f7d98e2017-08-04 21:45:48 -0700313my @mfiles = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700314
Joe Perches6f7d98e2017-08-04 21:45:48 -0700315sub read_maintainer_file {
316 my ($file) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700317
Joe Perches6f7d98e2017-08-04 21:45:48 -0700318 open (my $maint, '<', "$file")
319 or die "$P: Can't open MAINTAINERS file '$file': $!\n";
320 while (<$maint>) {
321 my $line = $_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700322
Joe Perches6f7d98e2017-08-04 21:45:48 -0700323 if ($line =~ m/^([A-Z]):\s*(.*)/) {
324 my $type = $1;
325 my $value = $2;
326
327 ##Filename pattern matching
328 if ($type eq "F" || $type eq "X") {
329 $value =~ s@\.@\\\.@g; ##Convert . to \.
330 $value =~ s/\*/\.\*/g; ##Convert * to .*
331 $value =~ s/\?/\./g; ##Convert ? to .
332 ##if pattern is a directory and it lacks a trailing slash, add one
333 if ((-d $value)) {
334 $value =~ s@([^/])$@$1/@;
335 }
336 } elsif ($type eq "K") {
337 $keyword_hash{@typevalue} = $value;
Joe Perches870020f2009-07-29 15:04:28 -0700338 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700339 push(@typevalue, "$type:$value");
340 } elsif (!(/^\s*$/ || /^\s*\#/)) {
341 $line =~ s/\n$//g;
342 push(@typevalue, $line);
Joe Perchescb7301c2009-04-07 20:40:12 -0700343 }
Joe Perches6f7d98e2017-08-04 21:45:48 -0700344 }
345 close($maint);
346}
347
348sub find_is_maintainer_file {
349 my ($file) = $_;
350 return if ($file !~ m@/MAINTAINERS$@);
351 $file = $File::Find::name;
352 return if (! -f $file);
353 push(@mfiles, $file);
354}
355
356sub find_ignore_git {
357 return grep { $_ !~ /^\.git$/; } @_;
358}
359
360if (-d "${lk_path}MAINTAINERS") {
361 opendir(DIR, "${lk_path}MAINTAINERS") or die $!;
362 my @files = readdir(DIR);
363 closedir(DIR);
364 foreach my $file (@files) {
365 push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./);
Joe Perchescb7301c2009-04-07 20:40:12 -0700366 }
367}
Joe Perchescb7301c2009-04-07 20:40:12 -0700368
Joe Perches6f7d98e2017-08-04 21:45:48 -0700369if ($find_maintainer_files) {
370 find( { wanted => \&find_is_maintainer_file,
371 preprocess => \&find_ignore_git,
372 no_chdir => 1,
373 }, "${lk_path}");
374} else {
375 push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS";
376}
377
378foreach my $file (@mfiles) {
379 read_maintainer_file("$file");
380}
Joe Perches8cbb3a72009-09-21 17:04:21 -0700381
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700382#
383# Read mail address map
384#
385
Joe Perchesb9e23312010-10-26 14:22:58 -0700386my $mailmap;
387
388read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700389
390sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700391 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700392 names => {},
393 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700394 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700395
Joe Perchesb9e23312010-10-26 14:22:58 -0700396 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700397
398 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800399 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700400
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700401 while (<$mailmap_file>) {
402 s/#.*$//; #strip comments
403 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700404
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700405 next if (/^\s*$/); #skip empty lines
406 #entries have one of the following formats:
407 # name1 <mail1>
408 # <mail1> <mail2>
409 # name1 <mail1> <mail2>
410 # name1 <mail1> name2 <mail2>
411 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700412
413 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700414 my $real_name = $1;
415 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700416
Joe Perches47abc722010-10-26 14:22:57 -0700417 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700418 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700419 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700420
Joe Perches0334b382011-07-25 17:13:13 -0700421 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700422 my $real_address = $1;
423 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700424
Joe Perches47abc722010-10-26 14:22:57 -0700425 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700426
Joe Perches0334b382011-07-25 17:13:13 -0700427 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700428 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700429 my $real_address = $2;
430 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700431
Joe Perches47abc722010-10-26 14:22:57 -0700432 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700433 ($real_name, $real_address) =
434 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700435 $mailmap->{names}->{$wrong_address} = $real_name;
436 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700437
Joe Perches0334b382011-07-25 17:13:13 -0700438 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700439 my $real_name = $1;
440 my $real_address = $2;
441 my $wrong_name = $3;
442 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700443
Joe Perches47abc722010-10-26 14:22:57 -0700444 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700445 ($real_name, $real_address) =
446 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700447
Joe Perchesb9e23312010-10-26 14:22:58 -0700448 $wrong_name =~ s/\s+$//;
449 ($wrong_name, $wrong_address) =
450 parse_email("$wrong_name <$wrong_address>");
451
452 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
453 $mailmap->{names}->{$wrong_email} = $real_name;
454 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700455 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700456 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700457 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700458}
459
Joe Perches4a7fdb52009-04-10 12:28:57 -0700460## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700461
462my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700463my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700464my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800465my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700466
Joe Perches64f77f32010-03-05 13:43:04 -0800467if (!@ARGV) {
468 push(@ARGV, "&STDIN");
469}
470
Joe Perches4a7fdb52009-04-10 12:28:57 -0700471foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800472 if ($file ne "&STDIN") {
473 ##if $file is a directory and it lacks a trailing slash, add one
474 if ((-d $file)) {
475 $file =~ s@([^/])$@$1/@;
476 } elsif (!(-f $file)) {
477 die "$P: file '${file}' not found\n";
478 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700479 }
Joe Perchesaec742e2016-08-10 08:45:11 -0700480 if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
Joe Perchesbe17bdd2016-01-20 14:58:24 -0800481 $file =~ s/^\Q${cur_path}\E//; #strip any absolute path
482 $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
Joe Perches4a7fdb52009-04-10 12:28:57 -0700483 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700484 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800485 open(my $f, '<', $file)
486 or die "$P: Can't open $file: $!\n";
487 my $text = do { local($/) ; <$f> };
488 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800489 if ($keywords) {
490 foreach my $line (keys %keyword_hash) {
491 if ($text =~ m/$keyword_hash{$line}/x) {
492 push(@keyword_tvi, $line);
493 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700494 }
495 }
Joe Perches03372db2010-03-05 13:43:00 -0800496 if ($file_emails) {
497 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;
498 push(@file_emails, clean_file_emails(@poss_addr));
499 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700500 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700501 } else {
502 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700503 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800504
Wolfram Sang3a4df132010-03-23 13:35:18 -0700505 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800506 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700507
508 # We can check arbitrary information before the patch
509 # like the commit message, mail headers, etc...
510 # This allows us to match arbitrary keywords against any part
511 # of a git format-patch generated file (subject tags, etc...)
512
513 my $patch_prefix = ""; #Parsing the intro
514
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800515 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700516 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800517 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700518 my $filename = $1;
519 $filename =~ s@^[^/]*/@@;
520 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700521 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700522 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700523 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700524 } elsif (m/^\@\@ -(\d+),(\d+)/) {
525 if ($email_git_blame) {
526 push(@range, "$lastfile:$1:$2");
527 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700528 } elsif ($keywords) {
529 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700530 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700531 push(@keyword_tvi, $line);
532 }
533 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700534 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700535 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800536 close($patch);
537
Joe Perches4a7fdb52009-04-10 12:28:57 -0700538 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700539 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700540 . "Add -f to options?\n";
541 }
542 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700543 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700544}
545
Joe Perches03372db2010-03-05 13:43:00 -0800546@file_emails = uniq(@file_emails);
547
Joe Perches683c6f82010-10-26 14:22:55 -0700548my %email_hash_name;
549my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700550my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700551my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700552my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700553my @scm = ();
554my @web = ();
555my @subsystem = ();
556my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700557my %deduplicate_name_hash = ();
558my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700559
Joe Perches6ef1c522010-10-26 14:22:56 -0700560my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700561
Joe Perches6ef1c522010-10-26 14:22:56 -0700562if (@maintainers) {
563 @maintainers = merge_email(@maintainers);
564 output(@maintainers);
565}
Joe Perchescb7301c2009-04-07 20:40:12 -0700566
567if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700568 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700569 output(@scm);
570}
Joe Perches683c6f82010-10-26 14:22:55 -0700571
Joe Perchescb7301c2009-04-07 20:40:12 -0700572if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700573 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700574 output(@status);
575}
576
577if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700578 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700579 output(@subsystem);
580}
581
582if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700583 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700584 output(@web);
585}
586
587exit($exit);
588
Joe Perches435de072015-06-25 15:01:50 -0700589sub ignore_email_address {
590 my ($address) = @_;
591
592 foreach my $ignore (@ignore_emails) {
593 return 1 if ($ignore eq $address);
594 }
595
596 return 0;
597}
598
Joe Perchesab6c9372011-01-12 16:59:50 -0800599sub range_is_maintained {
600 my ($start, $end) = @_;
601
602 for (my $i = $start; $i < $end; $i++) {
603 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700604 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800605 my $type = $1;
606 my $value = $2;
607 if ($type eq 'S') {
608 if ($value =~ /(maintain|support)/i) {
609 return 1;
610 }
611 }
612 }
613 }
614 return 0;
615}
616
617sub range_has_maintainer {
618 my ($start, $end) = @_;
619
620 for (my $i = $start; $i < $end; $i++) {
621 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700622 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800623 my $type = $1;
624 my $value = $2;
625 if ($type eq 'M') {
626 return 1;
627 }
628 }
629 }
630 return 0;
631}
632
Joe Perches6ef1c522010-10-26 14:22:56 -0700633sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700634 %email_hash_name = ();
635 %email_hash_address = ();
636 %commit_author_hash = ();
637 %commit_signer_hash = ();
638 @email_to = ();
639 %hash_list_to = ();
640 @list_to = ();
641 @scm = ();
642 @web = ();
643 @subsystem = ();
644 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700645 %deduplicate_name_hash = ();
646 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700647 if ($email_git_all_signature_types) {
648 $signature_pattern = "(.+?)[Bb][Yy]:";
649 } else {
650 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
651 }
652
653 # Find responsible parties
654
Joe Perchesb9e23312010-10-26 14:22:58 -0700655 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700656
Joe Perches683c6f82010-10-26 14:22:55 -0700657 foreach my $file (@files) {
658
659 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700660 my $tvi = find_first_section();
661 while ($tvi < @typevalue) {
662 my $start = find_starting_index($tvi);
663 my $end = find_ending_index($tvi);
664 my $exclude = 0;
665 my $i;
666
667 #Do not match excluded file patterns
668
669 for ($i = $start; $i < $end; $i++) {
670 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700671 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700672 my $type = $1;
673 my $value = $2;
674 if ($type eq 'X') {
675 if (file_match_pattern($file, $value)) {
676 $exclude = 1;
677 last;
678 }
679 }
680 }
681 }
682
683 if (!$exclude) {
684 for ($i = $start; $i < $end; $i++) {
685 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700686 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700687 my $type = $1;
688 my $value = $2;
689 if ($type eq 'F') {
690 if (file_match_pattern($file, $value)) {
691 my $value_pd = ($value =~ tr@/@@);
692 my $file_pd = ($file =~ tr@/@@);
693 $value_pd++ if (substr($value,-1,1) ne "/");
694 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800695 if ($value_pd >= $file_pd &&
696 range_is_maintained($start, $end) &&
697 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700698 $exact_pattern_match_hash{$file} = 1;
699 }
Joe Perches683c6f82010-10-26 14:22:55 -0700700 if ($pattern_depth == 0 ||
701 (($file_pd - $value_pd) < $pattern_depth)) {
702 $hash{$tvi} = $value_pd;
703 }
704 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700705 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800706 if ($file =~ m/$value/x) {
707 $hash{$tvi} = 0;
708 }
Joe Perches683c6f82010-10-26 14:22:55 -0700709 }
710 }
711 }
712 }
713 $tvi = $end + 1;
714 }
715
716 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
717 add_categories($line);
718 if ($sections) {
719 my $i;
720 my $start = find_starting_index($line);
721 my $end = find_ending_index($line);
722 for ($i = $start; $i < $end; $i++) {
723 my $line = $typevalue[$i];
724 if ($line =~ /^[FX]:/) { ##Restore file patterns
725 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
726 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
727 $line =~ s/\\\./\./g; ##Convert \. to .
728 $line =~ s/\.\*/\*/g; ##Convert .* to *
729 }
Joe Perches03aed212016-12-12 16:45:59 -0800730 my $count = $line =~ s/^([A-Z]):/$1:\t/g;
731 if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
732 print("$line\n");
733 }
Joe Perches683c6f82010-10-26 14:22:55 -0700734 }
735 print("\n");
736 }
737 }
Joe Perches683c6f82010-10-26 14:22:55 -0700738 }
739
740 if ($keywords) {
741 @keyword_tvi = sort_and_uniq(@keyword_tvi);
742 foreach my $line (@keyword_tvi) {
743 add_categories($line);
744 }
745 }
746
Joe Perchesb9e23312010-10-26 14:22:58 -0700747 foreach my $email (@email_to, @list_to) {
748 $email->[0] = deduplicate_email($email->[0]);
749 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700750
751 foreach my $file (@files) {
752 if ($email &&
753 ($email_git || ($email_git_fallback &&
754 !$exact_pattern_match_hash{$file}))) {
755 vcs_file_signoffs($file);
756 }
757 if ($email && $email_git_blame) {
758 vcs_file_blame($file);
759 }
760 }
761
Joe Perches683c6f82010-10-26 14:22:55 -0700762 if ($email) {
763 foreach my $chief (@penguin_chief) {
764 if ($chief =~ m/^(.*):(.*)/) {
765 my $email_address;
766
767 $email_address = format_email($1, $2, $email_usename);
768 if ($email_git_penguin_chiefs) {
769 push(@email_to, [$email_address, 'chief penguin']);
770 } else {
771 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
772 }
773 }
774 }
775
776 foreach my $email (@file_emails) {
777 my ($name, $address) = parse_email($email);
778
779 my $tmp_email = format_email($name, $address, $email_usename);
780 push_email_address($tmp_email, '');
781 add_role($tmp_email, 'in file');
782 }
783 }
784
785 my @to = ();
786 if ($email || $email_list) {
787 if ($email) {
788 @to = (@to, @email_to);
789 }
790 if ($email_list) {
791 @to = (@to, @list_to);
792 }
793 }
794
Joe Perches6ef1c522010-10-26 14:22:56 -0700795 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700796 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700797 }
Joe Perches683c6f82010-10-26 14:22:55 -0700798
799 return @to;
800}
801
Joe Perchescb7301c2009-04-07 20:40:12 -0700802sub file_match_pattern {
803 my ($file, $pattern) = @_;
804 if (substr($pattern, -1) eq "/") {
805 if ($file =~ m@^$pattern@) {
806 return 1;
807 }
808 } else {
809 if ($file =~ m@^$pattern@) {
810 my $s1 = ($file =~ tr@/@@);
811 my $s2 = ($pattern =~ tr@/@@);
812 if ($s1 == $s2) {
813 return 1;
814 }
815 }
816 }
817 return 0;
818}
819
820sub usage {
821 print <<EOT;
822usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700823 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700824version: $V
825
826MAINTAINER field selection options:
827 --email => print email address(es) if any
828 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700829 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700830 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700831 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700832 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700833 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
834 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
835 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700836 --git-blame => use git blame to find modified commits for patch or file
Brian Norris3cbcca82015-11-06 16:30:41 -0800837 --git-blame-signatures => when used with --git-blame, also include all commit signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700838 --git-since => git history to use (default: $email_git_since)
839 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700840 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700841 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700842 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700843 --n => include name 'Full Name <addr\@domain.tld>'
844 --l => include list(s) if any
845 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700846 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800847 --roles => show roles (status:subsystem, git-signer, list, etc...)
848 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800849 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700850 --scm => print SCM tree(s) if any
851 --status => print status if any
852 --subsystem => print subsystem name if any
853 --web => print website(s) if any
854
855Output type options:
856 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700857 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700858 --multiline => print 1 entry per line
859
Joe Perchescb7301c2009-04-07 20:40:12 -0700860Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700861 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700862 --keywords => scan patch for keywords (default: $keywords)
863 --sections => print all of the subsystem sections with pattern matches
Joe Perches03aed212016-12-12 16:45:59 -0800864 --letters => print all matching 'letter' types from all matching sections
Joe Perchesb9e23312010-10-26 14:22:58 -0700865 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700866 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700867 --help => show this help information
868
Joe Perches3fb55652009-09-21 17:04:17 -0700869Default options:
Brian Norris4f075102015-11-06 16:30:49 -0800870 [--email --nogit --git-fallback --m --r --n --l --multiline --pattern-depth=0
Joe Perches7e1863a2011-01-12 16:59:49 -0800871 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700872
Joe Perches870020f2009-07-29 15:04:28 -0700873Notes:
874 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700875 Used with "--git", git signators for _all_ files in and below
876 directory are examined as git recurses directories.
877 Any specified X: (exclude) pattern matches are _not_ ignored.
878 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800879 no individual file within the directory or subdirectory
880 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700881 Used with "--git-blame", does not iterate all files in directory
882 Using "--git-blame" is slow and may add old committers and authors
883 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800884 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
885 other automated tools that expect only ["name"] <email address>
886 may not work because of additional output after <email address>.
887 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
888 not the percentage of the entire file authored. # of commits is
889 not a good measure of amount of code authored. 1 major commit may
890 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800891 If git is not installed, but mercurial (hg) is installed and an .hg
892 repository exists, the following options apply to mercurial:
893 --git,
894 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
895 --git-blame
896 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700897 File ".get_maintainer.conf", if it exists in the linux kernel source root
898 directory, can change whatever get_maintainer defaults are desired.
899 Entries in this file can be any command line argument.
900 This file is prepended to any additional command line arguments.
901 Multiple lines and # comments are allowed.
Brian Norrisb1312bf2015-11-06 16:30:46 -0800902 Most options have both positive and negative forms.
903 The negative forms for --<foo> are --no<foo> and --no-<foo>.
904
Joe Perchescb7301c2009-04-07 20:40:12 -0700905EOT
906}
907
908sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700909 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700910
Joe Perches47abc722010-10-26 14:22:57 -0700911 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
912 $lk_path .= "/";
913 }
914 if ( (-f "${lk_path}COPYING")
915 && (-f "${lk_path}CREDITS")
916 && (-f "${lk_path}Kbuild")
Joe Perches6f7d98e2017-08-04 21:45:48 -0700917 && (-e "${lk_path}MAINTAINERS")
Joe Perches47abc722010-10-26 14:22:57 -0700918 && (-f "${lk_path}Makefile")
919 && (-f "${lk_path}README")
920 && (-d "${lk_path}Documentation")
921 && (-d "${lk_path}arch")
922 && (-d "${lk_path}include")
923 && (-d "${lk_path}drivers")
924 && (-d "${lk_path}fs")
925 && (-d "${lk_path}init")
926 && (-d "${lk_path}ipc")
927 && (-d "${lk_path}kernel")
928 && (-d "${lk_path}lib")
929 && (-d "${lk_path}scripts")) {
930 return 1;
931 }
932 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700933}
934
Joe Perches0e70e832009-09-21 17:04:20 -0700935sub parse_email {
936 my ($formatted_email) = @_;
937
938 my $name = "";
939 my $address = "";
940
Joe Perches11ecf532009-09-21 17:04:22 -0700941 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700942 $name = $1;
943 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700944 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700945 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700946 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700947 $address = $1;
948 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700949
950 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700951 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700952 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700953
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800954 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700955 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700956 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700957 }
Joe Perches0e70e832009-09-21 17:04:20 -0700958
959 return ($name, $address);
960}
961
962sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800963 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700964
965 my $formatted_email;
966
967 $name =~ s/^\s+|\s+$//g;
968 $name =~ s/^\"|\"$//g;
969 $address =~ s/^\s+|\s+$//g;
970
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800971 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700972 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
973 $name = "\"$name\"";
974 }
975
Joe Perchesa8af2432009-12-14 18:00:49 -0800976 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700977 if ("$name" eq "") {
978 $formatted_email = "$address";
979 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800980 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700981 }
982 } else {
983 $formatted_email = $address;
984 }
985
Joe Perchescb7301c2009-04-07 20:40:12 -0700986 return $formatted_email;
987}
988
Joe Perches272a8972010-01-08 14:42:48 -0800989sub find_first_section {
990 my $index = 0;
991
992 while ($index < @typevalue) {
993 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700994 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -0800995 last;
996 }
997 $index++;
998 }
999
1000 return $index;
1001}
1002
Joe Perchesb7816552009-09-21 17:04:24 -07001003sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -07001004 my ($index) = @_;
1005
1006 while ($index > 0) {
1007 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001008 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001009 last;
1010 }
1011 $index--;
1012 }
1013
1014 return $index;
1015}
1016
1017sub find_ending_index {
1018 my ($index) = @_;
1019
1020 while ($index < @typevalue) {
1021 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -07001022 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001023 last;
1024 }
1025 $index++;
1026 }
1027
1028 return $index;
1029}
1030
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001031sub get_subsystem_name {
1032 my ($index) = @_;
1033
1034 my $start = find_starting_index($index);
1035
1036 my $subsystem = $typevalue[$start];
1037 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1038 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
1039 $subsystem =~ s/\s*$//;
1040 $subsystem = $subsystem . "...";
1041 }
1042 return $subsystem;
1043}
1044
Joe Perches3c7385b2009-12-14 18:00:46 -08001045sub get_maintainer_role {
1046 my ($index) = @_;
1047
1048 my $i;
1049 my $start = find_starting_index($index);
1050 my $end = find_ending_index($index);
1051
Joe Perches0ede2742012-03-23 15:01:56 -07001052 my $role = "unknown";
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001053 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001054
1055 for ($i = $start + 1; $i < $end; $i++) {
1056 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001057 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001058 my $ptype = $1;
1059 my $pvalue = $2;
1060 if ($ptype eq "S") {
1061 $role = $pvalue;
1062 }
1063 }
1064 }
1065
1066 $role = lc($role);
1067 if ($role eq "supported") {
1068 $role = "supporter";
1069 } elsif ($role eq "maintained") {
1070 $role = "maintainer";
1071 } elsif ($role eq "odd fixes") {
1072 $role = "odd fixer";
1073 } elsif ($role eq "orphan") {
1074 $role = "orphan minder";
1075 } elsif ($role eq "obsolete") {
1076 $role = "obsolete minder";
1077 } elsif ($role eq "buried alive in reporters") {
1078 $role = "chief penguin";
1079 }
1080
1081 return $role . ":" . $subsystem;
1082}
1083
1084sub get_list_role {
1085 my ($index) = @_;
1086
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001087 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001088
1089 if ($subsystem eq "THE REST") {
1090 $subsystem = "";
1091 }
1092
1093 return $subsystem;
1094}
1095
Joe Perchescb7301c2009-04-07 20:40:12 -07001096sub add_categories {
1097 my ($index) = @_;
1098
Joe Perchesb7816552009-09-21 17:04:24 -07001099 my $i;
1100 my $start = find_starting_index($index);
1101 my $end = find_ending_index($index);
1102
1103 push(@subsystem, $typevalue[$start]);
1104
1105 for ($i = $start + 1; $i < $end; $i++) {
1106 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001107 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001108 my $ptype = $1;
1109 my $pvalue = $2;
1110 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001111 my $list_address = $pvalue;
1112 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001113 my $list_role = get_list_role($i);
1114
1115 if ($list_role ne "") {
1116 $list_role = ":" . $list_role;
1117 }
Joe Perches290603c2009-06-16 15:33:58 -07001118 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1119 $list_address = $1;
1120 $list_additional = $2;
1121 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001122 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001123 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001124 if (!$hash_list_to{lc($list_address)}) {
1125 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001126 push(@list_to, [$list_address,
1127 "subscriber list${list_role}"]);
1128 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001129 }
1130 } else {
1131 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001132 if (!$hash_list_to{lc($list_address)}) {
1133 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001134 if ($list_additional =~ m/moderated/) {
1135 push(@list_to, [$list_address,
1136 "moderated list${list_role}"]);
1137 } else {
1138 push(@list_to, [$list_address,
1139 "open list${list_role}"]);
1140 }
Joe Perches683c6f82010-10-26 14:22:55 -07001141 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001142 }
1143 }
1144 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001145 my ($name, $address) = parse_email($pvalue);
1146 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001147 if ($i > 0) {
1148 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001149 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001150 if ($1 eq "P") {
1151 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001152 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001153 }
1154 }
1155 }
1156 }
Joe Perches0e70e832009-09-21 17:04:20 -07001157 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001158 my $role = get_maintainer_role($i);
1159 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001160 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001161 } elsif ($ptype eq "R") {
1162 my ($name, $address) = parse_email($pvalue);
1163 if ($name eq "") {
1164 if ($i > 0) {
1165 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001166 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001167 if ($1 eq "P") {
1168 $name = $2;
1169 $pvalue = format_email($name, $address, $email_usename);
1170 }
1171 }
1172 }
1173 }
1174 if ($email_reviewer) {
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001175 my $subsystem = get_subsystem_name($i);
1176 push_email_addresses($pvalue, "reviewer:$subsystem");
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001177 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001178 } elsif ($ptype eq "T") {
1179 push(@scm, $pvalue);
1180 } elsif ($ptype eq "W") {
1181 push(@web, $pvalue);
1182 } elsif ($ptype eq "S") {
1183 push(@status, $pvalue);
1184 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001185 }
1186 }
1187}
1188
Joe Perches11ecf532009-09-21 17:04:22 -07001189sub email_inuse {
1190 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001191
Joe Perches11ecf532009-09-21 17:04:22 -07001192 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001193 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1194 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001195
Joe Perches0e70e832009-09-21 17:04:20 -07001196 return 0;
1197}
1198
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001199sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001200 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001201
Joe Perches0e70e832009-09-21 17:04:20 -07001202 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001203
Joe Perchesb7816552009-09-21 17:04:24 -07001204 if ($address eq "") {
1205 return 0;
1206 }
1207
Joe Perches11ecf532009-09-21 17:04:22 -07001208 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001209 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001210 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001211 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001212 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001213 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001214 }
Joe Perchesb7816552009-09-21 17:04:24 -07001215
1216 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001217}
1218
1219sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001220 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001221
1222 my @address_list = ();
1223
Joe Perches5f2441e2009-06-16 15:34:02 -07001224 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001225 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001226 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001227 my $array_count = shift(@address_list);
1228 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001229 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001230 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001231 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001232 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001233 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1234 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001235 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001236}
1237
Joe Perches3c7385b2009-12-14 18:00:46 -08001238sub add_role {
1239 my ($line, $role) = @_;
1240
1241 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001242 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001243
1244 foreach my $entry (@email_to) {
1245 if ($email_remove_duplicates) {
1246 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001247 if (($name eq $entry_name || $address eq $entry_address)
1248 && ($role eq "" || !($entry->[1] =~ m/$role/))
1249 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001250 if ($entry->[1] eq "") {
1251 $entry->[1] = "$role";
1252 } else {
1253 $entry->[1] = "$entry->[1],$role";
1254 }
1255 }
1256 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001257 if ($email eq $entry->[0]
1258 && ($role eq "" || !($entry->[1] =~ m/$role/))
1259 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001260 if ($entry->[1] eq "") {
1261 $entry->[1] = "$role";
1262 } else {
1263 $entry->[1] = "$entry->[1],$role";
1264 }
1265 }
1266 }
1267 }
1268}
1269
Joe Perchescb7301c2009-04-07 20:40:12 -07001270sub which {
1271 my ($bin) = @_;
1272
Joe Perchesf5f50782009-06-16 15:34:00 -07001273 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001274 if (-e "$path/$bin") {
1275 return "$path/$bin";
1276 }
1277 }
1278
1279 return "";
1280}
1281
Joe Perchesbcde44e2010-10-26 14:22:53 -07001282sub which_conf {
1283 my ($conf) = @_;
1284
1285 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1286 if (-e "$path/$conf") {
1287 return "$path/$conf";
1288 }
1289 }
1290
1291 return "";
1292}
1293
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001294sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001295 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001296
Joe Perches47abc722010-10-26 14:22:57 -07001297 my ($name, $address) = parse_email($line);
1298 my $email = format_email($name, $address, 1);
1299 my $real_name = $name;
1300 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001301
Joe Perches47abc722010-10-26 14:22:57 -07001302 if (exists $mailmap->{names}->{$email} ||
1303 exists $mailmap->{addresses}->{$email}) {
1304 if (exists $mailmap->{names}->{$email}) {
1305 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001306 }
Joe Perches47abc722010-10-26 14:22:57 -07001307 if (exists $mailmap->{addresses}->{$email}) {
1308 $real_address = $mailmap->{addresses}->{$email};
1309 }
1310 } else {
1311 if (exists $mailmap->{names}->{$address}) {
1312 $real_name = $mailmap->{names}->{$address};
1313 }
1314 if (exists $mailmap->{addresses}->{$address}) {
1315 $real_address = $mailmap->{addresses}->{$address};
1316 }
1317 }
1318 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001319}
1320
1321sub mailmap {
1322 my (@addresses) = @_;
1323
Joe Perchesb9e23312010-10-26 14:22:58 -07001324 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001325 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001326 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001327 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001328 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1329 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001330}
1331
1332sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001333 my %address_map;
1334 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001335
Joe Perches47abc722010-10-26 14:22:57 -07001336 foreach my $email (@emails) {
1337 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001338 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001339 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001340 $email = format_email($name, $address, 1);
1341 } else {
1342 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001343 }
Joe Perches47abc722010-10-26 14:22:57 -07001344 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001345}
1346
Joe Perches60db31a2009-12-14 18:00:50 -08001347sub git_execute_cmd {
1348 my ($cmd) = @_;
1349 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001350
Joe Perches60db31a2009-12-14 18:00:50 -08001351 my $output = `$cmd`;
1352 $output =~ s/^\s*//gm;
1353 @lines = split("\n", $output);
1354
1355 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001356}
1357
Joe Perches60db31a2009-12-14 18:00:50 -08001358sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001359 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001360 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001361
Joe Perches60db31a2009-12-14 18:00:50 -08001362 my $output = `$cmd`;
1363 @lines = split("\n", $output);
1364
1365 return @lines;
1366}
1367
Joe Perches683c6f82010-10-26 14:22:55 -07001368sub extract_formatted_signatures {
1369 my (@signature_lines) = @_;
1370
1371 my @type = @signature_lines;
1372
1373 s/\s*(.*):.*/$1/ for (@type);
1374
1375 # cut -f2- -d":"
1376 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1377
1378## Reformat email addresses (with names) to avoid badly written signatures
1379
1380 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001381 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001382 }
1383
1384 return (\@type, \@signature_lines);
1385}
1386
Joe Perches60db31a2009-12-14 18:00:50 -08001387sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001388 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001389 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001390 my @lines = ();
1391 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001392 my @authors = ();
1393 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001394
Joe Perches60db31a2009-12-14 18:00:50 -08001395 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001396
Joe Perches60db31a2009-12-14 18:00:50 -08001397 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001398 my $author_pattern = $VCS_cmds{"author_pattern"};
1399 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1400
1401 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001402
Joe Perches60db31a2009-12-14 18:00:50 -08001403 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001404
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001405 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001406 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001407 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001408
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001409# print("stats: <@stats>\n");
1410
1411 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001412
1413 save_commits_by_author(@lines) if ($interactive);
1414 save_commits_by_signer(@lines) if ($interactive);
1415
Joe Perches0e70e832009-09-21 17:04:20 -07001416 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001417 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001418 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001419
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001420 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001421 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001422
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001423 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001424}
1425
Joe Perches63ab52d2010-10-26 14:22:51 -07001426sub vcs_find_author {
1427 my ($cmd) = @_;
1428 my @lines = ();
1429
1430 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1431
1432 if (!$email_git_penguin_chiefs) {
1433 @lines = grep(!/${penguin_chiefs}/i, @lines);
1434 }
1435
1436 return @lines if !@lines;
1437
Joe Perches683c6f82010-10-26 14:22:55 -07001438 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001439 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001440 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1441 my $author = $1;
1442 my ($name, $address) = parse_email($author);
1443 $author = format_email($name, $address, 1);
1444 push(@authors, $author);
1445 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001446 }
1447
Joe Perches683c6f82010-10-26 14:22:55 -07001448 save_commits_by_author(@lines) if ($interactive);
1449 save_commits_by_signer(@lines) if ($interactive);
1450
1451 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001452}
1453
Joe Perches60db31a2009-12-14 18:00:50 -08001454sub vcs_save_commits {
1455 my ($cmd) = @_;
1456 my @lines = ();
1457 my @commits = ();
1458
1459 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1460
1461 foreach my $line (@lines) {
1462 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1463 push(@commits, $1);
1464 }
1465 }
1466
1467 return @commits;
1468}
1469
1470sub vcs_blame {
1471 my ($file) = @_;
1472 my $cmd;
1473 my @commits = ();
1474
1475 return @commits if (!(-f $file));
1476
1477 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1478 my @all_commits = ();
1479
1480 $cmd = $VCS_cmds{"blame_file_cmd"};
1481 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1482 @all_commits = vcs_save_commits($cmd);
1483
1484 foreach my $file_range_diff (@range) {
1485 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1486 my $diff_file = $1;
1487 my $diff_start = $2;
1488 my $diff_length = $3;
1489 next if ("$file" ne "$diff_file");
1490 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1491 push(@commits, $all_commits[$i]);
1492 }
1493 }
1494 } elsif (@range) {
1495 foreach my $file_range_diff (@range) {
1496 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1497 my $diff_file = $1;
1498 my $diff_start = $2;
1499 my $diff_length = $3;
1500 next if ("$file" ne "$diff_file");
1501 $cmd = $VCS_cmds{"blame_range_cmd"};
1502 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1503 push(@commits, vcs_save_commits($cmd));
1504 }
1505 } else {
1506 $cmd = $VCS_cmds{"blame_file_cmd"};
1507 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1508 @commits = vcs_save_commits($cmd);
1509 }
1510
Joe Perches63ab52d2010-10-26 14:22:51 -07001511 foreach my $commit (@commits) {
1512 $commit =~ s/^\^//g;
1513 }
1514
Joe Perches60db31a2009-12-14 18:00:50 -08001515 return @commits;
1516}
1517
1518my $printed_novcs = 0;
1519sub vcs_exists {
1520 %VCS_cmds = %VCS_cmds_git;
1521 return 1 if eval $VCS_cmds{"available"};
1522 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001523 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001524 %VCS_cmds = ();
1525 if (!$printed_novcs) {
1526 warn("$P: No supported VCS found. Add --nogit to options?\n");
1527 warn("Using a git repository produces better results.\n");
1528 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001529 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001530 $printed_novcs = 1;
1531 }
1532 return 0;
1533}
1534
Joe Perches683c6f82010-10-26 14:22:55 -07001535sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001536 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001537 return $vcs_used == 1;
1538}
1539
1540sub vcs_is_hg {
1541 return $vcs_used == 2;
1542}
1543
Joe Perches6ef1c522010-10-26 14:22:56 -07001544sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001545 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001546 my @list = @$list_ref;
1547
Joe Perches683c6f82010-10-26 14:22:55 -07001548 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001549
1550 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001551 my %authored;
1552 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001553 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001554 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001555 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001556 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1557 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001558 $authored{$count} = 0;
1559 $signed{$count} = 0;
1560 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001561 }
1562
1563 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001564 my $done = 0;
1565 my $print_options = 0;
1566 my $redraw = 1;
1567 while (!$done) {
1568 $count = 0;
1569 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001570 printf STDERR "\n%1s %2s %-65s",
1571 "*", "#", "email/list and role:stats";
1572 if ($email_git ||
1573 ($email_git_fallback && !$maintained) ||
1574 $email_git_blame) {
1575 print STDERR "auth sign";
1576 }
1577 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001578 foreach my $entry (@list) {
1579 my $email = $entry->[0];
1580 my $role = $entry->[1];
1581 my $sel = "";
1582 $sel = "*" if ($selected{$count});
1583 my $commit_author = $commit_author_hash{$email};
1584 my $commit_signer = $commit_signer_hash{$email};
1585 my $authored = 0;
1586 my $signed = 0;
1587 $authored++ for (@{$commit_author});
1588 $signed++ for (@{$commit_signer});
1589 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1590 printf STDERR "%4d %4d", $authored, $signed
1591 if ($authored > 0 || $signed > 0);
1592 printf STDERR "\n %s\n", $role;
1593 if ($authored{$count}) {
1594 my $commit_author = $commit_author_hash{$email};
1595 foreach my $ref (@{$commit_author}) {
1596 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001597 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001598 }
Joe Perches683c6f82010-10-26 14:22:55 -07001599 if ($signed{$count}) {
1600 my $commit_signer = $commit_signer_hash{$email};
1601 foreach my $ref (@{$commit_signer}) {
1602 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1603 }
1604 }
1605
1606 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001607 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001608 }
Joe Perches683c6f82010-10-26 14:22:55 -07001609 my $date_ref = \$email_git_since;
1610 $date_ref = \$email_hg_since if (vcs_is_hg());
1611 if ($print_options) {
1612 $print_options = 0;
1613 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001614 print STDERR <<EOT
1615
1616Version Control options:
1617g use git history [$email_git]
1618gf use git-fallback [$email_git_fallback]
1619b use git blame [$email_git_blame]
1620bs use blame signatures [$email_git_blame_signatures]
1621c# minimum commits [$email_git_min_signatures]
1622%# min percent [$email_git_min_percent]
1623d# history to use [$$date_ref]
1624x# max maintainers [$email_git_max_maintainers]
1625t all signature types [$email_git_all_signature_types]
1626m use .mailmap [$email_use_mailmap]
1627EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001628 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001629 print STDERR <<EOT
1630
1631Additional options:
16320 toggle all
1633tm toggle maintainers
1634tg toggle git entries
1635tl toggle open list entries
1636ts toggle subscriber list entries
1637f emails in file [$file_emails]
1638k keywords in file [$keywords]
1639r remove duplicates [$email_remove_duplicates]
1640p# pattern match depth [$pattern_depth]
1641EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001642 }
1643 print STDERR
1644"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1645
1646 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001647 chomp($input);
1648
Joe Perches683c6f82010-10-26 14:22:55 -07001649 $redraw = 1;
1650 my $rerun = 0;
1651 my @wish = split(/[, ]+/, $input);
1652 foreach my $nr (@wish) {
1653 $nr = lc($nr);
1654 my $sel = substr($nr, 0, 1);
1655 my $str = substr($nr, 1);
1656 my $val = 0;
1657 $val = $1 if $str =~ /^(\d+)$/;
1658
1659 if ($sel eq "y") {
1660 $interactive = 0;
1661 $done = 1;
1662 $output_rolestats = 0;
1663 $output_roles = 0;
1664 last;
1665 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1666 $selected{$nr - 1} = !$selected{$nr - 1};
1667 } elsif ($sel eq "*" || $sel eq '^') {
1668 my $toggle = 0;
1669 $toggle = 1 if ($sel eq '*');
1670 for (my $i = 0; $i < $count; $i++) {
1671 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001672 }
Joe Perches683c6f82010-10-26 14:22:55 -07001673 } elsif ($sel eq "0") {
1674 for (my $i = 0; $i < $count; $i++) {
1675 $selected{$i} = !$selected{$i};
1676 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001677 } elsif ($sel eq "t") {
1678 if (lc($str) eq "m") {
1679 for (my $i = 0; $i < $count; $i++) {
1680 $selected{$i} = !$selected{$i}
1681 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1682 }
1683 } elsif (lc($str) eq "g") {
1684 for (my $i = 0; $i < $count; $i++) {
1685 $selected{$i} = !$selected{$i}
1686 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1687 }
1688 } elsif (lc($str) eq "l") {
1689 for (my $i = 0; $i < $count; $i++) {
1690 $selected{$i} = !$selected{$i}
1691 if ($list[$i]->[1] =~ /^(open list)/i);
1692 }
1693 } elsif (lc($str) eq "s") {
1694 for (my $i = 0; $i < $count; $i++) {
1695 $selected{$i} = !$selected{$i}
1696 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1697 }
1698 }
Joe Perches683c6f82010-10-26 14:22:55 -07001699 } elsif ($sel eq "a") {
1700 if ($val > 0 && $val <= $count) {
1701 $authored{$val - 1} = !$authored{$val - 1};
1702 } elsif ($str eq '*' || $str eq '^') {
1703 my $toggle = 0;
1704 $toggle = 1 if ($str eq '*');
1705 for (my $i = 0; $i < $count; $i++) {
1706 $authored{$i} = $toggle;
1707 }
1708 }
1709 } elsif ($sel eq "s") {
1710 if ($val > 0 && $val <= $count) {
1711 $signed{$val - 1} = !$signed{$val - 1};
1712 } elsif ($str eq '*' || $str eq '^') {
1713 my $toggle = 0;
1714 $toggle = 1 if ($str eq '*');
1715 for (my $i = 0; $i < $count; $i++) {
1716 $signed{$i} = $toggle;
1717 }
1718 }
1719 } elsif ($sel eq "o") {
1720 $print_options = 1;
1721 $redraw = 1;
1722 } elsif ($sel eq "g") {
1723 if ($str eq "f") {
1724 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001725 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001726 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001727 }
Joe Perches683c6f82010-10-26 14:22:55 -07001728 $rerun = 1;
1729 } elsif ($sel eq "b") {
1730 if ($str eq "s") {
1731 bool_invert(\$email_git_blame_signatures);
1732 } else {
1733 bool_invert(\$email_git_blame);
1734 }
1735 $rerun = 1;
1736 } elsif ($sel eq "c") {
1737 if ($val > 0) {
1738 $email_git_min_signatures = $val;
1739 $rerun = 1;
1740 }
1741 } elsif ($sel eq "x") {
1742 if ($val > 0) {
1743 $email_git_max_maintainers = $val;
1744 $rerun = 1;
1745 }
1746 } elsif ($sel eq "%") {
1747 if ($str ne "" && $val >= 0) {
1748 $email_git_min_percent = $val;
1749 $rerun = 1;
1750 }
1751 } elsif ($sel eq "d") {
1752 if (vcs_is_git()) {
1753 $email_git_since = $str;
1754 } elsif (vcs_is_hg()) {
1755 $email_hg_since = $str;
1756 }
1757 $rerun = 1;
1758 } elsif ($sel eq "t") {
1759 bool_invert(\$email_git_all_signature_types);
1760 $rerun = 1;
1761 } elsif ($sel eq "f") {
1762 bool_invert(\$file_emails);
1763 $rerun = 1;
1764 } elsif ($sel eq "r") {
1765 bool_invert(\$email_remove_duplicates);
1766 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001767 } elsif ($sel eq "m") {
1768 bool_invert(\$email_use_mailmap);
1769 read_mailmap();
1770 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001771 } elsif ($sel eq "k") {
1772 bool_invert(\$keywords);
1773 $rerun = 1;
1774 } elsif ($sel eq "p") {
1775 if ($str ne "" && $val >= 0) {
1776 $pattern_depth = $val;
1777 $rerun = 1;
1778 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001779 } elsif ($sel eq "h" || $sel eq "?") {
1780 print STDERR <<EOT
1781
1782Interactive mode allows you to select the various maintainers, submitters,
1783commit signers and mailing lists that could be CC'd on a patch.
1784
1785Any *'d entry is selected.
1786
Joe Perches47abc722010-10-26 14:22:57 -07001787If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001788history of files in the patch. Also, each line of the current file can
1789be matched to its commit author and that commits signers with blame.
1790
1791Various knobs exist to control the length of time for active commit
1792tracking, the maximum number of commit authors and signers to add,
1793and such.
1794
1795Enter selections at the prompt until you are satisfied that the selected
1796maintainers are appropriate. You may enter multiple selections separated
1797by either commas or spaces.
1798
1799EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001800 } else {
1801 print STDERR "invalid option: '$nr'\n";
1802 $redraw = 0;
1803 }
1804 }
1805 if ($rerun) {
1806 print STDERR "git-blame can be very slow, please have patience..."
1807 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001808 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001809 }
1810 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001811
1812 #drop not selected entries
1813 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001814 my @new_emailto = ();
1815 foreach my $entry (@list) {
1816 if ($selected{$count}) {
1817 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001818 }
1819 $count++;
1820 }
Joe Perches683c6f82010-10-26 14:22:55 -07001821 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001822}
1823
Joe Perches683c6f82010-10-26 14:22:55 -07001824sub bool_invert {
1825 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001826
Joe Perches683c6f82010-10-26 14:22:55 -07001827 if ($$bool_ref) {
1828 $$bool_ref = 0;
1829 } else {
1830 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001831 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001832}
1833
Joe Perchesb9e23312010-10-26 14:22:58 -07001834sub deduplicate_email {
1835 my ($email) = @_;
1836
1837 my $matched = 0;
1838 my ($name, $address) = parse_email($email);
1839 $email = format_email($name, $address, 1);
1840 $email = mailmap_email($email);
1841
1842 return $email if (!$email_remove_duplicates);
1843
1844 ($name, $address) = parse_email($email);
1845
Joe Perchesfae99202010-10-26 14:22:58 -07001846 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001847 $name = $deduplicate_name_hash{lc($name)}->[0];
1848 $address = $deduplicate_name_hash{lc($name)}->[1];
1849 $matched = 1;
1850 } elsif ($deduplicate_address_hash{lc($address)}) {
1851 $name = $deduplicate_address_hash{lc($address)}->[0];
1852 $address = $deduplicate_address_hash{lc($address)}->[1];
1853 $matched = 1;
1854 }
1855 if (!$matched) {
1856 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1857 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1858 }
1859 $email = format_email($name, $address, 1);
1860 $email = mailmap_email($email);
1861 return $email;
1862}
1863
Joe Perches683c6f82010-10-26 14:22:55 -07001864sub save_commits_by_author {
1865 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001866
Joe Perches683c6f82010-10-26 14:22:55 -07001867 my @authors = ();
1868 my @commits = ();
1869 my @subjects = ();
1870
1871 foreach my $line (@lines) {
1872 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1873 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001874 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001875 push(@authors, $author);
1876 }
1877 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1878 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1879 }
1880
1881 for (my $i = 0; $i < @authors; $i++) {
1882 my $exists = 0;
1883 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1884 if (@{$ref}[0] eq $commits[$i] &&
1885 @{$ref}[1] eq $subjects[$i]) {
1886 $exists = 1;
1887 last;
1888 }
1889 }
1890 if (!$exists) {
1891 push(@{$commit_author_hash{$authors[$i]}},
1892 [ ($commits[$i], $subjects[$i]) ]);
1893 }
1894 }
1895}
1896
1897sub save_commits_by_signer {
1898 my (@lines) = @_;
1899
1900 my $commit = "";
1901 my $subject = "";
1902
1903 foreach my $line (@lines) {
1904 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1905 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1906 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1907 my @signatures = ($line);
1908 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1909 my @types = @$types_ref;
1910 my @signers = @$signers_ref;
1911
1912 my $type = $types[0];
1913 my $signer = $signers[0];
1914
Joe Perchesb9e23312010-10-26 14:22:58 -07001915 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001916
Joe Perches683c6f82010-10-26 14:22:55 -07001917 my $exists = 0;
1918 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1919 if (@{$ref}[0] eq $commit &&
1920 @{$ref}[1] eq $subject &&
1921 @{$ref}[2] eq $type) {
1922 $exists = 1;
1923 last;
1924 }
1925 }
1926 if (!$exists) {
1927 push(@{$commit_signer_hash{$signer}},
1928 [ ($commit, $subject, $type) ]);
1929 }
1930 }
1931 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001932}
1933
Joe Perches60db31a2009-12-14 18:00:50 -08001934sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001935 my ($role, $divisor, @lines) = @_;
1936
1937 my %hash;
1938 my $count = 0;
1939
Joe Perchesa8af2432009-12-14 18:00:49 -08001940 return if (@lines <= 0);
1941
1942 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001943 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001944 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001945 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001946
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001947 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001948
Joe Perches63ab52d2010-10-26 14:22:51 -07001949 return if (@lines <= 0);
1950
Joe Perches0e70e832009-09-21 17:04:20 -07001951 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001952
Joe Perches11ecf532009-09-21 17:04:22 -07001953 # uniq -c
1954 $hash{$_}++ for @lines;
1955
1956 # sort -rn
1957 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1958 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001959 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001960
Joe Perchesa8af2432009-12-14 18:00:49 -08001961 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07001962 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07001963 $count++;
1964 last if ($sign_offs < $email_git_min_signatures ||
1965 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001966 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001967 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001968 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001969 my $fmt_percent = sprintf("%.0f", $percent);
1970 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1971 } else {
1972 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001973 }
Joe Perchesf5492662009-09-21 17:04:13 -07001974 }
1975}
1976
Joe Perches60db31a2009-12-14 18:00:50 -08001977sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001978 my ($file) = @_;
1979
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001980 my $authors_ref;
1981 my $signers_ref;
1982 my $stats_ref;
1983 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001984 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001985 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001986 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001987
Joe Perches683c6f82010-10-26 14:22:55 -07001988 $vcs_used = vcs_exists();
1989 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001990
Joe Perches60db31a2009-12-14 18:00:50 -08001991 my $cmd = $VCS_cmds{"find_signers_cmd"};
1992 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1993
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001994 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1995
1996 @signers = @{$signers_ref} if defined $signers_ref;
1997 @authors = @{$authors_ref} if defined $authors_ref;
1998 @stats = @{$stats_ref} if defined $stats_ref;
1999
2000# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07002001
2002 foreach my $signer (@signers) {
2003 $signer = deduplicate_email($signer);
2004 }
2005
Joe Perches60db31a2009-12-14 18:00:50 -08002006 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002007 vcs_assign("authored", $commits, @authors);
2008 if ($#authors == $#stats) {
2009 my $stat_pattern = $VCS_cmds{"stat_pattern"};
2010 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
2011
2012 my $added = 0;
2013 my $deleted = 0;
2014 for (my $i = 0; $i <= $#stats; $i++) {
2015 if ($stats[$i] =~ /$stat_pattern/) {
2016 $added += $1;
2017 $deleted += $2;
2018 }
2019 }
2020 my @tmp_authors = uniq(@authors);
2021 foreach my $author (@tmp_authors) {
2022 $author = deduplicate_email($author);
2023 }
2024 @tmp_authors = uniq(@tmp_authors);
2025 my @list_added = ();
2026 my @list_deleted = ();
2027 foreach my $author (@tmp_authors) {
2028 my $auth_added = 0;
2029 my $auth_deleted = 0;
2030 for (my $i = 0; $i <= $#stats; $i++) {
2031 if ($author eq deduplicate_email($authors[$i]) &&
2032 $stats[$i] =~ /$stat_pattern/) {
2033 $auth_added += $1;
2034 $auth_deleted += $2;
2035 }
2036 }
2037 for (my $i = 0; $i < $auth_added; $i++) {
2038 push(@list_added, $author);
2039 }
2040 for (my $i = 0; $i < $auth_deleted; $i++) {
2041 push(@list_deleted, $author);
2042 }
2043 }
2044 vcs_assign("added_lines", $added, @list_added);
2045 vcs_assign("removed_lines", $deleted, @list_deleted);
2046 }
Joe Perchesa8af2432009-12-14 18:00:49 -08002047}
2048
Joe Perches60db31a2009-12-14 18:00:50 -08002049sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07002050 my ($file) = @_;
2051
Joe Perches60db31a2009-12-14 18:00:50 -08002052 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07002053 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002054 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002055 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002056 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002057
Joe Perches683c6f82010-10-26 14:22:55 -07002058 $vcs_used = vcs_exists();
2059 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002060
Joe Perches63ab52d2010-10-26 14:22:51 -07002061 @all_commits = vcs_blame($file);
2062 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002063 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002064 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002065
Joe Perches683c6f82010-10-26 14:22:55 -07002066 if ($email_git_blame_signatures) {
2067 if (vcs_is_hg()) {
2068 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002069 my $commit_authors_ref;
2070 my $commit_signers_ref;
2071 my $stats_ref;
2072 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002073 my @commit_signers = ();
2074 my $commit = join(" -r ", @commits);
2075 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002076
Joe Perches683c6f82010-10-26 14:22:55 -07002077 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2078 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002079
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002080 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2081 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2082 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002083
Joe Perches683c6f82010-10-26 14:22:55 -07002084 push(@signers, @commit_signers);
2085 } else {
2086 foreach my $commit (@commits) {
2087 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002088 my $commit_authors_ref;
2089 my $commit_signers_ref;
2090 my $stats_ref;
2091 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002092 my @commit_signers = ();
2093 my $cmd;
2094
2095 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2096 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2097
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002098 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2099 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2100 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002101
2102 push(@signers, @commit_signers);
2103 }
2104 }
Joe Perchesf5492662009-09-21 17:04:13 -07002105 }
2106
Joe Perchesa8af2432009-12-14 18:00:49 -08002107 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002108 if ($output_rolestats) {
2109 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002110 if (vcs_is_hg()) {{ # Double brace for last exit
2111 my $commit_count;
2112 my @commit_signers = ();
2113 @commits = uniq(@commits);
2114 @commits = sort(@commits);
2115 my $commit = join(" -r ", @commits);
2116 my $cmd;
2117
2118 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2119 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2120
2121 my @lines = ();
2122
2123 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2124
2125 if (!$email_git_penguin_chiefs) {
2126 @lines = grep(!/${penguin_chiefs}/i, @lines);
2127 }
2128
2129 last if !@lines;
2130
2131 my @authors = ();
2132 foreach my $line (@lines) {
2133 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2134 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002135 $author = deduplicate_email($author);
2136 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002137 }
2138 }
2139
2140 save_commits_by_author(@lines) if ($interactive);
2141 save_commits_by_signer(@lines) if ($interactive);
2142
2143 push(@signers, @authors);
2144 }}
2145 else {
2146 foreach my $commit (@commits) {
2147 my $i;
2148 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2149 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2150 my @author = vcs_find_author($cmd);
2151 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002152
2153 my $formatted_author = deduplicate_email($author[0]);
2154
Joe Perches683c6f82010-10-26 14:22:55 -07002155 my $count = grep(/$commit/, @all_commits);
2156 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002157 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002158 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002159 }
2160 }
2161 if (@blame_signers) {
2162 vcs_assign("authored lines", $total_lines, @blame_signers);
2163 }
2164 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002165 foreach my $signer (@signers) {
2166 $signer = deduplicate_email($signer);
2167 }
Joe Perches60db31a2009-12-14 18:00:50 -08002168 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002169 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002170 foreach my $signer (@signers) {
2171 $signer = deduplicate_email($signer);
2172 }
Joe Perches60db31a2009-12-14 18:00:50 -08002173 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002174 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002175}
2176
Joe Perches4cad35a2016-08-02 14:04:10 -07002177sub vcs_file_exists {
2178 my ($file) = @_;
2179
2180 my $exists;
2181
2182 my $vcs_used = vcs_exists();
2183 return 0 if (!$vcs_used);
2184
2185 my $cmd = $VCS_cmds{"file_exists_cmd"};
2186 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
Joe Perches8582fb52016-08-25 15:16:48 -07002187 $cmd .= " 2>&1";
Joe Perches4cad35a2016-08-02 14:04:10 -07002188 $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
2189
Joe Perches8582fb52016-08-25 15:16:48 -07002190 return 0 if ($? != 0);
2191
Joe Perches4cad35a2016-08-02 14:04:10 -07002192 return $exists;
2193}
2194
Joe Perchescb7301c2009-04-07 20:40:12 -07002195sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002196 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002197
2198 my %saw;
2199 @parms = grep(!$saw{$_}++, @parms);
2200 return @parms;
2201}
2202
2203sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002204 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002205
2206 my %saw;
2207 @parms = sort @parms;
2208 @parms = grep(!$saw{$_}++, @parms);
2209 return @parms;
2210}
2211
Joe Perches03372db2010-03-05 13:43:00 -08002212sub clean_file_emails {
2213 my (@file_emails) = @_;
2214 my @fmt_emails = ();
2215
2216 foreach my $email (@file_emails) {
2217 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2218 my ($name, $address) = parse_email($email);
2219 if ($name eq '"[,\.]"') {
2220 $name = "";
2221 }
2222
2223 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2224 if (@nw > 2) {
2225 my $first = $nw[@nw - 3];
2226 my $middle = $nw[@nw - 2];
2227 my $last = $nw[@nw - 1];
2228
2229 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2230 (length($first) == 2 && substr($first, -1) eq ".")) ||
2231 (length($middle) == 1 ||
2232 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2233 $name = "$first $middle $last";
2234 } else {
2235 $name = "$middle $last";
2236 }
2237 }
2238
2239 if (substr($name, -1) =~ /[,\.]/) {
2240 $name = substr($name, 0, length($name) - 1);
2241 } elsif (substr($name, -2) =~ /[,\.]"/) {
2242 $name = substr($name, 0, length($name) - 2) . '"';
2243 }
2244
2245 if (substr($name, 0, 1) =~ /[,\.]/) {
2246 $name = substr($name, 1, length($name) - 1);
2247 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2248 $name = '"' . substr($name, 2, length($name) - 2);
2249 }
2250
2251 my $fmt_email = format_email($name, $address, $email_usename);
2252 push(@fmt_emails, $fmt_email);
2253 }
2254 return @fmt_emails;
2255}
2256
Joe Perches3c7385b2009-12-14 18:00:46 -08002257sub merge_email {
2258 my @lines;
2259 my %saw;
2260
2261 for (@_) {
2262 my ($address, $role) = @$_;
2263 if (!$saw{$address}) {
2264 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002265 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002266 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002267 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002268 }
2269 $saw{$address} = 1;
2270 }
2271 }
2272
2273 return @lines;
2274}
2275
Joe Perchescb7301c2009-04-07 20:40:12 -07002276sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002277 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002278
2279 if ($output_multiline) {
2280 foreach my $line (@parms) {
2281 print("${line}\n");
2282 }
2283 } else {
2284 print(join($output_separator, @parms));
2285 print("\n");
2286 }
2287}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002288
2289my $rfc822re;
2290
2291sub make_rfc822re {
2292# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2293# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2294# This regexp will only work on addresses which have had comments stripped
2295# and replaced with rfc822_lwsp.
2296
2297 my $specials = '()<>@,;:\\\\".\\[\\]';
2298 my $controls = '\\000-\\037\\177';
2299
2300 my $dtext = "[^\\[\\]\\r\\\\]";
2301 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2302
2303 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2304
2305# Use zero-width assertion to spot the limit of an atom. A simple
2306# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2307 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2308 my $word = "(?:$atom|$quoted_string)";
2309 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2310
2311 my $sub_domain = "(?:$atom|$domain_literal)";
2312 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2313
2314 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2315
2316 my $phrase = "$word*";
2317 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2318 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2319 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2320
2321 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2322 my $address = "(?:$mailbox|$group)";
2323
2324 return "$rfc822_lwsp*$address";
2325}
2326
2327sub rfc822_strip_comments {
2328 my $s = shift;
2329# Recursively remove comments, and replace with a single space. The simpler
2330# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2331# chars in atoms, for example.
2332
2333 while ($s =~ s/^((?:[^"\\]|\\.)*
2334 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2335 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2336 return $s;
2337}
2338
2339# valid: returns true if the parameter is an RFC822 valid address
2340#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002341sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002342 my $s = rfc822_strip_comments(shift);
2343
2344 if (!$rfc822re) {
2345 $rfc822re = make_rfc822re();
2346 }
2347
2348 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2349}
2350
2351# validlist: In scalar context, returns true if the parameter is an RFC822
2352# valid list of addresses.
2353#
2354# In list context, returns an empty list on failure (an invalid
2355# address was found); otherwise a list whose first element is the
2356# number of addresses found and whose remaining elements are the
2357# addresses. This is needed to disambiguate failure (invalid)
2358# from success with no addresses found, because an empty string is
2359# a valid list.
2360
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002361sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002362 my $s = rfc822_strip_comments(shift);
2363
2364 if (!$rfc822re) {
2365 $rfc822re = make_rfc822re();
2366 }
2367 # * null list items are valid according to the RFC
2368 # * the '1' business is to aid in distinguishing failure from no results
2369
2370 my @r;
2371 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2372 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002373 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002374 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002375 }
2376 return wantarray ? (scalar(@r), @r) : 1;
2377 }
Joe Perches60db31a2009-12-14 18:00:50 -08002378 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002379}