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