xref: /rk3399_rockchip-uboot/scripts/get_maintainer.pl (revision ca746f044e3c8b047598601fdf3d732505a67c4c)
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);
1968dc8769SDaniel 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 = ();
65*ca746f04SAndy Flemingpush(@penguin_chief, "Tom Rini:trini\@konsulko.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
27768dc8769SDaniel Schwierzeckmy @maint_files = ();
27868dc8769SDaniel Schwierzeckpush(@maint_files, "${lk_path}MAINTAINERS");
27968dc8769SDaniel Schwierzeck
28068dc8769SDaniel Schwierzecksub maint_wanted {
28168dc8769SDaniel Schwierzeck    return unless $_ =~ /^MAINTAINERS/;
28268dc8769SDaniel Schwierzeck    push(@maint_files, "$File::Find::name");
28368dc8769SDaniel Schwierzeck}
28468dc8769SDaniel Schwierzeck
28568dc8769SDaniel SchwierzeckFile::Find::find(\&maint_wanted, "${lk_path}board");
28668dc8769SDaniel Schwierzeck
28768dc8769SDaniel Schwierzeckforeach my $maint_file (@maint_files) {
28868dc8769SDaniel Schwierzeck    my $maint;
28968dc8769SDaniel Schwierzeck    open ($maint, '<', "$maint_file")
29068dc8769SDaniel Schwierzeck	or die "$P: Can't open $maint_file: $!\n";
29168dc8769SDaniel Schwierzeck    read_maintainers($maint);
29268dc8769SDaniel Schwierzeck    close($maint);
29368dc8769SDaniel Schwierzeck}
29468dc8769SDaniel Schwierzeck
29568dc8769SDaniel Schwierzecksub read_maintainers {
29668dc8769SDaniel Schwierzeck    my ($maint) = @_;
29768dc8769SDaniel 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    }
32368dc8769SDaniel 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    }
83827e77183SDaniel Schwierzeck    if (   (-f "${lk_path}Kbuild")
83992bca398SDaniel Schwierzeck	&& (-f "${lk_path}MAINTAINERS")
84092bca398SDaniel Schwierzeck	&& (-f "${lk_path}Makefile")
84192bca398SDaniel Schwierzeck	&& (-f "${lk_path}README")
84292bca398SDaniel Schwierzeck	&& (-d "${lk_path}arch")
843ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}board")
844ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}common")
845ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}doc")
84692bca398SDaniel Schwierzeck	&& (-d "${lk_path}drivers")
847ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}dts")
84892bca398SDaniel Schwierzeck	&& (-d "${lk_path}fs")
84992bca398SDaniel Schwierzeck	&& (-d "${lk_path}lib")
850ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}include")
851ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}net")
852ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}post")
853ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}scripts")
854ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}test")
855ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}tools")) {
85692bca398SDaniel Schwierzeck	return 1;
85792bca398SDaniel Schwierzeck    }
85892bca398SDaniel Schwierzeck    return 0;
85992bca398SDaniel Schwierzeck}
86092bca398SDaniel Schwierzeck
86192bca398SDaniel Schwierzecksub parse_email {
86292bca398SDaniel Schwierzeck    my ($formatted_email) = @_;
86392bca398SDaniel Schwierzeck
86492bca398SDaniel Schwierzeck    my $name = "";
86592bca398SDaniel Schwierzeck    my $address = "";
86692bca398SDaniel Schwierzeck
86792bca398SDaniel Schwierzeck    if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
86892bca398SDaniel Schwierzeck	$name = $1;
86992bca398SDaniel Schwierzeck	$address = $2;
87092bca398SDaniel Schwierzeck    } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
87192bca398SDaniel Schwierzeck	$address = $1;
87292bca398SDaniel Schwierzeck    } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
87392bca398SDaniel Schwierzeck	$address = $1;
87492bca398SDaniel Schwierzeck    }
87592bca398SDaniel Schwierzeck
87692bca398SDaniel Schwierzeck    $name =~ s/^\s+|\s+$//g;
87792bca398SDaniel Schwierzeck    $name =~ s/^\"|\"$//g;
87892bca398SDaniel Schwierzeck    $address =~ s/^\s+|\s+$//g;
87992bca398SDaniel Schwierzeck
88092bca398SDaniel Schwierzeck    if ($name =~ /[^\w \-]/i) {  	 ##has "must quote" chars
88192bca398SDaniel Schwierzeck	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
88292bca398SDaniel Schwierzeck	$name = "\"$name\"";
88392bca398SDaniel Schwierzeck    }
88492bca398SDaniel Schwierzeck
88592bca398SDaniel Schwierzeck    return ($name, $address);
88692bca398SDaniel Schwierzeck}
88792bca398SDaniel Schwierzeck
88892bca398SDaniel Schwierzecksub format_email {
88992bca398SDaniel Schwierzeck    my ($name, $address, $usename) = @_;
89092bca398SDaniel Schwierzeck
89192bca398SDaniel Schwierzeck    my $formatted_email;
89292bca398SDaniel Schwierzeck
89392bca398SDaniel Schwierzeck    $name =~ s/^\s+|\s+$//g;
89492bca398SDaniel Schwierzeck    $name =~ s/^\"|\"$//g;
89592bca398SDaniel Schwierzeck    $address =~ s/^\s+|\s+$//g;
89692bca398SDaniel Schwierzeck
89792bca398SDaniel Schwierzeck    if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
89892bca398SDaniel Schwierzeck	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
89992bca398SDaniel Schwierzeck	$name = "\"$name\"";
90092bca398SDaniel Schwierzeck    }
90192bca398SDaniel Schwierzeck
90292bca398SDaniel Schwierzeck    if ($usename) {
90392bca398SDaniel Schwierzeck	if ("$name" eq "") {
90492bca398SDaniel Schwierzeck	    $formatted_email = "$address";
90592bca398SDaniel Schwierzeck	} else {
90692bca398SDaniel Schwierzeck	    $formatted_email = "$name <$address>";
90792bca398SDaniel Schwierzeck	}
90892bca398SDaniel Schwierzeck    } else {
90992bca398SDaniel Schwierzeck	$formatted_email = $address;
91092bca398SDaniel Schwierzeck    }
91192bca398SDaniel Schwierzeck
91292bca398SDaniel Schwierzeck    return $formatted_email;
91392bca398SDaniel Schwierzeck}
91492bca398SDaniel Schwierzeck
91592bca398SDaniel Schwierzecksub find_first_section {
91692bca398SDaniel Schwierzeck    my $index = 0;
91792bca398SDaniel Schwierzeck
91892bca398SDaniel Schwierzeck    while ($index < @typevalue) {
91992bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
92092bca398SDaniel Schwierzeck	if (($tv =~ m/^(\C):\s*(.*)/)) {
92192bca398SDaniel Schwierzeck	    last;
92292bca398SDaniel Schwierzeck	}
92392bca398SDaniel Schwierzeck	$index++;
92492bca398SDaniel Schwierzeck    }
92592bca398SDaniel Schwierzeck
92692bca398SDaniel Schwierzeck    return $index;
92792bca398SDaniel Schwierzeck}
92892bca398SDaniel Schwierzeck
92992bca398SDaniel Schwierzecksub find_starting_index {
93092bca398SDaniel Schwierzeck    my ($index) = @_;
93192bca398SDaniel Schwierzeck
93292bca398SDaniel Schwierzeck    while ($index > 0) {
93392bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
93492bca398SDaniel Schwierzeck	if (!($tv =~ m/^(\C):\s*(.*)/)) {
93592bca398SDaniel Schwierzeck	    last;
93692bca398SDaniel Schwierzeck	}
93792bca398SDaniel Schwierzeck	$index--;
93892bca398SDaniel Schwierzeck    }
93992bca398SDaniel Schwierzeck
94092bca398SDaniel Schwierzeck    return $index;
94192bca398SDaniel Schwierzeck}
94292bca398SDaniel Schwierzeck
94392bca398SDaniel Schwierzecksub find_ending_index {
94492bca398SDaniel Schwierzeck    my ($index) = @_;
94592bca398SDaniel Schwierzeck
94692bca398SDaniel Schwierzeck    while ($index < @typevalue) {
94792bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
94892bca398SDaniel Schwierzeck	if (!($tv =~ m/^(\C):\s*(.*)/)) {
94992bca398SDaniel Schwierzeck	    last;
95092bca398SDaniel Schwierzeck	}
95192bca398SDaniel Schwierzeck	$index++;
95292bca398SDaniel Schwierzeck    }
95392bca398SDaniel Schwierzeck
95492bca398SDaniel Schwierzeck    return $index;
95592bca398SDaniel Schwierzeck}
95692bca398SDaniel Schwierzeck
95792bca398SDaniel Schwierzecksub get_maintainer_role {
95892bca398SDaniel Schwierzeck    my ($index) = @_;
95992bca398SDaniel Schwierzeck
96092bca398SDaniel Schwierzeck    my $i;
96192bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
96292bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
96392bca398SDaniel Schwierzeck
96492bca398SDaniel Schwierzeck    my $role = "unknown";
96592bca398SDaniel Schwierzeck    my $subsystem = $typevalue[$start];
96692bca398SDaniel Schwierzeck    if (length($subsystem) > 20) {
96792bca398SDaniel Schwierzeck	$subsystem = substr($subsystem, 0, 17);
96892bca398SDaniel Schwierzeck	$subsystem =~ s/\s*$//;
96992bca398SDaniel Schwierzeck	$subsystem = $subsystem . "...";
97092bca398SDaniel Schwierzeck    }
97192bca398SDaniel Schwierzeck
97292bca398SDaniel Schwierzeck    for ($i = $start + 1; $i < $end; $i++) {
97392bca398SDaniel Schwierzeck	my $tv = $typevalue[$i];
97492bca398SDaniel Schwierzeck	if ($tv =~ m/^(\C):\s*(.*)/) {
97592bca398SDaniel Schwierzeck	    my $ptype = $1;
97692bca398SDaniel Schwierzeck	    my $pvalue = $2;
97792bca398SDaniel Schwierzeck	    if ($ptype eq "S") {
97892bca398SDaniel Schwierzeck		$role = $pvalue;
97992bca398SDaniel Schwierzeck	    }
98092bca398SDaniel Schwierzeck	}
98192bca398SDaniel Schwierzeck    }
98292bca398SDaniel Schwierzeck
98392bca398SDaniel Schwierzeck    $role = lc($role);
98492bca398SDaniel Schwierzeck    if      ($role eq "supported") {
98592bca398SDaniel Schwierzeck	$role = "supporter";
98692bca398SDaniel Schwierzeck    } elsif ($role eq "maintained") {
98792bca398SDaniel Schwierzeck	$role = "maintainer";
98892bca398SDaniel Schwierzeck    } elsif ($role eq "odd fixes") {
98992bca398SDaniel Schwierzeck	$role = "odd fixer";
99092bca398SDaniel Schwierzeck    } elsif ($role eq "orphan") {
99192bca398SDaniel Schwierzeck	$role = "orphan minder";
99292bca398SDaniel Schwierzeck    } elsif ($role eq "obsolete") {
99392bca398SDaniel Schwierzeck	$role = "obsolete minder";
99492bca398SDaniel Schwierzeck    } elsif ($role eq "buried alive in reporters") {
99592bca398SDaniel Schwierzeck	$role = "chief penguin";
99692bca398SDaniel Schwierzeck    }
99792bca398SDaniel Schwierzeck
99892bca398SDaniel Schwierzeck    return $role . ":" . $subsystem;
99992bca398SDaniel Schwierzeck}
100092bca398SDaniel Schwierzeck
100192bca398SDaniel Schwierzecksub get_list_role {
100292bca398SDaniel Schwierzeck    my ($index) = @_;
100392bca398SDaniel Schwierzeck
100492bca398SDaniel Schwierzeck    my $i;
100592bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
100692bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
100792bca398SDaniel Schwierzeck
100892bca398SDaniel Schwierzeck    my $subsystem = $typevalue[$start];
100992bca398SDaniel Schwierzeck    if (length($subsystem) > 20) {
101092bca398SDaniel Schwierzeck	$subsystem = substr($subsystem, 0, 17);
101192bca398SDaniel Schwierzeck	$subsystem =~ s/\s*$//;
101292bca398SDaniel Schwierzeck	$subsystem = $subsystem . "...";
101392bca398SDaniel Schwierzeck    }
101492bca398SDaniel Schwierzeck
101592bca398SDaniel Schwierzeck    if ($subsystem eq "THE REST") {
101692bca398SDaniel Schwierzeck	$subsystem = "";
101792bca398SDaniel Schwierzeck    }
101892bca398SDaniel Schwierzeck
101992bca398SDaniel Schwierzeck    return $subsystem;
102092bca398SDaniel Schwierzeck}
102192bca398SDaniel Schwierzeck
102292bca398SDaniel Schwierzecksub add_categories {
102392bca398SDaniel Schwierzeck    my ($index) = @_;
102492bca398SDaniel Schwierzeck
102592bca398SDaniel Schwierzeck    my $i;
102692bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
102792bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
102892bca398SDaniel Schwierzeck
102992bca398SDaniel Schwierzeck    push(@subsystem, $typevalue[$start]);
103092bca398SDaniel Schwierzeck
103192bca398SDaniel Schwierzeck    for ($i = $start + 1; $i < $end; $i++) {
103292bca398SDaniel Schwierzeck	my $tv = $typevalue[$i];
103392bca398SDaniel Schwierzeck	if ($tv =~ m/^(\C):\s*(.*)/) {
103492bca398SDaniel Schwierzeck	    my $ptype = $1;
103592bca398SDaniel Schwierzeck	    my $pvalue = $2;
103692bca398SDaniel Schwierzeck	    if ($ptype eq "L") {
103792bca398SDaniel Schwierzeck		my $list_address = $pvalue;
103892bca398SDaniel Schwierzeck		my $list_additional = "";
103992bca398SDaniel Schwierzeck		my $list_role = get_list_role($i);
104092bca398SDaniel Schwierzeck
104192bca398SDaniel Schwierzeck		if ($list_role ne "") {
104292bca398SDaniel Schwierzeck		    $list_role = ":" . $list_role;
104392bca398SDaniel Schwierzeck		}
104492bca398SDaniel Schwierzeck		if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
104592bca398SDaniel Schwierzeck		    $list_address = $1;
104692bca398SDaniel Schwierzeck		    $list_additional = $2;
104792bca398SDaniel Schwierzeck		}
104892bca398SDaniel Schwierzeck		if ($list_additional =~ m/subscribers-only/) {
104992bca398SDaniel Schwierzeck		    if ($email_subscriber_list) {
105092bca398SDaniel Schwierzeck			if (!$hash_list_to{lc($list_address)}) {
105192bca398SDaniel Schwierzeck			    $hash_list_to{lc($list_address)} = 1;
105292bca398SDaniel Schwierzeck			    push(@list_to, [$list_address,
105392bca398SDaniel Schwierzeck					    "subscriber list${list_role}"]);
105492bca398SDaniel Schwierzeck			}
105592bca398SDaniel Schwierzeck		    }
105692bca398SDaniel Schwierzeck		} else {
105792bca398SDaniel Schwierzeck		    if ($email_list) {
105892bca398SDaniel Schwierzeck			if (!$hash_list_to{lc($list_address)}) {
105992bca398SDaniel Schwierzeck			    $hash_list_to{lc($list_address)} = 1;
106092bca398SDaniel Schwierzeck			    if ($list_additional =~ m/moderated/) {
106192bca398SDaniel Schwierzeck				push(@list_to, [$list_address,
106292bca398SDaniel Schwierzeck						"moderated list${list_role}"]);
106392bca398SDaniel Schwierzeck			    } else {
106492bca398SDaniel Schwierzeck				push(@list_to, [$list_address,
106592bca398SDaniel Schwierzeck						"open list${list_role}"]);
106692bca398SDaniel Schwierzeck			    }
106792bca398SDaniel Schwierzeck			}
106892bca398SDaniel Schwierzeck		    }
106992bca398SDaniel Schwierzeck		}
107092bca398SDaniel Schwierzeck	    } elsif ($ptype eq "M") {
107192bca398SDaniel Schwierzeck		my ($name, $address) = parse_email($pvalue);
107292bca398SDaniel Schwierzeck		if ($name eq "") {
107392bca398SDaniel Schwierzeck		    if ($i > 0) {
107492bca398SDaniel Schwierzeck			my $tv = $typevalue[$i - 1];
107592bca398SDaniel Schwierzeck			if ($tv =~ m/^(\C):\s*(.*)/) {
107692bca398SDaniel Schwierzeck			    if ($1 eq "P") {
107792bca398SDaniel Schwierzeck				$name = $2;
107892bca398SDaniel Schwierzeck				$pvalue = format_email($name, $address, $email_usename);
107992bca398SDaniel Schwierzeck			    }
108092bca398SDaniel Schwierzeck			}
108192bca398SDaniel Schwierzeck		    }
108292bca398SDaniel Schwierzeck		}
108392bca398SDaniel Schwierzeck		if ($email_maintainer) {
108492bca398SDaniel Schwierzeck		    my $role = get_maintainer_role($i);
108592bca398SDaniel Schwierzeck		    push_email_addresses($pvalue, $role);
108692bca398SDaniel Schwierzeck		}
108792bca398SDaniel Schwierzeck	    } elsif ($ptype eq "T") {
108892bca398SDaniel Schwierzeck		push(@scm, $pvalue);
108992bca398SDaniel Schwierzeck	    } elsif ($ptype eq "W") {
109092bca398SDaniel Schwierzeck		push(@web, $pvalue);
109192bca398SDaniel Schwierzeck	    } elsif ($ptype eq "S") {
109292bca398SDaniel Schwierzeck		push(@status, $pvalue);
109392bca398SDaniel Schwierzeck	    }
109492bca398SDaniel Schwierzeck	}
109592bca398SDaniel Schwierzeck    }
109692bca398SDaniel Schwierzeck}
109792bca398SDaniel Schwierzeck
109892bca398SDaniel Schwierzecksub email_inuse {
109992bca398SDaniel Schwierzeck    my ($name, $address) = @_;
110092bca398SDaniel Schwierzeck
110192bca398SDaniel Schwierzeck    return 1 if (($name eq "") && ($address eq ""));
110292bca398SDaniel Schwierzeck    return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
110392bca398SDaniel Schwierzeck    return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
110492bca398SDaniel Schwierzeck
110592bca398SDaniel Schwierzeck    return 0;
110692bca398SDaniel Schwierzeck}
110792bca398SDaniel Schwierzeck
110892bca398SDaniel Schwierzecksub push_email_address {
110992bca398SDaniel Schwierzeck    my ($line, $role) = @_;
111092bca398SDaniel Schwierzeck
111192bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
111292bca398SDaniel Schwierzeck
111392bca398SDaniel Schwierzeck    if ($address eq "") {
111492bca398SDaniel Schwierzeck	return 0;
111592bca398SDaniel Schwierzeck    }
111692bca398SDaniel Schwierzeck
111792bca398SDaniel Schwierzeck    if (!$email_remove_duplicates) {
111892bca398SDaniel Schwierzeck	push(@email_to, [format_email($name, $address, $email_usename), $role]);
111992bca398SDaniel Schwierzeck    } elsif (!email_inuse($name, $address)) {
112092bca398SDaniel Schwierzeck	push(@email_to, [format_email($name, $address, $email_usename), $role]);
112192bca398SDaniel Schwierzeck	$email_hash_name{lc($name)}++ if ($name ne "");
112292bca398SDaniel Schwierzeck	$email_hash_address{lc($address)}++;
112392bca398SDaniel Schwierzeck    }
112492bca398SDaniel Schwierzeck
112592bca398SDaniel Schwierzeck    return 1;
112692bca398SDaniel Schwierzeck}
112792bca398SDaniel Schwierzeck
112892bca398SDaniel Schwierzecksub push_email_addresses {
112992bca398SDaniel Schwierzeck    my ($address, $role) = @_;
113092bca398SDaniel Schwierzeck
113192bca398SDaniel Schwierzeck    my @address_list = ();
113292bca398SDaniel Schwierzeck
113392bca398SDaniel Schwierzeck    if (rfc822_valid($address)) {
113492bca398SDaniel Schwierzeck	push_email_address($address, $role);
113592bca398SDaniel Schwierzeck    } elsif (@address_list = rfc822_validlist($address)) {
113692bca398SDaniel Schwierzeck	my $array_count = shift(@address_list);
113792bca398SDaniel Schwierzeck	while (my $entry = shift(@address_list)) {
113892bca398SDaniel Schwierzeck	    push_email_address($entry, $role);
113992bca398SDaniel Schwierzeck	}
114092bca398SDaniel Schwierzeck    } else {
114192bca398SDaniel Schwierzeck	if (!push_email_address($address, $role)) {
114292bca398SDaniel Schwierzeck	    warn("Invalid MAINTAINERS address: '" . $address . "'\n");
114392bca398SDaniel Schwierzeck	}
114492bca398SDaniel Schwierzeck    }
114592bca398SDaniel Schwierzeck}
114692bca398SDaniel Schwierzeck
114792bca398SDaniel Schwierzecksub add_role {
114892bca398SDaniel Schwierzeck    my ($line, $role) = @_;
114992bca398SDaniel Schwierzeck
115092bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
115192bca398SDaniel Schwierzeck    my $email = format_email($name, $address, $email_usename);
115292bca398SDaniel Schwierzeck
115392bca398SDaniel Schwierzeck    foreach my $entry (@email_to) {
115492bca398SDaniel Schwierzeck	if ($email_remove_duplicates) {
115592bca398SDaniel Schwierzeck	    my ($entry_name, $entry_address) = parse_email($entry->[0]);
115692bca398SDaniel Schwierzeck	    if (($name eq $entry_name || $address eq $entry_address)
115792bca398SDaniel Schwierzeck		&& ($role eq "" || !($entry->[1] =~ m/$role/))
115892bca398SDaniel Schwierzeck	    ) {
115992bca398SDaniel Schwierzeck		if ($entry->[1] eq "") {
116092bca398SDaniel Schwierzeck		    $entry->[1] = "$role";
116192bca398SDaniel Schwierzeck		} else {
116292bca398SDaniel Schwierzeck		    $entry->[1] = "$entry->[1],$role";
116392bca398SDaniel Schwierzeck		}
116492bca398SDaniel Schwierzeck	    }
116592bca398SDaniel Schwierzeck	} else {
116692bca398SDaniel Schwierzeck	    if ($email eq $entry->[0]
116792bca398SDaniel Schwierzeck		&& ($role eq "" || !($entry->[1] =~ m/$role/))
116892bca398SDaniel Schwierzeck	    ) {
116992bca398SDaniel Schwierzeck		if ($entry->[1] eq "") {
117092bca398SDaniel Schwierzeck		    $entry->[1] = "$role";
117192bca398SDaniel Schwierzeck		} else {
117292bca398SDaniel Schwierzeck		    $entry->[1] = "$entry->[1],$role";
117392bca398SDaniel Schwierzeck		}
117492bca398SDaniel Schwierzeck	    }
117592bca398SDaniel Schwierzeck	}
117692bca398SDaniel Schwierzeck    }
117792bca398SDaniel Schwierzeck}
117892bca398SDaniel Schwierzeck
117992bca398SDaniel Schwierzecksub which {
118092bca398SDaniel Schwierzeck    my ($bin) = @_;
118192bca398SDaniel Schwierzeck
118292bca398SDaniel Schwierzeck    foreach my $path (split(/:/, $ENV{PATH})) {
118392bca398SDaniel Schwierzeck	if (-e "$path/$bin") {
118492bca398SDaniel Schwierzeck	    return "$path/$bin";
118592bca398SDaniel Schwierzeck	}
118692bca398SDaniel Schwierzeck    }
118792bca398SDaniel Schwierzeck
118892bca398SDaniel Schwierzeck    return "";
118992bca398SDaniel Schwierzeck}
119092bca398SDaniel Schwierzeck
119192bca398SDaniel Schwierzecksub which_conf {
119292bca398SDaniel Schwierzeck    my ($conf) = @_;
119392bca398SDaniel Schwierzeck
119492bca398SDaniel Schwierzeck    foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
119592bca398SDaniel Schwierzeck	if (-e "$path/$conf") {
119692bca398SDaniel Schwierzeck	    return "$path/$conf";
119792bca398SDaniel Schwierzeck	}
119892bca398SDaniel Schwierzeck    }
119992bca398SDaniel Schwierzeck
120092bca398SDaniel Schwierzeck    return "";
120192bca398SDaniel Schwierzeck}
120292bca398SDaniel Schwierzeck
120392bca398SDaniel Schwierzecksub mailmap_email {
120492bca398SDaniel Schwierzeck    my ($line) = @_;
120592bca398SDaniel Schwierzeck
120692bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
120792bca398SDaniel Schwierzeck    my $email = format_email($name, $address, 1);
120892bca398SDaniel Schwierzeck    my $real_name = $name;
120992bca398SDaniel Schwierzeck    my $real_address = $address;
121092bca398SDaniel Schwierzeck
121192bca398SDaniel Schwierzeck    if (exists $mailmap->{names}->{$email} ||
121292bca398SDaniel Schwierzeck	exists $mailmap->{addresses}->{$email}) {
121392bca398SDaniel Schwierzeck	if (exists $mailmap->{names}->{$email}) {
121492bca398SDaniel Schwierzeck	    $real_name = $mailmap->{names}->{$email};
121592bca398SDaniel Schwierzeck	}
121692bca398SDaniel Schwierzeck	if (exists $mailmap->{addresses}->{$email}) {
121792bca398SDaniel Schwierzeck	    $real_address = $mailmap->{addresses}->{$email};
121892bca398SDaniel Schwierzeck	}
121992bca398SDaniel Schwierzeck    } else {
122092bca398SDaniel Schwierzeck	if (exists $mailmap->{names}->{$address}) {
122192bca398SDaniel Schwierzeck	    $real_name = $mailmap->{names}->{$address};
122292bca398SDaniel Schwierzeck	}
122392bca398SDaniel Schwierzeck	if (exists $mailmap->{addresses}->{$address}) {
122492bca398SDaniel Schwierzeck	    $real_address = $mailmap->{addresses}->{$address};
122592bca398SDaniel Schwierzeck	}
122692bca398SDaniel Schwierzeck    }
122792bca398SDaniel Schwierzeck    return format_email($real_name, $real_address, 1);
122892bca398SDaniel Schwierzeck}
122992bca398SDaniel Schwierzeck
123092bca398SDaniel Schwierzecksub mailmap {
123192bca398SDaniel Schwierzeck    my (@addresses) = @_;
123292bca398SDaniel Schwierzeck
123392bca398SDaniel Schwierzeck    my @mapped_emails = ();
123492bca398SDaniel Schwierzeck    foreach my $line (@addresses) {
123592bca398SDaniel Schwierzeck	push(@mapped_emails, mailmap_email($line));
123692bca398SDaniel Schwierzeck    }
123792bca398SDaniel Schwierzeck    merge_by_realname(@mapped_emails) if ($email_use_mailmap);
123892bca398SDaniel Schwierzeck    return @mapped_emails;
123992bca398SDaniel Schwierzeck}
124092bca398SDaniel Schwierzeck
124192bca398SDaniel Schwierzecksub merge_by_realname {
124292bca398SDaniel Schwierzeck    my %address_map;
124392bca398SDaniel Schwierzeck    my (@emails) = @_;
124492bca398SDaniel Schwierzeck
124592bca398SDaniel Schwierzeck    foreach my $email (@emails) {
124692bca398SDaniel Schwierzeck	my ($name, $address) = parse_email($email);
124792bca398SDaniel Schwierzeck	if (exists $address_map{$name}) {
124892bca398SDaniel Schwierzeck	    $address = $address_map{$name};
124992bca398SDaniel Schwierzeck	    $email = format_email($name, $address, 1);
125092bca398SDaniel Schwierzeck	} else {
125192bca398SDaniel Schwierzeck	    $address_map{$name} = $address;
125292bca398SDaniel Schwierzeck	}
125392bca398SDaniel Schwierzeck    }
125492bca398SDaniel Schwierzeck}
125592bca398SDaniel Schwierzeck
125692bca398SDaniel Schwierzecksub git_execute_cmd {
125792bca398SDaniel Schwierzeck    my ($cmd) = @_;
125892bca398SDaniel Schwierzeck    my @lines = ();
125992bca398SDaniel Schwierzeck
126092bca398SDaniel Schwierzeck    my $output = `$cmd`;
126192bca398SDaniel Schwierzeck    $output =~ s/^\s*//gm;
126292bca398SDaniel Schwierzeck    @lines = split("\n", $output);
126392bca398SDaniel Schwierzeck
126492bca398SDaniel Schwierzeck    return @lines;
126592bca398SDaniel Schwierzeck}
126692bca398SDaniel Schwierzeck
126792bca398SDaniel Schwierzecksub hg_execute_cmd {
126892bca398SDaniel Schwierzeck    my ($cmd) = @_;
126992bca398SDaniel Schwierzeck    my @lines = ();
127092bca398SDaniel Schwierzeck
127192bca398SDaniel Schwierzeck    my $output = `$cmd`;
127292bca398SDaniel Schwierzeck    @lines = split("\n", $output);
127392bca398SDaniel Schwierzeck
127492bca398SDaniel Schwierzeck    return @lines;
127592bca398SDaniel Schwierzeck}
127692bca398SDaniel Schwierzeck
127792bca398SDaniel Schwierzecksub extract_formatted_signatures {
127892bca398SDaniel Schwierzeck    my (@signature_lines) = @_;
127992bca398SDaniel Schwierzeck
128092bca398SDaniel Schwierzeck    my @type = @signature_lines;
128192bca398SDaniel Schwierzeck
128292bca398SDaniel Schwierzeck    s/\s*(.*):.*/$1/ for (@type);
128392bca398SDaniel Schwierzeck
128492bca398SDaniel Schwierzeck    # cut -f2- -d":"
128592bca398SDaniel Schwierzeck    s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
128692bca398SDaniel Schwierzeck
128792bca398SDaniel Schwierzeck## Reformat email addresses (with names) to avoid badly written signatures
128892bca398SDaniel Schwierzeck
128992bca398SDaniel Schwierzeck    foreach my $signer (@signature_lines) {
129092bca398SDaniel Schwierzeck	$signer = deduplicate_email($signer);
129192bca398SDaniel Schwierzeck    }
129292bca398SDaniel Schwierzeck
129392bca398SDaniel Schwierzeck    return (\@type, \@signature_lines);
129492bca398SDaniel Schwierzeck}
129592bca398SDaniel Schwierzeck
129692bca398SDaniel Schwierzecksub vcs_find_signers {
129792bca398SDaniel Schwierzeck    my ($cmd, $file) = @_;
129892bca398SDaniel Schwierzeck    my $commits;
129992bca398SDaniel Schwierzeck    my @lines = ();
130092bca398SDaniel Schwierzeck    my @signatures = ();
130192bca398SDaniel Schwierzeck    my @authors = ();
130292bca398SDaniel Schwierzeck    my @stats = ();
130392bca398SDaniel Schwierzeck
130492bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
130592bca398SDaniel Schwierzeck
130692bca398SDaniel Schwierzeck    my $pattern = $VCS_cmds{"commit_pattern"};
130792bca398SDaniel Schwierzeck    my $author_pattern = $VCS_cmds{"author_pattern"};
130892bca398SDaniel Schwierzeck    my $stat_pattern = $VCS_cmds{"stat_pattern"};
130992bca398SDaniel Schwierzeck
131092bca398SDaniel Schwierzeck    $stat_pattern =~ s/(\$\w+)/$1/eeg;		#interpolate $stat_pattern
131192bca398SDaniel Schwierzeck
131292bca398SDaniel Schwierzeck    $commits = grep(/$pattern/, @lines);	# of commits
131392bca398SDaniel Schwierzeck
131492bca398SDaniel Schwierzeck    @authors = grep(/$author_pattern/, @lines);
131592bca398SDaniel Schwierzeck    @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
131692bca398SDaniel Schwierzeck    @stats = grep(/$stat_pattern/, @lines);
131792bca398SDaniel Schwierzeck
131892bca398SDaniel Schwierzeck#    print("stats: <@stats>\n");
131992bca398SDaniel Schwierzeck
132092bca398SDaniel Schwierzeck    return (0, \@signatures, \@authors, \@stats) if !@signatures;
132192bca398SDaniel Schwierzeck
132292bca398SDaniel Schwierzeck    save_commits_by_author(@lines) if ($interactive);
132392bca398SDaniel Schwierzeck    save_commits_by_signer(@lines) if ($interactive);
132492bca398SDaniel Schwierzeck
132592bca398SDaniel Schwierzeck    if (!$email_git_penguin_chiefs) {
132692bca398SDaniel Schwierzeck	@signatures = grep(!/${penguin_chiefs}/i, @signatures);
132792bca398SDaniel Schwierzeck    }
132892bca398SDaniel Schwierzeck
132992bca398SDaniel Schwierzeck    my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
133092bca398SDaniel Schwierzeck    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
133192bca398SDaniel Schwierzeck
133292bca398SDaniel Schwierzeck    return ($commits, $signers_ref, $authors_ref, \@stats);
133392bca398SDaniel Schwierzeck}
133492bca398SDaniel Schwierzeck
133592bca398SDaniel Schwierzecksub vcs_find_author {
133692bca398SDaniel Schwierzeck    my ($cmd) = @_;
133792bca398SDaniel Schwierzeck    my @lines = ();
133892bca398SDaniel Schwierzeck
133992bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
134092bca398SDaniel Schwierzeck
134192bca398SDaniel Schwierzeck    if (!$email_git_penguin_chiefs) {
134292bca398SDaniel Schwierzeck	@lines = grep(!/${penguin_chiefs}/i, @lines);
134392bca398SDaniel Schwierzeck    }
134492bca398SDaniel Schwierzeck
134592bca398SDaniel Schwierzeck    return @lines if !@lines;
134692bca398SDaniel Schwierzeck
134792bca398SDaniel Schwierzeck    my @authors = ();
134892bca398SDaniel Schwierzeck    foreach my $line (@lines) {
134992bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
135092bca398SDaniel Schwierzeck	    my $author = $1;
135192bca398SDaniel Schwierzeck	    my ($name, $address) = parse_email($author);
135292bca398SDaniel Schwierzeck	    $author = format_email($name, $address, 1);
135392bca398SDaniel Schwierzeck	    push(@authors, $author);
135492bca398SDaniel Schwierzeck	}
135592bca398SDaniel Schwierzeck    }
135692bca398SDaniel Schwierzeck
135792bca398SDaniel Schwierzeck    save_commits_by_author(@lines) if ($interactive);
135892bca398SDaniel Schwierzeck    save_commits_by_signer(@lines) if ($interactive);
135992bca398SDaniel Schwierzeck
136092bca398SDaniel Schwierzeck    return @authors;
136192bca398SDaniel Schwierzeck}
136292bca398SDaniel Schwierzeck
136392bca398SDaniel Schwierzecksub vcs_save_commits {
136492bca398SDaniel Schwierzeck    my ($cmd) = @_;
136592bca398SDaniel Schwierzeck    my @lines = ();
136692bca398SDaniel Schwierzeck    my @commits = ();
136792bca398SDaniel Schwierzeck
136892bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
136992bca398SDaniel Schwierzeck
137092bca398SDaniel Schwierzeck    foreach my $line (@lines) {
137192bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
137292bca398SDaniel Schwierzeck	    push(@commits, $1);
137392bca398SDaniel Schwierzeck	}
137492bca398SDaniel Schwierzeck    }
137592bca398SDaniel Schwierzeck
137692bca398SDaniel Schwierzeck    return @commits;
137792bca398SDaniel Schwierzeck}
137892bca398SDaniel Schwierzeck
137992bca398SDaniel Schwierzecksub vcs_blame {
138092bca398SDaniel Schwierzeck    my ($file) = @_;
138192bca398SDaniel Schwierzeck    my $cmd;
138292bca398SDaniel Schwierzeck    my @commits = ();
138392bca398SDaniel Schwierzeck
138492bca398SDaniel Schwierzeck    return @commits if (!(-f $file));
138592bca398SDaniel Schwierzeck
138692bca398SDaniel Schwierzeck    if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
138792bca398SDaniel Schwierzeck	my @all_commits = ();
138892bca398SDaniel Schwierzeck
138992bca398SDaniel Schwierzeck	$cmd = $VCS_cmds{"blame_file_cmd"};
139092bca398SDaniel Schwierzeck	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
139192bca398SDaniel Schwierzeck	@all_commits = vcs_save_commits($cmd);
139292bca398SDaniel Schwierzeck
139392bca398SDaniel Schwierzeck	foreach my $file_range_diff (@range) {
139492bca398SDaniel Schwierzeck	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
139592bca398SDaniel Schwierzeck	    my $diff_file = $1;
139692bca398SDaniel Schwierzeck	    my $diff_start = $2;
139792bca398SDaniel Schwierzeck	    my $diff_length = $3;
139892bca398SDaniel Schwierzeck	    next if ("$file" ne "$diff_file");
139992bca398SDaniel Schwierzeck	    for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
140092bca398SDaniel Schwierzeck		push(@commits, $all_commits[$i]);
140192bca398SDaniel Schwierzeck	    }
140292bca398SDaniel Schwierzeck	}
140392bca398SDaniel Schwierzeck    } elsif (@range) {
140492bca398SDaniel Schwierzeck	foreach my $file_range_diff (@range) {
140592bca398SDaniel Schwierzeck	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
140692bca398SDaniel Schwierzeck	    my $diff_file = $1;
140792bca398SDaniel Schwierzeck	    my $diff_start = $2;
140892bca398SDaniel Schwierzeck	    my $diff_length = $3;
140992bca398SDaniel Schwierzeck	    next if ("$file" ne "$diff_file");
141092bca398SDaniel Schwierzeck	    $cmd = $VCS_cmds{"blame_range_cmd"};
141192bca398SDaniel Schwierzeck	    $cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
141292bca398SDaniel Schwierzeck	    push(@commits, vcs_save_commits($cmd));
141392bca398SDaniel Schwierzeck	}
141492bca398SDaniel Schwierzeck    } else {
141592bca398SDaniel Schwierzeck	$cmd = $VCS_cmds{"blame_file_cmd"};
141692bca398SDaniel Schwierzeck	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
141792bca398SDaniel Schwierzeck	@commits = vcs_save_commits($cmd);
141892bca398SDaniel Schwierzeck    }
141992bca398SDaniel Schwierzeck
142092bca398SDaniel Schwierzeck    foreach my $commit (@commits) {
142192bca398SDaniel Schwierzeck	$commit =~ s/^\^//g;
142292bca398SDaniel Schwierzeck    }
142392bca398SDaniel Schwierzeck
142492bca398SDaniel Schwierzeck    return @commits;
142592bca398SDaniel Schwierzeck}
142692bca398SDaniel Schwierzeck
142792bca398SDaniel Schwierzeckmy $printed_novcs = 0;
142892bca398SDaniel Schwierzecksub vcs_exists {
142992bca398SDaniel Schwierzeck    %VCS_cmds = %VCS_cmds_git;
143092bca398SDaniel Schwierzeck    return 1 if eval $VCS_cmds{"available"};
143192bca398SDaniel Schwierzeck    %VCS_cmds = %VCS_cmds_hg;
143292bca398SDaniel Schwierzeck    return 2 if eval $VCS_cmds{"available"};
143392bca398SDaniel Schwierzeck    %VCS_cmds = ();
143492bca398SDaniel Schwierzeck    if (!$printed_novcs) {
143592bca398SDaniel Schwierzeck	warn("$P: No supported VCS found.  Add --nogit to options?\n");
143692bca398SDaniel Schwierzeck	warn("Using a git repository produces better results.\n");
143792bca398SDaniel Schwierzeck	warn("Try Linus Torvalds' latest git repository using:\n");
143892bca398SDaniel Schwierzeck	warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
143992bca398SDaniel Schwierzeck	$printed_novcs = 1;
144092bca398SDaniel Schwierzeck    }
144192bca398SDaniel Schwierzeck    return 0;
144292bca398SDaniel Schwierzeck}
144392bca398SDaniel Schwierzeck
144492bca398SDaniel Schwierzecksub vcs_is_git {
144592bca398SDaniel Schwierzeck    vcs_exists();
144692bca398SDaniel Schwierzeck    return $vcs_used == 1;
144792bca398SDaniel Schwierzeck}
144892bca398SDaniel Schwierzeck
144992bca398SDaniel Schwierzecksub vcs_is_hg {
145092bca398SDaniel Schwierzeck    return $vcs_used == 2;
145192bca398SDaniel Schwierzeck}
145292bca398SDaniel Schwierzeck
145392bca398SDaniel Schwierzecksub interactive_get_maintainers {
145492bca398SDaniel Schwierzeck    my ($list_ref) = @_;
145592bca398SDaniel Schwierzeck    my @list = @$list_ref;
145692bca398SDaniel Schwierzeck
145792bca398SDaniel Schwierzeck    vcs_exists();
145892bca398SDaniel Schwierzeck
145992bca398SDaniel Schwierzeck    my %selected;
146092bca398SDaniel Schwierzeck    my %authored;
146192bca398SDaniel Schwierzeck    my %signed;
146292bca398SDaniel Schwierzeck    my $count = 0;
146392bca398SDaniel Schwierzeck    my $maintained = 0;
146492bca398SDaniel Schwierzeck    foreach my $entry (@list) {
146592bca398SDaniel Schwierzeck	$maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
146692bca398SDaniel Schwierzeck	$selected{$count} = 1;
146792bca398SDaniel Schwierzeck	$authored{$count} = 0;
146892bca398SDaniel Schwierzeck	$signed{$count} = 0;
146992bca398SDaniel Schwierzeck	$count++;
147092bca398SDaniel Schwierzeck    }
147192bca398SDaniel Schwierzeck
147292bca398SDaniel Schwierzeck    #menu loop
147392bca398SDaniel Schwierzeck    my $done = 0;
147492bca398SDaniel Schwierzeck    my $print_options = 0;
147592bca398SDaniel Schwierzeck    my $redraw = 1;
147692bca398SDaniel Schwierzeck    while (!$done) {
147792bca398SDaniel Schwierzeck	$count = 0;
147892bca398SDaniel Schwierzeck	if ($redraw) {
147992bca398SDaniel Schwierzeck	    printf STDERR "\n%1s %2s %-65s",
148092bca398SDaniel Schwierzeck			  "*", "#", "email/list and role:stats";
148192bca398SDaniel Schwierzeck	    if ($email_git ||
148292bca398SDaniel Schwierzeck		($email_git_fallback && !$maintained) ||
148392bca398SDaniel Schwierzeck		$email_git_blame) {
148492bca398SDaniel Schwierzeck		print STDERR "auth sign";
148592bca398SDaniel Schwierzeck	    }
148692bca398SDaniel Schwierzeck	    print STDERR "\n";
148792bca398SDaniel Schwierzeck	    foreach my $entry (@list) {
148892bca398SDaniel Schwierzeck		my $email = $entry->[0];
148992bca398SDaniel Schwierzeck		my $role = $entry->[1];
149092bca398SDaniel Schwierzeck		my $sel = "";
149192bca398SDaniel Schwierzeck		$sel = "*" if ($selected{$count});
149292bca398SDaniel Schwierzeck		my $commit_author = $commit_author_hash{$email};
149392bca398SDaniel Schwierzeck		my $commit_signer = $commit_signer_hash{$email};
149492bca398SDaniel Schwierzeck		my $authored = 0;
149592bca398SDaniel Schwierzeck		my $signed = 0;
149692bca398SDaniel Schwierzeck		$authored++ for (@{$commit_author});
149792bca398SDaniel Schwierzeck		$signed++ for (@{$commit_signer});
149892bca398SDaniel Schwierzeck		printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
149992bca398SDaniel Schwierzeck		printf STDERR "%4d %4d", $authored, $signed
150092bca398SDaniel Schwierzeck		    if ($authored > 0 || $signed > 0);
150192bca398SDaniel Schwierzeck		printf STDERR "\n     %s\n", $role;
150292bca398SDaniel Schwierzeck		if ($authored{$count}) {
150392bca398SDaniel Schwierzeck		    my $commit_author = $commit_author_hash{$email};
150492bca398SDaniel Schwierzeck		    foreach my $ref (@{$commit_author}) {
150592bca398SDaniel Schwierzeck			print STDERR "     Author: @{$ref}[1]\n";
150692bca398SDaniel Schwierzeck		    }
150792bca398SDaniel Schwierzeck		}
150892bca398SDaniel Schwierzeck		if ($signed{$count}) {
150992bca398SDaniel Schwierzeck		    my $commit_signer = $commit_signer_hash{$email};
151092bca398SDaniel Schwierzeck		    foreach my $ref (@{$commit_signer}) {
151192bca398SDaniel Schwierzeck			print STDERR "     @{$ref}[2]: @{$ref}[1]\n";
151292bca398SDaniel Schwierzeck		    }
151392bca398SDaniel Schwierzeck		}
151492bca398SDaniel Schwierzeck
151592bca398SDaniel Schwierzeck		$count++;
151692bca398SDaniel Schwierzeck	    }
151792bca398SDaniel Schwierzeck	}
151892bca398SDaniel Schwierzeck	my $date_ref = \$email_git_since;
151992bca398SDaniel Schwierzeck	$date_ref = \$email_hg_since if (vcs_is_hg());
152092bca398SDaniel Schwierzeck	if ($print_options) {
152192bca398SDaniel Schwierzeck	    $print_options = 0;
152292bca398SDaniel Schwierzeck	    if (vcs_exists()) {
152392bca398SDaniel Schwierzeck		print STDERR <<EOT
152492bca398SDaniel Schwierzeck
152592bca398SDaniel SchwierzeckVersion Control options:
152692bca398SDaniel Schwierzeckg  use git history      [$email_git]
152792bca398SDaniel Schwierzeckgf use git-fallback     [$email_git_fallback]
152892bca398SDaniel Schwierzeckb  use git blame        [$email_git_blame]
152992bca398SDaniel Schwierzeckbs use blame signatures [$email_git_blame_signatures]
153092bca398SDaniel Schwierzeckc# minimum commits      [$email_git_min_signatures]
153192bca398SDaniel Schwierzeck%# min percent          [$email_git_min_percent]
153292bca398SDaniel Schwierzeckd# history to use       [$$date_ref]
153392bca398SDaniel Schwierzeckx# max maintainers      [$email_git_max_maintainers]
153492bca398SDaniel Schwierzeckt  all signature types  [$email_git_all_signature_types]
153592bca398SDaniel Schwierzeckm  use .mailmap         [$email_use_mailmap]
153692bca398SDaniel SchwierzeckEOT
153792bca398SDaniel Schwierzeck	    }
153892bca398SDaniel Schwierzeck	    print STDERR <<EOT
153992bca398SDaniel Schwierzeck
154092bca398SDaniel SchwierzeckAdditional options:
154192bca398SDaniel Schwierzeck0  toggle all
154292bca398SDaniel Schwierzecktm toggle maintainers
154392bca398SDaniel Schwierzecktg toggle git entries
154492bca398SDaniel Schwierzecktl toggle open list entries
154592bca398SDaniel Schwierzeckts toggle subscriber list entries
154692bca398SDaniel Schwierzeckf  emails in file       [$file_emails]
154792bca398SDaniel Schwierzeckk  keywords in file     [$keywords]
154892bca398SDaniel Schwierzeckr  remove duplicates    [$email_remove_duplicates]
154992bca398SDaniel Schwierzeckp# pattern match depth  [$pattern_depth]
155092bca398SDaniel SchwierzeckEOT
155192bca398SDaniel Schwierzeck	}
155292bca398SDaniel Schwierzeck	print STDERR
155392bca398SDaniel Schwierzeck"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
155492bca398SDaniel Schwierzeck
155592bca398SDaniel Schwierzeck	my $input = <STDIN>;
155692bca398SDaniel Schwierzeck	chomp($input);
155792bca398SDaniel Schwierzeck
155892bca398SDaniel Schwierzeck	$redraw = 1;
155992bca398SDaniel Schwierzeck	my $rerun = 0;
156092bca398SDaniel Schwierzeck	my @wish = split(/[, ]+/, $input);
156192bca398SDaniel Schwierzeck	foreach my $nr (@wish) {
156292bca398SDaniel Schwierzeck	    $nr = lc($nr);
156392bca398SDaniel Schwierzeck	    my $sel = substr($nr, 0, 1);
156492bca398SDaniel Schwierzeck	    my $str = substr($nr, 1);
156592bca398SDaniel Schwierzeck	    my $val = 0;
156692bca398SDaniel Schwierzeck	    $val = $1 if $str =~ /^(\d+)$/;
156792bca398SDaniel Schwierzeck
156892bca398SDaniel Schwierzeck	    if ($sel eq "y") {
156992bca398SDaniel Schwierzeck		$interactive = 0;
157092bca398SDaniel Schwierzeck		$done = 1;
157192bca398SDaniel Schwierzeck		$output_rolestats = 0;
157292bca398SDaniel Schwierzeck		$output_roles = 0;
157392bca398SDaniel Schwierzeck		last;
157492bca398SDaniel Schwierzeck	    } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
157592bca398SDaniel Schwierzeck		$selected{$nr - 1} = !$selected{$nr - 1};
157692bca398SDaniel Schwierzeck	    } elsif ($sel eq "*" || $sel eq '^') {
157792bca398SDaniel Schwierzeck		my $toggle = 0;
157892bca398SDaniel Schwierzeck		$toggle = 1 if ($sel eq '*');
157992bca398SDaniel Schwierzeck		for (my $i = 0; $i < $count; $i++) {
158092bca398SDaniel Schwierzeck		    $selected{$i} = $toggle;
158192bca398SDaniel Schwierzeck		}
158292bca398SDaniel Schwierzeck	    } elsif ($sel eq "0") {
158392bca398SDaniel Schwierzeck		for (my $i = 0; $i < $count; $i++) {
158492bca398SDaniel Schwierzeck		    $selected{$i} = !$selected{$i};
158592bca398SDaniel Schwierzeck		}
158692bca398SDaniel Schwierzeck	    } elsif ($sel eq "t") {
158792bca398SDaniel Schwierzeck		if (lc($str) eq "m") {
158892bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
158992bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
159092bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
159192bca398SDaniel Schwierzeck		    }
159292bca398SDaniel Schwierzeck		} elsif (lc($str) eq "g") {
159392bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
159492bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
159592bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
159692bca398SDaniel Schwierzeck		    }
159792bca398SDaniel Schwierzeck		} elsif (lc($str) eq "l") {
159892bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
159992bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
160092bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(open list)/i);
160192bca398SDaniel Schwierzeck		    }
160292bca398SDaniel Schwierzeck		} elsif (lc($str) eq "s") {
160392bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
160492bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
160592bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(subscriber list)/i);
160692bca398SDaniel Schwierzeck		    }
160792bca398SDaniel Schwierzeck		}
160892bca398SDaniel Schwierzeck	    } elsif ($sel eq "a") {
160992bca398SDaniel Schwierzeck		if ($val > 0 && $val <= $count) {
161092bca398SDaniel Schwierzeck		    $authored{$val - 1} = !$authored{$val - 1};
161192bca398SDaniel Schwierzeck		} elsif ($str eq '*' || $str eq '^') {
161292bca398SDaniel Schwierzeck		    my $toggle = 0;
161392bca398SDaniel Schwierzeck		    $toggle = 1 if ($str eq '*');
161492bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
161592bca398SDaniel Schwierzeck			$authored{$i} = $toggle;
161692bca398SDaniel Schwierzeck		    }
161792bca398SDaniel Schwierzeck		}
161892bca398SDaniel Schwierzeck	    } elsif ($sel eq "s") {
161992bca398SDaniel Schwierzeck		if ($val > 0 && $val <= $count) {
162092bca398SDaniel Schwierzeck		    $signed{$val - 1} = !$signed{$val - 1};
162192bca398SDaniel Schwierzeck		} elsif ($str eq '*' || $str eq '^') {
162292bca398SDaniel Schwierzeck		    my $toggle = 0;
162392bca398SDaniel Schwierzeck		    $toggle = 1 if ($str eq '*');
162492bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
162592bca398SDaniel Schwierzeck			$signed{$i} = $toggle;
162692bca398SDaniel Schwierzeck		    }
162792bca398SDaniel Schwierzeck		}
162892bca398SDaniel Schwierzeck	    } elsif ($sel eq "o") {
162992bca398SDaniel Schwierzeck		$print_options = 1;
163092bca398SDaniel Schwierzeck		$redraw = 1;
163192bca398SDaniel Schwierzeck	    } elsif ($sel eq "g") {
163292bca398SDaniel Schwierzeck		if ($str eq "f") {
163392bca398SDaniel Schwierzeck		    bool_invert(\$email_git_fallback);
163492bca398SDaniel Schwierzeck		} else {
163592bca398SDaniel Schwierzeck		    bool_invert(\$email_git);
163692bca398SDaniel Schwierzeck		}
163792bca398SDaniel Schwierzeck		$rerun = 1;
163892bca398SDaniel Schwierzeck	    } elsif ($sel eq "b") {
163992bca398SDaniel Schwierzeck		if ($str eq "s") {
164092bca398SDaniel Schwierzeck		    bool_invert(\$email_git_blame_signatures);
164192bca398SDaniel Schwierzeck		} else {
164292bca398SDaniel Schwierzeck		    bool_invert(\$email_git_blame);
164392bca398SDaniel Schwierzeck		}
164492bca398SDaniel Schwierzeck		$rerun = 1;
164592bca398SDaniel Schwierzeck	    } elsif ($sel eq "c") {
164692bca398SDaniel Schwierzeck		if ($val > 0) {
164792bca398SDaniel Schwierzeck		    $email_git_min_signatures = $val;
164892bca398SDaniel Schwierzeck		    $rerun = 1;
164992bca398SDaniel Schwierzeck		}
165092bca398SDaniel Schwierzeck	    } elsif ($sel eq "x") {
165192bca398SDaniel Schwierzeck		if ($val > 0) {
165292bca398SDaniel Schwierzeck		    $email_git_max_maintainers = $val;
165392bca398SDaniel Schwierzeck		    $rerun = 1;
165492bca398SDaniel Schwierzeck		}
165592bca398SDaniel Schwierzeck	    } elsif ($sel eq "%") {
165692bca398SDaniel Schwierzeck		if ($str ne "" && $val >= 0) {
165792bca398SDaniel Schwierzeck		    $email_git_min_percent = $val;
165892bca398SDaniel Schwierzeck		    $rerun = 1;
165992bca398SDaniel Schwierzeck		}
166092bca398SDaniel Schwierzeck	    } elsif ($sel eq "d") {
166192bca398SDaniel Schwierzeck		if (vcs_is_git()) {
166292bca398SDaniel Schwierzeck		    $email_git_since = $str;
166392bca398SDaniel Schwierzeck		} elsif (vcs_is_hg()) {
166492bca398SDaniel Schwierzeck		    $email_hg_since = $str;
166592bca398SDaniel Schwierzeck		}
166692bca398SDaniel Schwierzeck		$rerun = 1;
166792bca398SDaniel Schwierzeck	    } elsif ($sel eq "t") {
166892bca398SDaniel Schwierzeck		bool_invert(\$email_git_all_signature_types);
166992bca398SDaniel Schwierzeck		$rerun = 1;
167092bca398SDaniel Schwierzeck	    } elsif ($sel eq "f") {
167192bca398SDaniel Schwierzeck		bool_invert(\$file_emails);
167292bca398SDaniel Schwierzeck		$rerun = 1;
167392bca398SDaniel Schwierzeck	    } elsif ($sel eq "r") {
167492bca398SDaniel Schwierzeck		bool_invert(\$email_remove_duplicates);
167592bca398SDaniel Schwierzeck		$rerun = 1;
167692bca398SDaniel Schwierzeck	    } elsif ($sel eq "m") {
167792bca398SDaniel Schwierzeck		bool_invert(\$email_use_mailmap);
167892bca398SDaniel Schwierzeck		read_mailmap();
167992bca398SDaniel Schwierzeck		$rerun = 1;
168092bca398SDaniel Schwierzeck	    } elsif ($sel eq "k") {
168192bca398SDaniel Schwierzeck		bool_invert(\$keywords);
168292bca398SDaniel Schwierzeck		$rerun = 1;
168392bca398SDaniel Schwierzeck	    } elsif ($sel eq "p") {
168492bca398SDaniel Schwierzeck		if ($str ne "" && $val >= 0) {
168592bca398SDaniel Schwierzeck		    $pattern_depth = $val;
168692bca398SDaniel Schwierzeck		    $rerun = 1;
168792bca398SDaniel Schwierzeck		}
168892bca398SDaniel Schwierzeck	    } elsif ($sel eq "h" || $sel eq "?") {
168992bca398SDaniel Schwierzeck		print STDERR <<EOT
169092bca398SDaniel Schwierzeck
169192bca398SDaniel SchwierzeckInteractive mode allows you to select the various maintainers, submitters,
169292bca398SDaniel Schwierzeckcommit signers and mailing lists that could be CC'd on a patch.
169392bca398SDaniel Schwierzeck
169492bca398SDaniel SchwierzeckAny *'d entry is selected.
169592bca398SDaniel Schwierzeck
169692bca398SDaniel SchwierzeckIf you have git or hg installed, you can choose to summarize the commit
169792bca398SDaniel Schwierzeckhistory of files in the patch.  Also, each line of the current file can
169892bca398SDaniel Schwierzeckbe matched to its commit author and that commits signers with blame.
169992bca398SDaniel Schwierzeck
170092bca398SDaniel SchwierzeckVarious knobs exist to control the length of time for active commit
170192bca398SDaniel Schwierzecktracking, the maximum number of commit authors and signers to add,
170292bca398SDaniel Schwierzeckand such.
170392bca398SDaniel Schwierzeck
170492bca398SDaniel SchwierzeckEnter selections at the prompt until you are satisfied that the selected
170592bca398SDaniel Schwierzeckmaintainers are appropriate.  You may enter multiple selections separated
170692bca398SDaniel Schwierzeckby either commas or spaces.
170792bca398SDaniel Schwierzeck
170892bca398SDaniel SchwierzeckEOT
170992bca398SDaniel Schwierzeck	    } else {
171092bca398SDaniel Schwierzeck		print STDERR "invalid option: '$nr'\n";
171192bca398SDaniel Schwierzeck		$redraw = 0;
171292bca398SDaniel Schwierzeck	    }
171392bca398SDaniel Schwierzeck	}
171492bca398SDaniel Schwierzeck	if ($rerun) {
171592bca398SDaniel Schwierzeck	    print STDERR "git-blame can be very slow, please have patience..."
171692bca398SDaniel Schwierzeck		if ($email_git_blame);
171792bca398SDaniel Schwierzeck	    goto &get_maintainers;
171892bca398SDaniel Schwierzeck	}
171992bca398SDaniel Schwierzeck    }
172092bca398SDaniel Schwierzeck
172192bca398SDaniel Schwierzeck    #drop not selected entries
172292bca398SDaniel Schwierzeck    $count = 0;
172392bca398SDaniel Schwierzeck    my @new_emailto = ();
172492bca398SDaniel Schwierzeck    foreach my $entry (@list) {
172592bca398SDaniel Schwierzeck	if ($selected{$count}) {
172692bca398SDaniel Schwierzeck	    push(@new_emailto, $list[$count]);
172792bca398SDaniel Schwierzeck	}
172892bca398SDaniel Schwierzeck	$count++;
172992bca398SDaniel Schwierzeck    }
173092bca398SDaniel Schwierzeck    return @new_emailto;
173192bca398SDaniel Schwierzeck}
173292bca398SDaniel Schwierzeck
173392bca398SDaniel Schwierzecksub bool_invert {
173492bca398SDaniel Schwierzeck    my ($bool_ref) = @_;
173592bca398SDaniel Schwierzeck
173692bca398SDaniel Schwierzeck    if ($$bool_ref) {
173792bca398SDaniel Schwierzeck	$$bool_ref = 0;
173892bca398SDaniel Schwierzeck    } else {
173992bca398SDaniel Schwierzeck	$$bool_ref = 1;
174092bca398SDaniel Schwierzeck    }
174192bca398SDaniel Schwierzeck}
174292bca398SDaniel Schwierzeck
174392bca398SDaniel Schwierzecksub deduplicate_email {
174492bca398SDaniel Schwierzeck    my ($email) = @_;
174592bca398SDaniel Schwierzeck
174692bca398SDaniel Schwierzeck    my $matched = 0;
174792bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($email);
174892bca398SDaniel Schwierzeck    $email = format_email($name, $address, 1);
174992bca398SDaniel Schwierzeck    $email = mailmap_email($email);
175092bca398SDaniel Schwierzeck
175192bca398SDaniel Schwierzeck    return $email if (!$email_remove_duplicates);
175292bca398SDaniel Schwierzeck
175392bca398SDaniel Schwierzeck    ($name, $address) = parse_email($email);
175492bca398SDaniel Schwierzeck
175592bca398SDaniel Schwierzeck    if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
175692bca398SDaniel Schwierzeck	$name = $deduplicate_name_hash{lc($name)}->[0];
175792bca398SDaniel Schwierzeck	$address = $deduplicate_name_hash{lc($name)}->[1];
175892bca398SDaniel Schwierzeck	$matched = 1;
175992bca398SDaniel Schwierzeck    } elsif ($deduplicate_address_hash{lc($address)}) {
176092bca398SDaniel Schwierzeck	$name = $deduplicate_address_hash{lc($address)}->[0];
176192bca398SDaniel Schwierzeck	$address = $deduplicate_address_hash{lc($address)}->[1];
176292bca398SDaniel Schwierzeck	$matched = 1;
176392bca398SDaniel Schwierzeck    }
176492bca398SDaniel Schwierzeck    if (!$matched) {
176592bca398SDaniel Schwierzeck	$deduplicate_name_hash{lc($name)} = [ $name, $address ];
176692bca398SDaniel Schwierzeck	$deduplicate_address_hash{lc($address)} = [ $name, $address ];
176792bca398SDaniel Schwierzeck    }
176892bca398SDaniel Schwierzeck    $email = format_email($name, $address, 1);
176992bca398SDaniel Schwierzeck    $email = mailmap_email($email);
177092bca398SDaniel Schwierzeck    return $email;
177192bca398SDaniel Schwierzeck}
177292bca398SDaniel Schwierzeck
177392bca398SDaniel Schwierzecksub save_commits_by_author {
177492bca398SDaniel Schwierzeck    my (@lines) = @_;
177592bca398SDaniel Schwierzeck
177692bca398SDaniel Schwierzeck    my @authors = ();
177792bca398SDaniel Schwierzeck    my @commits = ();
177892bca398SDaniel Schwierzeck    my @subjects = ();
177992bca398SDaniel Schwierzeck
178092bca398SDaniel Schwierzeck    foreach my $line (@lines) {
178192bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
178292bca398SDaniel Schwierzeck	    my $author = $1;
178392bca398SDaniel Schwierzeck	    $author = deduplicate_email($author);
178492bca398SDaniel Schwierzeck	    push(@authors, $author);
178592bca398SDaniel Schwierzeck	}
178692bca398SDaniel Schwierzeck	push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
178792bca398SDaniel Schwierzeck	push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
178892bca398SDaniel Schwierzeck    }
178992bca398SDaniel Schwierzeck
179092bca398SDaniel Schwierzeck    for (my $i = 0; $i < @authors; $i++) {
179192bca398SDaniel Schwierzeck	my $exists = 0;
179292bca398SDaniel Schwierzeck	foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
179392bca398SDaniel Schwierzeck	    if (@{$ref}[0] eq $commits[$i] &&
179492bca398SDaniel Schwierzeck		@{$ref}[1] eq $subjects[$i]) {
179592bca398SDaniel Schwierzeck		$exists = 1;
179692bca398SDaniel Schwierzeck		last;
179792bca398SDaniel Schwierzeck	    }
179892bca398SDaniel Schwierzeck	}
179992bca398SDaniel Schwierzeck	if (!$exists) {
180092bca398SDaniel Schwierzeck	    push(@{$commit_author_hash{$authors[$i]}},
180192bca398SDaniel Schwierzeck		 [ ($commits[$i], $subjects[$i]) ]);
180292bca398SDaniel Schwierzeck	}
180392bca398SDaniel Schwierzeck    }
180492bca398SDaniel Schwierzeck}
180592bca398SDaniel Schwierzeck
180692bca398SDaniel Schwierzecksub save_commits_by_signer {
180792bca398SDaniel Schwierzeck    my (@lines) = @_;
180892bca398SDaniel Schwierzeck
180992bca398SDaniel Schwierzeck    my $commit = "";
181092bca398SDaniel Schwierzeck    my $subject = "";
181192bca398SDaniel Schwierzeck
181292bca398SDaniel Schwierzeck    foreach my $line (@lines) {
181392bca398SDaniel Schwierzeck	$commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
181492bca398SDaniel Schwierzeck	$subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
181592bca398SDaniel Schwierzeck	if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
181692bca398SDaniel Schwierzeck	    my @signatures = ($line);
181792bca398SDaniel Schwierzeck	    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
181892bca398SDaniel Schwierzeck	    my @types = @$types_ref;
181992bca398SDaniel Schwierzeck	    my @signers = @$signers_ref;
182092bca398SDaniel Schwierzeck
182192bca398SDaniel Schwierzeck	    my $type = $types[0];
182292bca398SDaniel Schwierzeck	    my $signer = $signers[0];
182392bca398SDaniel Schwierzeck
182492bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
182592bca398SDaniel Schwierzeck
182692bca398SDaniel Schwierzeck	    my $exists = 0;
182792bca398SDaniel Schwierzeck	    foreach my $ref(@{$commit_signer_hash{$signer}}) {
182892bca398SDaniel Schwierzeck		if (@{$ref}[0] eq $commit &&
182992bca398SDaniel Schwierzeck		    @{$ref}[1] eq $subject &&
183092bca398SDaniel Schwierzeck		    @{$ref}[2] eq $type) {
183192bca398SDaniel Schwierzeck		    $exists = 1;
183292bca398SDaniel Schwierzeck		    last;
183392bca398SDaniel Schwierzeck		}
183492bca398SDaniel Schwierzeck	    }
183592bca398SDaniel Schwierzeck	    if (!$exists) {
183692bca398SDaniel Schwierzeck		push(@{$commit_signer_hash{$signer}},
183792bca398SDaniel Schwierzeck		     [ ($commit, $subject, $type) ]);
183892bca398SDaniel Schwierzeck	    }
183992bca398SDaniel Schwierzeck	}
184092bca398SDaniel Schwierzeck    }
184192bca398SDaniel Schwierzeck}
184292bca398SDaniel Schwierzeck
184392bca398SDaniel Schwierzecksub vcs_assign {
184492bca398SDaniel Schwierzeck    my ($role, $divisor, @lines) = @_;
184592bca398SDaniel Schwierzeck
184692bca398SDaniel Schwierzeck    my %hash;
184792bca398SDaniel Schwierzeck    my $count = 0;
184892bca398SDaniel Schwierzeck
184992bca398SDaniel Schwierzeck    return if (@lines <= 0);
185092bca398SDaniel Schwierzeck
185192bca398SDaniel Schwierzeck    if ($divisor <= 0) {
185292bca398SDaniel Schwierzeck	warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
185392bca398SDaniel Schwierzeck	$divisor = 1;
185492bca398SDaniel Schwierzeck    }
185592bca398SDaniel Schwierzeck
185692bca398SDaniel Schwierzeck    @lines = mailmap(@lines);
185792bca398SDaniel Schwierzeck
185892bca398SDaniel Schwierzeck    return if (@lines <= 0);
185992bca398SDaniel Schwierzeck
186092bca398SDaniel Schwierzeck    @lines = sort(@lines);
186192bca398SDaniel Schwierzeck
186292bca398SDaniel Schwierzeck    # uniq -c
186392bca398SDaniel Schwierzeck    $hash{$_}++ for @lines;
186492bca398SDaniel Schwierzeck
186592bca398SDaniel Schwierzeck    # sort -rn
186692bca398SDaniel Schwierzeck    foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
186792bca398SDaniel Schwierzeck	my $sign_offs = $hash{$line};
186892bca398SDaniel Schwierzeck	my $percent = $sign_offs * 100 / $divisor;
186992bca398SDaniel Schwierzeck
187092bca398SDaniel Schwierzeck	$percent = 100 if ($percent > 100);
187192bca398SDaniel Schwierzeck	$count++;
187292bca398SDaniel Schwierzeck	last if ($sign_offs < $email_git_min_signatures ||
187392bca398SDaniel Schwierzeck		 $count > $email_git_max_maintainers ||
187492bca398SDaniel Schwierzeck		 $percent < $email_git_min_percent);
187592bca398SDaniel Schwierzeck	push_email_address($line, '');
187692bca398SDaniel Schwierzeck	if ($output_rolestats) {
187792bca398SDaniel Schwierzeck	    my $fmt_percent = sprintf("%.0f", $percent);
187892bca398SDaniel Schwierzeck	    add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
187992bca398SDaniel Schwierzeck	} else {
188092bca398SDaniel Schwierzeck	    add_role($line, $role);
188192bca398SDaniel Schwierzeck	}
188292bca398SDaniel Schwierzeck    }
188392bca398SDaniel Schwierzeck}
188492bca398SDaniel Schwierzeck
188592bca398SDaniel Schwierzecksub vcs_file_signoffs {
188692bca398SDaniel Schwierzeck    my ($file) = @_;
188792bca398SDaniel Schwierzeck
188892bca398SDaniel Schwierzeck    my $authors_ref;
188992bca398SDaniel Schwierzeck    my $signers_ref;
189092bca398SDaniel Schwierzeck    my $stats_ref;
189192bca398SDaniel Schwierzeck    my @authors = ();
189292bca398SDaniel Schwierzeck    my @signers = ();
189392bca398SDaniel Schwierzeck    my @stats = ();
189492bca398SDaniel Schwierzeck    my $commits;
189592bca398SDaniel Schwierzeck
189692bca398SDaniel Schwierzeck    $vcs_used = vcs_exists();
189792bca398SDaniel Schwierzeck    return if (!$vcs_used);
189892bca398SDaniel Schwierzeck
189992bca398SDaniel Schwierzeck    my $cmd = $VCS_cmds{"find_signers_cmd"};
190092bca398SDaniel Schwierzeck    $cmd =~ s/(\$\w+)/$1/eeg;		# interpolate $cmd
190192bca398SDaniel Schwierzeck
190292bca398SDaniel Schwierzeck    ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
190392bca398SDaniel Schwierzeck
190492bca398SDaniel Schwierzeck    @signers = @{$signers_ref} if defined $signers_ref;
190592bca398SDaniel Schwierzeck    @authors = @{$authors_ref} if defined $authors_ref;
190692bca398SDaniel Schwierzeck    @stats = @{$stats_ref} if defined $stats_ref;
190792bca398SDaniel Schwierzeck
190892bca398SDaniel Schwierzeck#    print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
190992bca398SDaniel Schwierzeck
191092bca398SDaniel Schwierzeck    foreach my $signer (@signers) {
191192bca398SDaniel Schwierzeck	$signer = deduplicate_email($signer);
191292bca398SDaniel Schwierzeck    }
191392bca398SDaniel Schwierzeck
191492bca398SDaniel Schwierzeck    vcs_assign("commit_signer", $commits, @signers);
191592bca398SDaniel Schwierzeck    vcs_assign("authored", $commits, @authors);
191692bca398SDaniel Schwierzeck    if ($#authors == $#stats) {
191792bca398SDaniel Schwierzeck	my $stat_pattern = $VCS_cmds{"stat_pattern"};
191892bca398SDaniel Schwierzeck	$stat_pattern =~ s/(\$\w+)/$1/eeg;	#interpolate $stat_pattern
191992bca398SDaniel Schwierzeck
192092bca398SDaniel Schwierzeck	my $added = 0;
192192bca398SDaniel Schwierzeck	my $deleted = 0;
192292bca398SDaniel Schwierzeck	for (my $i = 0; $i <= $#stats; $i++) {
192392bca398SDaniel Schwierzeck	    if ($stats[$i] =~ /$stat_pattern/) {
192492bca398SDaniel Schwierzeck		$added += $1;
192592bca398SDaniel Schwierzeck		$deleted += $2;
192692bca398SDaniel Schwierzeck	    }
192792bca398SDaniel Schwierzeck	}
192892bca398SDaniel Schwierzeck	my @tmp_authors = uniq(@authors);
192992bca398SDaniel Schwierzeck	foreach my $author (@tmp_authors) {
193092bca398SDaniel Schwierzeck	    $author = deduplicate_email($author);
193192bca398SDaniel Schwierzeck	}
193292bca398SDaniel Schwierzeck	@tmp_authors = uniq(@tmp_authors);
193392bca398SDaniel Schwierzeck	my @list_added = ();
193492bca398SDaniel Schwierzeck	my @list_deleted = ();
193592bca398SDaniel Schwierzeck	foreach my $author (@tmp_authors) {
193692bca398SDaniel Schwierzeck	    my $auth_added = 0;
193792bca398SDaniel Schwierzeck	    my $auth_deleted = 0;
193892bca398SDaniel Schwierzeck	    for (my $i = 0; $i <= $#stats; $i++) {
193992bca398SDaniel Schwierzeck		if ($author eq deduplicate_email($authors[$i]) &&
194092bca398SDaniel Schwierzeck		    $stats[$i] =~ /$stat_pattern/) {
194192bca398SDaniel Schwierzeck		    $auth_added += $1;
194292bca398SDaniel Schwierzeck		    $auth_deleted += $2;
194392bca398SDaniel Schwierzeck		}
194492bca398SDaniel Schwierzeck	    }
194592bca398SDaniel Schwierzeck	    for (my $i = 0; $i < $auth_added; $i++) {
194692bca398SDaniel Schwierzeck		push(@list_added, $author);
194792bca398SDaniel Schwierzeck	    }
194892bca398SDaniel Schwierzeck	    for (my $i = 0; $i < $auth_deleted; $i++) {
194992bca398SDaniel Schwierzeck		push(@list_deleted, $author);
195092bca398SDaniel Schwierzeck	    }
195192bca398SDaniel Schwierzeck	}
195292bca398SDaniel Schwierzeck	vcs_assign("added_lines", $added, @list_added);
195392bca398SDaniel Schwierzeck	vcs_assign("removed_lines", $deleted, @list_deleted);
195492bca398SDaniel Schwierzeck    }
195592bca398SDaniel Schwierzeck}
195692bca398SDaniel Schwierzeck
195792bca398SDaniel Schwierzecksub vcs_file_blame {
195892bca398SDaniel Schwierzeck    my ($file) = @_;
195992bca398SDaniel Schwierzeck
196092bca398SDaniel Schwierzeck    my @signers = ();
196192bca398SDaniel Schwierzeck    my @all_commits = ();
196292bca398SDaniel Schwierzeck    my @commits = ();
196392bca398SDaniel Schwierzeck    my $total_commits;
196492bca398SDaniel Schwierzeck    my $total_lines;
196592bca398SDaniel Schwierzeck
196692bca398SDaniel Schwierzeck    $vcs_used = vcs_exists();
196792bca398SDaniel Schwierzeck    return if (!$vcs_used);
196892bca398SDaniel Schwierzeck
196992bca398SDaniel Schwierzeck    @all_commits = vcs_blame($file);
197092bca398SDaniel Schwierzeck    @commits = uniq(@all_commits);
197192bca398SDaniel Schwierzeck    $total_commits = @commits;
197292bca398SDaniel Schwierzeck    $total_lines = @all_commits;
197392bca398SDaniel Schwierzeck
197492bca398SDaniel Schwierzeck    if ($email_git_blame_signatures) {
197592bca398SDaniel Schwierzeck	if (vcs_is_hg()) {
197692bca398SDaniel Schwierzeck	    my $commit_count;
197792bca398SDaniel Schwierzeck	    my $commit_authors_ref;
197892bca398SDaniel Schwierzeck	    my $commit_signers_ref;
197992bca398SDaniel Schwierzeck	    my $stats_ref;
198092bca398SDaniel Schwierzeck	    my @commit_authors = ();
198192bca398SDaniel Schwierzeck	    my @commit_signers = ();
198292bca398SDaniel Schwierzeck	    my $commit = join(" -r ", @commits);
198392bca398SDaniel Schwierzeck	    my $cmd;
198492bca398SDaniel Schwierzeck
198592bca398SDaniel Schwierzeck	    $cmd = $VCS_cmds{"find_commit_signers_cmd"};
198692bca398SDaniel Schwierzeck	    $cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
198792bca398SDaniel Schwierzeck
198892bca398SDaniel Schwierzeck	    ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
198992bca398SDaniel Schwierzeck	    @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
199092bca398SDaniel Schwierzeck	    @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
199192bca398SDaniel Schwierzeck
199292bca398SDaniel Schwierzeck	    push(@signers, @commit_signers);
199392bca398SDaniel Schwierzeck	} else {
199492bca398SDaniel Schwierzeck	    foreach my $commit (@commits) {
199592bca398SDaniel Schwierzeck		my $commit_count;
199692bca398SDaniel Schwierzeck		my $commit_authors_ref;
199792bca398SDaniel Schwierzeck		my $commit_signers_ref;
199892bca398SDaniel Schwierzeck		my $stats_ref;
199992bca398SDaniel Schwierzeck		my @commit_authors = ();
200092bca398SDaniel Schwierzeck		my @commit_signers = ();
200192bca398SDaniel Schwierzeck		my $cmd;
200292bca398SDaniel Schwierzeck
200392bca398SDaniel Schwierzeck		$cmd = $VCS_cmds{"find_commit_signers_cmd"};
200492bca398SDaniel Schwierzeck		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
200592bca398SDaniel Schwierzeck
200692bca398SDaniel Schwierzeck		($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
200792bca398SDaniel Schwierzeck		@commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
200892bca398SDaniel Schwierzeck		@commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
200992bca398SDaniel Schwierzeck
201092bca398SDaniel Schwierzeck		push(@signers, @commit_signers);
201192bca398SDaniel Schwierzeck	    }
201292bca398SDaniel Schwierzeck	}
201392bca398SDaniel Schwierzeck    }
201492bca398SDaniel Schwierzeck
201592bca398SDaniel Schwierzeck    if ($from_filename) {
201692bca398SDaniel Schwierzeck	if ($output_rolestats) {
201792bca398SDaniel Schwierzeck	    my @blame_signers;
201892bca398SDaniel Schwierzeck	    if (vcs_is_hg()) {{		# Double brace for last exit
201992bca398SDaniel Schwierzeck		my $commit_count;
202092bca398SDaniel Schwierzeck		my @commit_signers = ();
202192bca398SDaniel Schwierzeck		@commits = uniq(@commits);
202292bca398SDaniel Schwierzeck		@commits = sort(@commits);
202392bca398SDaniel Schwierzeck		my $commit = join(" -r ", @commits);
202492bca398SDaniel Schwierzeck		my $cmd;
202592bca398SDaniel Schwierzeck
202692bca398SDaniel Schwierzeck		$cmd = $VCS_cmds{"find_commit_author_cmd"};
202792bca398SDaniel Schwierzeck		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
202892bca398SDaniel Schwierzeck
202992bca398SDaniel Schwierzeck		my @lines = ();
203092bca398SDaniel Schwierzeck
203192bca398SDaniel Schwierzeck		@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
203292bca398SDaniel Schwierzeck
203392bca398SDaniel Schwierzeck		if (!$email_git_penguin_chiefs) {
203492bca398SDaniel Schwierzeck		    @lines = grep(!/${penguin_chiefs}/i, @lines);
203592bca398SDaniel Schwierzeck		}
203692bca398SDaniel Schwierzeck
203792bca398SDaniel Schwierzeck		last if !@lines;
203892bca398SDaniel Schwierzeck
203992bca398SDaniel Schwierzeck		my @authors = ();
204092bca398SDaniel Schwierzeck		foreach my $line (@lines) {
204192bca398SDaniel Schwierzeck		    if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
204292bca398SDaniel Schwierzeck			my $author = $1;
204392bca398SDaniel Schwierzeck			$author = deduplicate_email($author);
204492bca398SDaniel Schwierzeck			push(@authors, $author);
204592bca398SDaniel Schwierzeck		    }
204692bca398SDaniel Schwierzeck		}
204792bca398SDaniel Schwierzeck
204892bca398SDaniel Schwierzeck		save_commits_by_author(@lines) if ($interactive);
204992bca398SDaniel Schwierzeck		save_commits_by_signer(@lines) if ($interactive);
205092bca398SDaniel Schwierzeck
205192bca398SDaniel Schwierzeck		push(@signers, @authors);
205292bca398SDaniel Schwierzeck	    }}
205392bca398SDaniel Schwierzeck	    else {
205492bca398SDaniel Schwierzeck		foreach my $commit (@commits) {
205592bca398SDaniel Schwierzeck		    my $i;
205692bca398SDaniel Schwierzeck		    my $cmd = $VCS_cmds{"find_commit_author_cmd"};
205792bca398SDaniel Schwierzeck		    $cmd =~ s/(\$\w+)/$1/eeg;	#interpolate $cmd
205892bca398SDaniel Schwierzeck		    my @author = vcs_find_author($cmd);
205992bca398SDaniel Schwierzeck		    next if !@author;
206092bca398SDaniel Schwierzeck
206192bca398SDaniel Schwierzeck		    my $formatted_author = deduplicate_email($author[0]);
206292bca398SDaniel Schwierzeck
206392bca398SDaniel Schwierzeck		    my $count = grep(/$commit/, @all_commits);
206492bca398SDaniel Schwierzeck		    for ($i = 0; $i < $count ; $i++) {
206592bca398SDaniel Schwierzeck			push(@blame_signers, $formatted_author);
206692bca398SDaniel Schwierzeck		    }
206792bca398SDaniel Schwierzeck		}
206892bca398SDaniel Schwierzeck	    }
206992bca398SDaniel Schwierzeck	    if (@blame_signers) {
207092bca398SDaniel Schwierzeck		vcs_assign("authored lines", $total_lines, @blame_signers);
207192bca398SDaniel Schwierzeck	    }
207292bca398SDaniel Schwierzeck	}
207392bca398SDaniel Schwierzeck	foreach my $signer (@signers) {
207492bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
207592bca398SDaniel Schwierzeck	}
207692bca398SDaniel Schwierzeck	vcs_assign("commits", $total_commits, @signers);
207792bca398SDaniel Schwierzeck    } else {
207892bca398SDaniel Schwierzeck	foreach my $signer (@signers) {
207992bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
208092bca398SDaniel Schwierzeck	}
208192bca398SDaniel Schwierzeck	vcs_assign("modified commits", $total_commits, @signers);
208292bca398SDaniel Schwierzeck    }
208392bca398SDaniel Schwierzeck}
208492bca398SDaniel Schwierzeck
208592bca398SDaniel Schwierzecksub uniq {
208692bca398SDaniel Schwierzeck    my (@parms) = @_;
208792bca398SDaniel Schwierzeck
208892bca398SDaniel Schwierzeck    my %saw;
208992bca398SDaniel Schwierzeck    @parms = grep(!$saw{$_}++, @parms);
209092bca398SDaniel Schwierzeck    return @parms;
209192bca398SDaniel Schwierzeck}
209292bca398SDaniel Schwierzeck
209392bca398SDaniel Schwierzecksub sort_and_uniq {
209492bca398SDaniel Schwierzeck    my (@parms) = @_;
209592bca398SDaniel Schwierzeck
209692bca398SDaniel Schwierzeck    my %saw;
209792bca398SDaniel Schwierzeck    @parms = sort @parms;
209892bca398SDaniel Schwierzeck    @parms = grep(!$saw{$_}++, @parms);
209992bca398SDaniel Schwierzeck    return @parms;
210092bca398SDaniel Schwierzeck}
210192bca398SDaniel Schwierzeck
210292bca398SDaniel Schwierzecksub clean_file_emails {
210392bca398SDaniel Schwierzeck    my (@file_emails) = @_;
210492bca398SDaniel Schwierzeck    my @fmt_emails = ();
210592bca398SDaniel Schwierzeck
210692bca398SDaniel Schwierzeck    foreach my $email (@file_emails) {
210792bca398SDaniel Schwierzeck	$email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
210892bca398SDaniel Schwierzeck	my ($name, $address) = parse_email($email);
210992bca398SDaniel Schwierzeck	if ($name eq '"[,\.]"') {
211092bca398SDaniel Schwierzeck	    $name = "";
211192bca398SDaniel Schwierzeck	}
211292bca398SDaniel Schwierzeck
211392bca398SDaniel Schwierzeck	my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
211492bca398SDaniel Schwierzeck	if (@nw > 2) {
211592bca398SDaniel Schwierzeck	    my $first = $nw[@nw - 3];
211692bca398SDaniel Schwierzeck	    my $middle = $nw[@nw - 2];
211792bca398SDaniel Schwierzeck	    my $last = $nw[@nw - 1];
211892bca398SDaniel Schwierzeck
211992bca398SDaniel Schwierzeck	    if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
212092bca398SDaniel Schwierzeck		 (length($first) == 2 && substr($first, -1) eq ".")) ||
212192bca398SDaniel Schwierzeck		(length($middle) == 1 ||
212292bca398SDaniel Schwierzeck		 (length($middle) == 2 && substr($middle, -1) eq "."))) {
212392bca398SDaniel Schwierzeck		$name = "$first $middle $last";
212492bca398SDaniel Schwierzeck	    } else {
212592bca398SDaniel Schwierzeck		$name = "$middle $last";
212692bca398SDaniel Schwierzeck	    }
212792bca398SDaniel Schwierzeck	}
212892bca398SDaniel Schwierzeck
212992bca398SDaniel Schwierzeck	if (substr($name, -1) =~ /[,\.]/) {
213092bca398SDaniel Schwierzeck	    $name = substr($name, 0, length($name) - 1);
213192bca398SDaniel Schwierzeck	} elsif (substr($name, -2) =~ /[,\.]"/) {
213292bca398SDaniel Schwierzeck	    $name = substr($name, 0, length($name) - 2) . '"';
213392bca398SDaniel Schwierzeck	}
213492bca398SDaniel Schwierzeck
213592bca398SDaniel Schwierzeck	if (substr($name, 0, 1) =~ /[,\.]/) {
213692bca398SDaniel Schwierzeck	    $name = substr($name, 1, length($name) - 1);
213792bca398SDaniel Schwierzeck	} elsif (substr($name, 0, 2) =~ /"[,\.]/) {
213892bca398SDaniel Schwierzeck	    $name = '"' . substr($name, 2, length($name) - 2);
213992bca398SDaniel Schwierzeck	}
214092bca398SDaniel Schwierzeck
214192bca398SDaniel Schwierzeck	my $fmt_email = format_email($name, $address, $email_usename);
214292bca398SDaniel Schwierzeck	push(@fmt_emails, $fmt_email);
214392bca398SDaniel Schwierzeck    }
214492bca398SDaniel Schwierzeck    return @fmt_emails;
214592bca398SDaniel Schwierzeck}
214692bca398SDaniel Schwierzeck
214792bca398SDaniel Schwierzecksub merge_email {
214892bca398SDaniel Schwierzeck    my @lines;
214992bca398SDaniel Schwierzeck    my %saw;
215092bca398SDaniel Schwierzeck
215192bca398SDaniel Schwierzeck    for (@_) {
215292bca398SDaniel Schwierzeck	my ($address, $role) = @$_;
215392bca398SDaniel Schwierzeck	if (!$saw{$address}) {
215492bca398SDaniel Schwierzeck	    if ($output_roles) {
215592bca398SDaniel Schwierzeck		push(@lines, "$address ($role)");
215692bca398SDaniel Schwierzeck	    } else {
215792bca398SDaniel Schwierzeck		push(@lines, $address);
215892bca398SDaniel Schwierzeck	    }
215992bca398SDaniel Schwierzeck	    $saw{$address} = 1;
216092bca398SDaniel Schwierzeck	}
216192bca398SDaniel Schwierzeck    }
216292bca398SDaniel Schwierzeck
216392bca398SDaniel Schwierzeck    return @lines;
216492bca398SDaniel Schwierzeck}
216592bca398SDaniel Schwierzeck
216692bca398SDaniel Schwierzecksub output {
216792bca398SDaniel Schwierzeck    my (@parms) = @_;
216892bca398SDaniel Schwierzeck
216992bca398SDaniel Schwierzeck    if ($output_multiline) {
217092bca398SDaniel Schwierzeck	foreach my $line (@parms) {
217192bca398SDaniel Schwierzeck	    print("${line}\n");
217292bca398SDaniel Schwierzeck	}
217392bca398SDaniel Schwierzeck    } else {
217492bca398SDaniel Schwierzeck	print(join($output_separator, @parms));
217592bca398SDaniel Schwierzeck	print("\n");
217692bca398SDaniel Schwierzeck    }
217792bca398SDaniel Schwierzeck}
217892bca398SDaniel Schwierzeck
217992bca398SDaniel Schwierzeckmy $rfc822re;
218092bca398SDaniel Schwierzeck
218192bca398SDaniel Schwierzecksub make_rfc822re {
218292bca398SDaniel Schwierzeck#   Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
218392bca398SDaniel Schwierzeck#   comment.  We must allow for rfc822_lwsp (or comments) after each of these.
218492bca398SDaniel Schwierzeck#   This regexp will only work on addresses which have had comments stripped
218592bca398SDaniel Schwierzeck#   and replaced with rfc822_lwsp.
218692bca398SDaniel Schwierzeck
218792bca398SDaniel Schwierzeck    my $specials = '()<>@,;:\\\\".\\[\\]';
218892bca398SDaniel Schwierzeck    my $controls = '\\000-\\037\\177';
218992bca398SDaniel Schwierzeck
219092bca398SDaniel Schwierzeck    my $dtext = "[^\\[\\]\\r\\\\]";
219192bca398SDaniel Schwierzeck    my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
219292bca398SDaniel Schwierzeck
219392bca398SDaniel Schwierzeck    my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
219492bca398SDaniel Schwierzeck
219592bca398SDaniel Schwierzeck#   Use zero-width assertion to spot the limit of an atom.  A simple
219692bca398SDaniel Schwierzeck#   $rfc822_lwsp* causes the regexp engine to hang occasionally.
219792bca398SDaniel Schwierzeck    my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
219892bca398SDaniel Schwierzeck    my $word = "(?:$atom|$quoted_string)";
219992bca398SDaniel Schwierzeck    my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
220092bca398SDaniel Schwierzeck
220192bca398SDaniel Schwierzeck    my $sub_domain = "(?:$atom|$domain_literal)";
220292bca398SDaniel Schwierzeck    my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
220392bca398SDaniel Schwierzeck
220492bca398SDaniel Schwierzeck    my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
220592bca398SDaniel Schwierzeck
220692bca398SDaniel Schwierzeck    my $phrase = "$word*";
220792bca398SDaniel Schwierzeck    my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
220892bca398SDaniel Schwierzeck    my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
220992bca398SDaniel Schwierzeck    my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
221092bca398SDaniel Schwierzeck
221192bca398SDaniel Schwierzeck    my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
221292bca398SDaniel Schwierzeck    my $address = "(?:$mailbox|$group)";
221392bca398SDaniel Schwierzeck
221492bca398SDaniel Schwierzeck    return "$rfc822_lwsp*$address";
221592bca398SDaniel Schwierzeck}
221692bca398SDaniel Schwierzeck
221792bca398SDaniel Schwierzecksub rfc822_strip_comments {
221892bca398SDaniel Schwierzeck    my $s = shift;
221992bca398SDaniel Schwierzeck#   Recursively remove comments, and replace with a single space.  The simpler
222092bca398SDaniel Schwierzeck#   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
222192bca398SDaniel Schwierzeck#   chars in atoms, for example.
222292bca398SDaniel Schwierzeck
222392bca398SDaniel Schwierzeck    while ($s =~ s/^((?:[^"\\]|\\.)*
222492bca398SDaniel Schwierzeck                    (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
222592bca398SDaniel Schwierzeck                    \((?:[^()\\]|\\.)*\)/$1 /osx) {}
222692bca398SDaniel Schwierzeck    return $s;
222792bca398SDaniel Schwierzeck}
222892bca398SDaniel Schwierzeck
222992bca398SDaniel Schwierzeck#   valid: returns true if the parameter is an RFC822 valid address
223092bca398SDaniel Schwierzeck#
223192bca398SDaniel Schwierzecksub rfc822_valid {
223292bca398SDaniel Schwierzeck    my $s = rfc822_strip_comments(shift);
223392bca398SDaniel Schwierzeck
223492bca398SDaniel Schwierzeck    if (!$rfc822re) {
223592bca398SDaniel Schwierzeck        $rfc822re = make_rfc822re();
223692bca398SDaniel Schwierzeck    }
223792bca398SDaniel Schwierzeck
223892bca398SDaniel Schwierzeck    return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
223992bca398SDaniel Schwierzeck}
224092bca398SDaniel Schwierzeck
224192bca398SDaniel Schwierzeck#   validlist: In scalar context, returns true if the parameter is an RFC822
224292bca398SDaniel Schwierzeck#              valid list of addresses.
224392bca398SDaniel Schwierzeck#
224492bca398SDaniel Schwierzeck#              In list context, returns an empty list on failure (an invalid
224592bca398SDaniel Schwierzeck#              address was found); otherwise a list whose first element is the
224692bca398SDaniel Schwierzeck#              number of addresses found and whose remaining elements are the
224792bca398SDaniel Schwierzeck#              addresses.  This is needed to disambiguate failure (invalid)
224892bca398SDaniel Schwierzeck#              from success with no addresses found, because an empty string is
224992bca398SDaniel Schwierzeck#              a valid list.
225092bca398SDaniel Schwierzeck
225192bca398SDaniel Schwierzecksub rfc822_validlist {
225292bca398SDaniel Schwierzeck    my $s = rfc822_strip_comments(shift);
225392bca398SDaniel Schwierzeck
225492bca398SDaniel Schwierzeck    if (!$rfc822re) {
225592bca398SDaniel Schwierzeck        $rfc822re = make_rfc822re();
225692bca398SDaniel Schwierzeck    }
225792bca398SDaniel Schwierzeck    # * null list items are valid according to the RFC
225892bca398SDaniel Schwierzeck    # * the '1' business is to aid in distinguishing failure from no results
225992bca398SDaniel Schwierzeck
226092bca398SDaniel Schwierzeck    my @r;
226192bca398SDaniel Schwierzeck    if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
226292bca398SDaniel Schwierzeck	$s =~ m/^$rfc822_char*$/) {
226392bca398SDaniel Schwierzeck        while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
226492bca398SDaniel Schwierzeck            push(@r, $1);
226592bca398SDaniel Schwierzeck        }
226692bca398SDaniel Schwierzeck        return wantarray ? (scalar(@r), @r) : 1;
226792bca398SDaniel Schwierzeck    }
226892bca398SDaniel Schwierzeck    return wantarray ? () : 0;
226992bca398SDaniel Schwierzeck}
2270