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