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