xref: /openbmc/u-boot/scripts/get_maintainer.pl (revision 68dc8769)
192bca398SDaniel Schwierzeck#!/usr/bin/perl -w
292bca398SDaniel Schwierzeck# (c) 2007, Joe Perches <joe@perches.com>
392bca398SDaniel Schwierzeck#           created from checkpatch.pl
492bca398SDaniel Schwierzeck#
592bca398SDaniel Schwierzeck# Print selected MAINTAINERS information for
692bca398SDaniel Schwierzeck# the files modified in a patch or for a file
792bca398SDaniel Schwierzeck#
892bca398SDaniel Schwierzeck# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
992bca398SDaniel Schwierzeck#        perl scripts/get_maintainer.pl [OPTIONS] -f <file>
1092bca398SDaniel Schwierzeck#
1192bca398SDaniel Schwierzeck# Licensed under the terms of the GNU GPL License version 2
1292bca398SDaniel Schwierzeck
1392bca398SDaniel Schwierzeckuse strict;
1492bca398SDaniel Schwierzeck
1592bca398SDaniel Schwierzeckmy $P = $0;
1692bca398SDaniel Schwierzeckmy $V = '0.26';
1792bca398SDaniel Schwierzeck
1892bca398SDaniel Schwierzeckuse Getopt::Long qw(:config no_auto_abbrev);
19*68dc8769SDaniel Schwierzeckuse File::Find;
2092bca398SDaniel Schwierzeck
2192bca398SDaniel Schwierzeckmy $lk_path = "./";
2292bca398SDaniel Schwierzeckmy $email = 1;
2392bca398SDaniel Schwierzeckmy $email_usename = 1;
2492bca398SDaniel Schwierzeckmy $email_maintainer = 1;
2592bca398SDaniel Schwierzeckmy $email_list = 1;
2692bca398SDaniel Schwierzeckmy $email_subscriber_list = 0;
2792bca398SDaniel Schwierzeckmy $email_git_penguin_chiefs = 0;
2892bca398SDaniel Schwierzeckmy $email_git = 0;
2992bca398SDaniel Schwierzeckmy $email_git_all_signature_types = 0;
3092bca398SDaniel Schwierzeckmy $email_git_blame = 0;
3192bca398SDaniel Schwierzeckmy $email_git_blame_signatures = 1;
3292bca398SDaniel Schwierzeckmy $email_git_fallback = 1;
3392bca398SDaniel Schwierzeckmy $email_git_min_signatures = 1;
3492bca398SDaniel Schwierzeckmy $email_git_max_maintainers = 5;
3592bca398SDaniel Schwierzeckmy $email_git_min_percent = 5;
3692bca398SDaniel Schwierzeckmy $email_git_since = "1-year-ago";
3792bca398SDaniel Schwierzeckmy $email_hg_since = "-365";
3892bca398SDaniel Schwierzeckmy $interactive = 0;
3992bca398SDaniel Schwierzeckmy $email_remove_duplicates = 1;
4092bca398SDaniel Schwierzeckmy $email_use_mailmap = 1;
4192bca398SDaniel Schwierzeckmy $output_multiline = 1;
4292bca398SDaniel Schwierzeckmy $output_separator = ", ";
4392bca398SDaniel Schwierzeckmy $output_roles = 0;
4492bca398SDaniel Schwierzeckmy $output_rolestats = 1;
4592bca398SDaniel Schwierzeckmy $scm = 0;
4692bca398SDaniel Schwierzeckmy $web = 0;
4792bca398SDaniel Schwierzeckmy $subsystem = 0;
4892bca398SDaniel Schwierzeckmy $status = 0;
4992bca398SDaniel Schwierzeckmy $keywords = 1;
5092bca398SDaniel Schwierzeckmy $sections = 0;
5192bca398SDaniel Schwierzeckmy $file_emails = 0;
5292bca398SDaniel Schwierzeckmy $from_filename = 0;
5392bca398SDaniel Schwierzeckmy $pattern_depth = 0;
5492bca398SDaniel Schwierzeckmy $version = 0;
5592bca398SDaniel Schwierzeckmy $help = 0;
5692bca398SDaniel Schwierzeck
5792bca398SDaniel Schwierzeckmy $vcs_used = 0;
5892bca398SDaniel Schwierzeck
5992bca398SDaniel Schwierzeckmy $exit = 0;
6092bca398SDaniel Schwierzeck
6192bca398SDaniel Schwierzeckmy %commit_author_hash;
6292bca398SDaniel Schwierzeckmy %commit_signer_hash;
6392bca398SDaniel Schwierzeck
6492bca398SDaniel Schwierzeckmy @penguin_chief = ();
65ee360cd2SDaniel Schwierzeckpush(@penguin_chief, "Tom Rini:trini\@ti.com");
6692bca398SDaniel Schwierzeck
6792bca398SDaniel Schwierzeckmy @penguin_chief_names = ();
6892bca398SDaniel Schwierzeckforeach my $chief (@penguin_chief) {
6992bca398SDaniel Schwierzeck    if ($chief =~ m/^(.*):(.*)/) {
7092bca398SDaniel Schwierzeck	my $chief_name = $1;
7192bca398SDaniel Schwierzeck	my $chief_addr = $2;
7292bca398SDaniel Schwierzeck	push(@penguin_chief_names, $chief_name);
7392bca398SDaniel Schwierzeck    }
7492bca398SDaniel Schwierzeck}
7592bca398SDaniel Schwierzeckmy $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
7692bca398SDaniel Schwierzeck
7792bca398SDaniel Schwierzeck# Signature types of people who are either
7892bca398SDaniel Schwierzeck# 	a) responsible for the code in question, or
7992bca398SDaniel Schwierzeck# 	b) familiar enough with it to give relevant feedback
8092bca398SDaniel Schwierzeckmy @signature_tags = ();
8192bca398SDaniel Schwierzeckpush(@signature_tags, "Signed-off-by:");
8292bca398SDaniel Schwierzeckpush(@signature_tags, "Reviewed-by:");
8392bca398SDaniel Schwierzeckpush(@signature_tags, "Acked-by:");
8492bca398SDaniel Schwierzeck
8592bca398SDaniel Schwierzeckmy $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
8692bca398SDaniel Schwierzeck
8792bca398SDaniel Schwierzeck# rfc822 email address - preloaded methods go here.
8892bca398SDaniel Schwierzeckmy $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
8992bca398SDaniel Schwierzeckmy $rfc822_char = '[\\000-\\377]';
9092bca398SDaniel Schwierzeck
9192bca398SDaniel Schwierzeck# VCS command support: class-like functions and strings
9292bca398SDaniel Schwierzeck
9392bca398SDaniel Schwierzeckmy %VCS_cmds;
9492bca398SDaniel Schwierzeck
9592bca398SDaniel Schwierzeckmy %VCS_cmds_git = (
9692bca398SDaniel Schwierzeck    "execute_cmd" => \&git_execute_cmd,
9792bca398SDaniel Schwierzeck    "available" => '(which("git") ne "") && (-e ".git")',
9892bca398SDaniel Schwierzeck    "find_signers_cmd" =>
9992bca398SDaniel Schwierzeck	"git log --no-color --follow --since=\$email_git_since " .
10092bca398SDaniel Schwierzeck	    '--numstat --no-merges ' .
10192bca398SDaniel Schwierzeck	    '--format="GitCommit: %H%n' .
10292bca398SDaniel Schwierzeck		      'GitAuthor: %an <%ae>%n' .
10392bca398SDaniel Schwierzeck		      'GitDate: %aD%n' .
10492bca398SDaniel Schwierzeck		      'GitSubject: %s%n' .
10592bca398SDaniel Schwierzeck		      '%b%n"' .
10692bca398SDaniel Schwierzeck	    " -- \$file",
10792bca398SDaniel Schwierzeck    "find_commit_signers_cmd" =>
10892bca398SDaniel Schwierzeck	"git log --no-color " .
10992bca398SDaniel Schwierzeck	    '--numstat ' .
11092bca398SDaniel Schwierzeck	    '--format="GitCommit: %H%n' .
11192bca398SDaniel Schwierzeck		      'GitAuthor: %an <%ae>%n' .
11292bca398SDaniel Schwierzeck		      'GitDate: %aD%n' .
11392bca398SDaniel Schwierzeck		      'GitSubject: %s%n' .
11492bca398SDaniel Schwierzeck		      '%b%n"' .
11592bca398SDaniel Schwierzeck	    " -1 \$commit",
11692bca398SDaniel Schwierzeck    "find_commit_author_cmd" =>
11792bca398SDaniel Schwierzeck	"git log --no-color " .
11892bca398SDaniel Schwierzeck	    '--numstat ' .
11992bca398SDaniel Schwierzeck	    '--format="GitCommit: %H%n' .
12092bca398SDaniel Schwierzeck		      'GitAuthor: %an <%ae>%n' .
12192bca398SDaniel Schwierzeck		      'GitDate: %aD%n' .
12292bca398SDaniel Schwierzeck		      'GitSubject: %s%n"' .
12392bca398SDaniel Schwierzeck	    " -1 \$commit",
12492bca398SDaniel Schwierzeck    "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
12592bca398SDaniel Schwierzeck    "blame_file_cmd" => "git blame -l \$file",
12692bca398SDaniel Schwierzeck    "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
12792bca398SDaniel Schwierzeck    "blame_commit_pattern" => "^([0-9a-f]+) ",
12892bca398SDaniel Schwierzeck    "author_pattern" => "^GitAuthor: (.*)",
12992bca398SDaniel Schwierzeck    "subject_pattern" => "^GitSubject: (.*)",
13092bca398SDaniel Schwierzeck    "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
13192bca398SDaniel Schwierzeck);
13292bca398SDaniel Schwierzeck
13392bca398SDaniel Schwierzeckmy %VCS_cmds_hg = (
13492bca398SDaniel Schwierzeck    "execute_cmd" => \&hg_execute_cmd,
13592bca398SDaniel Schwierzeck    "available" => '(which("hg") ne "") && (-d ".hg")',
13692bca398SDaniel Schwierzeck    "find_signers_cmd" =>
13792bca398SDaniel Schwierzeck	"hg log --date=\$email_hg_since " .
13892bca398SDaniel Schwierzeck	    "--template='HgCommit: {node}\\n" .
13992bca398SDaniel Schwierzeck	                "HgAuthor: {author}\\n" .
14092bca398SDaniel Schwierzeck			"HgSubject: {desc}\\n'" .
14192bca398SDaniel Schwierzeck	    " -- \$file",
14292bca398SDaniel Schwierzeck    "find_commit_signers_cmd" =>
14392bca398SDaniel Schwierzeck	"hg log " .
14492bca398SDaniel Schwierzeck	    "--template='HgSubject: {desc}\\n'" .
14592bca398SDaniel Schwierzeck	    " -r \$commit",
14692bca398SDaniel Schwierzeck    "find_commit_author_cmd" =>
14792bca398SDaniel Schwierzeck	"hg log " .
14892bca398SDaniel Schwierzeck	    "--template='HgCommit: {node}\\n" .
14992bca398SDaniel Schwierzeck		        "HgAuthor: {author}\\n" .
15092bca398SDaniel Schwierzeck			"HgSubject: {desc|firstline}\\n'" .
15192bca398SDaniel Schwierzeck	    " -r \$commit",
15292bca398SDaniel Schwierzeck    "blame_range_cmd" => "",		# not supported
15392bca398SDaniel Schwierzeck    "blame_file_cmd" => "hg blame -n \$file",
15492bca398SDaniel Schwierzeck    "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
15592bca398SDaniel Schwierzeck    "blame_commit_pattern" => "^([ 0-9a-f]+):",
15692bca398SDaniel Schwierzeck    "author_pattern" => "^HgAuthor: (.*)",
15792bca398SDaniel Schwierzeck    "subject_pattern" => "^HgSubject: (.*)",
15892bca398SDaniel Schwierzeck    "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
15992bca398SDaniel Schwierzeck);
16092bca398SDaniel Schwierzeck
16192bca398SDaniel Schwierzeckmy $conf = which_conf(".get_maintainer.conf");
16292bca398SDaniel Schwierzeckif (-f $conf) {
16392bca398SDaniel Schwierzeck    my @conf_args;
16492bca398SDaniel Schwierzeck    open(my $conffile, '<', "$conf")
16592bca398SDaniel Schwierzeck	or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
16692bca398SDaniel Schwierzeck
16792bca398SDaniel Schwierzeck    while (<$conffile>) {
16892bca398SDaniel Schwierzeck	my $line = $_;
16992bca398SDaniel Schwierzeck
17092bca398SDaniel Schwierzeck	$line =~ s/\s*\n?$//g;
17192bca398SDaniel Schwierzeck	$line =~ s/^\s*//g;
17292bca398SDaniel Schwierzeck	$line =~ s/\s+/ /g;
17392bca398SDaniel Schwierzeck
17492bca398SDaniel Schwierzeck	next if ($line =~ m/^\s*#/);
17592bca398SDaniel Schwierzeck	next if ($line =~ m/^\s*$/);
17692bca398SDaniel Schwierzeck
17792bca398SDaniel Schwierzeck	my @words = split(" ", $line);
17892bca398SDaniel Schwierzeck	foreach my $word (@words) {
17992bca398SDaniel Schwierzeck	    last if ($word =~ m/^#/);
18092bca398SDaniel Schwierzeck	    push (@conf_args, $word);
18192bca398SDaniel Schwierzeck	}
18292bca398SDaniel Schwierzeck    }
18392bca398SDaniel Schwierzeck    close($conffile);
18492bca398SDaniel Schwierzeck    unshift(@ARGV, @conf_args) if @conf_args;
18592bca398SDaniel Schwierzeck}
18692bca398SDaniel Schwierzeck
18792bca398SDaniel Schwierzeckif (!GetOptions(
18892bca398SDaniel Schwierzeck		'email!' => \$email,
18992bca398SDaniel Schwierzeck		'git!' => \$email_git,
19092bca398SDaniel Schwierzeck		'git-all-signature-types!' => \$email_git_all_signature_types,
19192bca398SDaniel Schwierzeck		'git-blame!' => \$email_git_blame,
19292bca398SDaniel Schwierzeck		'git-blame-signatures!' => \$email_git_blame_signatures,
19392bca398SDaniel Schwierzeck		'git-fallback!' => \$email_git_fallback,
19492bca398SDaniel Schwierzeck		'git-chief-penguins!' => \$email_git_penguin_chiefs,
19592bca398SDaniel Schwierzeck		'git-min-signatures=i' => \$email_git_min_signatures,
19692bca398SDaniel Schwierzeck		'git-max-maintainers=i' => \$email_git_max_maintainers,
19792bca398SDaniel Schwierzeck		'git-min-percent=i' => \$email_git_min_percent,
19892bca398SDaniel Schwierzeck		'git-since=s' => \$email_git_since,
19992bca398SDaniel Schwierzeck		'hg-since=s' => \$email_hg_since,
20092bca398SDaniel Schwierzeck		'i|interactive!' => \$interactive,
20192bca398SDaniel Schwierzeck		'remove-duplicates!' => \$email_remove_duplicates,
20292bca398SDaniel Schwierzeck		'mailmap!' => \$email_use_mailmap,
20392bca398SDaniel Schwierzeck		'm!' => \$email_maintainer,
20492bca398SDaniel Schwierzeck		'n!' => \$email_usename,
20592bca398SDaniel Schwierzeck		'l!' => \$email_list,
20692bca398SDaniel Schwierzeck		's!' => \$email_subscriber_list,
20792bca398SDaniel Schwierzeck		'multiline!' => \$output_multiline,
20892bca398SDaniel Schwierzeck		'roles!' => \$output_roles,
20992bca398SDaniel Schwierzeck		'rolestats!' => \$output_rolestats,
21092bca398SDaniel Schwierzeck		'separator=s' => \$output_separator,
21192bca398SDaniel Schwierzeck		'subsystem!' => \$subsystem,
21292bca398SDaniel Schwierzeck		'status!' => \$status,
21392bca398SDaniel Schwierzeck		'scm!' => \$scm,
21492bca398SDaniel Schwierzeck		'web!' => \$web,
21592bca398SDaniel Schwierzeck		'pattern-depth=i' => \$pattern_depth,
21692bca398SDaniel Schwierzeck		'k|keywords!' => \$keywords,
21792bca398SDaniel Schwierzeck		'sections!' => \$sections,
21892bca398SDaniel Schwierzeck		'fe|file-emails!' => \$file_emails,
21992bca398SDaniel Schwierzeck		'f|file' => \$from_filename,
22092bca398SDaniel Schwierzeck		'v|version' => \$version,
22192bca398SDaniel Schwierzeck		'h|help|usage' => \$help,
22292bca398SDaniel Schwierzeck		)) {
22392bca398SDaniel Schwierzeck    die "$P: invalid argument - use --help if necessary\n";
22492bca398SDaniel Schwierzeck}
22592bca398SDaniel Schwierzeck
22692bca398SDaniel Schwierzeckif ($help != 0) {
22792bca398SDaniel Schwierzeck    usage();
22892bca398SDaniel Schwierzeck    exit 0;
22992bca398SDaniel Schwierzeck}
23092bca398SDaniel Schwierzeck
23192bca398SDaniel Schwierzeckif ($version != 0) {
23292bca398SDaniel Schwierzeck    print("${P} ${V}\n");
23392bca398SDaniel Schwierzeck    exit 0;
23492bca398SDaniel Schwierzeck}
23592bca398SDaniel Schwierzeck
23692bca398SDaniel Schwierzeckif (-t STDIN && !@ARGV) {
23792bca398SDaniel Schwierzeck    # We're talking to a terminal, but have no command line arguments.
23892bca398SDaniel Schwierzeck    die "$P: missing patchfile or -f file - use --help if necessary\n";
23992bca398SDaniel Schwierzeck}
24092bca398SDaniel Schwierzeck
24192bca398SDaniel Schwierzeck$output_multiline = 0 if ($output_separator ne ", ");
24292bca398SDaniel Schwierzeck$output_rolestats = 1 if ($interactive);
24392bca398SDaniel Schwierzeck$output_roles = 1 if ($output_rolestats);
24492bca398SDaniel Schwierzeck
24592bca398SDaniel Schwierzeckif ($sections) {
24692bca398SDaniel Schwierzeck    $email = 0;
24792bca398SDaniel Schwierzeck    $email_list = 0;
24892bca398SDaniel Schwierzeck    $scm = 0;
24992bca398SDaniel Schwierzeck    $status = 0;
25092bca398SDaniel Schwierzeck    $subsystem = 0;
25192bca398SDaniel Schwierzeck    $web = 0;
25292bca398SDaniel Schwierzeck    $keywords = 0;
25392bca398SDaniel Schwierzeck    $interactive = 0;
25492bca398SDaniel Schwierzeck} else {
25592bca398SDaniel Schwierzeck    my $selections = $email + $scm + $status + $subsystem + $web;
25692bca398SDaniel Schwierzeck    if ($selections == 0) {
25792bca398SDaniel Schwierzeck	die "$P:  Missing required option: email, scm, status, subsystem or web\n";
25892bca398SDaniel Schwierzeck    }
25992bca398SDaniel Schwierzeck}
26092bca398SDaniel Schwierzeck
26192bca398SDaniel Schwierzeckif ($email &&
26292bca398SDaniel Schwierzeck    ($email_maintainer + $email_list + $email_subscriber_list +
26392bca398SDaniel Schwierzeck     $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
26492bca398SDaniel Schwierzeck    die "$P: Please select at least 1 email option\n";
26592bca398SDaniel Schwierzeck}
26692bca398SDaniel Schwierzeck
26792bca398SDaniel Schwierzeckif (!top_of_kernel_tree($lk_path)) {
26892bca398SDaniel Schwierzeck    die "$P: The current directory does not appear to be "
26992bca398SDaniel Schwierzeck	. "a linux kernel source tree.\n";
27092bca398SDaniel Schwierzeck}
27192bca398SDaniel Schwierzeck
27292bca398SDaniel Schwierzeck## Read MAINTAINERS for type/value pairs
27392bca398SDaniel Schwierzeck
27492bca398SDaniel Schwierzeckmy @typevalue = ();
27592bca398SDaniel Schwierzeckmy %keyword_hash;
27692bca398SDaniel Schwierzeck
277*68dc8769SDaniel Schwierzeckmy @maint_files = ();
278*68dc8769SDaniel Schwierzeckpush(@maint_files, "${lk_path}MAINTAINERS");
279*68dc8769SDaniel Schwierzeck
280*68dc8769SDaniel Schwierzecksub maint_wanted {
281*68dc8769SDaniel Schwierzeck    return unless $_ =~ /^MAINTAINERS/;
282*68dc8769SDaniel Schwierzeck    push(@maint_files, "$File::Find::name");
283*68dc8769SDaniel Schwierzeck}
284*68dc8769SDaniel Schwierzeck
285*68dc8769SDaniel SchwierzeckFile::Find::find(\&maint_wanted, "${lk_path}board");
286*68dc8769SDaniel Schwierzeck
287*68dc8769SDaniel Schwierzeckforeach my $maint_file (@maint_files) {
288*68dc8769SDaniel Schwierzeck    my $maint;
289*68dc8769SDaniel Schwierzeck    open ($maint, '<', "$maint_file")
290*68dc8769SDaniel Schwierzeck	or die "$P: Can't open $maint_file: $!\n";
291*68dc8769SDaniel Schwierzeck    read_maintainers($maint);
292*68dc8769SDaniel Schwierzeck    close($maint);
293*68dc8769SDaniel Schwierzeck}
294*68dc8769SDaniel Schwierzeck
295*68dc8769SDaniel Schwierzecksub read_maintainers {
296*68dc8769SDaniel Schwierzeck    my ($maint) = @_;
297*68dc8769SDaniel Schwierzeck
29892bca398SDaniel Schwierzeck    while (<$maint>) {
29992bca398SDaniel Schwierzeck	my $line = $_;
30092bca398SDaniel Schwierzeck
30192bca398SDaniel Schwierzeck	if ($line =~ m/^(\C):\s*(.*)/) {
30292bca398SDaniel Schwierzeck	    my $type = $1;
30392bca398SDaniel Schwierzeck	    my $value = $2;
30492bca398SDaniel Schwierzeck
30592bca398SDaniel Schwierzeck	    ##Filename pattern matching
30692bca398SDaniel Schwierzeck	    if ($type eq "F" || $type eq "X") {
30792bca398SDaniel Schwierzeck		$value =~ s@\.@\\\.@g;       ##Convert . to \.
30892bca398SDaniel Schwierzeck		$value =~ s/\*/\.\*/g;       ##Convert * to .*
30992bca398SDaniel Schwierzeck		$value =~ s/\?/\./g;         ##Convert ? to .
31092bca398SDaniel Schwierzeck		##if pattern is a directory and it lacks a trailing slash, add one
31192bca398SDaniel Schwierzeck		if ((-d $value)) {
31292bca398SDaniel Schwierzeck		    $value =~ s@([^/])$@$1/@;
31392bca398SDaniel Schwierzeck		}
31492bca398SDaniel Schwierzeck	    } elsif ($type eq "K") {
31592bca398SDaniel Schwierzeck		$keyword_hash{@typevalue} = $value;
31692bca398SDaniel Schwierzeck	    }
31792bca398SDaniel Schwierzeck	    push(@typevalue, "$type:$value");
31892bca398SDaniel Schwierzeck	} elsif (!/^(\s)*$/) {
31992bca398SDaniel Schwierzeck	    $line =~ s/\n$//g;
32092bca398SDaniel Schwierzeck	    push(@typevalue, $line);
32192bca398SDaniel Schwierzeck	}
32292bca398SDaniel Schwierzeck    }
323*68dc8769SDaniel Schwierzeck}
32492bca398SDaniel Schwierzeck
32592bca398SDaniel Schwierzeck
32692bca398SDaniel Schwierzeck#
32792bca398SDaniel Schwierzeck# Read mail address map
32892bca398SDaniel Schwierzeck#
32992bca398SDaniel Schwierzeck
33092bca398SDaniel Schwierzeckmy $mailmap;
33192bca398SDaniel Schwierzeck
33292bca398SDaniel Schwierzeckread_mailmap();
33392bca398SDaniel Schwierzeck
33492bca398SDaniel Schwierzecksub read_mailmap {
33592bca398SDaniel Schwierzeck    $mailmap = {
33692bca398SDaniel Schwierzeck	names => {},
33792bca398SDaniel Schwierzeck	addresses => {}
33892bca398SDaniel Schwierzeck    };
33992bca398SDaniel Schwierzeck
34092bca398SDaniel Schwierzeck    return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
34192bca398SDaniel Schwierzeck
34292bca398SDaniel Schwierzeck    open(my $mailmap_file, '<', "${lk_path}.mailmap")
34392bca398SDaniel Schwierzeck	or warn "$P: Can't open .mailmap: $!\n";
34492bca398SDaniel Schwierzeck
34592bca398SDaniel Schwierzeck    while (<$mailmap_file>) {
34692bca398SDaniel Schwierzeck	s/#.*$//; #strip comments
34792bca398SDaniel Schwierzeck	s/^\s+|\s+$//g; #trim
34892bca398SDaniel Schwierzeck
34992bca398SDaniel Schwierzeck	next if (/^\s*$/); #skip empty lines
35092bca398SDaniel Schwierzeck	#entries have one of the following formats:
35192bca398SDaniel Schwierzeck	# name1 <mail1>
35292bca398SDaniel Schwierzeck	# <mail1> <mail2>
35392bca398SDaniel Schwierzeck	# name1 <mail1> <mail2>
35492bca398SDaniel Schwierzeck	# name1 <mail1> name2 <mail2>
35592bca398SDaniel Schwierzeck	# (see man git-shortlog)
35692bca398SDaniel Schwierzeck
35792bca398SDaniel Schwierzeck	if (/^([^<]+)<([^>]+)>$/) {
35892bca398SDaniel Schwierzeck	    my $real_name = $1;
35992bca398SDaniel Schwierzeck	    my $address = $2;
36092bca398SDaniel Schwierzeck
36192bca398SDaniel Schwierzeck	    $real_name =~ s/\s+$//;
36292bca398SDaniel Schwierzeck	    ($real_name, $address) = parse_email("$real_name <$address>");
36392bca398SDaniel Schwierzeck	    $mailmap->{names}->{$address} = $real_name;
36492bca398SDaniel Schwierzeck
36592bca398SDaniel Schwierzeck	} elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
36692bca398SDaniel Schwierzeck	    my $real_address = $1;
36792bca398SDaniel Schwierzeck	    my $wrong_address = $2;
36892bca398SDaniel Schwierzeck
36992bca398SDaniel Schwierzeck	    $mailmap->{addresses}->{$wrong_address} = $real_address;
37092bca398SDaniel Schwierzeck
37192bca398SDaniel Schwierzeck	} elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
37292bca398SDaniel Schwierzeck	    my $real_name = $1;
37392bca398SDaniel Schwierzeck	    my $real_address = $2;
37492bca398SDaniel Schwierzeck	    my $wrong_address = $3;
37592bca398SDaniel Schwierzeck
37692bca398SDaniel Schwierzeck	    $real_name =~ s/\s+$//;
37792bca398SDaniel Schwierzeck	    ($real_name, $real_address) =
37892bca398SDaniel Schwierzeck		parse_email("$real_name <$real_address>");
37992bca398SDaniel Schwierzeck	    $mailmap->{names}->{$wrong_address} = $real_name;
38092bca398SDaniel Schwierzeck	    $mailmap->{addresses}->{$wrong_address} = $real_address;
38192bca398SDaniel Schwierzeck
38292bca398SDaniel Schwierzeck	} elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
38392bca398SDaniel Schwierzeck	    my $real_name = $1;
38492bca398SDaniel Schwierzeck	    my $real_address = $2;
38592bca398SDaniel Schwierzeck	    my $wrong_name = $3;
38692bca398SDaniel Schwierzeck	    my $wrong_address = $4;
38792bca398SDaniel Schwierzeck
38892bca398SDaniel Schwierzeck	    $real_name =~ s/\s+$//;
38992bca398SDaniel Schwierzeck	    ($real_name, $real_address) =
39092bca398SDaniel Schwierzeck		parse_email("$real_name <$real_address>");
39192bca398SDaniel Schwierzeck
39292bca398SDaniel Schwierzeck	    $wrong_name =~ s/\s+$//;
39392bca398SDaniel Schwierzeck	    ($wrong_name, $wrong_address) =
39492bca398SDaniel Schwierzeck		parse_email("$wrong_name <$wrong_address>");
39592bca398SDaniel Schwierzeck
39692bca398SDaniel Schwierzeck	    my $wrong_email = format_email($wrong_name, $wrong_address, 1);
39792bca398SDaniel Schwierzeck	    $mailmap->{names}->{$wrong_email} = $real_name;
39892bca398SDaniel Schwierzeck	    $mailmap->{addresses}->{$wrong_email} = $real_address;
39992bca398SDaniel Schwierzeck	}
40092bca398SDaniel Schwierzeck    }
40192bca398SDaniel Schwierzeck    close($mailmap_file);
40292bca398SDaniel Schwierzeck}
40392bca398SDaniel Schwierzeck
40492bca398SDaniel Schwierzeck## use the filenames on the command line or find the filenames in the patchfiles
40592bca398SDaniel Schwierzeck
40692bca398SDaniel Schwierzeckmy @files = ();
40792bca398SDaniel Schwierzeckmy @range = ();
40892bca398SDaniel Schwierzeckmy @keyword_tvi = ();
40992bca398SDaniel Schwierzeckmy @file_emails = ();
41092bca398SDaniel Schwierzeck
41192bca398SDaniel Schwierzeckif (!@ARGV) {
41292bca398SDaniel Schwierzeck    push(@ARGV, "&STDIN");
41392bca398SDaniel Schwierzeck}
41492bca398SDaniel Schwierzeck
41592bca398SDaniel Schwierzeckforeach my $file (@ARGV) {
41692bca398SDaniel Schwierzeck    if ($file ne "&STDIN") {
41792bca398SDaniel Schwierzeck	##if $file is a directory and it lacks a trailing slash, add one
41892bca398SDaniel Schwierzeck	if ((-d $file)) {
41992bca398SDaniel Schwierzeck	    $file =~ s@([^/])$@$1/@;
42092bca398SDaniel Schwierzeck	} elsif (!(-f $file)) {
42192bca398SDaniel Schwierzeck	    die "$P: file '${file}' not found\n";
42292bca398SDaniel Schwierzeck	}
42392bca398SDaniel Schwierzeck    }
42492bca398SDaniel Schwierzeck    if ($from_filename) {
42592bca398SDaniel Schwierzeck	push(@files, $file);
42692bca398SDaniel Schwierzeck	if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
42792bca398SDaniel Schwierzeck	    open(my $f, '<', $file)
42892bca398SDaniel Schwierzeck		or die "$P: Can't open $file: $!\n";
42992bca398SDaniel Schwierzeck	    my $text = do { local($/) ; <$f> };
43092bca398SDaniel Schwierzeck	    close($f);
43192bca398SDaniel Schwierzeck	    if ($keywords) {
43292bca398SDaniel Schwierzeck		foreach my $line (keys %keyword_hash) {
43392bca398SDaniel Schwierzeck		    if ($text =~ m/$keyword_hash{$line}/x) {
43492bca398SDaniel Schwierzeck			push(@keyword_tvi, $line);
43592bca398SDaniel Schwierzeck		    }
43692bca398SDaniel Schwierzeck		}
43792bca398SDaniel Schwierzeck	    }
43892bca398SDaniel Schwierzeck	    if ($file_emails) {
43992bca398SDaniel 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;
44092bca398SDaniel Schwierzeck		push(@file_emails, clean_file_emails(@poss_addr));
44192bca398SDaniel Schwierzeck	    }
44292bca398SDaniel Schwierzeck	}
44392bca398SDaniel Schwierzeck    } else {
44492bca398SDaniel Schwierzeck	my $file_cnt = @files;
44592bca398SDaniel Schwierzeck	my $lastfile;
44692bca398SDaniel Schwierzeck
44792bca398SDaniel Schwierzeck	open(my $patch, "< $file")
44892bca398SDaniel Schwierzeck	    or die "$P: Can't open $file: $!\n";
44992bca398SDaniel Schwierzeck
45092bca398SDaniel Schwierzeck	# We can check arbitrary information before the patch
45192bca398SDaniel Schwierzeck	# like the commit message, mail headers, etc...
45292bca398SDaniel Schwierzeck	# This allows us to match arbitrary keywords against any part
45392bca398SDaniel Schwierzeck	# of a git format-patch generated file (subject tags, etc...)
45492bca398SDaniel Schwierzeck
45592bca398SDaniel Schwierzeck	my $patch_prefix = "";			#Parsing the intro
45692bca398SDaniel Schwierzeck
45792bca398SDaniel Schwierzeck	while (<$patch>) {
45892bca398SDaniel Schwierzeck	    my $patch_line = $_;
45992bca398SDaniel Schwierzeck	    if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
46092bca398SDaniel Schwierzeck		my $filename = $1;
46192bca398SDaniel Schwierzeck		$filename =~ s@^[^/]*/@@;
46292bca398SDaniel Schwierzeck		$filename =~ s@\n@@;
46392bca398SDaniel Schwierzeck		$lastfile = $filename;
46492bca398SDaniel Schwierzeck		push(@files, $filename);
46592bca398SDaniel Schwierzeck		$patch_prefix = "^[+-].*";	#Now parsing the actual patch
46692bca398SDaniel Schwierzeck	    } elsif (m/^\@\@ -(\d+),(\d+)/) {
46792bca398SDaniel Schwierzeck		if ($email_git_blame) {
46892bca398SDaniel Schwierzeck		    push(@range, "$lastfile:$1:$2");
46992bca398SDaniel Schwierzeck		}
47092bca398SDaniel Schwierzeck	    } elsif ($keywords) {
47192bca398SDaniel Schwierzeck		foreach my $line (keys %keyword_hash) {
47292bca398SDaniel Schwierzeck		    if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
47392bca398SDaniel Schwierzeck			push(@keyword_tvi, $line);
47492bca398SDaniel Schwierzeck		    }
47592bca398SDaniel Schwierzeck		}
47692bca398SDaniel Schwierzeck	    }
47792bca398SDaniel Schwierzeck	}
47892bca398SDaniel Schwierzeck	close($patch);
47992bca398SDaniel Schwierzeck
48092bca398SDaniel Schwierzeck	if ($file_cnt == @files) {
48192bca398SDaniel Schwierzeck	    warn "$P: file '${file}' doesn't appear to be a patch.  "
48292bca398SDaniel Schwierzeck		. "Add -f to options?\n";
48392bca398SDaniel Schwierzeck	}
48492bca398SDaniel Schwierzeck	@files = sort_and_uniq(@files);
48592bca398SDaniel Schwierzeck    }
48692bca398SDaniel Schwierzeck}
48792bca398SDaniel Schwierzeck
48892bca398SDaniel Schwierzeck@file_emails = uniq(@file_emails);
48992bca398SDaniel Schwierzeck
49092bca398SDaniel Schwierzeckmy %email_hash_name;
49192bca398SDaniel Schwierzeckmy %email_hash_address;
49292bca398SDaniel Schwierzeckmy @email_to = ();
49392bca398SDaniel Schwierzeckmy %hash_list_to;
49492bca398SDaniel Schwierzeckmy @list_to = ();
49592bca398SDaniel Schwierzeckmy @scm = ();
49692bca398SDaniel Schwierzeckmy @web = ();
49792bca398SDaniel Schwierzeckmy @subsystem = ();
49892bca398SDaniel Schwierzeckmy @status = ();
49992bca398SDaniel Schwierzeckmy %deduplicate_name_hash = ();
50092bca398SDaniel Schwierzeckmy %deduplicate_address_hash = ();
50192bca398SDaniel Schwierzeck
50292bca398SDaniel Schwierzeckmy @maintainers = get_maintainers();
50392bca398SDaniel Schwierzeck
50492bca398SDaniel Schwierzeckif (@maintainers) {
50592bca398SDaniel Schwierzeck    @maintainers = merge_email(@maintainers);
50692bca398SDaniel Schwierzeck    output(@maintainers);
50792bca398SDaniel Schwierzeck}
50892bca398SDaniel Schwierzeck
50992bca398SDaniel Schwierzeckif ($scm) {
51092bca398SDaniel Schwierzeck    @scm = uniq(@scm);
51192bca398SDaniel Schwierzeck    output(@scm);
51292bca398SDaniel Schwierzeck}
51392bca398SDaniel Schwierzeck
51492bca398SDaniel Schwierzeckif ($status) {
51592bca398SDaniel Schwierzeck    @status = uniq(@status);
51692bca398SDaniel Schwierzeck    output(@status);
51792bca398SDaniel Schwierzeck}
51892bca398SDaniel Schwierzeck
51992bca398SDaniel Schwierzeckif ($subsystem) {
52092bca398SDaniel Schwierzeck    @subsystem = uniq(@subsystem);
52192bca398SDaniel Schwierzeck    output(@subsystem);
52292bca398SDaniel Schwierzeck}
52392bca398SDaniel Schwierzeck
52492bca398SDaniel Schwierzeckif ($web) {
52592bca398SDaniel Schwierzeck    @web = uniq(@web);
52692bca398SDaniel Schwierzeck    output(@web);
52792bca398SDaniel Schwierzeck}
52892bca398SDaniel Schwierzeck
52992bca398SDaniel Schwierzeckexit($exit);
53092bca398SDaniel Schwierzeck
53192bca398SDaniel Schwierzecksub range_is_maintained {
53292bca398SDaniel Schwierzeck    my ($start, $end) = @_;
53392bca398SDaniel Schwierzeck
53492bca398SDaniel Schwierzeck    for (my $i = $start; $i < $end; $i++) {
53592bca398SDaniel Schwierzeck	my $line = $typevalue[$i];
53692bca398SDaniel Schwierzeck	if ($line =~ m/^(\C):\s*(.*)/) {
53792bca398SDaniel Schwierzeck	    my $type = $1;
53892bca398SDaniel Schwierzeck	    my $value = $2;
53992bca398SDaniel Schwierzeck	    if ($type eq 'S') {
54092bca398SDaniel Schwierzeck		if ($value =~ /(maintain|support)/i) {
54192bca398SDaniel Schwierzeck		    return 1;
54292bca398SDaniel Schwierzeck		}
54392bca398SDaniel Schwierzeck	    }
54492bca398SDaniel Schwierzeck	}
54592bca398SDaniel Schwierzeck    }
54692bca398SDaniel Schwierzeck    return 0;
54792bca398SDaniel Schwierzeck}
54892bca398SDaniel Schwierzeck
54992bca398SDaniel Schwierzecksub range_has_maintainer {
55092bca398SDaniel Schwierzeck    my ($start, $end) = @_;
55192bca398SDaniel Schwierzeck
55292bca398SDaniel Schwierzeck    for (my $i = $start; $i < $end; $i++) {
55392bca398SDaniel Schwierzeck	my $line = $typevalue[$i];
55492bca398SDaniel Schwierzeck	if ($line =~ m/^(\C):\s*(.*)/) {
55592bca398SDaniel Schwierzeck	    my $type = $1;
55692bca398SDaniel Schwierzeck	    my $value = $2;
55792bca398SDaniel Schwierzeck	    if ($type eq 'M') {
55892bca398SDaniel Schwierzeck		return 1;
55992bca398SDaniel Schwierzeck	    }
56092bca398SDaniel Schwierzeck	}
56192bca398SDaniel Schwierzeck    }
56292bca398SDaniel Schwierzeck    return 0;
56392bca398SDaniel Schwierzeck}
56492bca398SDaniel Schwierzeck
56592bca398SDaniel Schwierzecksub get_maintainers {
56692bca398SDaniel Schwierzeck    %email_hash_name = ();
56792bca398SDaniel Schwierzeck    %email_hash_address = ();
56892bca398SDaniel Schwierzeck    %commit_author_hash = ();
56992bca398SDaniel Schwierzeck    %commit_signer_hash = ();
57092bca398SDaniel Schwierzeck    @email_to = ();
57192bca398SDaniel Schwierzeck    %hash_list_to = ();
57292bca398SDaniel Schwierzeck    @list_to = ();
57392bca398SDaniel Schwierzeck    @scm = ();
57492bca398SDaniel Schwierzeck    @web = ();
57592bca398SDaniel Schwierzeck    @subsystem = ();
57692bca398SDaniel Schwierzeck    @status = ();
57792bca398SDaniel Schwierzeck    %deduplicate_name_hash = ();
57892bca398SDaniel Schwierzeck    %deduplicate_address_hash = ();
57992bca398SDaniel Schwierzeck    if ($email_git_all_signature_types) {
58092bca398SDaniel Schwierzeck	$signature_pattern = "(.+?)[Bb][Yy]:";
58192bca398SDaniel Schwierzeck    } else {
58292bca398SDaniel Schwierzeck	$signature_pattern = "\(" . join("|", @signature_tags) . "\)";
58392bca398SDaniel Schwierzeck    }
58492bca398SDaniel Schwierzeck
58592bca398SDaniel Schwierzeck    # Find responsible parties
58692bca398SDaniel Schwierzeck
58792bca398SDaniel Schwierzeck    my %exact_pattern_match_hash = ();
58892bca398SDaniel Schwierzeck
58992bca398SDaniel Schwierzeck    foreach my $file (@files) {
59092bca398SDaniel Schwierzeck
59192bca398SDaniel Schwierzeck	my %hash;
59292bca398SDaniel Schwierzeck	my $tvi = find_first_section();
59392bca398SDaniel Schwierzeck	while ($tvi < @typevalue) {
59492bca398SDaniel Schwierzeck	    my $start = find_starting_index($tvi);
59592bca398SDaniel Schwierzeck	    my $end = find_ending_index($tvi);
59692bca398SDaniel Schwierzeck	    my $exclude = 0;
59792bca398SDaniel Schwierzeck	    my $i;
59892bca398SDaniel Schwierzeck
59992bca398SDaniel Schwierzeck	    #Do not match excluded file patterns
60092bca398SDaniel Schwierzeck
60192bca398SDaniel Schwierzeck	    for ($i = $start; $i < $end; $i++) {
60292bca398SDaniel Schwierzeck		my $line = $typevalue[$i];
60392bca398SDaniel Schwierzeck		if ($line =~ m/^(\C):\s*(.*)/) {
60492bca398SDaniel Schwierzeck		    my $type = $1;
60592bca398SDaniel Schwierzeck		    my $value = $2;
60692bca398SDaniel Schwierzeck		    if ($type eq 'X') {
60792bca398SDaniel Schwierzeck			if (file_match_pattern($file, $value)) {
60892bca398SDaniel Schwierzeck			    $exclude = 1;
60992bca398SDaniel Schwierzeck			    last;
61092bca398SDaniel Schwierzeck			}
61192bca398SDaniel Schwierzeck		    }
61292bca398SDaniel Schwierzeck		}
61392bca398SDaniel Schwierzeck	    }
61492bca398SDaniel Schwierzeck
61592bca398SDaniel Schwierzeck	    if (!$exclude) {
61692bca398SDaniel Schwierzeck		for ($i = $start; $i < $end; $i++) {
61792bca398SDaniel Schwierzeck		    my $line = $typevalue[$i];
61892bca398SDaniel Schwierzeck		    if ($line =~ m/^(\C):\s*(.*)/) {
61992bca398SDaniel Schwierzeck			my $type = $1;
62092bca398SDaniel Schwierzeck			my $value = $2;
62192bca398SDaniel Schwierzeck			if ($type eq 'F') {
62292bca398SDaniel Schwierzeck			    if (file_match_pattern($file, $value)) {
62392bca398SDaniel Schwierzeck				my $value_pd = ($value =~ tr@/@@);
62492bca398SDaniel Schwierzeck				my $file_pd = ($file  =~ tr@/@@);
62592bca398SDaniel Schwierzeck				$value_pd++ if (substr($value,-1,1) ne "/");
62692bca398SDaniel Schwierzeck				$value_pd = -1 if ($value =~ /^\.\*/);
62792bca398SDaniel Schwierzeck				if ($value_pd >= $file_pd &&
62892bca398SDaniel Schwierzeck				    range_is_maintained($start, $end) &&
62992bca398SDaniel Schwierzeck				    range_has_maintainer($start, $end)) {
63092bca398SDaniel Schwierzeck				    $exact_pattern_match_hash{$file} = 1;
63192bca398SDaniel Schwierzeck				}
63292bca398SDaniel Schwierzeck				if ($pattern_depth == 0 ||
63392bca398SDaniel Schwierzeck				    (($file_pd - $value_pd) < $pattern_depth)) {
63492bca398SDaniel Schwierzeck				    $hash{$tvi} = $value_pd;
63592bca398SDaniel Schwierzeck				}
63692bca398SDaniel Schwierzeck			    }
63792bca398SDaniel Schwierzeck			} elsif ($type eq 'N') {
63892bca398SDaniel Schwierzeck			    if ($file =~ m/$value/x) {
63992bca398SDaniel Schwierzeck				$hash{$tvi} = 0;
64092bca398SDaniel Schwierzeck			    }
64192bca398SDaniel Schwierzeck			}
64292bca398SDaniel Schwierzeck		    }
64392bca398SDaniel Schwierzeck		}
64492bca398SDaniel Schwierzeck	    }
64592bca398SDaniel Schwierzeck	    $tvi = $end + 1;
64692bca398SDaniel Schwierzeck	}
64792bca398SDaniel Schwierzeck
64892bca398SDaniel Schwierzeck	foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
64992bca398SDaniel Schwierzeck	    add_categories($line);
65092bca398SDaniel Schwierzeck	    if ($sections) {
65192bca398SDaniel Schwierzeck		my $i;
65292bca398SDaniel Schwierzeck		my $start = find_starting_index($line);
65392bca398SDaniel Schwierzeck		my $end = find_ending_index($line);
65492bca398SDaniel Schwierzeck		for ($i = $start; $i < $end; $i++) {
65592bca398SDaniel Schwierzeck		    my $line = $typevalue[$i];
65692bca398SDaniel Schwierzeck		    if ($line =~ /^[FX]:/) {		##Restore file patterns
65792bca398SDaniel Schwierzeck			$line =~ s/([^\\])\.([^\*])/$1\?$2/g;
65892bca398SDaniel Schwierzeck			$line =~ s/([^\\])\.$/$1\?/g;	##Convert . back to ?
65992bca398SDaniel Schwierzeck			$line =~ s/\\\./\./g;       	##Convert \. to .
66092bca398SDaniel Schwierzeck			$line =~ s/\.\*/\*/g;       	##Convert .* to *
66192bca398SDaniel Schwierzeck		    }
66292bca398SDaniel Schwierzeck		    $line =~ s/^([A-Z]):/$1:\t/g;
66392bca398SDaniel Schwierzeck		    print("$line\n");
66492bca398SDaniel Schwierzeck		}
66592bca398SDaniel Schwierzeck		print("\n");
66692bca398SDaniel Schwierzeck	    }
66792bca398SDaniel Schwierzeck	}
66892bca398SDaniel Schwierzeck    }
66992bca398SDaniel Schwierzeck
67092bca398SDaniel Schwierzeck    if ($keywords) {
67192bca398SDaniel Schwierzeck	@keyword_tvi = sort_and_uniq(@keyword_tvi);
67292bca398SDaniel Schwierzeck	foreach my $line (@keyword_tvi) {
67392bca398SDaniel Schwierzeck	    add_categories($line);
67492bca398SDaniel Schwierzeck	}
67592bca398SDaniel Schwierzeck    }
67692bca398SDaniel Schwierzeck
67792bca398SDaniel Schwierzeck    foreach my $email (@email_to, @list_to) {
67892bca398SDaniel Schwierzeck	$email->[0] = deduplicate_email($email->[0]);
67992bca398SDaniel Schwierzeck    }
68092bca398SDaniel Schwierzeck
68192bca398SDaniel Schwierzeck    foreach my $file (@files) {
68292bca398SDaniel Schwierzeck	if ($email &&
68392bca398SDaniel Schwierzeck	    ($email_git || ($email_git_fallback &&
68492bca398SDaniel Schwierzeck			    !$exact_pattern_match_hash{$file}))) {
68592bca398SDaniel Schwierzeck	    vcs_file_signoffs($file);
68692bca398SDaniel Schwierzeck	}
68792bca398SDaniel Schwierzeck	if ($email && $email_git_blame) {
68892bca398SDaniel Schwierzeck	    vcs_file_blame($file);
68992bca398SDaniel Schwierzeck	}
69092bca398SDaniel Schwierzeck    }
69192bca398SDaniel Schwierzeck
69292bca398SDaniel Schwierzeck    if ($email) {
69392bca398SDaniel Schwierzeck	foreach my $chief (@penguin_chief) {
69492bca398SDaniel Schwierzeck	    if ($chief =~ m/^(.*):(.*)/) {
69592bca398SDaniel Schwierzeck		my $email_address;
69692bca398SDaniel Schwierzeck
69792bca398SDaniel Schwierzeck		$email_address = format_email($1, $2, $email_usename);
69892bca398SDaniel Schwierzeck		if ($email_git_penguin_chiefs) {
69992bca398SDaniel Schwierzeck		    push(@email_to, [$email_address, 'chief penguin']);
70092bca398SDaniel Schwierzeck		} else {
70192bca398SDaniel Schwierzeck		    @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
70292bca398SDaniel Schwierzeck		}
70392bca398SDaniel Schwierzeck	    }
70492bca398SDaniel Schwierzeck	}
70592bca398SDaniel Schwierzeck
70692bca398SDaniel Schwierzeck	foreach my $email (@file_emails) {
70792bca398SDaniel Schwierzeck	    my ($name, $address) = parse_email($email);
70892bca398SDaniel Schwierzeck
70992bca398SDaniel Schwierzeck	    my $tmp_email = format_email($name, $address, $email_usename);
71092bca398SDaniel Schwierzeck	    push_email_address($tmp_email, '');
71192bca398SDaniel Schwierzeck	    add_role($tmp_email, 'in file');
71292bca398SDaniel Schwierzeck	}
71392bca398SDaniel Schwierzeck    }
71492bca398SDaniel Schwierzeck
71592bca398SDaniel Schwierzeck    my @to = ();
71692bca398SDaniel Schwierzeck    if ($email || $email_list) {
71792bca398SDaniel Schwierzeck	if ($email) {
71892bca398SDaniel Schwierzeck	    @to = (@to, @email_to);
71992bca398SDaniel Schwierzeck	}
72092bca398SDaniel Schwierzeck	if ($email_list) {
72192bca398SDaniel Schwierzeck	    @to = (@to, @list_to);
72292bca398SDaniel Schwierzeck	}
72392bca398SDaniel Schwierzeck    }
72492bca398SDaniel Schwierzeck
72592bca398SDaniel Schwierzeck    if ($interactive) {
72692bca398SDaniel Schwierzeck	@to = interactive_get_maintainers(\@to);
72792bca398SDaniel Schwierzeck    }
72892bca398SDaniel Schwierzeck
72992bca398SDaniel Schwierzeck    return @to;
73092bca398SDaniel Schwierzeck}
73192bca398SDaniel Schwierzeck
73292bca398SDaniel Schwierzecksub file_match_pattern {
73392bca398SDaniel Schwierzeck    my ($file, $pattern) = @_;
73492bca398SDaniel Schwierzeck    if (substr($pattern, -1) eq "/") {
73592bca398SDaniel Schwierzeck	if ($file =~ m@^$pattern@) {
73692bca398SDaniel Schwierzeck	    return 1;
73792bca398SDaniel Schwierzeck	}
73892bca398SDaniel Schwierzeck    } else {
73992bca398SDaniel Schwierzeck	if ($file =~ m@^$pattern@) {
74092bca398SDaniel Schwierzeck	    my $s1 = ($file =~ tr@/@@);
74192bca398SDaniel Schwierzeck	    my $s2 = ($pattern =~ tr@/@@);
74292bca398SDaniel Schwierzeck	    if ($s1 == $s2) {
74392bca398SDaniel Schwierzeck		return 1;
74492bca398SDaniel Schwierzeck	    }
74592bca398SDaniel Schwierzeck	}
74692bca398SDaniel Schwierzeck    }
74792bca398SDaniel Schwierzeck    return 0;
74892bca398SDaniel Schwierzeck}
74992bca398SDaniel Schwierzeck
75092bca398SDaniel Schwierzecksub usage {
75192bca398SDaniel Schwierzeck    print <<EOT;
75292bca398SDaniel Schwierzeckusage: $P [options] patchfile
75392bca398SDaniel Schwierzeck       $P [options] -f file|directory
75492bca398SDaniel Schwierzeckversion: $V
75592bca398SDaniel Schwierzeck
75692bca398SDaniel SchwierzeckMAINTAINER field selection options:
75792bca398SDaniel Schwierzeck  --email => print email address(es) if any
75892bca398SDaniel Schwierzeck    --git => include recent git \*-by: signers
75992bca398SDaniel Schwierzeck    --git-all-signature-types => include signers regardless of signature type
76092bca398SDaniel Schwierzeck        or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
76192bca398SDaniel Schwierzeck    --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
76292bca398SDaniel Schwierzeck    --git-chief-penguins => include ${penguin_chiefs}
76392bca398SDaniel Schwierzeck    --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
76492bca398SDaniel Schwierzeck    --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
76592bca398SDaniel Schwierzeck    --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
76692bca398SDaniel Schwierzeck    --git-blame => use git blame to find modified commits for patch or file
76792bca398SDaniel Schwierzeck    --git-since => git history to use (default: $email_git_since)
76892bca398SDaniel Schwierzeck    --hg-since => hg history to use (default: $email_hg_since)
76992bca398SDaniel Schwierzeck    --interactive => display a menu (mostly useful if used with the --git option)
77092bca398SDaniel Schwierzeck    --m => include maintainer(s) if any
77192bca398SDaniel Schwierzeck    --n => include name 'Full Name <addr\@domain.tld>'
77292bca398SDaniel Schwierzeck    --l => include list(s) if any
77392bca398SDaniel Schwierzeck    --s => include subscriber only list(s) if any
77492bca398SDaniel Schwierzeck    --remove-duplicates => minimize duplicate email names/addresses
77592bca398SDaniel Schwierzeck    --roles => show roles (status:subsystem, git-signer, list, etc...)
77692bca398SDaniel Schwierzeck    --rolestats => show roles and statistics (commits/total_commits, %)
77792bca398SDaniel Schwierzeck    --file-emails => add email addresses found in -f file (default: 0 (off))
77892bca398SDaniel Schwierzeck  --scm => print SCM tree(s) if any
77992bca398SDaniel Schwierzeck  --status => print status if any
78092bca398SDaniel Schwierzeck  --subsystem => print subsystem name if any
78192bca398SDaniel Schwierzeck  --web => print website(s) if any
78292bca398SDaniel Schwierzeck
78392bca398SDaniel SchwierzeckOutput type options:
78492bca398SDaniel Schwierzeck  --separator [, ] => separator for multiple entries on 1 line
78592bca398SDaniel Schwierzeck    using --separator also sets --nomultiline if --separator is not [, ]
78692bca398SDaniel Schwierzeck  --multiline => print 1 entry per line
78792bca398SDaniel Schwierzeck
78892bca398SDaniel SchwierzeckOther options:
78992bca398SDaniel Schwierzeck  --pattern-depth => Number of pattern directory traversals (default: 0 (all))
79092bca398SDaniel Schwierzeck  --keywords => scan patch for keywords (default: $keywords)
79192bca398SDaniel Schwierzeck  --sections => print all of the subsystem sections with pattern matches
79292bca398SDaniel Schwierzeck  --mailmap => use .mailmap file (default: $email_use_mailmap)
79392bca398SDaniel Schwierzeck  --version => show version
79492bca398SDaniel Schwierzeck  --help => show this help information
79592bca398SDaniel Schwierzeck
79692bca398SDaniel SchwierzeckDefault options:
79792bca398SDaniel Schwierzeck  [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
79892bca398SDaniel Schwierzeck   --remove-duplicates --rolestats]
79992bca398SDaniel Schwierzeck
80092bca398SDaniel SchwierzeckNotes:
80192bca398SDaniel Schwierzeck  Using "-f directory" may give unexpected results:
80292bca398SDaniel Schwierzeck      Used with "--git", git signators for _all_ files in and below
80392bca398SDaniel Schwierzeck          directory are examined as git recurses directories.
80492bca398SDaniel Schwierzeck          Any specified X: (exclude) pattern matches are _not_ ignored.
80592bca398SDaniel Schwierzeck      Used with "--nogit", directory is used as a pattern match,
80692bca398SDaniel Schwierzeck          no individual file within the directory or subdirectory
80792bca398SDaniel Schwierzeck          is matched.
80892bca398SDaniel Schwierzeck      Used with "--git-blame", does not iterate all files in directory
80992bca398SDaniel Schwierzeck  Using "--git-blame" is slow and may add old committers and authors
81092bca398SDaniel Schwierzeck      that are no longer active maintainers to the output.
81192bca398SDaniel Schwierzeck  Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
81292bca398SDaniel Schwierzeck      other automated tools that expect only ["name"] <email address>
81392bca398SDaniel Schwierzeck      may not work because of additional output after <email address>.
81492bca398SDaniel Schwierzeck  Using "--rolestats" and "--git-blame" shows the #/total=% commits,
81592bca398SDaniel Schwierzeck      not the percentage of the entire file authored.  # of commits is
81692bca398SDaniel Schwierzeck      not a good measure of amount of code authored.  1 major commit may
81792bca398SDaniel Schwierzeck      contain a thousand lines, 5 trivial commits may modify a single line.
81892bca398SDaniel Schwierzeck  If git is not installed, but mercurial (hg) is installed and an .hg
81992bca398SDaniel Schwierzeck      repository exists, the following options apply to mercurial:
82092bca398SDaniel Schwierzeck          --git,
82192bca398SDaniel Schwierzeck          --git-min-signatures, --git-max-maintainers, --git-min-percent, and
82292bca398SDaniel Schwierzeck          --git-blame
82392bca398SDaniel Schwierzeck      Use --hg-since not --git-since to control date selection
82492bca398SDaniel Schwierzeck  File ".get_maintainer.conf", if it exists in the linux kernel source root
82592bca398SDaniel Schwierzeck      directory, can change whatever get_maintainer defaults are desired.
82692bca398SDaniel Schwierzeck      Entries in this file can be any command line argument.
82792bca398SDaniel Schwierzeck      This file is prepended to any additional command line arguments.
82892bca398SDaniel Schwierzeck      Multiple lines and # comments are allowed.
82992bca398SDaniel SchwierzeckEOT
83092bca398SDaniel Schwierzeck}
83192bca398SDaniel Schwierzeck
83292bca398SDaniel Schwierzecksub top_of_kernel_tree {
83392bca398SDaniel Schwierzeck    my ($lk_path) = @_;
83492bca398SDaniel Schwierzeck
83592bca398SDaniel Schwierzeck    if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
83692bca398SDaniel Schwierzeck	$lk_path .= "/";
83792bca398SDaniel Schwierzeck    }
838ee360cd2SDaniel Schwierzeck    if (   (-f "${lk_path}CREDITS")
83992bca398SDaniel Schwierzeck	&& (-f "${lk_path}Kbuild")
84092bca398SDaniel Schwierzeck	&& (-f "${lk_path}MAINTAINERS")
84192bca398SDaniel Schwierzeck	&& (-f "${lk_path}Makefile")
84292bca398SDaniel Schwierzeck	&& (-f "${lk_path}README")
84392bca398SDaniel Schwierzeck	&& (-d "${lk_path}arch")
844ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}board")
845ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}common")
846ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}doc")
84792bca398SDaniel Schwierzeck	&& (-d "${lk_path}drivers")
848ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}dts")
84992bca398SDaniel Schwierzeck	&& (-d "${lk_path}fs")
85092bca398SDaniel Schwierzeck	&& (-d "${lk_path}lib")
851ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}include")
852ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}net")
853ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}post")
854ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}scripts")
855ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}test")
856ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}tools")) {
85792bca398SDaniel Schwierzeck	return 1;
85892bca398SDaniel Schwierzeck    }
85992bca398SDaniel Schwierzeck    return 0;
86092bca398SDaniel Schwierzeck}
86192bca398SDaniel Schwierzeck
86292bca398SDaniel Schwierzecksub parse_email {
86392bca398SDaniel Schwierzeck    my ($formatted_email) = @_;
86492bca398SDaniel Schwierzeck
86592bca398SDaniel Schwierzeck    my $name = "";
86692bca398SDaniel Schwierzeck    my $address = "";
86792bca398SDaniel Schwierzeck
86892bca398SDaniel Schwierzeck    if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
86992bca398SDaniel Schwierzeck	$name = $1;
87092bca398SDaniel Schwierzeck	$address = $2;
87192bca398SDaniel Schwierzeck    } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
87292bca398SDaniel Schwierzeck	$address = $1;
87392bca398SDaniel Schwierzeck    } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
87492bca398SDaniel Schwierzeck	$address = $1;
87592bca398SDaniel Schwierzeck    }
87692bca398SDaniel Schwierzeck
87792bca398SDaniel Schwierzeck    $name =~ s/^\s+|\s+$//g;
87892bca398SDaniel Schwierzeck    $name =~ s/^\"|\"$//g;
87992bca398SDaniel Schwierzeck    $address =~ s/^\s+|\s+$//g;
88092bca398SDaniel Schwierzeck
88192bca398SDaniel Schwierzeck    if ($name =~ /[^\w \-]/i) {  	 ##has "must quote" chars
88292bca398SDaniel Schwierzeck	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
88392bca398SDaniel Schwierzeck	$name = "\"$name\"";
88492bca398SDaniel Schwierzeck    }
88592bca398SDaniel Schwierzeck
88692bca398SDaniel Schwierzeck    return ($name, $address);
88792bca398SDaniel Schwierzeck}
88892bca398SDaniel Schwierzeck
88992bca398SDaniel Schwierzecksub format_email {
89092bca398SDaniel Schwierzeck    my ($name, $address, $usename) = @_;
89192bca398SDaniel Schwierzeck
89292bca398SDaniel Schwierzeck    my $formatted_email;
89392bca398SDaniel Schwierzeck
89492bca398SDaniel Schwierzeck    $name =~ s/^\s+|\s+$//g;
89592bca398SDaniel Schwierzeck    $name =~ s/^\"|\"$//g;
89692bca398SDaniel Schwierzeck    $address =~ s/^\s+|\s+$//g;
89792bca398SDaniel Schwierzeck
89892bca398SDaniel Schwierzeck    if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
89992bca398SDaniel Schwierzeck	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
90092bca398SDaniel Schwierzeck	$name = "\"$name\"";
90192bca398SDaniel Schwierzeck    }
90292bca398SDaniel Schwierzeck
90392bca398SDaniel Schwierzeck    if ($usename) {
90492bca398SDaniel Schwierzeck	if ("$name" eq "") {
90592bca398SDaniel Schwierzeck	    $formatted_email = "$address";
90692bca398SDaniel Schwierzeck	} else {
90792bca398SDaniel Schwierzeck	    $formatted_email = "$name <$address>";
90892bca398SDaniel Schwierzeck	}
90992bca398SDaniel Schwierzeck    } else {
91092bca398SDaniel Schwierzeck	$formatted_email = $address;
91192bca398SDaniel Schwierzeck    }
91292bca398SDaniel Schwierzeck
91392bca398SDaniel Schwierzeck    return $formatted_email;
91492bca398SDaniel Schwierzeck}
91592bca398SDaniel Schwierzeck
91692bca398SDaniel Schwierzecksub find_first_section {
91792bca398SDaniel Schwierzeck    my $index = 0;
91892bca398SDaniel Schwierzeck
91992bca398SDaniel Schwierzeck    while ($index < @typevalue) {
92092bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
92192bca398SDaniel Schwierzeck	if (($tv =~ m/^(\C):\s*(.*)/)) {
92292bca398SDaniel Schwierzeck	    last;
92392bca398SDaniel Schwierzeck	}
92492bca398SDaniel Schwierzeck	$index++;
92592bca398SDaniel Schwierzeck    }
92692bca398SDaniel Schwierzeck
92792bca398SDaniel Schwierzeck    return $index;
92892bca398SDaniel Schwierzeck}
92992bca398SDaniel Schwierzeck
93092bca398SDaniel Schwierzecksub find_starting_index {
93192bca398SDaniel Schwierzeck    my ($index) = @_;
93292bca398SDaniel Schwierzeck
93392bca398SDaniel Schwierzeck    while ($index > 0) {
93492bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
93592bca398SDaniel Schwierzeck	if (!($tv =~ m/^(\C):\s*(.*)/)) {
93692bca398SDaniel Schwierzeck	    last;
93792bca398SDaniel Schwierzeck	}
93892bca398SDaniel Schwierzeck	$index--;
93992bca398SDaniel Schwierzeck    }
94092bca398SDaniel Schwierzeck
94192bca398SDaniel Schwierzeck    return $index;
94292bca398SDaniel Schwierzeck}
94392bca398SDaniel Schwierzeck
94492bca398SDaniel Schwierzecksub find_ending_index {
94592bca398SDaniel Schwierzeck    my ($index) = @_;
94692bca398SDaniel Schwierzeck
94792bca398SDaniel Schwierzeck    while ($index < @typevalue) {
94892bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
94992bca398SDaniel Schwierzeck	if (!($tv =~ m/^(\C):\s*(.*)/)) {
95092bca398SDaniel Schwierzeck	    last;
95192bca398SDaniel Schwierzeck	}
95292bca398SDaniel Schwierzeck	$index++;
95392bca398SDaniel Schwierzeck    }
95492bca398SDaniel Schwierzeck
95592bca398SDaniel Schwierzeck    return $index;
95692bca398SDaniel Schwierzeck}
95792bca398SDaniel Schwierzeck
95892bca398SDaniel Schwierzecksub get_maintainer_role {
95992bca398SDaniel Schwierzeck    my ($index) = @_;
96092bca398SDaniel Schwierzeck
96192bca398SDaniel Schwierzeck    my $i;
96292bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
96392bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
96492bca398SDaniel Schwierzeck
96592bca398SDaniel Schwierzeck    my $role = "unknown";
96692bca398SDaniel Schwierzeck    my $subsystem = $typevalue[$start];
96792bca398SDaniel Schwierzeck    if (length($subsystem) > 20) {
96892bca398SDaniel Schwierzeck	$subsystem = substr($subsystem, 0, 17);
96992bca398SDaniel Schwierzeck	$subsystem =~ s/\s*$//;
97092bca398SDaniel Schwierzeck	$subsystem = $subsystem . "...";
97192bca398SDaniel Schwierzeck    }
97292bca398SDaniel Schwierzeck
97392bca398SDaniel Schwierzeck    for ($i = $start + 1; $i < $end; $i++) {
97492bca398SDaniel Schwierzeck	my $tv = $typevalue[$i];
97592bca398SDaniel Schwierzeck	if ($tv =~ m/^(\C):\s*(.*)/) {
97692bca398SDaniel Schwierzeck	    my $ptype = $1;
97792bca398SDaniel Schwierzeck	    my $pvalue = $2;
97892bca398SDaniel Schwierzeck	    if ($ptype eq "S") {
97992bca398SDaniel Schwierzeck		$role = $pvalue;
98092bca398SDaniel Schwierzeck	    }
98192bca398SDaniel Schwierzeck	}
98292bca398SDaniel Schwierzeck    }
98392bca398SDaniel Schwierzeck
98492bca398SDaniel Schwierzeck    $role = lc($role);
98592bca398SDaniel Schwierzeck    if      ($role eq "supported") {
98692bca398SDaniel Schwierzeck	$role = "supporter";
98792bca398SDaniel Schwierzeck    } elsif ($role eq "maintained") {
98892bca398SDaniel Schwierzeck	$role = "maintainer";
98992bca398SDaniel Schwierzeck    } elsif ($role eq "odd fixes") {
99092bca398SDaniel Schwierzeck	$role = "odd fixer";
99192bca398SDaniel Schwierzeck    } elsif ($role eq "orphan") {
99292bca398SDaniel Schwierzeck	$role = "orphan minder";
99392bca398SDaniel Schwierzeck    } elsif ($role eq "obsolete") {
99492bca398SDaniel Schwierzeck	$role = "obsolete minder";
99592bca398SDaniel Schwierzeck    } elsif ($role eq "buried alive in reporters") {
99692bca398SDaniel Schwierzeck	$role = "chief penguin";
99792bca398SDaniel Schwierzeck    }
99892bca398SDaniel Schwierzeck
99992bca398SDaniel Schwierzeck    return $role . ":" . $subsystem;
100092bca398SDaniel Schwierzeck}
100192bca398SDaniel Schwierzeck
100292bca398SDaniel Schwierzecksub get_list_role {
100392bca398SDaniel Schwierzeck    my ($index) = @_;
100492bca398SDaniel Schwierzeck
100592bca398SDaniel Schwierzeck    my $i;
100692bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
100792bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
100892bca398SDaniel Schwierzeck
100992bca398SDaniel Schwierzeck    my $subsystem = $typevalue[$start];
101092bca398SDaniel Schwierzeck    if (length($subsystem) > 20) {
101192bca398SDaniel Schwierzeck	$subsystem = substr($subsystem, 0, 17);
101292bca398SDaniel Schwierzeck	$subsystem =~ s/\s*$//;
101392bca398SDaniel Schwierzeck	$subsystem = $subsystem . "...";
101492bca398SDaniel Schwierzeck    }
101592bca398SDaniel Schwierzeck
101692bca398SDaniel Schwierzeck    if ($subsystem eq "THE REST") {
101792bca398SDaniel Schwierzeck	$subsystem = "";
101892bca398SDaniel Schwierzeck    }
101992bca398SDaniel Schwierzeck
102092bca398SDaniel Schwierzeck    return $subsystem;
102192bca398SDaniel Schwierzeck}
102292bca398SDaniel Schwierzeck
102392bca398SDaniel Schwierzecksub add_categories {
102492bca398SDaniel Schwierzeck    my ($index) = @_;
102592bca398SDaniel Schwierzeck
102692bca398SDaniel Schwierzeck    my $i;
102792bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
102892bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
102992bca398SDaniel Schwierzeck
103092bca398SDaniel Schwierzeck    push(@subsystem, $typevalue[$start]);
103192bca398SDaniel Schwierzeck
103292bca398SDaniel Schwierzeck    for ($i = $start + 1; $i < $end; $i++) {
103392bca398SDaniel Schwierzeck	my $tv = $typevalue[$i];
103492bca398SDaniel Schwierzeck	if ($tv =~ m/^(\C):\s*(.*)/) {
103592bca398SDaniel Schwierzeck	    my $ptype = $1;
103692bca398SDaniel Schwierzeck	    my $pvalue = $2;
103792bca398SDaniel Schwierzeck	    if ($ptype eq "L") {
103892bca398SDaniel Schwierzeck		my $list_address = $pvalue;
103992bca398SDaniel Schwierzeck		my $list_additional = "";
104092bca398SDaniel Schwierzeck		my $list_role = get_list_role($i);
104192bca398SDaniel Schwierzeck
104292bca398SDaniel Schwierzeck		if ($list_role ne "") {
104392bca398SDaniel Schwierzeck		    $list_role = ":" . $list_role;
104492bca398SDaniel Schwierzeck		}
104592bca398SDaniel Schwierzeck		if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
104692bca398SDaniel Schwierzeck		    $list_address = $1;
104792bca398SDaniel Schwierzeck		    $list_additional = $2;
104892bca398SDaniel Schwierzeck		}
104992bca398SDaniel Schwierzeck		if ($list_additional =~ m/subscribers-only/) {
105092bca398SDaniel Schwierzeck		    if ($email_subscriber_list) {
105192bca398SDaniel Schwierzeck			if (!$hash_list_to{lc($list_address)}) {
105292bca398SDaniel Schwierzeck			    $hash_list_to{lc($list_address)} = 1;
105392bca398SDaniel Schwierzeck			    push(@list_to, [$list_address,
105492bca398SDaniel Schwierzeck					    "subscriber list${list_role}"]);
105592bca398SDaniel Schwierzeck			}
105692bca398SDaniel Schwierzeck		    }
105792bca398SDaniel Schwierzeck		} else {
105892bca398SDaniel Schwierzeck		    if ($email_list) {
105992bca398SDaniel Schwierzeck			if (!$hash_list_to{lc($list_address)}) {
106092bca398SDaniel Schwierzeck			    $hash_list_to{lc($list_address)} = 1;
106192bca398SDaniel Schwierzeck			    if ($list_additional =~ m/moderated/) {
106292bca398SDaniel Schwierzeck				push(@list_to, [$list_address,
106392bca398SDaniel Schwierzeck						"moderated list${list_role}"]);
106492bca398SDaniel Schwierzeck			    } else {
106592bca398SDaniel Schwierzeck				push(@list_to, [$list_address,
106692bca398SDaniel Schwierzeck						"open list${list_role}"]);
106792bca398SDaniel Schwierzeck			    }
106892bca398SDaniel Schwierzeck			}
106992bca398SDaniel Schwierzeck		    }
107092bca398SDaniel Schwierzeck		}
107192bca398SDaniel Schwierzeck	    } elsif ($ptype eq "M") {
107292bca398SDaniel Schwierzeck		my ($name, $address) = parse_email($pvalue);
107392bca398SDaniel Schwierzeck		if ($name eq "") {
107492bca398SDaniel Schwierzeck		    if ($i > 0) {
107592bca398SDaniel Schwierzeck			my $tv = $typevalue[$i - 1];
107692bca398SDaniel Schwierzeck			if ($tv =~ m/^(\C):\s*(.*)/) {
107792bca398SDaniel Schwierzeck			    if ($1 eq "P") {
107892bca398SDaniel Schwierzeck				$name = $2;
107992bca398SDaniel Schwierzeck				$pvalue = format_email($name, $address, $email_usename);
108092bca398SDaniel Schwierzeck			    }
108192bca398SDaniel Schwierzeck			}
108292bca398SDaniel Schwierzeck		    }
108392bca398SDaniel Schwierzeck		}
108492bca398SDaniel Schwierzeck		if ($email_maintainer) {
108592bca398SDaniel Schwierzeck		    my $role = get_maintainer_role($i);
108692bca398SDaniel Schwierzeck		    push_email_addresses($pvalue, $role);
108792bca398SDaniel Schwierzeck		}
108892bca398SDaniel Schwierzeck	    } elsif ($ptype eq "T") {
108992bca398SDaniel Schwierzeck		push(@scm, $pvalue);
109092bca398SDaniel Schwierzeck	    } elsif ($ptype eq "W") {
109192bca398SDaniel Schwierzeck		push(@web, $pvalue);
109292bca398SDaniel Schwierzeck	    } elsif ($ptype eq "S") {
109392bca398SDaniel Schwierzeck		push(@status, $pvalue);
109492bca398SDaniel Schwierzeck	    }
109592bca398SDaniel Schwierzeck	}
109692bca398SDaniel Schwierzeck    }
109792bca398SDaniel Schwierzeck}
109892bca398SDaniel Schwierzeck
109992bca398SDaniel Schwierzecksub email_inuse {
110092bca398SDaniel Schwierzeck    my ($name, $address) = @_;
110192bca398SDaniel Schwierzeck
110292bca398SDaniel Schwierzeck    return 1 if (($name eq "") && ($address eq ""));
110392bca398SDaniel Schwierzeck    return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
110492bca398SDaniel Schwierzeck    return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
110592bca398SDaniel Schwierzeck
110692bca398SDaniel Schwierzeck    return 0;
110792bca398SDaniel Schwierzeck}
110892bca398SDaniel Schwierzeck
110992bca398SDaniel Schwierzecksub push_email_address {
111092bca398SDaniel Schwierzeck    my ($line, $role) = @_;
111192bca398SDaniel Schwierzeck
111292bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
111392bca398SDaniel Schwierzeck
111492bca398SDaniel Schwierzeck    if ($address eq "") {
111592bca398SDaniel Schwierzeck	return 0;
111692bca398SDaniel Schwierzeck    }
111792bca398SDaniel Schwierzeck
111892bca398SDaniel Schwierzeck    if (!$email_remove_duplicates) {
111992bca398SDaniel Schwierzeck	push(@email_to, [format_email($name, $address, $email_usename), $role]);
112092bca398SDaniel Schwierzeck    } elsif (!email_inuse($name, $address)) {
112192bca398SDaniel Schwierzeck	push(@email_to, [format_email($name, $address, $email_usename), $role]);
112292bca398SDaniel Schwierzeck	$email_hash_name{lc($name)}++ if ($name ne "");
112392bca398SDaniel Schwierzeck	$email_hash_address{lc($address)}++;
112492bca398SDaniel Schwierzeck    }
112592bca398SDaniel Schwierzeck
112692bca398SDaniel Schwierzeck    return 1;
112792bca398SDaniel Schwierzeck}
112892bca398SDaniel Schwierzeck
112992bca398SDaniel Schwierzecksub push_email_addresses {
113092bca398SDaniel Schwierzeck    my ($address, $role) = @_;
113192bca398SDaniel Schwierzeck
113292bca398SDaniel Schwierzeck    my @address_list = ();
113392bca398SDaniel Schwierzeck
113492bca398SDaniel Schwierzeck    if (rfc822_valid($address)) {
113592bca398SDaniel Schwierzeck	push_email_address($address, $role);
113692bca398SDaniel Schwierzeck    } elsif (@address_list = rfc822_validlist($address)) {
113792bca398SDaniel Schwierzeck	my $array_count = shift(@address_list);
113892bca398SDaniel Schwierzeck	while (my $entry = shift(@address_list)) {
113992bca398SDaniel Schwierzeck	    push_email_address($entry, $role);
114092bca398SDaniel Schwierzeck	}
114192bca398SDaniel Schwierzeck    } else {
114292bca398SDaniel Schwierzeck	if (!push_email_address($address, $role)) {
114392bca398SDaniel Schwierzeck	    warn("Invalid MAINTAINERS address: '" . $address . "'\n");
114492bca398SDaniel Schwierzeck	}
114592bca398SDaniel Schwierzeck    }
114692bca398SDaniel Schwierzeck}
114792bca398SDaniel Schwierzeck
114892bca398SDaniel Schwierzecksub add_role {
114992bca398SDaniel Schwierzeck    my ($line, $role) = @_;
115092bca398SDaniel Schwierzeck
115192bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
115292bca398SDaniel Schwierzeck    my $email = format_email($name, $address, $email_usename);
115392bca398SDaniel Schwierzeck
115492bca398SDaniel Schwierzeck    foreach my $entry (@email_to) {
115592bca398SDaniel Schwierzeck	if ($email_remove_duplicates) {
115692bca398SDaniel Schwierzeck	    my ($entry_name, $entry_address) = parse_email($entry->[0]);
115792bca398SDaniel Schwierzeck	    if (($name eq $entry_name || $address eq $entry_address)
115892bca398SDaniel Schwierzeck		&& ($role eq "" || !($entry->[1] =~ m/$role/))
115992bca398SDaniel Schwierzeck	    ) {
116092bca398SDaniel Schwierzeck		if ($entry->[1] eq "") {
116192bca398SDaniel Schwierzeck		    $entry->[1] = "$role";
116292bca398SDaniel Schwierzeck		} else {
116392bca398SDaniel Schwierzeck		    $entry->[1] = "$entry->[1],$role";
116492bca398SDaniel Schwierzeck		}
116592bca398SDaniel Schwierzeck	    }
116692bca398SDaniel Schwierzeck	} else {
116792bca398SDaniel Schwierzeck	    if ($email eq $entry->[0]
116892bca398SDaniel Schwierzeck		&& ($role eq "" || !($entry->[1] =~ m/$role/))
116992bca398SDaniel Schwierzeck	    ) {
117092bca398SDaniel Schwierzeck		if ($entry->[1] eq "") {
117192bca398SDaniel Schwierzeck		    $entry->[1] = "$role";
117292bca398SDaniel Schwierzeck		} else {
117392bca398SDaniel Schwierzeck		    $entry->[1] = "$entry->[1],$role";
117492bca398SDaniel Schwierzeck		}
117592bca398SDaniel Schwierzeck	    }
117692bca398SDaniel Schwierzeck	}
117792bca398SDaniel Schwierzeck    }
117892bca398SDaniel Schwierzeck}
117992bca398SDaniel Schwierzeck
118092bca398SDaniel Schwierzecksub which {
118192bca398SDaniel Schwierzeck    my ($bin) = @_;
118292bca398SDaniel Schwierzeck
118392bca398SDaniel Schwierzeck    foreach my $path (split(/:/, $ENV{PATH})) {
118492bca398SDaniel Schwierzeck	if (-e "$path/$bin") {
118592bca398SDaniel Schwierzeck	    return "$path/$bin";
118692bca398SDaniel Schwierzeck	}
118792bca398SDaniel Schwierzeck    }
118892bca398SDaniel Schwierzeck
118992bca398SDaniel Schwierzeck    return "";
119092bca398SDaniel Schwierzeck}
119192bca398SDaniel Schwierzeck
119292bca398SDaniel Schwierzecksub which_conf {
119392bca398SDaniel Schwierzeck    my ($conf) = @_;
119492bca398SDaniel Schwierzeck
119592bca398SDaniel Schwierzeck    foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
119692bca398SDaniel Schwierzeck	if (-e "$path/$conf") {
119792bca398SDaniel Schwierzeck	    return "$path/$conf";
119892bca398SDaniel Schwierzeck	}
119992bca398SDaniel Schwierzeck    }
120092bca398SDaniel Schwierzeck
120192bca398SDaniel Schwierzeck    return "";
120292bca398SDaniel Schwierzeck}
120392bca398SDaniel Schwierzeck
120492bca398SDaniel Schwierzecksub mailmap_email {
120592bca398SDaniel Schwierzeck    my ($line) = @_;
120692bca398SDaniel Schwierzeck
120792bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
120892bca398SDaniel Schwierzeck    my $email = format_email($name, $address, 1);
120992bca398SDaniel Schwierzeck    my $real_name = $name;
121092bca398SDaniel Schwierzeck    my $real_address = $address;
121192bca398SDaniel Schwierzeck
121292bca398SDaniel Schwierzeck    if (exists $mailmap->{names}->{$email} ||
121392bca398SDaniel Schwierzeck	exists $mailmap->{addresses}->{$email}) {
121492bca398SDaniel Schwierzeck	if (exists $mailmap->{names}->{$email}) {
121592bca398SDaniel Schwierzeck	    $real_name = $mailmap->{names}->{$email};
121692bca398SDaniel Schwierzeck	}
121792bca398SDaniel Schwierzeck	if (exists $mailmap->{addresses}->{$email}) {
121892bca398SDaniel Schwierzeck	    $real_address = $mailmap->{addresses}->{$email};
121992bca398SDaniel Schwierzeck	}
122092bca398SDaniel Schwierzeck    } else {
122192bca398SDaniel Schwierzeck	if (exists $mailmap->{names}->{$address}) {
122292bca398SDaniel Schwierzeck	    $real_name = $mailmap->{names}->{$address};
122392bca398SDaniel Schwierzeck	}
122492bca398SDaniel Schwierzeck	if (exists $mailmap->{addresses}->{$address}) {
122592bca398SDaniel Schwierzeck	    $real_address = $mailmap->{addresses}->{$address};
122692bca398SDaniel Schwierzeck	}
122792bca398SDaniel Schwierzeck    }
122892bca398SDaniel Schwierzeck    return format_email($real_name, $real_address, 1);
122992bca398SDaniel Schwierzeck}
123092bca398SDaniel Schwierzeck
123192bca398SDaniel Schwierzecksub mailmap {
123292bca398SDaniel Schwierzeck    my (@addresses) = @_;
123392bca398SDaniel Schwierzeck
123492bca398SDaniel Schwierzeck    my @mapped_emails = ();
123592bca398SDaniel Schwierzeck    foreach my $line (@addresses) {
123692bca398SDaniel Schwierzeck	push(@mapped_emails, mailmap_email($line));
123792bca398SDaniel Schwierzeck    }
123892bca398SDaniel Schwierzeck    merge_by_realname(@mapped_emails) if ($email_use_mailmap);
123992bca398SDaniel Schwierzeck    return @mapped_emails;
124092bca398SDaniel Schwierzeck}
124192bca398SDaniel Schwierzeck
124292bca398SDaniel Schwierzecksub merge_by_realname {
124392bca398SDaniel Schwierzeck    my %address_map;
124492bca398SDaniel Schwierzeck    my (@emails) = @_;
124592bca398SDaniel Schwierzeck
124692bca398SDaniel Schwierzeck    foreach my $email (@emails) {
124792bca398SDaniel Schwierzeck	my ($name, $address) = parse_email($email);
124892bca398SDaniel Schwierzeck	if (exists $address_map{$name}) {
124992bca398SDaniel Schwierzeck	    $address = $address_map{$name};
125092bca398SDaniel Schwierzeck	    $email = format_email($name, $address, 1);
125192bca398SDaniel Schwierzeck	} else {
125292bca398SDaniel Schwierzeck	    $address_map{$name} = $address;
125392bca398SDaniel Schwierzeck	}
125492bca398SDaniel Schwierzeck    }
125592bca398SDaniel Schwierzeck}
125692bca398SDaniel Schwierzeck
125792bca398SDaniel Schwierzecksub git_execute_cmd {
125892bca398SDaniel Schwierzeck    my ($cmd) = @_;
125992bca398SDaniel Schwierzeck    my @lines = ();
126092bca398SDaniel Schwierzeck
126192bca398SDaniel Schwierzeck    my $output = `$cmd`;
126292bca398SDaniel Schwierzeck    $output =~ s/^\s*//gm;
126392bca398SDaniel Schwierzeck    @lines = split("\n", $output);
126492bca398SDaniel Schwierzeck
126592bca398SDaniel Schwierzeck    return @lines;
126692bca398SDaniel Schwierzeck}
126792bca398SDaniel Schwierzeck
126892bca398SDaniel Schwierzecksub hg_execute_cmd {
126992bca398SDaniel Schwierzeck    my ($cmd) = @_;
127092bca398SDaniel Schwierzeck    my @lines = ();
127192bca398SDaniel Schwierzeck
127292bca398SDaniel Schwierzeck    my $output = `$cmd`;
127392bca398SDaniel Schwierzeck    @lines = split("\n", $output);
127492bca398SDaniel Schwierzeck
127592bca398SDaniel Schwierzeck    return @lines;
127692bca398SDaniel Schwierzeck}
127792bca398SDaniel Schwierzeck
127892bca398SDaniel Schwierzecksub extract_formatted_signatures {
127992bca398SDaniel Schwierzeck    my (@signature_lines) = @_;
128092bca398SDaniel Schwierzeck
128192bca398SDaniel Schwierzeck    my @type = @signature_lines;
128292bca398SDaniel Schwierzeck
128392bca398SDaniel Schwierzeck    s/\s*(.*):.*/$1/ for (@type);
128492bca398SDaniel Schwierzeck
128592bca398SDaniel Schwierzeck    # cut -f2- -d":"
128692bca398SDaniel Schwierzeck    s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
128792bca398SDaniel Schwierzeck
128892bca398SDaniel Schwierzeck## Reformat email addresses (with names) to avoid badly written signatures
128992bca398SDaniel Schwierzeck
129092bca398SDaniel Schwierzeck    foreach my $signer (@signature_lines) {
129192bca398SDaniel Schwierzeck	$signer = deduplicate_email($signer);
129292bca398SDaniel Schwierzeck    }
129392bca398SDaniel Schwierzeck
129492bca398SDaniel Schwierzeck    return (\@type, \@signature_lines);
129592bca398SDaniel Schwierzeck}
129692bca398SDaniel Schwierzeck
129792bca398SDaniel Schwierzecksub vcs_find_signers {
129892bca398SDaniel Schwierzeck    my ($cmd, $file) = @_;
129992bca398SDaniel Schwierzeck    my $commits;
130092bca398SDaniel Schwierzeck    my @lines = ();
130192bca398SDaniel Schwierzeck    my @signatures = ();
130292bca398SDaniel Schwierzeck    my @authors = ();
130392bca398SDaniel Schwierzeck    my @stats = ();
130492bca398SDaniel Schwierzeck
130592bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
130692bca398SDaniel Schwierzeck
130792bca398SDaniel Schwierzeck    my $pattern = $VCS_cmds{"commit_pattern"};
130892bca398SDaniel Schwierzeck    my $author_pattern = $VCS_cmds{"author_pattern"};
130992bca398SDaniel Schwierzeck    my $stat_pattern = $VCS_cmds{"stat_pattern"};
131092bca398SDaniel Schwierzeck
131192bca398SDaniel Schwierzeck    $stat_pattern =~ s/(\$\w+)/$1/eeg;		#interpolate $stat_pattern
131292bca398SDaniel Schwierzeck
131392bca398SDaniel Schwierzeck    $commits = grep(/$pattern/, @lines);	# of commits
131492bca398SDaniel Schwierzeck
131592bca398SDaniel Schwierzeck    @authors = grep(/$author_pattern/, @lines);
131692bca398SDaniel Schwierzeck    @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
131792bca398SDaniel Schwierzeck    @stats = grep(/$stat_pattern/, @lines);
131892bca398SDaniel Schwierzeck
131992bca398SDaniel Schwierzeck#    print("stats: <@stats>\n");
132092bca398SDaniel Schwierzeck
132192bca398SDaniel Schwierzeck    return (0, \@signatures, \@authors, \@stats) if !@signatures;
132292bca398SDaniel Schwierzeck
132392bca398SDaniel Schwierzeck    save_commits_by_author(@lines) if ($interactive);
132492bca398SDaniel Schwierzeck    save_commits_by_signer(@lines) if ($interactive);
132592bca398SDaniel Schwierzeck
132692bca398SDaniel Schwierzeck    if (!$email_git_penguin_chiefs) {
132792bca398SDaniel Schwierzeck	@signatures = grep(!/${penguin_chiefs}/i, @signatures);
132892bca398SDaniel Schwierzeck    }
132992bca398SDaniel Schwierzeck
133092bca398SDaniel Schwierzeck    my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
133192bca398SDaniel Schwierzeck    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
133292bca398SDaniel Schwierzeck
133392bca398SDaniel Schwierzeck    return ($commits, $signers_ref, $authors_ref, \@stats);
133492bca398SDaniel Schwierzeck}
133592bca398SDaniel Schwierzeck
133692bca398SDaniel Schwierzecksub vcs_find_author {
133792bca398SDaniel Schwierzeck    my ($cmd) = @_;
133892bca398SDaniel Schwierzeck    my @lines = ();
133992bca398SDaniel Schwierzeck
134092bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
134192bca398SDaniel Schwierzeck
134292bca398SDaniel Schwierzeck    if (!$email_git_penguin_chiefs) {
134392bca398SDaniel Schwierzeck	@lines = grep(!/${penguin_chiefs}/i, @lines);
134492bca398SDaniel Schwierzeck    }
134592bca398SDaniel Schwierzeck
134692bca398SDaniel Schwierzeck    return @lines if !@lines;
134792bca398SDaniel Schwierzeck
134892bca398SDaniel Schwierzeck    my @authors = ();
134992bca398SDaniel Schwierzeck    foreach my $line (@lines) {
135092bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
135192bca398SDaniel Schwierzeck	    my $author = $1;
135292bca398SDaniel Schwierzeck	    my ($name, $address) = parse_email($author);
135392bca398SDaniel Schwierzeck	    $author = format_email($name, $address, 1);
135492bca398SDaniel Schwierzeck	    push(@authors, $author);
135592bca398SDaniel Schwierzeck	}
135692bca398SDaniel Schwierzeck    }
135792bca398SDaniel Schwierzeck
135892bca398SDaniel Schwierzeck    save_commits_by_author(@lines) if ($interactive);
135992bca398SDaniel Schwierzeck    save_commits_by_signer(@lines) if ($interactive);
136092bca398SDaniel Schwierzeck
136192bca398SDaniel Schwierzeck    return @authors;
136292bca398SDaniel Schwierzeck}
136392bca398SDaniel Schwierzeck
136492bca398SDaniel Schwierzecksub vcs_save_commits {
136592bca398SDaniel Schwierzeck    my ($cmd) = @_;
136692bca398SDaniel Schwierzeck    my @lines = ();
136792bca398SDaniel Schwierzeck    my @commits = ();
136892bca398SDaniel Schwierzeck
136992bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
137092bca398SDaniel Schwierzeck
137192bca398SDaniel Schwierzeck    foreach my $line (@lines) {
137292bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
137392bca398SDaniel Schwierzeck	    push(@commits, $1);
137492bca398SDaniel Schwierzeck	}
137592bca398SDaniel Schwierzeck    }
137692bca398SDaniel Schwierzeck
137792bca398SDaniel Schwierzeck    return @commits;
137892bca398SDaniel Schwierzeck}
137992bca398SDaniel Schwierzeck
138092bca398SDaniel Schwierzecksub vcs_blame {
138192bca398SDaniel Schwierzeck    my ($file) = @_;
138292bca398SDaniel Schwierzeck    my $cmd;
138392bca398SDaniel Schwierzeck    my @commits = ();
138492bca398SDaniel Schwierzeck
138592bca398SDaniel Schwierzeck    return @commits if (!(-f $file));
138692bca398SDaniel Schwierzeck
138792bca398SDaniel Schwierzeck    if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
138892bca398SDaniel Schwierzeck	my @all_commits = ();
138992bca398SDaniel Schwierzeck
139092bca398SDaniel Schwierzeck	$cmd = $VCS_cmds{"blame_file_cmd"};
139192bca398SDaniel Schwierzeck	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
139292bca398SDaniel Schwierzeck	@all_commits = vcs_save_commits($cmd);
139392bca398SDaniel Schwierzeck
139492bca398SDaniel Schwierzeck	foreach my $file_range_diff (@range) {
139592bca398SDaniel Schwierzeck	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
139692bca398SDaniel Schwierzeck	    my $diff_file = $1;
139792bca398SDaniel Schwierzeck	    my $diff_start = $2;
139892bca398SDaniel Schwierzeck	    my $diff_length = $3;
139992bca398SDaniel Schwierzeck	    next if ("$file" ne "$diff_file");
140092bca398SDaniel Schwierzeck	    for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
140192bca398SDaniel Schwierzeck		push(@commits, $all_commits[$i]);
140292bca398SDaniel Schwierzeck	    }
140392bca398SDaniel Schwierzeck	}
140492bca398SDaniel Schwierzeck    } elsif (@range) {
140592bca398SDaniel Schwierzeck	foreach my $file_range_diff (@range) {
140692bca398SDaniel Schwierzeck	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
140792bca398SDaniel Schwierzeck	    my $diff_file = $1;
140892bca398SDaniel Schwierzeck	    my $diff_start = $2;
140992bca398SDaniel Schwierzeck	    my $diff_length = $3;
141092bca398SDaniel Schwierzeck	    next if ("$file" ne "$diff_file");
141192bca398SDaniel Schwierzeck	    $cmd = $VCS_cmds{"blame_range_cmd"};
141292bca398SDaniel Schwierzeck	    $cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
141392bca398SDaniel Schwierzeck	    push(@commits, vcs_save_commits($cmd));
141492bca398SDaniel Schwierzeck	}
141592bca398SDaniel Schwierzeck    } else {
141692bca398SDaniel Schwierzeck	$cmd = $VCS_cmds{"blame_file_cmd"};
141792bca398SDaniel Schwierzeck	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
141892bca398SDaniel Schwierzeck	@commits = vcs_save_commits($cmd);
141992bca398SDaniel Schwierzeck    }
142092bca398SDaniel Schwierzeck
142192bca398SDaniel Schwierzeck    foreach my $commit (@commits) {
142292bca398SDaniel Schwierzeck	$commit =~ s/^\^//g;
142392bca398SDaniel Schwierzeck    }
142492bca398SDaniel Schwierzeck
142592bca398SDaniel Schwierzeck    return @commits;
142692bca398SDaniel Schwierzeck}
142792bca398SDaniel Schwierzeck
142892bca398SDaniel Schwierzeckmy $printed_novcs = 0;
142992bca398SDaniel Schwierzecksub vcs_exists {
143092bca398SDaniel Schwierzeck    %VCS_cmds = %VCS_cmds_git;
143192bca398SDaniel Schwierzeck    return 1 if eval $VCS_cmds{"available"};
143292bca398SDaniel Schwierzeck    %VCS_cmds = %VCS_cmds_hg;
143392bca398SDaniel Schwierzeck    return 2 if eval $VCS_cmds{"available"};
143492bca398SDaniel Schwierzeck    %VCS_cmds = ();
143592bca398SDaniel Schwierzeck    if (!$printed_novcs) {
143692bca398SDaniel Schwierzeck	warn("$P: No supported VCS found.  Add --nogit to options?\n");
143792bca398SDaniel Schwierzeck	warn("Using a git repository produces better results.\n");
143892bca398SDaniel Schwierzeck	warn("Try Linus Torvalds' latest git repository using:\n");
143992bca398SDaniel Schwierzeck	warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
144092bca398SDaniel Schwierzeck	$printed_novcs = 1;
144192bca398SDaniel Schwierzeck    }
144292bca398SDaniel Schwierzeck    return 0;
144392bca398SDaniel Schwierzeck}
144492bca398SDaniel Schwierzeck
144592bca398SDaniel Schwierzecksub vcs_is_git {
144692bca398SDaniel Schwierzeck    vcs_exists();
144792bca398SDaniel Schwierzeck    return $vcs_used == 1;
144892bca398SDaniel Schwierzeck}
144992bca398SDaniel Schwierzeck
145092bca398SDaniel Schwierzecksub vcs_is_hg {
145192bca398SDaniel Schwierzeck    return $vcs_used == 2;
145292bca398SDaniel Schwierzeck}
145392bca398SDaniel Schwierzeck
145492bca398SDaniel Schwierzecksub interactive_get_maintainers {
145592bca398SDaniel Schwierzeck    my ($list_ref) = @_;
145692bca398SDaniel Schwierzeck    my @list = @$list_ref;
145792bca398SDaniel Schwierzeck
145892bca398SDaniel Schwierzeck    vcs_exists();
145992bca398SDaniel Schwierzeck
146092bca398SDaniel Schwierzeck    my %selected;
146192bca398SDaniel Schwierzeck    my %authored;
146292bca398SDaniel Schwierzeck    my %signed;
146392bca398SDaniel Schwierzeck    my $count = 0;
146492bca398SDaniel Schwierzeck    my $maintained = 0;
146592bca398SDaniel Schwierzeck    foreach my $entry (@list) {
146692bca398SDaniel Schwierzeck	$maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
146792bca398SDaniel Schwierzeck	$selected{$count} = 1;
146892bca398SDaniel Schwierzeck	$authored{$count} = 0;
146992bca398SDaniel Schwierzeck	$signed{$count} = 0;
147092bca398SDaniel Schwierzeck	$count++;
147192bca398SDaniel Schwierzeck    }
147292bca398SDaniel Schwierzeck
147392bca398SDaniel Schwierzeck    #menu loop
147492bca398SDaniel Schwierzeck    my $done = 0;
147592bca398SDaniel Schwierzeck    my $print_options = 0;
147692bca398SDaniel Schwierzeck    my $redraw = 1;
147792bca398SDaniel Schwierzeck    while (!$done) {
147892bca398SDaniel Schwierzeck	$count = 0;
147992bca398SDaniel Schwierzeck	if ($redraw) {
148092bca398SDaniel Schwierzeck	    printf STDERR "\n%1s %2s %-65s",
148192bca398SDaniel Schwierzeck			  "*", "#", "email/list and role:stats";
148292bca398SDaniel Schwierzeck	    if ($email_git ||
148392bca398SDaniel Schwierzeck		($email_git_fallback && !$maintained) ||
148492bca398SDaniel Schwierzeck		$email_git_blame) {
148592bca398SDaniel Schwierzeck		print STDERR "auth sign";
148692bca398SDaniel Schwierzeck	    }
148792bca398SDaniel Schwierzeck	    print STDERR "\n";
148892bca398SDaniel Schwierzeck	    foreach my $entry (@list) {
148992bca398SDaniel Schwierzeck		my $email = $entry->[0];
149092bca398SDaniel Schwierzeck		my $role = $entry->[1];
149192bca398SDaniel Schwierzeck		my $sel = "";
149292bca398SDaniel Schwierzeck		$sel = "*" if ($selected{$count});
149392bca398SDaniel Schwierzeck		my $commit_author = $commit_author_hash{$email};
149492bca398SDaniel Schwierzeck		my $commit_signer = $commit_signer_hash{$email};
149592bca398SDaniel Schwierzeck		my $authored = 0;
149692bca398SDaniel Schwierzeck		my $signed = 0;
149792bca398SDaniel Schwierzeck		$authored++ for (@{$commit_author});
149892bca398SDaniel Schwierzeck		$signed++ for (@{$commit_signer});
149992bca398SDaniel Schwierzeck		printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
150092bca398SDaniel Schwierzeck		printf STDERR "%4d %4d", $authored, $signed
150192bca398SDaniel Schwierzeck		    if ($authored > 0 || $signed > 0);
150292bca398SDaniel Schwierzeck		printf STDERR "\n     %s\n", $role;
150392bca398SDaniel Schwierzeck		if ($authored{$count}) {
150492bca398SDaniel Schwierzeck		    my $commit_author = $commit_author_hash{$email};
150592bca398SDaniel Schwierzeck		    foreach my $ref (@{$commit_author}) {
150692bca398SDaniel Schwierzeck			print STDERR "     Author: @{$ref}[1]\n";
150792bca398SDaniel Schwierzeck		    }
150892bca398SDaniel Schwierzeck		}
150992bca398SDaniel Schwierzeck		if ($signed{$count}) {
151092bca398SDaniel Schwierzeck		    my $commit_signer = $commit_signer_hash{$email};
151192bca398SDaniel Schwierzeck		    foreach my $ref (@{$commit_signer}) {
151292bca398SDaniel Schwierzeck			print STDERR "     @{$ref}[2]: @{$ref}[1]\n";
151392bca398SDaniel Schwierzeck		    }
151492bca398SDaniel Schwierzeck		}
151592bca398SDaniel Schwierzeck
151692bca398SDaniel Schwierzeck		$count++;
151792bca398SDaniel Schwierzeck	    }
151892bca398SDaniel Schwierzeck	}
151992bca398SDaniel Schwierzeck	my $date_ref = \$email_git_since;
152092bca398SDaniel Schwierzeck	$date_ref = \$email_hg_since if (vcs_is_hg());
152192bca398SDaniel Schwierzeck	if ($print_options) {
152292bca398SDaniel Schwierzeck	    $print_options = 0;
152392bca398SDaniel Schwierzeck	    if (vcs_exists()) {
152492bca398SDaniel Schwierzeck		print STDERR <<EOT
152592bca398SDaniel Schwierzeck
152692bca398SDaniel SchwierzeckVersion Control options:
152792bca398SDaniel Schwierzeckg  use git history      [$email_git]
152892bca398SDaniel Schwierzeckgf use git-fallback     [$email_git_fallback]
152992bca398SDaniel Schwierzeckb  use git blame        [$email_git_blame]
153092bca398SDaniel Schwierzeckbs use blame signatures [$email_git_blame_signatures]
153192bca398SDaniel Schwierzeckc# minimum commits      [$email_git_min_signatures]
153292bca398SDaniel Schwierzeck%# min percent          [$email_git_min_percent]
153392bca398SDaniel Schwierzeckd# history to use       [$$date_ref]
153492bca398SDaniel Schwierzeckx# max maintainers      [$email_git_max_maintainers]
153592bca398SDaniel Schwierzeckt  all signature types  [$email_git_all_signature_types]
153692bca398SDaniel Schwierzeckm  use .mailmap         [$email_use_mailmap]
153792bca398SDaniel SchwierzeckEOT
153892bca398SDaniel Schwierzeck	    }
153992bca398SDaniel Schwierzeck	    print STDERR <<EOT
154092bca398SDaniel Schwierzeck
154192bca398SDaniel SchwierzeckAdditional options:
154292bca398SDaniel Schwierzeck0  toggle all
154392bca398SDaniel Schwierzecktm toggle maintainers
154492bca398SDaniel Schwierzecktg toggle git entries
154592bca398SDaniel Schwierzecktl toggle open list entries
154692bca398SDaniel Schwierzeckts toggle subscriber list entries
154792bca398SDaniel Schwierzeckf  emails in file       [$file_emails]
154892bca398SDaniel Schwierzeckk  keywords in file     [$keywords]
154992bca398SDaniel Schwierzeckr  remove duplicates    [$email_remove_duplicates]
155092bca398SDaniel Schwierzeckp# pattern match depth  [$pattern_depth]
155192bca398SDaniel SchwierzeckEOT
155292bca398SDaniel Schwierzeck	}
155392bca398SDaniel Schwierzeck	print STDERR
155492bca398SDaniel Schwierzeck"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
155592bca398SDaniel Schwierzeck
155692bca398SDaniel Schwierzeck	my $input = <STDIN>;
155792bca398SDaniel Schwierzeck	chomp($input);
155892bca398SDaniel Schwierzeck
155992bca398SDaniel Schwierzeck	$redraw = 1;
156092bca398SDaniel Schwierzeck	my $rerun = 0;
156192bca398SDaniel Schwierzeck	my @wish = split(/[, ]+/, $input);
156292bca398SDaniel Schwierzeck	foreach my $nr (@wish) {
156392bca398SDaniel Schwierzeck	    $nr = lc($nr);
156492bca398SDaniel Schwierzeck	    my $sel = substr($nr, 0, 1);
156592bca398SDaniel Schwierzeck	    my $str = substr($nr, 1);
156692bca398SDaniel Schwierzeck	    my $val = 0;
156792bca398SDaniel Schwierzeck	    $val = $1 if $str =~ /^(\d+)$/;
156892bca398SDaniel Schwierzeck
156992bca398SDaniel Schwierzeck	    if ($sel eq "y") {
157092bca398SDaniel Schwierzeck		$interactive = 0;
157192bca398SDaniel Schwierzeck		$done = 1;
157292bca398SDaniel Schwierzeck		$output_rolestats = 0;
157392bca398SDaniel Schwierzeck		$output_roles = 0;
157492bca398SDaniel Schwierzeck		last;
157592bca398SDaniel Schwierzeck	    } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
157692bca398SDaniel Schwierzeck		$selected{$nr - 1} = !$selected{$nr - 1};
157792bca398SDaniel Schwierzeck	    } elsif ($sel eq "*" || $sel eq '^') {
157892bca398SDaniel Schwierzeck		my $toggle = 0;
157992bca398SDaniel Schwierzeck		$toggle = 1 if ($sel eq '*');
158092bca398SDaniel Schwierzeck		for (my $i = 0; $i < $count; $i++) {
158192bca398SDaniel Schwierzeck		    $selected{$i} = $toggle;
158292bca398SDaniel Schwierzeck		}
158392bca398SDaniel Schwierzeck	    } elsif ($sel eq "0") {
158492bca398SDaniel Schwierzeck		for (my $i = 0; $i < $count; $i++) {
158592bca398SDaniel Schwierzeck		    $selected{$i} = !$selected{$i};
158692bca398SDaniel Schwierzeck		}
158792bca398SDaniel Schwierzeck	    } elsif ($sel eq "t") {
158892bca398SDaniel Schwierzeck		if (lc($str) eq "m") {
158992bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
159092bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
159192bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
159292bca398SDaniel Schwierzeck		    }
159392bca398SDaniel Schwierzeck		} elsif (lc($str) eq "g") {
159492bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
159592bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
159692bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
159792bca398SDaniel Schwierzeck		    }
159892bca398SDaniel Schwierzeck		} elsif (lc($str) eq "l") {
159992bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
160092bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
160192bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(open list)/i);
160292bca398SDaniel Schwierzeck		    }
160392bca398SDaniel Schwierzeck		} elsif (lc($str) eq "s") {
160492bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
160592bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
160692bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(subscriber list)/i);
160792bca398SDaniel Schwierzeck		    }
160892bca398SDaniel Schwierzeck		}
160992bca398SDaniel Schwierzeck	    } elsif ($sel eq "a") {
161092bca398SDaniel Schwierzeck		if ($val > 0 && $val <= $count) {
161192bca398SDaniel Schwierzeck		    $authored{$val - 1} = !$authored{$val - 1};
161292bca398SDaniel Schwierzeck		} elsif ($str eq '*' || $str eq '^') {
161392bca398SDaniel Schwierzeck		    my $toggle = 0;
161492bca398SDaniel Schwierzeck		    $toggle = 1 if ($str eq '*');
161592bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
161692bca398SDaniel Schwierzeck			$authored{$i} = $toggle;
161792bca398SDaniel Schwierzeck		    }
161892bca398SDaniel Schwierzeck		}
161992bca398SDaniel Schwierzeck	    } elsif ($sel eq "s") {
162092bca398SDaniel Schwierzeck		if ($val > 0 && $val <= $count) {
162192bca398SDaniel Schwierzeck		    $signed{$val - 1} = !$signed{$val - 1};
162292bca398SDaniel Schwierzeck		} elsif ($str eq '*' || $str eq '^') {
162392bca398SDaniel Schwierzeck		    my $toggle = 0;
162492bca398SDaniel Schwierzeck		    $toggle = 1 if ($str eq '*');
162592bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
162692bca398SDaniel Schwierzeck			$signed{$i} = $toggle;
162792bca398SDaniel Schwierzeck		    }
162892bca398SDaniel Schwierzeck		}
162992bca398SDaniel Schwierzeck	    } elsif ($sel eq "o") {
163092bca398SDaniel Schwierzeck		$print_options = 1;
163192bca398SDaniel Schwierzeck		$redraw = 1;
163292bca398SDaniel Schwierzeck	    } elsif ($sel eq "g") {
163392bca398SDaniel Schwierzeck		if ($str eq "f") {
163492bca398SDaniel Schwierzeck		    bool_invert(\$email_git_fallback);
163592bca398SDaniel Schwierzeck		} else {
163692bca398SDaniel Schwierzeck		    bool_invert(\$email_git);
163792bca398SDaniel Schwierzeck		}
163892bca398SDaniel Schwierzeck		$rerun = 1;
163992bca398SDaniel Schwierzeck	    } elsif ($sel eq "b") {
164092bca398SDaniel Schwierzeck		if ($str eq "s") {
164192bca398SDaniel Schwierzeck		    bool_invert(\$email_git_blame_signatures);
164292bca398SDaniel Schwierzeck		} else {
164392bca398SDaniel Schwierzeck		    bool_invert(\$email_git_blame);
164492bca398SDaniel Schwierzeck		}
164592bca398SDaniel Schwierzeck		$rerun = 1;
164692bca398SDaniel Schwierzeck	    } elsif ($sel eq "c") {
164792bca398SDaniel Schwierzeck		if ($val > 0) {
164892bca398SDaniel Schwierzeck		    $email_git_min_signatures = $val;
164992bca398SDaniel Schwierzeck		    $rerun = 1;
165092bca398SDaniel Schwierzeck		}
165192bca398SDaniel Schwierzeck	    } elsif ($sel eq "x") {
165292bca398SDaniel Schwierzeck		if ($val > 0) {
165392bca398SDaniel Schwierzeck		    $email_git_max_maintainers = $val;
165492bca398SDaniel Schwierzeck		    $rerun = 1;
165592bca398SDaniel Schwierzeck		}
165692bca398SDaniel Schwierzeck	    } elsif ($sel eq "%") {
165792bca398SDaniel Schwierzeck		if ($str ne "" && $val >= 0) {
165892bca398SDaniel Schwierzeck		    $email_git_min_percent = $val;
165992bca398SDaniel Schwierzeck		    $rerun = 1;
166092bca398SDaniel Schwierzeck		}
166192bca398SDaniel Schwierzeck	    } elsif ($sel eq "d") {
166292bca398SDaniel Schwierzeck		if (vcs_is_git()) {
166392bca398SDaniel Schwierzeck		    $email_git_since = $str;
166492bca398SDaniel Schwierzeck		} elsif (vcs_is_hg()) {
166592bca398SDaniel Schwierzeck		    $email_hg_since = $str;
166692bca398SDaniel Schwierzeck		}
166792bca398SDaniel Schwierzeck		$rerun = 1;
166892bca398SDaniel Schwierzeck	    } elsif ($sel eq "t") {
166992bca398SDaniel Schwierzeck		bool_invert(\$email_git_all_signature_types);
167092bca398SDaniel Schwierzeck		$rerun = 1;
167192bca398SDaniel Schwierzeck	    } elsif ($sel eq "f") {
167292bca398SDaniel Schwierzeck		bool_invert(\$file_emails);
167392bca398SDaniel Schwierzeck		$rerun = 1;
167492bca398SDaniel Schwierzeck	    } elsif ($sel eq "r") {
167592bca398SDaniel Schwierzeck		bool_invert(\$email_remove_duplicates);
167692bca398SDaniel Schwierzeck		$rerun = 1;
167792bca398SDaniel Schwierzeck	    } elsif ($sel eq "m") {
167892bca398SDaniel Schwierzeck		bool_invert(\$email_use_mailmap);
167992bca398SDaniel Schwierzeck		read_mailmap();
168092bca398SDaniel Schwierzeck		$rerun = 1;
168192bca398SDaniel Schwierzeck	    } elsif ($sel eq "k") {
168292bca398SDaniel Schwierzeck		bool_invert(\$keywords);
168392bca398SDaniel Schwierzeck		$rerun = 1;
168492bca398SDaniel Schwierzeck	    } elsif ($sel eq "p") {
168592bca398SDaniel Schwierzeck		if ($str ne "" && $val >= 0) {
168692bca398SDaniel Schwierzeck		    $pattern_depth = $val;
168792bca398SDaniel Schwierzeck		    $rerun = 1;
168892bca398SDaniel Schwierzeck		}
168992bca398SDaniel Schwierzeck	    } elsif ($sel eq "h" || $sel eq "?") {
169092bca398SDaniel Schwierzeck		print STDERR <<EOT
169192bca398SDaniel Schwierzeck
169292bca398SDaniel SchwierzeckInteractive mode allows you to select the various maintainers, submitters,
169392bca398SDaniel Schwierzeckcommit signers and mailing lists that could be CC'd on a patch.
169492bca398SDaniel Schwierzeck
169592bca398SDaniel SchwierzeckAny *'d entry is selected.
169692bca398SDaniel Schwierzeck
169792bca398SDaniel SchwierzeckIf you have git or hg installed, you can choose to summarize the commit
169892bca398SDaniel Schwierzeckhistory of files in the patch.  Also, each line of the current file can
169992bca398SDaniel Schwierzeckbe matched to its commit author and that commits signers with blame.
170092bca398SDaniel Schwierzeck
170192bca398SDaniel SchwierzeckVarious knobs exist to control the length of time for active commit
170292bca398SDaniel Schwierzecktracking, the maximum number of commit authors and signers to add,
170392bca398SDaniel Schwierzeckand such.
170492bca398SDaniel Schwierzeck
170592bca398SDaniel SchwierzeckEnter selections at the prompt until you are satisfied that the selected
170692bca398SDaniel Schwierzeckmaintainers are appropriate.  You may enter multiple selections separated
170792bca398SDaniel Schwierzeckby either commas or spaces.
170892bca398SDaniel Schwierzeck
170992bca398SDaniel SchwierzeckEOT
171092bca398SDaniel Schwierzeck	    } else {
171192bca398SDaniel Schwierzeck		print STDERR "invalid option: '$nr'\n";
171292bca398SDaniel Schwierzeck		$redraw = 0;
171392bca398SDaniel Schwierzeck	    }
171492bca398SDaniel Schwierzeck	}
171592bca398SDaniel Schwierzeck	if ($rerun) {
171692bca398SDaniel Schwierzeck	    print STDERR "git-blame can be very slow, please have patience..."
171792bca398SDaniel Schwierzeck		if ($email_git_blame);
171892bca398SDaniel Schwierzeck	    goto &get_maintainers;
171992bca398SDaniel Schwierzeck	}
172092bca398SDaniel Schwierzeck    }
172192bca398SDaniel Schwierzeck
172292bca398SDaniel Schwierzeck    #drop not selected entries
172392bca398SDaniel Schwierzeck    $count = 0;
172492bca398SDaniel Schwierzeck    my @new_emailto = ();
172592bca398SDaniel Schwierzeck    foreach my $entry (@list) {
172692bca398SDaniel Schwierzeck	if ($selected{$count}) {
172792bca398SDaniel Schwierzeck	    push(@new_emailto, $list[$count]);
172892bca398SDaniel Schwierzeck	}
172992bca398SDaniel Schwierzeck	$count++;
173092bca398SDaniel Schwierzeck    }
173192bca398SDaniel Schwierzeck    return @new_emailto;
173292bca398SDaniel Schwierzeck}
173392bca398SDaniel Schwierzeck
173492bca398SDaniel Schwierzecksub bool_invert {
173592bca398SDaniel Schwierzeck    my ($bool_ref) = @_;
173692bca398SDaniel Schwierzeck
173792bca398SDaniel Schwierzeck    if ($$bool_ref) {
173892bca398SDaniel Schwierzeck	$$bool_ref = 0;
173992bca398SDaniel Schwierzeck    } else {
174092bca398SDaniel Schwierzeck	$$bool_ref = 1;
174192bca398SDaniel Schwierzeck    }
174292bca398SDaniel Schwierzeck}
174392bca398SDaniel Schwierzeck
174492bca398SDaniel Schwierzecksub deduplicate_email {
174592bca398SDaniel Schwierzeck    my ($email) = @_;
174692bca398SDaniel Schwierzeck
174792bca398SDaniel Schwierzeck    my $matched = 0;
174892bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($email);
174992bca398SDaniel Schwierzeck    $email = format_email($name, $address, 1);
175092bca398SDaniel Schwierzeck    $email = mailmap_email($email);
175192bca398SDaniel Schwierzeck
175292bca398SDaniel Schwierzeck    return $email if (!$email_remove_duplicates);
175392bca398SDaniel Schwierzeck
175492bca398SDaniel Schwierzeck    ($name, $address) = parse_email($email);
175592bca398SDaniel Schwierzeck
175692bca398SDaniel Schwierzeck    if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
175792bca398SDaniel Schwierzeck	$name = $deduplicate_name_hash{lc($name)}->[0];
175892bca398SDaniel Schwierzeck	$address = $deduplicate_name_hash{lc($name)}->[1];
175992bca398SDaniel Schwierzeck	$matched = 1;
176092bca398SDaniel Schwierzeck    } elsif ($deduplicate_address_hash{lc($address)}) {
176192bca398SDaniel Schwierzeck	$name = $deduplicate_address_hash{lc($address)}->[0];
176292bca398SDaniel Schwierzeck	$address = $deduplicate_address_hash{lc($address)}->[1];
176392bca398SDaniel Schwierzeck	$matched = 1;
176492bca398SDaniel Schwierzeck    }
176592bca398SDaniel Schwierzeck    if (!$matched) {
176692bca398SDaniel Schwierzeck	$deduplicate_name_hash{lc($name)} = [ $name, $address ];
176792bca398SDaniel Schwierzeck	$deduplicate_address_hash{lc($address)} = [ $name, $address ];
176892bca398SDaniel Schwierzeck    }
176992bca398SDaniel Schwierzeck    $email = format_email($name, $address, 1);
177092bca398SDaniel Schwierzeck    $email = mailmap_email($email);
177192bca398SDaniel Schwierzeck    return $email;
177292bca398SDaniel Schwierzeck}
177392bca398SDaniel Schwierzeck
177492bca398SDaniel Schwierzecksub save_commits_by_author {
177592bca398SDaniel Schwierzeck    my (@lines) = @_;
177692bca398SDaniel Schwierzeck
177792bca398SDaniel Schwierzeck    my @authors = ();
177892bca398SDaniel Schwierzeck    my @commits = ();
177992bca398SDaniel Schwierzeck    my @subjects = ();
178092bca398SDaniel Schwierzeck
178192bca398SDaniel Schwierzeck    foreach my $line (@lines) {
178292bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
178392bca398SDaniel Schwierzeck	    my $author = $1;
178492bca398SDaniel Schwierzeck	    $author = deduplicate_email($author);
178592bca398SDaniel Schwierzeck	    push(@authors, $author);
178692bca398SDaniel Schwierzeck	}
178792bca398SDaniel Schwierzeck	push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
178892bca398SDaniel Schwierzeck	push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
178992bca398SDaniel Schwierzeck    }
179092bca398SDaniel Schwierzeck
179192bca398SDaniel Schwierzeck    for (my $i = 0; $i < @authors; $i++) {
179292bca398SDaniel Schwierzeck	my $exists = 0;
179392bca398SDaniel Schwierzeck	foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
179492bca398SDaniel Schwierzeck	    if (@{$ref}[0] eq $commits[$i] &&
179592bca398SDaniel Schwierzeck		@{$ref}[1] eq $subjects[$i]) {
179692bca398SDaniel Schwierzeck		$exists = 1;
179792bca398SDaniel Schwierzeck		last;
179892bca398SDaniel Schwierzeck	    }
179992bca398SDaniel Schwierzeck	}
180092bca398SDaniel Schwierzeck	if (!$exists) {
180192bca398SDaniel Schwierzeck	    push(@{$commit_author_hash{$authors[$i]}},
180292bca398SDaniel Schwierzeck		 [ ($commits[$i], $subjects[$i]) ]);
180392bca398SDaniel Schwierzeck	}
180492bca398SDaniel Schwierzeck    }
180592bca398SDaniel Schwierzeck}
180692bca398SDaniel Schwierzeck
180792bca398SDaniel Schwierzecksub save_commits_by_signer {
180892bca398SDaniel Schwierzeck    my (@lines) = @_;
180992bca398SDaniel Schwierzeck
181092bca398SDaniel Schwierzeck    my $commit = "";
181192bca398SDaniel Schwierzeck    my $subject = "";
181292bca398SDaniel Schwierzeck
181392bca398SDaniel Schwierzeck    foreach my $line (@lines) {
181492bca398SDaniel Schwierzeck	$commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
181592bca398SDaniel Schwierzeck	$subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
181692bca398SDaniel Schwierzeck	if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
181792bca398SDaniel Schwierzeck	    my @signatures = ($line);
181892bca398SDaniel Schwierzeck	    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
181992bca398SDaniel Schwierzeck	    my @types = @$types_ref;
182092bca398SDaniel Schwierzeck	    my @signers = @$signers_ref;
182192bca398SDaniel Schwierzeck
182292bca398SDaniel Schwierzeck	    my $type = $types[0];
182392bca398SDaniel Schwierzeck	    my $signer = $signers[0];
182492bca398SDaniel Schwierzeck
182592bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
182692bca398SDaniel Schwierzeck
182792bca398SDaniel Schwierzeck	    my $exists = 0;
182892bca398SDaniel Schwierzeck	    foreach my $ref(@{$commit_signer_hash{$signer}}) {
182992bca398SDaniel Schwierzeck		if (@{$ref}[0] eq $commit &&
183092bca398SDaniel Schwierzeck		    @{$ref}[1] eq $subject &&
183192bca398SDaniel Schwierzeck		    @{$ref}[2] eq $type) {
183292bca398SDaniel Schwierzeck		    $exists = 1;
183392bca398SDaniel Schwierzeck		    last;
183492bca398SDaniel Schwierzeck		}
183592bca398SDaniel Schwierzeck	    }
183692bca398SDaniel Schwierzeck	    if (!$exists) {
183792bca398SDaniel Schwierzeck		push(@{$commit_signer_hash{$signer}},
183892bca398SDaniel Schwierzeck		     [ ($commit, $subject, $type) ]);
183992bca398SDaniel Schwierzeck	    }
184092bca398SDaniel Schwierzeck	}
184192bca398SDaniel Schwierzeck    }
184292bca398SDaniel Schwierzeck}
184392bca398SDaniel Schwierzeck
184492bca398SDaniel Schwierzecksub vcs_assign {
184592bca398SDaniel Schwierzeck    my ($role, $divisor, @lines) = @_;
184692bca398SDaniel Schwierzeck
184792bca398SDaniel Schwierzeck    my %hash;
184892bca398SDaniel Schwierzeck    my $count = 0;
184992bca398SDaniel Schwierzeck
185092bca398SDaniel Schwierzeck    return if (@lines <= 0);
185192bca398SDaniel Schwierzeck
185292bca398SDaniel Schwierzeck    if ($divisor <= 0) {
185392bca398SDaniel Schwierzeck	warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
185492bca398SDaniel Schwierzeck	$divisor = 1;
185592bca398SDaniel Schwierzeck    }
185692bca398SDaniel Schwierzeck
185792bca398SDaniel Schwierzeck    @lines = mailmap(@lines);
185892bca398SDaniel Schwierzeck
185992bca398SDaniel Schwierzeck    return if (@lines <= 0);
186092bca398SDaniel Schwierzeck
186192bca398SDaniel Schwierzeck    @lines = sort(@lines);
186292bca398SDaniel Schwierzeck
186392bca398SDaniel Schwierzeck    # uniq -c
186492bca398SDaniel Schwierzeck    $hash{$_}++ for @lines;
186592bca398SDaniel Schwierzeck
186692bca398SDaniel Schwierzeck    # sort -rn
186792bca398SDaniel Schwierzeck    foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
186892bca398SDaniel Schwierzeck	my $sign_offs = $hash{$line};
186992bca398SDaniel Schwierzeck	my $percent = $sign_offs * 100 / $divisor;
187092bca398SDaniel Schwierzeck
187192bca398SDaniel Schwierzeck	$percent = 100 if ($percent > 100);
187292bca398SDaniel Schwierzeck	$count++;
187392bca398SDaniel Schwierzeck	last if ($sign_offs < $email_git_min_signatures ||
187492bca398SDaniel Schwierzeck		 $count > $email_git_max_maintainers ||
187592bca398SDaniel Schwierzeck		 $percent < $email_git_min_percent);
187692bca398SDaniel Schwierzeck	push_email_address($line, '');
187792bca398SDaniel Schwierzeck	if ($output_rolestats) {
187892bca398SDaniel Schwierzeck	    my $fmt_percent = sprintf("%.0f", $percent);
187992bca398SDaniel Schwierzeck	    add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
188092bca398SDaniel Schwierzeck	} else {
188192bca398SDaniel Schwierzeck	    add_role($line, $role);
188292bca398SDaniel Schwierzeck	}
188392bca398SDaniel Schwierzeck    }
188492bca398SDaniel Schwierzeck}
188592bca398SDaniel Schwierzeck
188692bca398SDaniel Schwierzecksub vcs_file_signoffs {
188792bca398SDaniel Schwierzeck    my ($file) = @_;
188892bca398SDaniel Schwierzeck
188992bca398SDaniel Schwierzeck    my $authors_ref;
189092bca398SDaniel Schwierzeck    my $signers_ref;
189192bca398SDaniel Schwierzeck    my $stats_ref;
189292bca398SDaniel Schwierzeck    my @authors = ();
189392bca398SDaniel Schwierzeck    my @signers = ();
189492bca398SDaniel Schwierzeck    my @stats = ();
189592bca398SDaniel Schwierzeck    my $commits;
189692bca398SDaniel Schwierzeck
189792bca398SDaniel Schwierzeck    $vcs_used = vcs_exists();
189892bca398SDaniel Schwierzeck    return if (!$vcs_used);
189992bca398SDaniel Schwierzeck
190092bca398SDaniel Schwierzeck    my $cmd = $VCS_cmds{"find_signers_cmd"};
190192bca398SDaniel Schwierzeck    $cmd =~ s/(\$\w+)/$1/eeg;		# interpolate $cmd
190292bca398SDaniel Schwierzeck
190392bca398SDaniel Schwierzeck    ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
190492bca398SDaniel Schwierzeck
190592bca398SDaniel Schwierzeck    @signers = @{$signers_ref} if defined $signers_ref;
190692bca398SDaniel Schwierzeck    @authors = @{$authors_ref} if defined $authors_ref;
190792bca398SDaniel Schwierzeck    @stats = @{$stats_ref} if defined $stats_ref;
190892bca398SDaniel Schwierzeck
190992bca398SDaniel Schwierzeck#    print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
191092bca398SDaniel Schwierzeck
191192bca398SDaniel Schwierzeck    foreach my $signer (@signers) {
191292bca398SDaniel Schwierzeck	$signer = deduplicate_email($signer);
191392bca398SDaniel Schwierzeck    }
191492bca398SDaniel Schwierzeck
191592bca398SDaniel Schwierzeck    vcs_assign("commit_signer", $commits, @signers);
191692bca398SDaniel Schwierzeck    vcs_assign("authored", $commits, @authors);
191792bca398SDaniel Schwierzeck    if ($#authors == $#stats) {
191892bca398SDaniel Schwierzeck	my $stat_pattern = $VCS_cmds{"stat_pattern"};
191992bca398SDaniel Schwierzeck	$stat_pattern =~ s/(\$\w+)/$1/eeg;	#interpolate $stat_pattern
192092bca398SDaniel Schwierzeck
192192bca398SDaniel Schwierzeck	my $added = 0;
192292bca398SDaniel Schwierzeck	my $deleted = 0;
192392bca398SDaniel Schwierzeck	for (my $i = 0; $i <= $#stats; $i++) {
192492bca398SDaniel Schwierzeck	    if ($stats[$i] =~ /$stat_pattern/) {
192592bca398SDaniel Schwierzeck		$added += $1;
192692bca398SDaniel Schwierzeck		$deleted += $2;
192792bca398SDaniel Schwierzeck	    }
192892bca398SDaniel Schwierzeck	}
192992bca398SDaniel Schwierzeck	my @tmp_authors = uniq(@authors);
193092bca398SDaniel Schwierzeck	foreach my $author (@tmp_authors) {
193192bca398SDaniel Schwierzeck	    $author = deduplicate_email($author);
193292bca398SDaniel Schwierzeck	}
193392bca398SDaniel Schwierzeck	@tmp_authors = uniq(@tmp_authors);
193492bca398SDaniel Schwierzeck	my @list_added = ();
193592bca398SDaniel Schwierzeck	my @list_deleted = ();
193692bca398SDaniel Schwierzeck	foreach my $author (@tmp_authors) {
193792bca398SDaniel Schwierzeck	    my $auth_added = 0;
193892bca398SDaniel Schwierzeck	    my $auth_deleted = 0;
193992bca398SDaniel Schwierzeck	    for (my $i = 0; $i <= $#stats; $i++) {
194092bca398SDaniel Schwierzeck		if ($author eq deduplicate_email($authors[$i]) &&
194192bca398SDaniel Schwierzeck		    $stats[$i] =~ /$stat_pattern/) {
194292bca398SDaniel Schwierzeck		    $auth_added += $1;
194392bca398SDaniel Schwierzeck		    $auth_deleted += $2;
194492bca398SDaniel Schwierzeck		}
194592bca398SDaniel Schwierzeck	    }
194692bca398SDaniel Schwierzeck	    for (my $i = 0; $i < $auth_added; $i++) {
194792bca398SDaniel Schwierzeck		push(@list_added, $author);
194892bca398SDaniel Schwierzeck	    }
194992bca398SDaniel Schwierzeck	    for (my $i = 0; $i < $auth_deleted; $i++) {
195092bca398SDaniel Schwierzeck		push(@list_deleted, $author);
195192bca398SDaniel Schwierzeck	    }
195292bca398SDaniel Schwierzeck	}
195392bca398SDaniel Schwierzeck	vcs_assign("added_lines", $added, @list_added);
195492bca398SDaniel Schwierzeck	vcs_assign("removed_lines", $deleted, @list_deleted);
195592bca398SDaniel Schwierzeck    }
195692bca398SDaniel Schwierzeck}
195792bca398SDaniel Schwierzeck
195892bca398SDaniel Schwierzecksub vcs_file_blame {
195992bca398SDaniel Schwierzeck    my ($file) = @_;
196092bca398SDaniel Schwierzeck
196192bca398SDaniel Schwierzeck    my @signers = ();
196292bca398SDaniel Schwierzeck    my @all_commits = ();
196392bca398SDaniel Schwierzeck    my @commits = ();
196492bca398SDaniel Schwierzeck    my $total_commits;
196592bca398SDaniel Schwierzeck    my $total_lines;
196692bca398SDaniel Schwierzeck
196792bca398SDaniel Schwierzeck    $vcs_used = vcs_exists();
196892bca398SDaniel Schwierzeck    return if (!$vcs_used);
196992bca398SDaniel Schwierzeck
197092bca398SDaniel Schwierzeck    @all_commits = vcs_blame($file);
197192bca398SDaniel Schwierzeck    @commits = uniq(@all_commits);
197292bca398SDaniel Schwierzeck    $total_commits = @commits;
197392bca398SDaniel Schwierzeck    $total_lines = @all_commits;
197492bca398SDaniel Schwierzeck
197592bca398SDaniel Schwierzeck    if ($email_git_blame_signatures) {
197692bca398SDaniel Schwierzeck	if (vcs_is_hg()) {
197792bca398SDaniel Schwierzeck	    my $commit_count;
197892bca398SDaniel Schwierzeck	    my $commit_authors_ref;
197992bca398SDaniel Schwierzeck	    my $commit_signers_ref;
198092bca398SDaniel Schwierzeck	    my $stats_ref;
198192bca398SDaniel Schwierzeck	    my @commit_authors = ();
198292bca398SDaniel Schwierzeck	    my @commit_signers = ();
198392bca398SDaniel Schwierzeck	    my $commit = join(" -r ", @commits);
198492bca398SDaniel Schwierzeck	    my $cmd;
198592bca398SDaniel Schwierzeck
198692bca398SDaniel Schwierzeck	    $cmd = $VCS_cmds{"find_commit_signers_cmd"};
198792bca398SDaniel Schwierzeck	    $cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
198892bca398SDaniel Schwierzeck
198992bca398SDaniel Schwierzeck	    ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
199092bca398SDaniel Schwierzeck	    @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
199192bca398SDaniel Schwierzeck	    @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
199292bca398SDaniel Schwierzeck
199392bca398SDaniel Schwierzeck	    push(@signers, @commit_signers);
199492bca398SDaniel Schwierzeck	} else {
199592bca398SDaniel Schwierzeck	    foreach my $commit (@commits) {
199692bca398SDaniel Schwierzeck		my $commit_count;
199792bca398SDaniel Schwierzeck		my $commit_authors_ref;
199892bca398SDaniel Schwierzeck		my $commit_signers_ref;
199992bca398SDaniel Schwierzeck		my $stats_ref;
200092bca398SDaniel Schwierzeck		my @commit_authors = ();
200192bca398SDaniel Schwierzeck		my @commit_signers = ();
200292bca398SDaniel Schwierzeck		my $cmd;
200392bca398SDaniel Schwierzeck
200492bca398SDaniel Schwierzeck		$cmd = $VCS_cmds{"find_commit_signers_cmd"};
200592bca398SDaniel Schwierzeck		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
200692bca398SDaniel Schwierzeck
200792bca398SDaniel Schwierzeck		($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
200892bca398SDaniel Schwierzeck		@commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
200992bca398SDaniel Schwierzeck		@commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
201092bca398SDaniel Schwierzeck
201192bca398SDaniel Schwierzeck		push(@signers, @commit_signers);
201292bca398SDaniel Schwierzeck	    }
201392bca398SDaniel Schwierzeck	}
201492bca398SDaniel Schwierzeck    }
201592bca398SDaniel Schwierzeck
201692bca398SDaniel Schwierzeck    if ($from_filename) {
201792bca398SDaniel Schwierzeck	if ($output_rolestats) {
201892bca398SDaniel Schwierzeck	    my @blame_signers;
201992bca398SDaniel Schwierzeck	    if (vcs_is_hg()) {{		# Double brace for last exit
202092bca398SDaniel Schwierzeck		my $commit_count;
202192bca398SDaniel Schwierzeck		my @commit_signers = ();
202292bca398SDaniel Schwierzeck		@commits = uniq(@commits);
202392bca398SDaniel Schwierzeck		@commits = sort(@commits);
202492bca398SDaniel Schwierzeck		my $commit = join(" -r ", @commits);
202592bca398SDaniel Schwierzeck		my $cmd;
202692bca398SDaniel Schwierzeck
202792bca398SDaniel Schwierzeck		$cmd = $VCS_cmds{"find_commit_author_cmd"};
202892bca398SDaniel Schwierzeck		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
202992bca398SDaniel Schwierzeck
203092bca398SDaniel Schwierzeck		my @lines = ();
203192bca398SDaniel Schwierzeck
203292bca398SDaniel Schwierzeck		@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
203392bca398SDaniel Schwierzeck
203492bca398SDaniel Schwierzeck		if (!$email_git_penguin_chiefs) {
203592bca398SDaniel Schwierzeck		    @lines = grep(!/${penguin_chiefs}/i, @lines);
203692bca398SDaniel Schwierzeck		}
203792bca398SDaniel Schwierzeck
203892bca398SDaniel Schwierzeck		last if !@lines;
203992bca398SDaniel Schwierzeck
204092bca398SDaniel Schwierzeck		my @authors = ();
204192bca398SDaniel Schwierzeck		foreach my $line (@lines) {
204292bca398SDaniel Schwierzeck		    if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
204392bca398SDaniel Schwierzeck			my $author = $1;
204492bca398SDaniel Schwierzeck			$author = deduplicate_email($author);
204592bca398SDaniel Schwierzeck			push(@authors, $author);
204692bca398SDaniel Schwierzeck		    }
204792bca398SDaniel Schwierzeck		}
204892bca398SDaniel Schwierzeck
204992bca398SDaniel Schwierzeck		save_commits_by_author(@lines) if ($interactive);
205092bca398SDaniel Schwierzeck		save_commits_by_signer(@lines) if ($interactive);
205192bca398SDaniel Schwierzeck
205292bca398SDaniel Schwierzeck		push(@signers, @authors);
205392bca398SDaniel Schwierzeck	    }}
205492bca398SDaniel Schwierzeck	    else {
205592bca398SDaniel Schwierzeck		foreach my $commit (@commits) {
205692bca398SDaniel Schwierzeck		    my $i;
205792bca398SDaniel Schwierzeck		    my $cmd = $VCS_cmds{"find_commit_author_cmd"};
205892bca398SDaniel Schwierzeck		    $cmd =~ s/(\$\w+)/$1/eeg;	#interpolate $cmd
205992bca398SDaniel Schwierzeck		    my @author = vcs_find_author($cmd);
206092bca398SDaniel Schwierzeck		    next if !@author;
206192bca398SDaniel Schwierzeck
206292bca398SDaniel Schwierzeck		    my $formatted_author = deduplicate_email($author[0]);
206392bca398SDaniel Schwierzeck
206492bca398SDaniel Schwierzeck		    my $count = grep(/$commit/, @all_commits);
206592bca398SDaniel Schwierzeck		    for ($i = 0; $i < $count ; $i++) {
206692bca398SDaniel Schwierzeck			push(@blame_signers, $formatted_author);
206792bca398SDaniel Schwierzeck		    }
206892bca398SDaniel Schwierzeck		}
206992bca398SDaniel Schwierzeck	    }
207092bca398SDaniel Schwierzeck	    if (@blame_signers) {
207192bca398SDaniel Schwierzeck		vcs_assign("authored lines", $total_lines, @blame_signers);
207292bca398SDaniel Schwierzeck	    }
207392bca398SDaniel Schwierzeck	}
207492bca398SDaniel Schwierzeck	foreach my $signer (@signers) {
207592bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
207692bca398SDaniel Schwierzeck	}
207792bca398SDaniel Schwierzeck	vcs_assign("commits", $total_commits, @signers);
207892bca398SDaniel Schwierzeck    } else {
207992bca398SDaniel Schwierzeck	foreach my $signer (@signers) {
208092bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
208192bca398SDaniel Schwierzeck	}
208292bca398SDaniel Schwierzeck	vcs_assign("modified commits", $total_commits, @signers);
208392bca398SDaniel Schwierzeck    }
208492bca398SDaniel Schwierzeck}
208592bca398SDaniel Schwierzeck
208692bca398SDaniel Schwierzecksub uniq {
208792bca398SDaniel Schwierzeck    my (@parms) = @_;
208892bca398SDaniel Schwierzeck
208992bca398SDaniel Schwierzeck    my %saw;
209092bca398SDaniel Schwierzeck    @parms = grep(!$saw{$_}++, @parms);
209192bca398SDaniel Schwierzeck    return @parms;
209292bca398SDaniel Schwierzeck}
209392bca398SDaniel Schwierzeck
209492bca398SDaniel Schwierzecksub sort_and_uniq {
209592bca398SDaniel Schwierzeck    my (@parms) = @_;
209692bca398SDaniel Schwierzeck
209792bca398SDaniel Schwierzeck    my %saw;
209892bca398SDaniel Schwierzeck    @parms = sort @parms;
209992bca398SDaniel Schwierzeck    @parms = grep(!$saw{$_}++, @parms);
210092bca398SDaniel Schwierzeck    return @parms;
210192bca398SDaniel Schwierzeck}
210292bca398SDaniel Schwierzeck
210392bca398SDaniel Schwierzecksub clean_file_emails {
210492bca398SDaniel Schwierzeck    my (@file_emails) = @_;
210592bca398SDaniel Schwierzeck    my @fmt_emails = ();
210692bca398SDaniel Schwierzeck
210792bca398SDaniel Schwierzeck    foreach my $email (@file_emails) {
210892bca398SDaniel Schwierzeck	$email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
210992bca398SDaniel Schwierzeck	my ($name, $address) = parse_email($email);
211092bca398SDaniel Schwierzeck	if ($name eq '"[,\.]"') {
211192bca398SDaniel Schwierzeck	    $name = "";
211292bca398SDaniel Schwierzeck	}
211392bca398SDaniel Schwierzeck
211492bca398SDaniel Schwierzeck	my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
211592bca398SDaniel Schwierzeck	if (@nw > 2) {
211692bca398SDaniel Schwierzeck	    my $first = $nw[@nw - 3];
211792bca398SDaniel Schwierzeck	    my $middle = $nw[@nw - 2];
211892bca398SDaniel Schwierzeck	    my $last = $nw[@nw - 1];
211992bca398SDaniel Schwierzeck
212092bca398SDaniel Schwierzeck	    if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
212192bca398SDaniel Schwierzeck		 (length($first) == 2 && substr($first, -1) eq ".")) ||
212292bca398SDaniel Schwierzeck		(length($middle) == 1 ||
212392bca398SDaniel Schwierzeck		 (length($middle) == 2 && substr($middle, -1) eq "."))) {
212492bca398SDaniel Schwierzeck		$name = "$first $middle $last";
212592bca398SDaniel Schwierzeck	    } else {
212692bca398SDaniel Schwierzeck		$name = "$middle $last";
212792bca398SDaniel Schwierzeck	    }
212892bca398SDaniel Schwierzeck	}
212992bca398SDaniel Schwierzeck
213092bca398SDaniel Schwierzeck	if (substr($name, -1) =~ /[,\.]/) {
213192bca398SDaniel Schwierzeck	    $name = substr($name, 0, length($name) - 1);
213292bca398SDaniel Schwierzeck	} elsif (substr($name, -2) =~ /[,\.]"/) {
213392bca398SDaniel Schwierzeck	    $name = substr($name, 0, length($name) - 2) . '"';
213492bca398SDaniel Schwierzeck	}
213592bca398SDaniel Schwierzeck
213692bca398SDaniel Schwierzeck	if (substr($name, 0, 1) =~ /[,\.]/) {
213792bca398SDaniel Schwierzeck	    $name = substr($name, 1, length($name) - 1);
213892bca398SDaniel Schwierzeck	} elsif (substr($name, 0, 2) =~ /"[,\.]/) {
213992bca398SDaniel Schwierzeck	    $name = '"' . substr($name, 2, length($name) - 2);
214092bca398SDaniel Schwierzeck	}
214192bca398SDaniel Schwierzeck
214292bca398SDaniel Schwierzeck	my $fmt_email = format_email($name, $address, $email_usename);
214392bca398SDaniel Schwierzeck	push(@fmt_emails, $fmt_email);
214492bca398SDaniel Schwierzeck    }
214592bca398SDaniel Schwierzeck    return @fmt_emails;
214692bca398SDaniel Schwierzeck}
214792bca398SDaniel Schwierzeck
214892bca398SDaniel Schwierzecksub merge_email {
214992bca398SDaniel Schwierzeck    my @lines;
215092bca398SDaniel Schwierzeck    my %saw;
215192bca398SDaniel Schwierzeck
215292bca398SDaniel Schwierzeck    for (@_) {
215392bca398SDaniel Schwierzeck	my ($address, $role) = @$_;
215492bca398SDaniel Schwierzeck	if (!$saw{$address}) {
215592bca398SDaniel Schwierzeck	    if ($output_roles) {
215692bca398SDaniel Schwierzeck		push(@lines, "$address ($role)");
215792bca398SDaniel Schwierzeck	    } else {
215892bca398SDaniel Schwierzeck		push(@lines, $address);
215992bca398SDaniel Schwierzeck	    }
216092bca398SDaniel Schwierzeck	    $saw{$address} = 1;
216192bca398SDaniel Schwierzeck	}
216292bca398SDaniel Schwierzeck    }
216392bca398SDaniel Schwierzeck
216492bca398SDaniel Schwierzeck    return @lines;
216592bca398SDaniel Schwierzeck}
216692bca398SDaniel Schwierzeck
216792bca398SDaniel Schwierzecksub output {
216892bca398SDaniel Schwierzeck    my (@parms) = @_;
216992bca398SDaniel Schwierzeck
217092bca398SDaniel Schwierzeck    if ($output_multiline) {
217192bca398SDaniel Schwierzeck	foreach my $line (@parms) {
217292bca398SDaniel Schwierzeck	    print("${line}\n");
217392bca398SDaniel Schwierzeck	}
217492bca398SDaniel Schwierzeck    } else {
217592bca398SDaniel Schwierzeck	print(join($output_separator, @parms));
217692bca398SDaniel Schwierzeck	print("\n");
217792bca398SDaniel Schwierzeck    }
217892bca398SDaniel Schwierzeck}
217992bca398SDaniel Schwierzeck
218092bca398SDaniel Schwierzeckmy $rfc822re;
218192bca398SDaniel Schwierzeck
218292bca398SDaniel Schwierzecksub make_rfc822re {
218392bca398SDaniel Schwierzeck#   Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
218492bca398SDaniel Schwierzeck#   comment.  We must allow for rfc822_lwsp (or comments) after each of these.
218592bca398SDaniel Schwierzeck#   This regexp will only work on addresses which have had comments stripped
218692bca398SDaniel Schwierzeck#   and replaced with rfc822_lwsp.
218792bca398SDaniel Schwierzeck
218892bca398SDaniel Schwierzeck    my $specials = '()<>@,;:\\\\".\\[\\]';
218992bca398SDaniel Schwierzeck    my $controls = '\\000-\\037\\177';
219092bca398SDaniel Schwierzeck
219192bca398SDaniel Schwierzeck    my $dtext = "[^\\[\\]\\r\\\\]";
219292bca398SDaniel Schwierzeck    my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
219392bca398SDaniel Schwierzeck
219492bca398SDaniel Schwierzeck    my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
219592bca398SDaniel Schwierzeck
219692bca398SDaniel Schwierzeck#   Use zero-width assertion to spot the limit of an atom.  A simple
219792bca398SDaniel Schwierzeck#   $rfc822_lwsp* causes the regexp engine to hang occasionally.
219892bca398SDaniel Schwierzeck    my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
219992bca398SDaniel Schwierzeck    my $word = "(?:$atom|$quoted_string)";
220092bca398SDaniel Schwierzeck    my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
220192bca398SDaniel Schwierzeck
220292bca398SDaniel Schwierzeck    my $sub_domain = "(?:$atom|$domain_literal)";
220392bca398SDaniel Schwierzeck    my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
220492bca398SDaniel Schwierzeck
220592bca398SDaniel Schwierzeck    my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
220692bca398SDaniel Schwierzeck
220792bca398SDaniel Schwierzeck    my $phrase = "$word*";
220892bca398SDaniel Schwierzeck    my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
220992bca398SDaniel Schwierzeck    my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
221092bca398SDaniel Schwierzeck    my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
221192bca398SDaniel Schwierzeck
221292bca398SDaniel Schwierzeck    my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
221392bca398SDaniel Schwierzeck    my $address = "(?:$mailbox|$group)";
221492bca398SDaniel Schwierzeck
221592bca398SDaniel Schwierzeck    return "$rfc822_lwsp*$address";
221692bca398SDaniel Schwierzeck}
221792bca398SDaniel Schwierzeck
221892bca398SDaniel Schwierzecksub rfc822_strip_comments {
221992bca398SDaniel Schwierzeck    my $s = shift;
222092bca398SDaniel Schwierzeck#   Recursively remove comments, and replace with a single space.  The simpler
222192bca398SDaniel Schwierzeck#   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
222292bca398SDaniel Schwierzeck#   chars in atoms, for example.
222392bca398SDaniel Schwierzeck
222492bca398SDaniel Schwierzeck    while ($s =~ s/^((?:[^"\\]|\\.)*
222592bca398SDaniel Schwierzeck                    (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
222692bca398SDaniel Schwierzeck                    \((?:[^()\\]|\\.)*\)/$1 /osx) {}
222792bca398SDaniel Schwierzeck    return $s;
222892bca398SDaniel Schwierzeck}
222992bca398SDaniel Schwierzeck
223092bca398SDaniel Schwierzeck#   valid: returns true if the parameter is an RFC822 valid address
223192bca398SDaniel Schwierzeck#
223292bca398SDaniel Schwierzecksub rfc822_valid {
223392bca398SDaniel Schwierzeck    my $s = rfc822_strip_comments(shift);
223492bca398SDaniel Schwierzeck
223592bca398SDaniel Schwierzeck    if (!$rfc822re) {
223692bca398SDaniel Schwierzeck        $rfc822re = make_rfc822re();
223792bca398SDaniel Schwierzeck    }
223892bca398SDaniel Schwierzeck
223992bca398SDaniel Schwierzeck    return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
224092bca398SDaniel Schwierzeck}
224192bca398SDaniel Schwierzeck
224292bca398SDaniel Schwierzeck#   validlist: In scalar context, returns true if the parameter is an RFC822
224392bca398SDaniel Schwierzeck#              valid list of addresses.
224492bca398SDaniel Schwierzeck#
224592bca398SDaniel Schwierzeck#              In list context, returns an empty list on failure (an invalid
224692bca398SDaniel Schwierzeck#              address was found); otherwise a list whose first element is the
224792bca398SDaniel Schwierzeck#              number of addresses found and whose remaining elements are the
224892bca398SDaniel Schwierzeck#              addresses.  This is needed to disambiguate failure (invalid)
224992bca398SDaniel Schwierzeck#              from success with no addresses found, because an empty string is
225092bca398SDaniel Schwierzeck#              a valid list.
225192bca398SDaniel Schwierzeck
225292bca398SDaniel Schwierzecksub rfc822_validlist {
225392bca398SDaniel Schwierzeck    my $s = rfc822_strip_comments(shift);
225492bca398SDaniel Schwierzeck
225592bca398SDaniel Schwierzeck    if (!$rfc822re) {
225692bca398SDaniel Schwierzeck        $rfc822re = make_rfc822re();
225792bca398SDaniel Schwierzeck    }
225892bca398SDaniel Schwierzeck    # * null list items are valid according to the RFC
225992bca398SDaniel Schwierzeck    # * the '1' business is to aid in distinguishing failure from no results
226092bca398SDaniel Schwierzeck
226192bca398SDaniel Schwierzeck    my @r;
226292bca398SDaniel Schwierzeck    if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
226392bca398SDaniel Schwierzeck	$s =~ m/^$rfc822_char*$/) {
226492bca398SDaniel Schwierzeck        while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
226592bca398SDaniel Schwierzeck            push(@r, $1);
226692bca398SDaniel Schwierzeck        }
226792bca398SDaniel Schwierzeck        return wantarray ? (scalar(@r), @r) : 1;
226892bca398SDaniel Schwierzeck    }
226992bca398SDaniel Schwierzeck    return wantarray ? () : 0;
227092bca398SDaniel Schwierzeck}
2271