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