xref: /openbmc/u-boot/scripts/get_maintainer.pl (revision 5512f5cc)
159ab72d7SHeinrich 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
1359ab72d7SHeinrich 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);
2059ab72d7SHeinrich Schuchardtuse Cwd;
2168dc8769SDaniel Schwierzeckuse File::Find;
2292bca398SDaniel Schwierzeck
2359ab72d7SHeinrich Schuchardtmy $cur_path = fastgetcwd() . '/';
2492bca398SDaniel Schwierzeckmy $lk_path = "./";
2592bca398SDaniel Schwierzeckmy $email = 1;
2692bca398SDaniel Schwierzeckmy $email_usename = 1;
2792bca398SDaniel Schwierzeckmy $email_maintainer = 1;
2859ab72d7SHeinrich 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;
4959ab72d7SHeinrich Schuchardtmy $output_section_maxlen = 50;
5092bca398SDaniel Schwierzeckmy $scm = 0;
5192bca398SDaniel Schwierzeckmy $web = 0;
5292bca398SDaniel Schwierzeckmy $subsystem = 0;
5392bca398SDaniel Schwierzeckmy $status = 0;
5459ab72d7SHeinrich 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;
60*2f8ffb06SHeinrich Schuchardtmy $self_test = undef;
6192bca398SDaniel Schwierzeckmy $version = 0;
6292bca398SDaniel Schwierzeckmy $help = 0;
63b79372aeSHeinrich Schuchardtmy $find_maintainer_files = 1;
6492bca398SDaniel Schwierzeck
6592bca398SDaniel Schwierzeckmy $vcs_used = 0;
6692bca398SDaniel Schwierzeck
6792bca398SDaniel Schwierzeckmy $exit = 0;
6892bca398SDaniel Schwierzeck
6992bca398SDaniel Schwierzeckmy %commit_author_hash;
7092bca398SDaniel Schwierzeckmy %commit_signer_hash;
7192bca398SDaniel Schwierzeck
7292bca398SDaniel Schwierzeckmy @penguin_chief = ();
73ca746f04SAndy Flemingpush(@penguin_chief, "Tom Rini:trini\@konsulko.com");
7492bca398SDaniel Schwierzeck
7592bca398SDaniel Schwierzeckmy @penguin_chief_names = ();
7692bca398SDaniel Schwierzeckforeach my $chief (@penguin_chief) {
7792bca398SDaniel Schwierzeck    if ($chief =~ m/^(.*):(.*)/) {
7892bca398SDaniel Schwierzeck	my $chief_name = $1;
7992bca398SDaniel Schwierzeck	my $chief_addr = $2;
8092bca398SDaniel Schwierzeck	push(@penguin_chief_names, $chief_name);
8192bca398SDaniel Schwierzeck    }
8292bca398SDaniel Schwierzeck}
8392bca398SDaniel Schwierzeckmy $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
8492bca398SDaniel Schwierzeck
8592bca398SDaniel Schwierzeck# Signature types of people who are either
8692bca398SDaniel Schwierzeck# 	a) responsible for the code in question, or
8792bca398SDaniel Schwierzeck# 	b) familiar enough with it to give relevant feedback
8892bca398SDaniel Schwierzeckmy @signature_tags = ();
8992bca398SDaniel Schwierzeckpush(@signature_tags, "Signed-off-by:");
9092bca398SDaniel Schwierzeckpush(@signature_tags, "Reviewed-by:");
9192bca398SDaniel Schwierzeckpush(@signature_tags, "Acked-by:");
9292bca398SDaniel Schwierzeck
9392bca398SDaniel Schwierzeckmy $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
9492bca398SDaniel Schwierzeck
9592bca398SDaniel Schwierzeck# rfc822 email address - preloaded methods go here.
9692bca398SDaniel Schwierzeckmy $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
9792bca398SDaniel Schwierzeckmy $rfc822_char = '[\\000-\\377]';
9892bca398SDaniel Schwierzeck
9992bca398SDaniel Schwierzeck# VCS command support: class-like functions and strings
10092bca398SDaniel Schwierzeck
10192bca398SDaniel Schwierzeckmy %VCS_cmds;
10292bca398SDaniel Schwierzeck
10392bca398SDaniel Schwierzeckmy %VCS_cmds_git = (
10492bca398SDaniel Schwierzeck    "execute_cmd" => \&git_execute_cmd,
10592bca398SDaniel Schwierzeck    "available" => '(which("git") ne "") && (-e ".git")',
10692bca398SDaniel Schwierzeck    "find_signers_cmd" =>
10792bca398SDaniel Schwierzeck	"git log --no-color --follow --since=\$email_git_since " .
10892bca398SDaniel Schwierzeck	    '--numstat --no-merges ' .
10992bca398SDaniel Schwierzeck	    '--format="GitCommit: %H%n' .
11092bca398SDaniel Schwierzeck		      'GitAuthor: %an <%ae>%n' .
11192bca398SDaniel Schwierzeck		      'GitDate: %aD%n' .
11292bca398SDaniel Schwierzeck		      'GitSubject: %s%n' .
11392bca398SDaniel Schwierzeck		      '%b%n"' .
11492bca398SDaniel Schwierzeck	    " -- \$file",
11592bca398SDaniel Schwierzeck    "find_commit_signers_cmd" =>
11692bca398SDaniel Schwierzeck	"git log --no-color " .
11792bca398SDaniel Schwierzeck	    '--numstat ' .
11892bca398SDaniel Schwierzeck	    '--format="GitCommit: %H%n' .
11992bca398SDaniel Schwierzeck		      'GitAuthor: %an <%ae>%n' .
12092bca398SDaniel Schwierzeck		      'GitDate: %aD%n' .
12192bca398SDaniel Schwierzeck		      'GitSubject: %s%n' .
12292bca398SDaniel Schwierzeck		      '%b%n"' .
12392bca398SDaniel Schwierzeck	    " -1 \$commit",
12492bca398SDaniel Schwierzeck    "find_commit_author_cmd" =>
12592bca398SDaniel Schwierzeck	"git log --no-color " .
12692bca398SDaniel Schwierzeck	    '--numstat ' .
12792bca398SDaniel Schwierzeck	    '--format="GitCommit: %H%n' .
12892bca398SDaniel Schwierzeck		      'GitAuthor: %an <%ae>%n' .
12992bca398SDaniel Schwierzeck		      'GitDate: %aD%n' .
13092bca398SDaniel Schwierzeck		      'GitSubject: %s%n"' .
13192bca398SDaniel Schwierzeck	    " -1 \$commit",
13292bca398SDaniel Schwierzeck    "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
13392bca398SDaniel Schwierzeck    "blame_file_cmd" => "git blame -l \$file",
13492bca398SDaniel Schwierzeck    "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
13592bca398SDaniel Schwierzeck    "blame_commit_pattern" => "^([0-9a-f]+) ",
13692bca398SDaniel Schwierzeck    "author_pattern" => "^GitAuthor: (.*)",
13792bca398SDaniel Schwierzeck    "subject_pattern" => "^GitSubject: (.*)",
13892bca398SDaniel Schwierzeck    "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
13959ab72d7SHeinrich Schuchardt    "file_exists_cmd" => "git ls-files \$file",
140*2f8ffb06SHeinrich Schuchardt    "list_files_cmd" => "git ls-files \$file",
14192bca398SDaniel Schwierzeck);
14292bca398SDaniel Schwierzeck
14392bca398SDaniel Schwierzeckmy %VCS_cmds_hg = (
14492bca398SDaniel Schwierzeck    "execute_cmd" => \&hg_execute_cmd,
14592bca398SDaniel Schwierzeck    "available" => '(which("hg") ne "") && (-d ".hg")',
14692bca398SDaniel Schwierzeck    "find_signers_cmd" =>
14792bca398SDaniel Schwierzeck	"hg log --date=\$email_hg_since " .
14892bca398SDaniel Schwierzeck	    "--template='HgCommit: {node}\\n" .
14992bca398SDaniel Schwierzeck	                "HgAuthor: {author}\\n" .
15092bca398SDaniel Schwierzeck			"HgSubject: {desc}\\n'" .
15192bca398SDaniel Schwierzeck	    " -- \$file",
15292bca398SDaniel Schwierzeck    "find_commit_signers_cmd" =>
15392bca398SDaniel Schwierzeck	"hg log " .
15492bca398SDaniel Schwierzeck	    "--template='HgSubject: {desc}\\n'" .
15592bca398SDaniel Schwierzeck	    " -r \$commit",
15692bca398SDaniel Schwierzeck    "find_commit_author_cmd" =>
15792bca398SDaniel Schwierzeck	"hg log " .
15892bca398SDaniel Schwierzeck	    "--template='HgCommit: {node}\\n" .
15992bca398SDaniel Schwierzeck		        "HgAuthor: {author}\\n" .
16092bca398SDaniel Schwierzeck			"HgSubject: {desc|firstline}\\n'" .
16192bca398SDaniel Schwierzeck	    " -r \$commit",
16292bca398SDaniel Schwierzeck    "blame_range_cmd" => "",		# not supported
16392bca398SDaniel Schwierzeck    "blame_file_cmd" => "hg blame -n \$file",
16492bca398SDaniel Schwierzeck    "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
16592bca398SDaniel Schwierzeck    "blame_commit_pattern" => "^([ 0-9a-f]+):",
16692bca398SDaniel Schwierzeck    "author_pattern" => "^HgAuthor: (.*)",
16792bca398SDaniel Schwierzeck    "subject_pattern" => "^HgSubject: (.*)",
16892bca398SDaniel Schwierzeck    "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
16959ab72d7SHeinrich Schuchardt    "file_exists_cmd" => "hg files \$file",
170*2f8ffb06SHeinrich Schuchardt    "list_files_cmd" => "hg manifest -R \$file",
17192bca398SDaniel Schwierzeck);
17292bca398SDaniel Schwierzeck
17392bca398SDaniel Schwierzeckmy $conf = which_conf(".get_maintainer.conf");
17492bca398SDaniel Schwierzeckif (-f $conf) {
17592bca398SDaniel Schwierzeck    my @conf_args;
17692bca398SDaniel Schwierzeck    open(my $conffile, '<', "$conf")
17792bca398SDaniel Schwierzeck	or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
17892bca398SDaniel Schwierzeck
17992bca398SDaniel Schwierzeck    while (<$conffile>) {
18092bca398SDaniel Schwierzeck	my $line = $_;
18192bca398SDaniel Schwierzeck
18292bca398SDaniel Schwierzeck	$line =~ s/\s*\n?$//g;
18392bca398SDaniel Schwierzeck	$line =~ s/^\s*//g;
18492bca398SDaniel Schwierzeck	$line =~ s/\s+/ /g;
18592bca398SDaniel Schwierzeck
18692bca398SDaniel Schwierzeck	next if ($line =~ m/^\s*#/);
18792bca398SDaniel Schwierzeck	next if ($line =~ m/^\s*$/);
18892bca398SDaniel Schwierzeck
18992bca398SDaniel Schwierzeck	my @words = split(" ", $line);
19092bca398SDaniel Schwierzeck	foreach my $word (@words) {
19192bca398SDaniel Schwierzeck	    last if ($word =~ m/^#/);
19292bca398SDaniel Schwierzeck	    push (@conf_args, $word);
19392bca398SDaniel Schwierzeck	}
19492bca398SDaniel Schwierzeck    }
19592bca398SDaniel Schwierzeck    close($conffile);
19692bca398SDaniel Schwierzeck    unshift(@ARGV, @conf_args) if @conf_args;
19792bca398SDaniel Schwierzeck}
19892bca398SDaniel Schwierzeck
19959ab72d7SHeinrich Schuchardtmy @ignore_emails = ();
20059ab72d7SHeinrich Schuchardtmy $ignore_file = which_conf(".get_maintainer.ignore");
20159ab72d7SHeinrich Schuchardtif (-f $ignore_file) {
20259ab72d7SHeinrich Schuchardt    open(my $ignore, '<', "$ignore_file")
20359ab72d7SHeinrich Schuchardt	or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
20459ab72d7SHeinrich Schuchardt    while (<$ignore>) {
20559ab72d7SHeinrich Schuchardt	my $line = $_;
20659ab72d7SHeinrich Schuchardt
20759ab72d7SHeinrich Schuchardt	$line =~ s/\s*\n?$//;
20859ab72d7SHeinrich Schuchardt	$line =~ s/^\s*//;
20959ab72d7SHeinrich Schuchardt	$line =~ s/\s+$//;
21059ab72d7SHeinrich Schuchardt	$line =~ s/#.*$//;
21159ab72d7SHeinrich Schuchardt
21259ab72d7SHeinrich Schuchardt	next if ($line =~ m/^\s*$/);
21359ab72d7SHeinrich Schuchardt	if (rfc822_valid($line)) {
21459ab72d7SHeinrich Schuchardt	    push(@ignore_emails, $line);
21559ab72d7SHeinrich Schuchardt	}
21659ab72d7SHeinrich Schuchardt    }
21759ab72d7SHeinrich Schuchardt    close($ignore);
21859ab72d7SHeinrich Schuchardt}
21959ab72d7SHeinrich Schuchardt
220*2f8ffb06SHeinrich Schuchardtif ($#ARGV > 0) {
221*2f8ffb06SHeinrich Schuchardt    foreach (@ARGV) {
222*2f8ffb06SHeinrich Schuchardt        if ($_ =~ /^-{1,2}self-test(?:=|$)/) {
223*2f8ffb06SHeinrich Schuchardt            die "$P: using --self-test does not allow any other option or argument\n";
224*2f8ffb06SHeinrich Schuchardt        }
225*2f8ffb06SHeinrich Schuchardt    }
226*2f8ffb06SHeinrich Schuchardt}
227*2f8ffb06SHeinrich Schuchardt
22892bca398SDaniel Schwierzeckif (!GetOptions(
22992bca398SDaniel Schwierzeck		'email!' => \$email,
23092bca398SDaniel Schwierzeck		'git!' => \$email_git,
23192bca398SDaniel Schwierzeck		'git-all-signature-types!' => \$email_git_all_signature_types,
23292bca398SDaniel Schwierzeck		'git-blame!' => \$email_git_blame,
23392bca398SDaniel Schwierzeck		'git-blame-signatures!' => \$email_git_blame_signatures,
23492bca398SDaniel Schwierzeck		'git-fallback!' => \$email_git_fallback,
23592bca398SDaniel Schwierzeck		'git-chief-penguins!' => \$email_git_penguin_chiefs,
23692bca398SDaniel Schwierzeck		'git-min-signatures=i' => \$email_git_min_signatures,
23792bca398SDaniel Schwierzeck		'git-max-maintainers=i' => \$email_git_max_maintainers,
23892bca398SDaniel Schwierzeck		'git-min-percent=i' => \$email_git_min_percent,
23992bca398SDaniel Schwierzeck		'git-since=s' => \$email_git_since,
24092bca398SDaniel Schwierzeck		'hg-since=s' => \$email_hg_since,
24192bca398SDaniel Schwierzeck		'i|interactive!' => \$interactive,
24292bca398SDaniel Schwierzeck		'remove-duplicates!' => \$email_remove_duplicates,
24392bca398SDaniel Schwierzeck		'mailmap!' => \$email_use_mailmap,
24492bca398SDaniel Schwierzeck		'm!' => \$email_maintainer,
24559ab72d7SHeinrich Schuchardt		'r!' => \$email_reviewer,
24692bca398SDaniel Schwierzeck		'n!' => \$email_usename,
24792bca398SDaniel Schwierzeck		'l!' => \$email_list,
24892bca398SDaniel Schwierzeck		's!' => \$email_subscriber_list,
24992bca398SDaniel Schwierzeck		'multiline!' => \$output_multiline,
25092bca398SDaniel Schwierzeck		'roles!' => \$output_roles,
25192bca398SDaniel Schwierzeck		'rolestats!' => \$output_rolestats,
25292bca398SDaniel Schwierzeck		'separator=s' => \$output_separator,
25392bca398SDaniel Schwierzeck		'subsystem!' => \$subsystem,
25492bca398SDaniel Schwierzeck		'status!' => \$status,
25592bca398SDaniel Schwierzeck		'scm!' => \$scm,
25692bca398SDaniel Schwierzeck		'web!' => \$web,
25759ab72d7SHeinrich Schuchardt		'letters=s' => \$letters,
25892bca398SDaniel Schwierzeck		'pattern-depth=i' => \$pattern_depth,
25992bca398SDaniel Schwierzeck		'k|keywords!' => \$keywords,
26092bca398SDaniel Schwierzeck		'sections!' => \$sections,
26192bca398SDaniel Schwierzeck		'fe|file-emails!' => \$file_emails,
26292bca398SDaniel Schwierzeck		'f|file' => \$from_filename,
26359ab72d7SHeinrich Schuchardt		'find-maintainer-files' => \$find_maintainer_files,
264*2f8ffb06SHeinrich Schuchardt		'self-test:s' => \$self_test,
26592bca398SDaniel Schwierzeck		'v|version' => \$version,
26692bca398SDaniel Schwierzeck		'h|help|usage' => \$help,
26792bca398SDaniel Schwierzeck		)) {
26892bca398SDaniel Schwierzeck    die "$P: invalid argument - use --help if necessary\n";
26992bca398SDaniel Schwierzeck}
27092bca398SDaniel Schwierzeck
27192bca398SDaniel Schwierzeckif ($help != 0) {
27292bca398SDaniel Schwierzeck    usage();
27392bca398SDaniel Schwierzeck    exit 0;
27492bca398SDaniel Schwierzeck}
27592bca398SDaniel Schwierzeck
27692bca398SDaniel Schwierzeckif ($version != 0) {
27792bca398SDaniel Schwierzeck    print("${P} ${V}\n");
27892bca398SDaniel Schwierzeck    exit 0;
27992bca398SDaniel Schwierzeck}
28092bca398SDaniel Schwierzeck
281*2f8ffb06SHeinrich Schuchardtif (defined $self_test) {
282*2f8ffb06SHeinrich Schuchardt    read_all_maintainer_files();
283*2f8ffb06SHeinrich Schuchardt    self_test();
284*2f8ffb06SHeinrich Schuchardt    exit 0;
285*2f8ffb06SHeinrich Schuchardt}
286*2f8ffb06SHeinrich Schuchardt
28792bca398SDaniel Schwierzeckif (-t STDIN && !@ARGV) {
28892bca398SDaniel Schwierzeck    # We're talking to a terminal, but have no command line arguments.
28992bca398SDaniel Schwierzeck    die "$P: missing patchfile or -f file - use --help if necessary\n";
29092bca398SDaniel Schwierzeck}
29192bca398SDaniel Schwierzeck
29292bca398SDaniel Schwierzeck$output_multiline = 0 if ($output_separator ne ", ");
29392bca398SDaniel Schwierzeck$output_rolestats = 1 if ($interactive);
29492bca398SDaniel Schwierzeck$output_roles = 1 if ($output_rolestats);
29592bca398SDaniel Schwierzeck
29659ab72d7SHeinrich Schuchardtif ($sections || $letters ne "") {
29759ab72d7SHeinrich Schuchardt    $sections = 1;
29892bca398SDaniel Schwierzeck    $email = 0;
29992bca398SDaniel Schwierzeck    $email_list = 0;
30092bca398SDaniel Schwierzeck    $scm = 0;
30192bca398SDaniel Schwierzeck    $status = 0;
30292bca398SDaniel Schwierzeck    $subsystem = 0;
30392bca398SDaniel Schwierzeck    $web = 0;
30492bca398SDaniel Schwierzeck    $keywords = 0;
30592bca398SDaniel Schwierzeck    $interactive = 0;
30692bca398SDaniel Schwierzeck} else {
30792bca398SDaniel Schwierzeck    my $selections = $email + $scm + $status + $subsystem + $web;
30892bca398SDaniel Schwierzeck    if ($selections == 0) {
30992bca398SDaniel Schwierzeck	die "$P:  Missing required option: email, scm, status, subsystem or web\n";
31092bca398SDaniel Schwierzeck    }
31192bca398SDaniel Schwierzeck}
31292bca398SDaniel Schwierzeck
31392bca398SDaniel Schwierzeckif ($email &&
31459ab72d7SHeinrich Schuchardt    ($email_maintainer + $email_reviewer +
31559ab72d7SHeinrich Schuchardt     $email_list + $email_subscriber_list +
31692bca398SDaniel Schwierzeck     $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
31792bca398SDaniel Schwierzeck    die "$P: Please select at least 1 email option\n";
31892bca398SDaniel Schwierzeck}
31992bca398SDaniel Schwierzeck
32092bca398SDaniel Schwierzeckif (!top_of_kernel_tree($lk_path)) {
32192bca398SDaniel Schwierzeck    die "$P: The current directory does not appear to be "
32259ab72d7SHeinrich Schuchardt	. "a U-Boot source tree.\n";
32392bca398SDaniel Schwierzeck}
32492bca398SDaniel Schwierzeck
32592bca398SDaniel Schwierzeck## Read MAINTAINERS for type/value pairs
32692bca398SDaniel Schwierzeck
32792bca398SDaniel Schwierzeckmy @typevalue = ();
32892bca398SDaniel Schwierzeckmy %keyword_hash;
32959ab72d7SHeinrich Schuchardtmy @mfiles = ();
330*2f8ffb06SHeinrich Schuchardtmy @self_test_info = ();
33192bca398SDaniel Schwierzeck
33259ab72d7SHeinrich Schuchardtsub read_maintainer_file {
33359ab72d7SHeinrich Schuchardt    my ($file) = @_;
33468dc8769SDaniel Schwierzeck
33559ab72d7SHeinrich Schuchardt    open (my $maint, '<', "$file")
33659ab72d7SHeinrich Schuchardt	or die "$P: Can't open MAINTAINERS file '$file': $!\n";
337*2f8ffb06SHeinrich Schuchardt    my $i = 1;
33892bca398SDaniel Schwierzeck    while (<$maint>) {
33992bca398SDaniel Schwierzeck	my $line = $_;
340*2f8ffb06SHeinrich Schuchardt	chomp $line;
34192bca398SDaniel Schwierzeck
34235729218SHeiko Schocher	if ($line =~ m/^([A-Z]):\s*(.*)/) {
34392bca398SDaniel Schwierzeck	    my $type = $1;
34492bca398SDaniel Schwierzeck	    my $value = $2;
34592bca398SDaniel Schwierzeck
34692bca398SDaniel Schwierzeck	    ##Filename pattern matching
34792bca398SDaniel Schwierzeck	    if ($type eq "F" || $type eq "X") {
34892bca398SDaniel Schwierzeck		$value =~ s@\.@\\\.@g;       ##Convert . to \.
34992bca398SDaniel Schwierzeck		$value =~ s/\*/\.\*/g;       ##Convert * to .*
35092bca398SDaniel Schwierzeck		$value =~ s/\?/\./g;         ##Convert ? to .
35192bca398SDaniel Schwierzeck		##if pattern is a directory and it lacks a trailing slash, add one
35292bca398SDaniel Schwierzeck		if ((-d $value)) {
35392bca398SDaniel Schwierzeck		    $value =~ s@([^/])$@$1/@;
35492bca398SDaniel Schwierzeck		}
35592bca398SDaniel Schwierzeck	    } elsif ($type eq "K") {
35692bca398SDaniel Schwierzeck		$keyword_hash{@typevalue} = $value;
35792bca398SDaniel Schwierzeck	    }
35892bca398SDaniel Schwierzeck	    push(@typevalue, "$type:$value");
35959ab72d7SHeinrich Schuchardt	} elsif (!(/^\s*$/ || /^\s*\#/)) {
36092bca398SDaniel Schwierzeck	    push(@typevalue, $line);
36192bca398SDaniel Schwierzeck	}
362*2f8ffb06SHeinrich Schuchardt	if (defined $self_test) {
363*2f8ffb06SHeinrich Schuchardt	    push(@self_test_info, {file=>$file, linenr=>$i, line=>$line});
364*2f8ffb06SHeinrich Schuchardt	}
365*2f8ffb06SHeinrich Schuchardt	$i++;
36692bca398SDaniel Schwierzeck    }
36759ab72d7SHeinrich Schuchardt    close($maint);
36868dc8769SDaniel Schwierzeck}
36992bca398SDaniel Schwierzeck
37059ab72d7SHeinrich Schuchardtsub find_is_maintainer_file {
37159ab72d7SHeinrich Schuchardt    my ($file) = $_;
37259ab72d7SHeinrich Schuchardt    return if ($file !~ m@/MAINTAINERS$@);
37359ab72d7SHeinrich Schuchardt    $file = $File::Find::name;
37459ab72d7SHeinrich Schuchardt    return if (! -f $file);
37559ab72d7SHeinrich Schuchardt    push(@mfiles, $file);
37659ab72d7SHeinrich Schuchardt}
37759ab72d7SHeinrich Schuchardt
37859ab72d7SHeinrich Schuchardtsub find_ignore_git {
37959ab72d7SHeinrich Schuchardt    return grep { $_ !~ /^\.git$/; } @_;
38059ab72d7SHeinrich Schuchardt}
38159ab72d7SHeinrich Schuchardt
382*2f8ffb06SHeinrich Schuchardtread_all_maintainer_files();
383*2f8ffb06SHeinrich Schuchardt
384*2f8ffb06SHeinrich Schuchardtsub read_all_maintainer_files {
38559ab72d7SHeinrich Schuchardt    if (-d "${lk_path}MAINTAINERS") {
38659ab72d7SHeinrich Schuchardt        opendir(DIR, "${lk_path}MAINTAINERS") or die $!;
38759ab72d7SHeinrich Schuchardt        my @files = readdir(DIR);
38859ab72d7SHeinrich Schuchardt        closedir(DIR);
38959ab72d7SHeinrich Schuchardt        foreach my $file (@files) {
39059ab72d7SHeinrich Schuchardt            push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./);
39159ab72d7SHeinrich Schuchardt        }
39259ab72d7SHeinrich Schuchardt    }
39359ab72d7SHeinrich Schuchardt
39459ab72d7SHeinrich Schuchardt    if ($find_maintainer_files) {
39559ab72d7SHeinrich Schuchardt        find( { wanted => \&find_is_maintainer_file,
39659ab72d7SHeinrich Schuchardt                preprocess => \&find_ignore_git,
39759ab72d7SHeinrich Schuchardt                no_chdir => 1,
39859ab72d7SHeinrich Schuchardt        }, "${lk_path}");
39959ab72d7SHeinrich Schuchardt    } else {
40059ab72d7SHeinrich Schuchardt        push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS";
40159ab72d7SHeinrich Schuchardt    }
40259ab72d7SHeinrich Schuchardt
40359ab72d7SHeinrich Schuchardt    foreach my $file (@mfiles) {
40459ab72d7SHeinrich Schuchardt        read_maintainer_file("$file");
40559ab72d7SHeinrich Schuchardt    }
406*2f8ffb06SHeinrich Schuchardt}
40792bca398SDaniel Schwierzeck
40892bca398SDaniel Schwierzeck#
40992bca398SDaniel Schwierzeck# Read mail address map
41092bca398SDaniel Schwierzeck#
41192bca398SDaniel Schwierzeck
41292bca398SDaniel Schwierzeckmy $mailmap;
41392bca398SDaniel Schwierzeck
41492bca398SDaniel Schwierzeckread_mailmap();
41592bca398SDaniel Schwierzeck
41692bca398SDaniel Schwierzecksub read_mailmap {
41792bca398SDaniel Schwierzeck    $mailmap = {
41892bca398SDaniel Schwierzeck	names => {},
41992bca398SDaniel Schwierzeck	addresses => {}
42092bca398SDaniel Schwierzeck    };
42192bca398SDaniel Schwierzeck
42292bca398SDaniel Schwierzeck    return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
42392bca398SDaniel Schwierzeck
42492bca398SDaniel Schwierzeck    open(my $mailmap_file, '<', "${lk_path}.mailmap")
42592bca398SDaniel Schwierzeck	or warn "$P: Can't open .mailmap: $!\n";
42692bca398SDaniel Schwierzeck
42792bca398SDaniel Schwierzeck    while (<$mailmap_file>) {
42892bca398SDaniel Schwierzeck	s/#.*$//; #strip comments
42992bca398SDaniel Schwierzeck	s/^\s+|\s+$//g; #trim
43092bca398SDaniel Schwierzeck
43192bca398SDaniel Schwierzeck	next if (/^\s*$/); #skip empty lines
43292bca398SDaniel Schwierzeck	#entries have one of the following formats:
43392bca398SDaniel Schwierzeck	# name1 <mail1>
43492bca398SDaniel Schwierzeck	# <mail1> <mail2>
43592bca398SDaniel Schwierzeck	# name1 <mail1> <mail2>
43692bca398SDaniel Schwierzeck	# name1 <mail1> name2 <mail2>
43792bca398SDaniel Schwierzeck	# (see man git-shortlog)
43892bca398SDaniel Schwierzeck
43992bca398SDaniel Schwierzeck	if (/^([^<]+)<([^>]+)>$/) {
44092bca398SDaniel Schwierzeck	    my $real_name = $1;
44192bca398SDaniel Schwierzeck	    my $address = $2;
44292bca398SDaniel Schwierzeck
44392bca398SDaniel Schwierzeck	    $real_name =~ s/\s+$//;
44492bca398SDaniel Schwierzeck	    ($real_name, $address) = parse_email("$real_name <$address>");
44592bca398SDaniel Schwierzeck	    $mailmap->{names}->{$address} = $real_name;
44692bca398SDaniel Schwierzeck
44792bca398SDaniel Schwierzeck	} elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
44892bca398SDaniel Schwierzeck	    my $real_address = $1;
44992bca398SDaniel Schwierzeck	    my $wrong_address = $2;
45092bca398SDaniel Schwierzeck
45192bca398SDaniel Schwierzeck	    $mailmap->{addresses}->{$wrong_address} = $real_address;
45292bca398SDaniel Schwierzeck
45392bca398SDaniel Schwierzeck	} elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
45492bca398SDaniel Schwierzeck	    my $real_name = $1;
45592bca398SDaniel Schwierzeck	    my $real_address = $2;
45692bca398SDaniel Schwierzeck	    my $wrong_address = $3;
45792bca398SDaniel Schwierzeck
45892bca398SDaniel Schwierzeck	    $real_name =~ s/\s+$//;
45992bca398SDaniel Schwierzeck	    ($real_name, $real_address) =
46092bca398SDaniel Schwierzeck		parse_email("$real_name <$real_address>");
46192bca398SDaniel Schwierzeck	    $mailmap->{names}->{$wrong_address} = $real_name;
46292bca398SDaniel Schwierzeck	    $mailmap->{addresses}->{$wrong_address} = $real_address;
46392bca398SDaniel Schwierzeck
46492bca398SDaniel Schwierzeck	} elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
46592bca398SDaniel Schwierzeck	    my $real_name = $1;
46692bca398SDaniel Schwierzeck	    my $real_address = $2;
46792bca398SDaniel Schwierzeck	    my $wrong_name = $3;
46892bca398SDaniel Schwierzeck	    my $wrong_address = $4;
46992bca398SDaniel Schwierzeck
47092bca398SDaniel Schwierzeck	    $real_name =~ s/\s+$//;
47192bca398SDaniel Schwierzeck	    ($real_name, $real_address) =
47292bca398SDaniel Schwierzeck		parse_email("$real_name <$real_address>");
47392bca398SDaniel Schwierzeck
47492bca398SDaniel Schwierzeck	    $wrong_name =~ s/\s+$//;
47592bca398SDaniel Schwierzeck	    ($wrong_name, $wrong_address) =
47692bca398SDaniel Schwierzeck		parse_email("$wrong_name <$wrong_address>");
47792bca398SDaniel Schwierzeck
47892bca398SDaniel Schwierzeck	    my $wrong_email = format_email($wrong_name, $wrong_address, 1);
47992bca398SDaniel Schwierzeck	    $mailmap->{names}->{$wrong_email} = $real_name;
48092bca398SDaniel Schwierzeck	    $mailmap->{addresses}->{$wrong_email} = $real_address;
48192bca398SDaniel Schwierzeck	}
48292bca398SDaniel Schwierzeck    }
48392bca398SDaniel Schwierzeck    close($mailmap_file);
48492bca398SDaniel Schwierzeck}
48592bca398SDaniel Schwierzeck
48692bca398SDaniel Schwierzeck## use the filenames on the command line or find the filenames in the patchfiles
48792bca398SDaniel Schwierzeck
48892bca398SDaniel Schwierzeckmy @files = ();
48992bca398SDaniel Schwierzeckmy @range = ();
49092bca398SDaniel Schwierzeckmy @keyword_tvi = ();
49192bca398SDaniel Schwierzeckmy @file_emails = ();
49292bca398SDaniel Schwierzeck
49392bca398SDaniel Schwierzeckif (!@ARGV) {
49492bca398SDaniel Schwierzeck    push(@ARGV, "&STDIN");
49592bca398SDaniel Schwierzeck}
49692bca398SDaniel Schwierzeck
49792bca398SDaniel Schwierzeckforeach my $file (@ARGV) {
49892bca398SDaniel Schwierzeck    if ($file ne "&STDIN") {
49992bca398SDaniel Schwierzeck	##if $file is a directory and it lacks a trailing slash, add one
50092bca398SDaniel Schwierzeck	if ((-d $file)) {
50192bca398SDaniel Schwierzeck	    $file =~ s@([^/])$@$1/@;
50292bca398SDaniel Schwierzeck	} elsif (!(-f $file)) {
50392bca398SDaniel Schwierzeck	    die "$P: file '${file}' not found\n";
50492bca398SDaniel Schwierzeck	}
50592bca398SDaniel Schwierzeck    }
50659ab72d7SHeinrich Schuchardt    if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
50759ab72d7SHeinrich Schuchardt	$file =~ s/^\Q${cur_path}\E//;	#strip any absolute path
50859ab72d7SHeinrich Schuchardt	$file =~ s/^\Q${lk_path}\E//;	#or the path to the lk tree
50992bca398SDaniel Schwierzeck	push(@files, $file);
51092bca398SDaniel Schwierzeck	if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
51192bca398SDaniel Schwierzeck	    open(my $f, '<', $file)
51292bca398SDaniel Schwierzeck		or die "$P: Can't open $file: $!\n";
51392bca398SDaniel Schwierzeck	    my $text = do { local($/) ; <$f> };
51492bca398SDaniel Schwierzeck	    close($f);
51592bca398SDaniel Schwierzeck	    if ($keywords) {
51692bca398SDaniel Schwierzeck		foreach my $line (keys %keyword_hash) {
51792bca398SDaniel Schwierzeck		    if ($text =~ m/$keyword_hash{$line}/x) {
51892bca398SDaniel Schwierzeck			push(@keyword_tvi, $line);
51992bca398SDaniel Schwierzeck		    }
52092bca398SDaniel Schwierzeck		}
52192bca398SDaniel Schwierzeck	    }
52292bca398SDaniel Schwierzeck	    if ($file_emails) {
52392bca398SDaniel 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;
52492bca398SDaniel Schwierzeck		push(@file_emails, clean_file_emails(@poss_addr));
52592bca398SDaniel Schwierzeck	    }
52692bca398SDaniel Schwierzeck	}
52792bca398SDaniel Schwierzeck    } else {
52892bca398SDaniel Schwierzeck	my $file_cnt = @files;
52992bca398SDaniel Schwierzeck	my $lastfile;
53092bca398SDaniel Schwierzeck
53192bca398SDaniel Schwierzeck	open(my $patch, "< $file")
53292bca398SDaniel Schwierzeck	    or die "$P: Can't open $file: $!\n";
53392bca398SDaniel Schwierzeck
53492bca398SDaniel Schwierzeck	# We can check arbitrary information before the patch
53592bca398SDaniel Schwierzeck	# like the commit message, mail headers, etc...
53692bca398SDaniel Schwierzeck	# This allows us to match arbitrary keywords against any part
53792bca398SDaniel Schwierzeck	# of a git format-patch generated file (subject tags, etc...)
53892bca398SDaniel Schwierzeck
53992bca398SDaniel Schwierzeck	my $patch_prefix = "";			#Parsing the intro
54092bca398SDaniel Schwierzeck
54192bca398SDaniel Schwierzeck	while (<$patch>) {
54292bca398SDaniel Schwierzeck	    my $patch_line = $_;
54392bca398SDaniel Schwierzeck	    if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
54492bca398SDaniel Schwierzeck		my $filename = $1;
54592bca398SDaniel Schwierzeck		$filename =~ s@^[^/]*/@@;
54692bca398SDaniel Schwierzeck		$filename =~ s@\n@@;
54792bca398SDaniel Schwierzeck		$lastfile = $filename;
54892bca398SDaniel Schwierzeck		push(@files, $filename);
54992bca398SDaniel Schwierzeck		$patch_prefix = "^[+-].*";	#Now parsing the actual patch
55092bca398SDaniel Schwierzeck	    } elsif (m/^\@\@ -(\d+),(\d+)/) {
55192bca398SDaniel Schwierzeck		if ($email_git_blame) {
55292bca398SDaniel Schwierzeck		    push(@range, "$lastfile:$1:$2");
55392bca398SDaniel Schwierzeck		}
55492bca398SDaniel Schwierzeck	    } elsif ($keywords) {
55592bca398SDaniel Schwierzeck		foreach my $line (keys %keyword_hash) {
55692bca398SDaniel Schwierzeck		    if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
55792bca398SDaniel Schwierzeck			push(@keyword_tvi, $line);
55892bca398SDaniel Schwierzeck		    }
55992bca398SDaniel Schwierzeck		}
56092bca398SDaniel Schwierzeck	    }
56192bca398SDaniel Schwierzeck	}
56292bca398SDaniel Schwierzeck	close($patch);
56392bca398SDaniel Schwierzeck
56492bca398SDaniel Schwierzeck	if ($file_cnt == @files) {
56592bca398SDaniel Schwierzeck	    warn "$P: file '${file}' doesn't appear to be a patch.  "
56692bca398SDaniel Schwierzeck		. "Add -f to options?\n";
56792bca398SDaniel Schwierzeck	}
56892bca398SDaniel Schwierzeck	@files = sort_and_uniq(@files);
56992bca398SDaniel Schwierzeck    }
57092bca398SDaniel Schwierzeck}
57192bca398SDaniel Schwierzeck
57292bca398SDaniel Schwierzeck@file_emails = uniq(@file_emails);
57392bca398SDaniel Schwierzeck
57492bca398SDaniel Schwierzeckmy %email_hash_name;
57592bca398SDaniel Schwierzeckmy %email_hash_address;
57692bca398SDaniel Schwierzeckmy @email_to = ();
57792bca398SDaniel Schwierzeckmy %hash_list_to;
57892bca398SDaniel Schwierzeckmy @list_to = ();
57992bca398SDaniel Schwierzeckmy @scm = ();
58092bca398SDaniel Schwierzeckmy @web = ();
58192bca398SDaniel Schwierzeckmy @subsystem = ();
58292bca398SDaniel Schwierzeckmy @status = ();
58392bca398SDaniel Schwierzeckmy %deduplicate_name_hash = ();
58492bca398SDaniel Schwierzeckmy %deduplicate_address_hash = ();
58592bca398SDaniel Schwierzeck
58692bca398SDaniel Schwierzeckmy @maintainers = get_maintainers();
58792bca398SDaniel Schwierzeck
58892bca398SDaniel Schwierzeckif (@maintainers) {
58992bca398SDaniel Schwierzeck    @maintainers = merge_email(@maintainers);
59092bca398SDaniel Schwierzeck    output(@maintainers);
59192bca398SDaniel Schwierzeck}
59292bca398SDaniel Schwierzeck
59392bca398SDaniel Schwierzeckif ($scm) {
59492bca398SDaniel Schwierzeck    @scm = uniq(@scm);
59592bca398SDaniel Schwierzeck    output(@scm);
59692bca398SDaniel Schwierzeck}
59792bca398SDaniel Schwierzeck
59892bca398SDaniel Schwierzeckif ($status) {
59992bca398SDaniel Schwierzeck    @status = uniq(@status);
60092bca398SDaniel Schwierzeck    output(@status);
60192bca398SDaniel Schwierzeck}
60292bca398SDaniel Schwierzeck
60392bca398SDaniel Schwierzeckif ($subsystem) {
60492bca398SDaniel Schwierzeck    @subsystem = uniq(@subsystem);
60592bca398SDaniel Schwierzeck    output(@subsystem);
60692bca398SDaniel Schwierzeck}
60792bca398SDaniel Schwierzeck
60892bca398SDaniel Schwierzeckif ($web) {
60992bca398SDaniel Schwierzeck    @web = uniq(@web);
61092bca398SDaniel Schwierzeck    output(@web);
61192bca398SDaniel Schwierzeck}
61292bca398SDaniel Schwierzeck
61392bca398SDaniel Schwierzeckexit($exit);
61492bca398SDaniel Schwierzeck
615*2f8ffb06SHeinrich Schuchardtsub self_test {
616*2f8ffb06SHeinrich Schuchardt    my @lsfiles = ();
617*2f8ffb06SHeinrich Schuchardt    my @good_links = ();
618*2f8ffb06SHeinrich Schuchardt    my @bad_links = ();
619*2f8ffb06SHeinrich Schuchardt    my @section_headers = ();
620*2f8ffb06SHeinrich Schuchardt    my $index = 0;
621*2f8ffb06SHeinrich Schuchardt
622*2f8ffb06SHeinrich Schuchardt    @lsfiles = vcs_list_files($lk_path);
623*2f8ffb06SHeinrich Schuchardt
624*2f8ffb06SHeinrich Schuchardt    for my $x (@self_test_info) {
625*2f8ffb06SHeinrich Schuchardt	$index++;
626*2f8ffb06SHeinrich Schuchardt
627*2f8ffb06SHeinrich Schuchardt	## Section header duplication and missing section content
628*2f8ffb06SHeinrich Schuchardt	if (($self_test eq "" || $self_test =~ /\bsections\b/) &&
629*2f8ffb06SHeinrich Schuchardt	    $x->{line} =~ /^\S[^:]/ &&
630*2f8ffb06SHeinrich Schuchardt	    defined $self_test_info[$index] &&
631*2f8ffb06SHeinrich Schuchardt	    $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) {
632*2f8ffb06SHeinrich Schuchardt	    my $has_S = 0;
633*2f8ffb06SHeinrich Schuchardt	    my $has_F = 0;
634*2f8ffb06SHeinrich Schuchardt	    my $has_ML = 0;
635*2f8ffb06SHeinrich Schuchardt	    my $status = "";
636*2f8ffb06SHeinrich Schuchardt	    if (grep(m@^\Q$x->{line}\E@, @section_headers)) {
637*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n");
638*2f8ffb06SHeinrich Schuchardt	    } else {
639*2f8ffb06SHeinrich Schuchardt		push(@section_headers, $x->{line});
640*2f8ffb06SHeinrich Schuchardt	    }
641*2f8ffb06SHeinrich Schuchardt	    my $nextline = $index;
642*2f8ffb06SHeinrich Schuchardt	    while (defined $self_test_info[$nextline] &&
643*2f8ffb06SHeinrich Schuchardt		   $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) {
644*2f8ffb06SHeinrich Schuchardt		my $type = $1;
645*2f8ffb06SHeinrich Schuchardt		my $value = $2;
646*2f8ffb06SHeinrich Schuchardt		if ($type eq "S") {
647*2f8ffb06SHeinrich Schuchardt		    $has_S = 1;
648*2f8ffb06SHeinrich Schuchardt		    $status = $value;
649*2f8ffb06SHeinrich Schuchardt		} elsif ($type eq "F" || $type eq "N") {
650*2f8ffb06SHeinrich Schuchardt		    $has_F = 1;
651*2f8ffb06SHeinrich Schuchardt		} elsif ($type eq "M" || $type eq "R" || $type eq "L") {
652*2f8ffb06SHeinrich Schuchardt		    $has_ML = 1;
653*2f8ffb06SHeinrich Schuchardt		}
654*2f8ffb06SHeinrich Schuchardt		$nextline++;
655*2f8ffb06SHeinrich Schuchardt	    }
656*2f8ffb06SHeinrich Schuchardt	    if (!$has_ML && $status !~ /orphan|obsolete/i) {
657*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n");
658*2f8ffb06SHeinrich Schuchardt	    }
659*2f8ffb06SHeinrich Schuchardt	    if (!$has_S) {
660*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n");
661*2f8ffb06SHeinrich Schuchardt	    }
662*2f8ffb06SHeinrich Schuchardt	    if (!$has_F) {
663*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n");
664*2f8ffb06SHeinrich Schuchardt	    }
665*2f8ffb06SHeinrich Schuchardt	}
666*2f8ffb06SHeinrich Schuchardt
667*2f8ffb06SHeinrich Schuchardt	next if ($x->{line} !~ /^([A-Z]):\s*(.*)/);
668*2f8ffb06SHeinrich Schuchardt
669*2f8ffb06SHeinrich Schuchardt	my $type = $1;
670*2f8ffb06SHeinrich Schuchardt	my $value = $2;
671*2f8ffb06SHeinrich Schuchardt
672*2f8ffb06SHeinrich Schuchardt	## Filename pattern matching
673*2f8ffb06SHeinrich Schuchardt	if (($type eq "F" || $type eq "X") &&
674*2f8ffb06SHeinrich Schuchardt	    ($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
675*2f8ffb06SHeinrich Schuchardt	    $value =~ s@\.@\\\.@g;       ##Convert . to \.
676*2f8ffb06SHeinrich Schuchardt	    $value =~ s/\*/\.\*/g;       ##Convert * to .*
677*2f8ffb06SHeinrich Schuchardt	    $value =~ s/\?/\./g;         ##Convert ? to .
678*2f8ffb06SHeinrich Schuchardt	    ##if pattern is a directory and it lacks a trailing slash, add one
679*2f8ffb06SHeinrich Schuchardt	    if ((-d $value)) {
680*2f8ffb06SHeinrich Schuchardt		$value =~ s@([^/])$@$1/@;
681*2f8ffb06SHeinrich Schuchardt	    }
682*2f8ffb06SHeinrich Schuchardt	    if (!grep(m@^$value@, @lsfiles)) {
683*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n");
684*2f8ffb06SHeinrich Schuchardt	    }
685*2f8ffb06SHeinrich Schuchardt
686*2f8ffb06SHeinrich Schuchardt	## Link reachability
687*2f8ffb06SHeinrich Schuchardt	} elsif (($type eq "W" || $type eq "Q" || $type eq "B") &&
688*2f8ffb06SHeinrich Schuchardt		 $value =~ /^https?:/ &&
689*2f8ffb06SHeinrich Schuchardt		 ($self_test eq "" || $self_test =~ /\blinks\b/)) {
690*2f8ffb06SHeinrich Schuchardt	    next if (grep(m@^\Q$value\E$@, @good_links));
691*2f8ffb06SHeinrich Schuchardt	    my $isbad = 0;
692*2f8ffb06SHeinrich Schuchardt	    if (grep(m@^\Q$value\E$@, @bad_links)) {
693*2f8ffb06SHeinrich Schuchardt	        $isbad = 1;
694*2f8ffb06SHeinrich Schuchardt	    } else {
695*2f8ffb06SHeinrich Schuchardt		my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`;
696*2f8ffb06SHeinrich Schuchardt		if ($? == 0) {
697*2f8ffb06SHeinrich Schuchardt		    push(@good_links, $value);
698*2f8ffb06SHeinrich Schuchardt		} else {
699*2f8ffb06SHeinrich Schuchardt		    push(@bad_links, $value);
700*2f8ffb06SHeinrich Schuchardt		    $isbad = 1;
701*2f8ffb06SHeinrich Schuchardt		}
702*2f8ffb06SHeinrich Schuchardt	    }
703*2f8ffb06SHeinrich Schuchardt	    if ($isbad) {
704*2f8ffb06SHeinrich Schuchardt	        print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
705*2f8ffb06SHeinrich Schuchardt	    }
706*2f8ffb06SHeinrich Schuchardt
707*2f8ffb06SHeinrich Schuchardt	## SCM reachability
708*2f8ffb06SHeinrich Schuchardt	} elsif ($type eq "T" &&
709*2f8ffb06SHeinrich Schuchardt		 ($self_test eq "" || $self_test =~ /\bscm\b/)) {
710*2f8ffb06SHeinrich Schuchardt	    next if (grep(m@^\Q$value\E$@, @good_links));
711*2f8ffb06SHeinrich Schuchardt	    my $isbad = 0;
712*2f8ffb06SHeinrich Schuchardt	    if (grep(m@^\Q$value\E$@, @bad_links)) {
713*2f8ffb06SHeinrich Schuchardt	        $isbad = 1;
714*2f8ffb06SHeinrich Schuchardt            } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) {
715*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n");
716*2f8ffb06SHeinrich Schuchardt	    } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) {
717*2f8ffb06SHeinrich Schuchardt		my $url = $1;
718*2f8ffb06SHeinrich Schuchardt		my $branch = "";
719*2f8ffb06SHeinrich Schuchardt		$branch = $3 if $3;
720*2f8ffb06SHeinrich Schuchardt		my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`;
721*2f8ffb06SHeinrich Schuchardt		if ($? == 0) {
722*2f8ffb06SHeinrich Schuchardt		    push(@good_links, $value);
723*2f8ffb06SHeinrich Schuchardt		} else {
724*2f8ffb06SHeinrich Schuchardt		    push(@bad_links, $value);
725*2f8ffb06SHeinrich Schuchardt		    $isbad = 1;
726*2f8ffb06SHeinrich Schuchardt		}
727*2f8ffb06SHeinrich Schuchardt	    } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) {
728*2f8ffb06SHeinrich Schuchardt		my $url = $1;
729*2f8ffb06SHeinrich Schuchardt		my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`;
730*2f8ffb06SHeinrich Schuchardt		if ($? == 0) {
731*2f8ffb06SHeinrich Schuchardt		    push(@good_links, $value);
732*2f8ffb06SHeinrich Schuchardt		} else {
733*2f8ffb06SHeinrich Schuchardt		    push(@bad_links, $value);
734*2f8ffb06SHeinrich Schuchardt		    $isbad = 1;
735*2f8ffb06SHeinrich Schuchardt		}
736*2f8ffb06SHeinrich Schuchardt	    }
737*2f8ffb06SHeinrich Schuchardt	    if ($isbad) {
738*2f8ffb06SHeinrich Schuchardt		print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
739*2f8ffb06SHeinrich Schuchardt	    }
740*2f8ffb06SHeinrich Schuchardt	}
741*2f8ffb06SHeinrich Schuchardt    }
742*2f8ffb06SHeinrich Schuchardt}
743*2f8ffb06SHeinrich Schuchardt
74459ab72d7SHeinrich Schuchardtsub ignore_email_address {
74559ab72d7SHeinrich Schuchardt    my ($address) = @_;
74659ab72d7SHeinrich Schuchardt
74759ab72d7SHeinrich Schuchardt    foreach my $ignore (@ignore_emails) {
74859ab72d7SHeinrich Schuchardt	return 1 if ($ignore eq $address);
74959ab72d7SHeinrich Schuchardt    }
75059ab72d7SHeinrich Schuchardt
75159ab72d7SHeinrich Schuchardt    return 0;
75259ab72d7SHeinrich Schuchardt}
75359ab72d7SHeinrich Schuchardt
75492bca398SDaniel Schwierzecksub range_is_maintained {
75592bca398SDaniel Schwierzeck    my ($start, $end) = @_;
75692bca398SDaniel Schwierzeck
75792bca398SDaniel Schwierzeck    for (my $i = $start; $i < $end; $i++) {
75892bca398SDaniel Schwierzeck	my $line = $typevalue[$i];
75935729218SHeiko Schocher	if ($line =~ m/^([A-Z]):\s*(.*)/) {
76092bca398SDaniel Schwierzeck	    my $type = $1;
76192bca398SDaniel Schwierzeck	    my $value = $2;
76292bca398SDaniel Schwierzeck	    if ($type eq 'S') {
76392bca398SDaniel Schwierzeck		if ($value =~ /(maintain|support)/i) {
76492bca398SDaniel Schwierzeck		    return 1;
76592bca398SDaniel Schwierzeck		}
76692bca398SDaniel Schwierzeck	    }
76792bca398SDaniel Schwierzeck	}
76892bca398SDaniel Schwierzeck    }
76992bca398SDaniel Schwierzeck    return 0;
77092bca398SDaniel Schwierzeck}
77192bca398SDaniel Schwierzeck
77292bca398SDaniel Schwierzecksub range_has_maintainer {
77392bca398SDaniel Schwierzeck    my ($start, $end) = @_;
77492bca398SDaniel Schwierzeck
77592bca398SDaniel Schwierzeck    for (my $i = $start; $i < $end; $i++) {
77692bca398SDaniel Schwierzeck	my $line = $typevalue[$i];
77735729218SHeiko Schocher	if ($line =~ m/^([A-Z]):\s*(.*)/) {
77892bca398SDaniel Schwierzeck	    my $type = $1;
77992bca398SDaniel Schwierzeck	    my $value = $2;
78092bca398SDaniel Schwierzeck	    if ($type eq 'M') {
78192bca398SDaniel Schwierzeck		return 1;
78292bca398SDaniel Schwierzeck	    }
78392bca398SDaniel Schwierzeck	}
78492bca398SDaniel Schwierzeck    }
78592bca398SDaniel Schwierzeck    return 0;
78692bca398SDaniel Schwierzeck}
78792bca398SDaniel Schwierzeck
78892bca398SDaniel Schwierzecksub get_maintainers {
78992bca398SDaniel Schwierzeck    %email_hash_name = ();
79092bca398SDaniel Schwierzeck    %email_hash_address = ();
79192bca398SDaniel Schwierzeck    %commit_author_hash = ();
79292bca398SDaniel Schwierzeck    %commit_signer_hash = ();
79392bca398SDaniel Schwierzeck    @email_to = ();
79492bca398SDaniel Schwierzeck    %hash_list_to = ();
79592bca398SDaniel Schwierzeck    @list_to = ();
79692bca398SDaniel Schwierzeck    @scm = ();
79792bca398SDaniel Schwierzeck    @web = ();
79892bca398SDaniel Schwierzeck    @subsystem = ();
79992bca398SDaniel Schwierzeck    @status = ();
80092bca398SDaniel Schwierzeck    %deduplicate_name_hash = ();
80192bca398SDaniel Schwierzeck    %deduplicate_address_hash = ();
80292bca398SDaniel Schwierzeck    if ($email_git_all_signature_types) {
80392bca398SDaniel Schwierzeck	$signature_pattern = "(.+?)[Bb][Yy]:";
80492bca398SDaniel Schwierzeck    } else {
80592bca398SDaniel Schwierzeck	$signature_pattern = "\(" . join("|", @signature_tags) . "\)";
80692bca398SDaniel Schwierzeck    }
80792bca398SDaniel Schwierzeck
80892bca398SDaniel Schwierzeck    # Find responsible parties
80992bca398SDaniel Schwierzeck
81092bca398SDaniel Schwierzeck    my %exact_pattern_match_hash = ();
81192bca398SDaniel Schwierzeck
81292bca398SDaniel Schwierzeck    foreach my $file (@files) {
81392bca398SDaniel Schwierzeck
81492bca398SDaniel Schwierzeck	my %hash;
81592bca398SDaniel Schwierzeck	my $tvi = find_first_section();
81692bca398SDaniel Schwierzeck	while ($tvi < @typevalue) {
81792bca398SDaniel Schwierzeck	    my $start = find_starting_index($tvi);
81892bca398SDaniel Schwierzeck	    my $end = find_ending_index($tvi);
81992bca398SDaniel Schwierzeck	    my $exclude = 0;
82092bca398SDaniel Schwierzeck	    my $i;
82192bca398SDaniel Schwierzeck
82292bca398SDaniel Schwierzeck	    #Do not match excluded file patterns
82392bca398SDaniel Schwierzeck
82492bca398SDaniel Schwierzeck	    for ($i = $start; $i < $end; $i++) {
82592bca398SDaniel Schwierzeck		my $line = $typevalue[$i];
82635729218SHeiko Schocher		if ($line =~ m/^([A-Z]):\s*(.*)/) {
82792bca398SDaniel Schwierzeck		    my $type = $1;
82892bca398SDaniel Schwierzeck		    my $value = $2;
82992bca398SDaniel Schwierzeck		    if ($type eq 'X') {
83092bca398SDaniel Schwierzeck			if (file_match_pattern($file, $value)) {
83192bca398SDaniel Schwierzeck			    $exclude = 1;
83292bca398SDaniel Schwierzeck			    last;
83392bca398SDaniel Schwierzeck			}
83492bca398SDaniel Schwierzeck		    }
83592bca398SDaniel Schwierzeck		}
83692bca398SDaniel Schwierzeck	    }
83792bca398SDaniel Schwierzeck
83892bca398SDaniel Schwierzeck	    if (!$exclude) {
83992bca398SDaniel Schwierzeck		for ($i = $start; $i < $end; $i++) {
84092bca398SDaniel Schwierzeck		    my $line = $typevalue[$i];
84135729218SHeiko Schocher		    if ($line =~ m/^([A-Z]):\s*(.*)/) {
84292bca398SDaniel Schwierzeck			my $type = $1;
84392bca398SDaniel Schwierzeck			my $value = $2;
84492bca398SDaniel Schwierzeck			if ($type eq 'F') {
84592bca398SDaniel Schwierzeck			    if (file_match_pattern($file, $value)) {
84692bca398SDaniel Schwierzeck				my $value_pd = ($value =~ tr@/@@);
84792bca398SDaniel Schwierzeck				my $file_pd = ($file  =~ tr@/@@);
84892bca398SDaniel Schwierzeck				$value_pd++ if (substr($value,-1,1) ne "/");
84992bca398SDaniel Schwierzeck				$value_pd = -1 if ($value =~ /^\.\*/);
85092bca398SDaniel Schwierzeck				if ($value_pd >= $file_pd &&
85192bca398SDaniel Schwierzeck				    range_is_maintained($start, $end) &&
85292bca398SDaniel Schwierzeck				    range_has_maintainer($start, $end)) {
85392bca398SDaniel Schwierzeck				    $exact_pattern_match_hash{$file} = 1;
85492bca398SDaniel Schwierzeck				}
85592bca398SDaniel Schwierzeck				if ($pattern_depth == 0 ||
85692bca398SDaniel Schwierzeck				    (($file_pd - $value_pd) < $pattern_depth)) {
85792bca398SDaniel Schwierzeck				    $hash{$tvi} = $value_pd;
85892bca398SDaniel Schwierzeck				}
85992bca398SDaniel Schwierzeck			    }
86092bca398SDaniel Schwierzeck			} elsif ($type eq 'N') {
86192bca398SDaniel Schwierzeck			    if ($file =~ m/$value/x) {
86292bca398SDaniel Schwierzeck				$hash{$tvi} = 0;
86392bca398SDaniel Schwierzeck			    }
86492bca398SDaniel Schwierzeck			}
86592bca398SDaniel Schwierzeck		    }
86692bca398SDaniel Schwierzeck		}
86792bca398SDaniel Schwierzeck	    }
86892bca398SDaniel Schwierzeck	    $tvi = $end + 1;
86992bca398SDaniel Schwierzeck	}
87092bca398SDaniel Schwierzeck
87192bca398SDaniel Schwierzeck	foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
87292bca398SDaniel Schwierzeck	    add_categories($line);
87392bca398SDaniel Schwierzeck	    if ($sections) {
87492bca398SDaniel Schwierzeck		my $i;
87592bca398SDaniel Schwierzeck		my $start = find_starting_index($line);
87692bca398SDaniel Schwierzeck		my $end = find_ending_index($line);
87792bca398SDaniel Schwierzeck		for ($i = $start; $i < $end; $i++) {
87892bca398SDaniel Schwierzeck		    my $line = $typevalue[$i];
87992bca398SDaniel Schwierzeck		    if ($line =~ /^[FX]:/) {		##Restore file patterns
88092bca398SDaniel Schwierzeck			$line =~ s/([^\\])\.([^\*])/$1\?$2/g;
88192bca398SDaniel Schwierzeck			$line =~ s/([^\\])\.$/$1\?/g;	##Convert . back to ?
88292bca398SDaniel Schwierzeck			$line =~ s/\\\./\./g;       	##Convert \. to .
88392bca398SDaniel Schwierzeck			$line =~ s/\.\*/\*/g;       	##Convert .* to *
88492bca398SDaniel Schwierzeck		    }
88559ab72d7SHeinrich Schuchardt		    my $count = $line =~ s/^([A-Z]):/$1:\t/g;
88659ab72d7SHeinrich Schuchardt		    if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
88792bca398SDaniel Schwierzeck			print("$line\n");
88892bca398SDaniel Schwierzeck		    }
88959ab72d7SHeinrich Schuchardt		}
89092bca398SDaniel Schwierzeck		print("\n");
89192bca398SDaniel Schwierzeck	    }
89292bca398SDaniel Schwierzeck	}
89392bca398SDaniel Schwierzeck    }
89492bca398SDaniel Schwierzeck
89592bca398SDaniel Schwierzeck    if ($keywords) {
89692bca398SDaniel Schwierzeck	@keyword_tvi = sort_and_uniq(@keyword_tvi);
89792bca398SDaniel Schwierzeck	foreach my $line (@keyword_tvi) {
89892bca398SDaniel Schwierzeck	    add_categories($line);
89992bca398SDaniel Schwierzeck	}
90092bca398SDaniel Schwierzeck    }
90192bca398SDaniel Schwierzeck
90292bca398SDaniel Schwierzeck    foreach my $email (@email_to, @list_to) {
90392bca398SDaniel Schwierzeck	$email->[0] = deduplicate_email($email->[0]);
90492bca398SDaniel Schwierzeck    }
90592bca398SDaniel Schwierzeck
90692bca398SDaniel Schwierzeck    foreach my $file (@files) {
90792bca398SDaniel Schwierzeck	if ($email &&
90892bca398SDaniel Schwierzeck	    ($email_git || ($email_git_fallback &&
90992bca398SDaniel Schwierzeck			    !$exact_pattern_match_hash{$file}))) {
91092bca398SDaniel Schwierzeck	    vcs_file_signoffs($file);
91192bca398SDaniel Schwierzeck	}
91292bca398SDaniel Schwierzeck	if ($email && $email_git_blame) {
91392bca398SDaniel Schwierzeck	    vcs_file_blame($file);
91492bca398SDaniel Schwierzeck	}
91592bca398SDaniel Schwierzeck    }
91692bca398SDaniel Schwierzeck
91792bca398SDaniel Schwierzeck    if ($email) {
91892bca398SDaniel Schwierzeck	foreach my $chief (@penguin_chief) {
91992bca398SDaniel Schwierzeck	    if ($chief =~ m/^(.*):(.*)/) {
92092bca398SDaniel Schwierzeck		my $email_address;
92192bca398SDaniel Schwierzeck
92292bca398SDaniel Schwierzeck		$email_address = format_email($1, $2, $email_usename);
92392bca398SDaniel Schwierzeck		if ($email_git_penguin_chiefs) {
92492bca398SDaniel Schwierzeck		    push(@email_to, [$email_address, 'chief penguin']);
92592bca398SDaniel Schwierzeck		} else {
92692bca398SDaniel Schwierzeck		    @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
92792bca398SDaniel Schwierzeck		}
92892bca398SDaniel Schwierzeck	    }
92992bca398SDaniel Schwierzeck	}
93092bca398SDaniel Schwierzeck
93192bca398SDaniel Schwierzeck	foreach my $email (@file_emails) {
93292bca398SDaniel Schwierzeck	    my ($name, $address) = parse_email($email);
93392bca398SDaniel Schwierzeck
93492bca398SDaniel Schwierzeck	    my $tmp_email = format_email($name, $address, $email_usename);
93592bca398SDaniel Schwierzeck	    push_email_address($tmp_email, '');
93692bca398SDaniel Schwierzeck	    add_role($tmp_email, 'in file');
93792bca398SDaniel Schwierzeck	}
93892bca398SDaniel Schwierzeck    }
93992bca398SDaniel Schwierzeck
94092bca398SDaniel Schwierzeck    my @to = ();
94192bca398SDaniel Schwierzeck    if ($email || $email_list) {
94292bca398SDaniel Schwierzeck	if ($email) {
94392bca398SDaniel Schwierzeck	    @to = (@to, @email_to);
94492bca398SDaniel Schwierzeck	}
94592bca398SDaniel Schwierzeck	if ($email_list) {
94692bca398SDaniel Schwierzeck	    @to = (@to, @list_to);
94792bca398SDaniel Schwierzeck	}
94892bca398SDaniel Schwierzeck    }
94992bca398SDaniel Schwierzeck
95092bca398SDaniel Schwierzeck    if ($interactive) {
95192bca398SDaniel Schwierzeck	@to = interactive_get_maintainers(\@to);
95292bca398SDaniel Schwierzeck    }
95392bca398SDaniel Schwierzeck
95492bca398SDaniel Schwierzeck    return @to;
95592bca398SDaniel Schwierzeck}
95692bca398SDaniel Schwierzeck
95792bca398SDaniel Schwierzecksub file_match_pattern {
95892bca398SDaniel Schwierzeck    my ($file, $pattern) = @_;
95992bca398SDaniel Schwierzeck    if (substr($pattern, -1) eq "/") {
96092bca398SDaniel Schwierzeck	if ($file =~ m@^$pattern@) {
96192bca398SDaniel Schwierzeck	    return 1;
96292bca398SDaniel Schwierzeck	}
96392bca398SDaniel Schwierzeck    } else {
96492bca398SDaniel Schwierzeck	if ($file =~ m@^$pattern@) {
96592bca398SDaniel Schwierzeck	    my $s1 = ($file =~ tr@/@@);
96692bca398SDaniel Schwierzeck	    my $s2 = ($pattern =~ tr@/@@);
96792bca398SDaniel Schwierzeck	    if ($s1 == $s2) {
96892bca398SDaniel Schwierzeck		return 1;
96992bca398SDaniel Schwierzeck	    }
97092bca398SDaniel Schwierzeck	}
97192bca398SDaniel Schwierzeck    }
97292bca398SDaniel Schwierzeck    return 0;
97392bca398SDaniel Schwierzeck}
97492bca398SDaniel Schwierzeck
97592bca398SDaniel Schwierzecksub usage {
97692bca398SDaniel Schwierzeck    print <<EOT;
97792bca398SDaniel Schwierzeckusage: $P [options] patchfile
97892bca398SDaniel Schwierzeck       $P [options] -f file|directory
97992bca398SDaniel Schwierzeckversion: $V
98092bca398SDaniel Schwierzeck
98192bca398SDaniel SchwierzeckMAINTAINER field selection options:
98292bca398SDaniel Schwierzeck  --email => print email address(es) if any
98392bca398SDaniel Schwierzeck    --git => include recent git \*-by: signers
98492bca398SDaniel Schwierzeck    --git-all-signature-types => include signers regardless of signature type
98592bca398SDaniel Schwierzeck        or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
98692bca398SDaniel Schwierzeck    --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
98792bca398SDaniel Schwierzeck    --git-chief-penguins => include ${penguin_chiefs}
98892bca398SDaniel Schwierzeck    --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
98992bca398SDaniel Schwierzeck    --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
99092bca398SDaniel Schwierzeck    --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
99192bca398SDaniel Schwierzeck    --git-blame => use git blame to find modified commits for patch or file
99259ab72d7SHeinrich Schuchardt    --git-blame-signatures => when used with --git-blame, also include all commit signers
99392bca398SDaniel Schwierzeck    --git-since => git history to use (default: $email_git_since)
99492bca398SDaniel Schwierzeck    --hg-since => hg history to use (default: $email_hg_since)
99592bca398SDaniel Schwierzeck    --interactive => display a menu (mostly useful if used with the --git option)
99692bca398SDaniel Schwierzeck    --m => include maintainer(s) if any
99759ab72d7SHeinrich Schuchardt    --r => include reviewer(s) if any
99892bca398SDaniel Schwierzeck    --n => include name 'Full Name <addr\@domain.tld>'
99992bca398SDaniel Schwierzeck    --l => include list(s) if any
100092bca398SDaniel Schwierzeck    --s => include subscriber only list(s) if any
100192bca398SDaniel Schwierzeck    --remove-duplicates => minimize duplicate email names/addresses
100292bca398SDaniel Schwierzeck    --roles => show roles (status:subsystem, git-signer, list, etc...)
100392bca398SDaniel Schwierzeck    --rolestats => show roles and statistics (commits/total_commits, %)
100492bca398SDaniel Schwierzeck    --file-emails => add email addresses found in -f file (default: 0 (off))
100592bca398SDaniel Schwierzeck  --scm => print SCM tree(s) if any
100692bca398SDaniel Schwierzeck  --status => print status if any
100792bca398SDaniel Schwierzeck  --subsystem => print subsystem name if any
100892bca398SDaniel Schwierzeck  --web => print website(s) if any
100992bca398SDaniel Schwierzeck
101092bca398SDaniel SchwierzeckOutput type options:
101192bca398SDaniel Schwierzeck  --separator [, ] => separator for multiple entries on 1 line
101292bca398SDaniel Schwierzeck    using --separator also sets --nomultiline if --separator is not [, ]
101392bca398SDaniel Schwierzeck  --multiline => print 1 entry per line
101492bca398SDaniel Schwierzeck
101592bca398SDaniel SchwierzeckOther options:
101692bca398SDaniel Schwierzeck  --pattern-depth => Number of pattern directory traversals (default: 0 (all))
101792bca398SDaniel Schwierzeck  --keywords => scan patch for keywords (default: $keywords)
101892bca398SDaniel Schwierzeck  --sections => print all of the subsystem sections with pattern matches
101959ab72d7SHeinrich Schuchardt  --letters => print all matching 'letter' types from all matching sections
102092bca398SDaniel Schwierzeck  --mailmap => use .mailmap file (default: $email_use_mailmap)
1021*2f8ffb06SHeinrich Schuchardt  --self-test => show potential issues with MAINTAINERS file content
102292bca398SDaniel Schwierzeck  --version => show version
102392bca398SDaniel Schwierzeck  --help => show this help information
102492bca398SDaniel Schwierzeck
102592bca398SDaniel SchwierzeckDefault options:
102659ab72d7SHeinrich Schuchardt  [--email --nogit --git-fallback --m --r --n --l --multiline --pattern-depth=0
102792bca398SDaniel Schwierzeck   --remove-duplicates --rolestats]
102892bca398SDaniel Schwierzeck
102992bca398SDaniel SchwierzeckNotes:
103092bca398SDaniel Schwierzeck  Using "-f directory" may give unexpected results:
103192bca398SDaniel Schwierzeck      Used with "--git", git signators for _all_ files in and below
103292bca398SDaniel Schwierzeck          directory are examined as git recurses directories.
103392bca398SDaniel Schwierzeck          Any specified X: (exclude) pattern matches are _not_ ignored.
103492bca398SDaniel Schwierzeck      Used with "--nogit", directory is used as a pattern match,
103592bca398SDaniel Schwierzeck          no individual file within the directory or subdirectory
103692bca398SDaniel Schwierzeck          is matched.
103792bca398SDaniel Schwierzeck      Used with "--git-blame", does not iterate all files in directory
103892bca398SDaniel Schwierzeck  Using "--git-blame" is slow and may add old committers and authors
103992bca398SDaniel Schwierzeck      that are no longer active maintainers to the output.
104092bca398SDaniel Schwierzeck  Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
104192bca398SDaniel Schwierzeck      other automated tools that expect only ["name"] <email address>
104292bca398SDaniel Schwierzeck      may not work because of additional output after <email address>.
104392bca398SDaniel Schwierzeck  Using "--rolestats" and "--git-blame" shows the #/total=% commits,
104492bca398SDaniel Schwierzeck      not the percentage of the entire file authored.  # of commits is
104592bca398SDaniel Schwierzeck      not a good measure of amount of code authored.  1 major commit may
104692bca398SDaniel Schwierzeck      contain a thousand lines, 5 trivial commits may modify a single line.
104792bca398SDaniel Schwierzeck  If git is not installed, but mercurial (hg) is installed and an .hg
104892bca398SDaniel Schwierzeck      repository exists, the following options apply to mercurial:
104992bca398SDaniel Schwierzeck          --git,
105092bca398SDaniel Schwierzeck          --git-min-signatures, --git-max-maintainers, --git-min-percent, and
105192bca398SDaniel Schwierzeck          --git-blame
105292bca398SDaniel Schwierzeck      Use --hg-since not --git-since to control date selection
105392bca398SDaniel Schwierzeck  File ".get_maintainer.conf", if it exists in the linux kernel source root
105492bca398SDaniel Schwierzeck      directory, can change whatever get_maintainer defaults are desired.
105592bca398SDaniel Schwierzeck      Entries in this file can be any command line argument.
105692bca398SDaniel Schwierzeck      This file is prepended to any additional command line arguments.
105792bca398SDaniel Schwierzeck      Multiple lines and # comments are allowed.
105859ab72d7SHeinrich Schuchardt  Most options have both positive and negative forms.
105959ab72d7SHeinrich Schuchardt      The negative forms for --<foo> are --no<foo> and --no-<foo>.
106059ab72d7SHeinrich Schuchardt
106192bca398SDaniel SchwierzeckEOT
106292bca398SDaniel Schwierzeck}
106392bca398SDaniel Schwierzeck
106492bca398SDaniel Schwierzecksub top_of_kernel_tree {
106592bca398SDaniel Schwierzeck    my ($lk_path) = @_;
106692bca398SDaniel Schwierzeck
106792bca398SDaniel Schwierzeck    if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
106892bca398SDaniel Schwierzeck	$lk_path .= "/";
106992bca398SDaniel Schwierzeck    }
107027e77183SDaniel Schwierzeck    if (   (-f "${lk_path}Kbuild")
107159ab72d7SHeinrich Schuchardt	&& (-e "${lk_path}MAINTAINERS")
107292bca398SDaniel Schwierzeck	&& (-f "${lk_path}Makefile")
107392bca398SDaniel Schwierzeck	&& (-f "${lk_path}README")
107492bca398SDaniel Schwierzeck	&& (-d "${lk_path}arch")
1075ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}board")
1076ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}common")
1077ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}doc")
107892bca398SDaniel Schwierzeck	&& (-d "${lk_path}drivers")
1079ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}dts")
108092bca398SDaniel Schwierzeck	&& (-d "${lk_path}fs")
108192bca398SDaniel Schwierzeck	&& (-d "${lk_path}lib")
1082ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}include")
1083ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}net")
1084ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}post")
1085ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}scripts")
1086ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}test")
1087ee360cd2SDaniel Schwierzeck	&& (-d "${lk_path}tools")) {
108892bca398SDaniel Schwierzeck	return 1;
108992bca398SDaniel Schwierzeck    }
109092bca398SDaniel Schwierzeck    return 0;
109192bca398SDaniel Schwierzeck}
109292bca398SDaniel Schwierzeck
109392bca398SDaniel Schwierzecksub parse_email {
109492bca398SDaniel Schwierzeck    my ($formatted_email) = @_;
109592bca398SDaniel Schwierzeck
109692bca398SDaniel Schwierzeck    my $name = "";
109792bca398SDaniel Schwierzeck    my $address = "";
109892bca398SDaniel Schwierzeck
109992bca398SDaniel Schwierzeck    if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
110092bca398SDaniel Schwierzeck	$name = $1;
110192bca398SDaniel Schwierzeck	$address = $2;
110292bca398SDaniel Schwierzeck    } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
110392bca398SDaniel Schwierzeck	$address = $1;
110492bca398SDaniel Schwierzeck    } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
110592bca398SDaniel Schwierzeck	$address = $1;
110692bca398SDaniel Schwierzeck    }
110792bca398SDaniel Schwierzeck
110892bca398SDaniel Schwierzeck    $name =~ s/^\s+|\s+$//g;
110992bca398SDaniel Schwierzeck    $name =~ s/^\"|\"$//g;
111092bca398SDaniel Schwierzeck    $address =~ s/^\s+|\s+$//g;
111192bca398SDaniel Schwierzeck
111292bca398SDaniel Schwierzeck    if ($name =~ /[^\w \-]/i) {  	 ##has "must quote" chars
111392bca398SDaniel Schwierzeck	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
111492bca398SDaniel Schwierzeck	$name = "\"$name\"";
111592bca398SDaniel Schwierzeck    }
111692bca398SDaniel Schwierzeck
111792bca398SDaniel Schwierzeck    return ($name, $address);
111892bca398SDaniel Schwierzeck}
111992bca398SDaniel Schwierzeck
112092bca398SDaniel Schwierzecksub format_email {
112192bca398SDaniel Schwierzeck    my ($name, $address, $usename) = @_;
112292bca398SDaniel Schwierzeck
112392bca398SDaniel Schwierzeck    my $formatted_email;
112492bca398SDaniel Schwierzeck
112592bca398SDaniel Schwierzeck    $name =~ s/^\s+|\s+$//g;
112692bca398SDaniel Schwierzeck    $name =~ s/^\"|\"$//g;
112792bca398SDaniel Schwierzeck    $address =~ s/^\s+|\s+$//g;
112892bca398SDaniel Schwierzeck
112992bca398SDaniel Schwierzeck    if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
113092bca398SDaniel Schwierzeck	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
113192bca398SDaniel Schwierzeck	$name = "\"$name\"";
113292bca398SDaniel Schwierzeck    }
113392bca398SDaniel Schwierzeck
113492bca398SDaniel Schwierzeck    if ($usename) {
113592bca398SDaniel Schwierzeck	if ("$name" eq "") {
113692bca398SDaniel Schwierzeck	    $formatted_email = "$address";
113792bca398SDaniel Schwierzeck	} else {
113892bca398SDaniel Schwierzeck	    $formatted_email = "$name <$address>";
113992bca398SDaniel Schwierzeck	}
114092bca398SDaniel Schwierzeck    } else {
114192bca398SDaniel Schwierzeck	$formatted_email = $address;
114292bca398SDaniel Schwierzeck    }
114392bca398SDaniel Schwierzeck
114492bca398SDaniel Schwierzeck    return $formatted_email;
114592bca398SDaniel Schwierzeck}
114692bca398SDaniel Schwierzeck
114792bca398SDaniel Schwierzecksub find_first_section {
114892bca398SDaniel Schwierzeck    my $index = 0;
114992bca398SDaniel Schwierzeck
115092bca398SDaniel Schwierzeck    while ($index < @typevalue) {
115192bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
115235729218SHeiko Schocher	if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
115392bca398SDaniel Schwierzeck	    last;
115492bca398SDaniel Schwierzeck	}
115592bca398SDaniel Schwierzeck	$index++;
115692bca398SDaniel Schwierzeck    }
115792bca398SDaniel Schwierzeck
115892bca398SDaniel Schwierzeck    return $index;
115992bca398SDaniel Schwierzeck}
116092bca398SDaniel Schwierzeck
116192bca398SDaniel Schwierzecksub find_starting_index {
116292bca398SDaniel Schwierzeck    my ($index) = @_;
116392bca398SDaniel Schwierzeck
116492bca398SDaniel Schwierzeck    while ($index > 0) {
116592bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
116635729218SHeiko Schocher	if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
116792bca398SDaniel Schwierzeck	    last;
116892bca398SDaniel Schwierzeck	}
116992bca398SDaniel Schwierzeck	$index--;
117092bca398SDaniel Schwierzeck    }
117192bca398SDaniel Schwierzeck
117292bca398SDaniel Schwierzeck    return $index;
117392bca398SDaniel Schwierzeck}
117492bca398SDaniel Schwierzeck
117592bca398SDaniel Schwierzecksub find_ending_index {
117692bca398SDaniel Schwierzeck    my ($index) = @_;
117792bca398SDaniel Schwierzeck
117892bca398SDaniel Schwierzeck    while ($index < @typevalue) {
117992bca398SDaniel Schwierzeck	my $tv = $typevalue[$index];
118035729218SHeiko Schocher	if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
118192bca398SDaniel Schwierzeck	    last;
118292bca398SDaniel Schwierzeck	}
118392bca398SDaniel Schwierzeck	$index++;
118492bca398SDaniel Schwierzeck    }
118592bca398SDaniel Schwierzeck
118692bca398SDaniel Schwierzeck    return $index;
118792bca398SDaniel Schwierzeck}
118892bca398SDaniel Schwierzeck
118959ab72d7SHeinrich Schuchardtsub get_subsystem_name {
119059ab72d7SHeinrich Schuchardt    my ($index) = @_;
119159ab72d7SHeinrich Schuchardt
119259ab72d7SHeinrich Schuchardt    my $start = find_starting_index($index);
119359ab72d7SHeinrich Schuchardt
119459ab72d7SHeinrich Schuchardt    my $subsystem = $typevalue[$start];
119559ab72d7SHeinrich Schuchardt    if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
119659ab72d7SHeinrich Schuchardt	$subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
119759ab72d7SHeinrich Schuchardt	$subsystem =~ s/\s*$//;
119859ab72d7SHeinrich Schuchardt	$subsystem = $subsystem . "...";
119959ab72d7SHeinrich Schuchardt    }
120059ab72d7SHeinrich Schuchardt    return $subsystem;
120159ab72d7SHeinrich Schuchardt}
120259ab72d7SHeinrich Schuchardt
120392bca398SDaniel Schwierzecksub get_maintainer_role {
120492bca398SDaniel Schwierzeck    my ($index) = @_;
120592bca398SDaniel Schwierzeck
120692bca398SDaniel Schwierzeck    my $i;
120792bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
120892bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
120992bca398SDaniel Schwierzeck
121092bca398SDaniel Schwierzeck    my $role = "unknown";
121159ab72d7SHeinrich Schuchardt    my $subsystem = get_subsystem_name($index);
121292bca398SDaniel Schwierzeck
121392bca398SDaniel Schwierzeck    for ($i = $start + 1; $i < $end; $i++) {
121492bca398SDaniel Schwierzeck	my $tv = $typevalue[$i];
121535729218SHeiko Schocher	if ($tv =~ m/^([A-Z]):\s*(.*)/) {
121692bca398SDaniel Schwierzeck	    my $ptype = $1;
121792bca398SDaniel Schwierzeck	    my $pvalue = $2;
121892bca398SDaniel Schwierzeck	    if ($ptype eq "S") {
121992bca398SDaniel Schwierzeck		$role = $pvalue;
122092bca398SDaniel Schwierzeck	    }
122192bca398SDaniel Schwierzeck	}
122292bca398SDaniel Schwierzeck    }
122392bca398SDaniel Schwierzeck
122492bca398SDaniel Schwierzeck    $role = lc($role);
122592bca398SDaniel Schwierzeck    if      ($role eq "supported") {
122692bca398SDaniel Schwierzeck	$role = "supporter";
122792bca398SDaniel Schwierzeck    } elsif ($role eq "maintained") {
122892bca398SDaniel Schwierzeck	$role = "maintainer";
122992bca398SDaniel Schwierzeck    } elsif ($role eq "odd fixes") {
123092bca398SDaniel Schwierzeck	$role = "odd fixer";
123192bca398SDaniel Schwierzeck    } elsif ($role eq "orphan") {
123292bca398SDaniel Schwierzeck	$role = "orphan minder";
123392bca398SDaniel Schwierzeck    } elsif ($role eq "obsolete") {
123492bca398SDaniel Schwierzeck	$role = "obsolete minder";
123592bca398SDaniel Schwierzeck    } elsif ($role eq "buried alive in reporters") {
123692bca398SDaniel Schwierzeck	$role = "chief penguin";
123792bca398SDaniel Schwierzeck    }
123892bca398SDaniel Schwierzeck
123992bca398SDaniel Schwierzeck    return $role . ":" . $subsystem;
124092bca398SDaniel Schwierzeck}
124192bca398SDaniel Schwierzeck
124292bca398SDaniel Schwierzecksub get_list_role {
124392bca398SDaniel Schwierzeck    my ($index) = @_;
124492bca398SDaniel Schwierzeck
124559ab72d7SHeinrich Schuchardt    my $subsystem = get_subsystem_name($index);
124692bca398SDaniel Schwierzeck
124792bca398SDaniel Schwierzeck    if ($subsystem eq "THE REST") {
124892bca398SDaniel Schwierzeck	$subsystem = "";
124992bca398SDaniel Schwierzeck    }
125092bca398SDaniel Schwierzeck
125192bca398SDaniel Schwierzeck    return $subsystem;
125292bca398SDaniel Schwierzeck}
125392bca398SDaniel Schwierzeck
125492bca398SDaniel Schwierzecksub add_categories {
125592bca398SDaniel Schwierzeck    my ($index) = @_;
125692bca398SDaniel Schwierzeck
125792bca398SDaniel Schwierzeck    my $i;
125892bca398SDaniel Schwierzeck    my $start = find_starting_index($index);
125992bca398SDaniel Schwierzeck    my $end = find_ending_index($index);
126092bca398SDaniel Schwierzeck
126192bca398SDaniel Schwierzeck    push(@subsystem, $typevalue[$start]);
126292bca398SDaniel Schwierzeck
126392bca398SDaniel Schwierzeck    for ($i = $start + 1; $i < $end; $i++) {
126492bca398SDaniel Schwierzeck	my $tv = $typevalue[$i];
126535729218SHeiko Schocher	if ($tv =~ m/^([A-Z]):\s*(.*)/) {
126692bca398SDaniel Schwierzeck	    my $ptype = $1;
126792bca398SDaniel Schwierzeck	    my $pvalue = $2;
126892bca398SDaniel Schwierzeck	    if ($ptype eq "L") {
126992bca398SDaniel Schwierzeck		my $list_address = $pvalue;
127092bca398SDaniel Schwierzeck		my $list_additional = "";
127192bca398SDaniel Schwierzeck		my $list_role = get_list_role($i);
127292bca398SDaniel Schwierzeck
127392bca398SDaniel Schwierzeck		if ($list_role ne "") {
127492bca398SDaniel Schwierzeck		    $list_role = ":" . $list_role;
127592bca398SDaniel Schwierzeck		}
127692bca398SDaniel Schwierzeck		if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
127792bca398SDaniel Schwierzeck		    $list_address = $1;
127892bca398SDaniel Schwierzeck		    $list_additional = $2;
127992bca398SDaniel Schwierzeck		}
128092bca398SDaniel Schwierzeck		if ($list_additional =~ m/subscribers-only/) {
128192bca398SDaniel Schwierzeck		    if ($email_subscriber_list) {
128292bca398SDaniel Schwierzeck			if (!$hash_list_to{lc($list_address)}) {
128392bca398SDaniel Schwierzeck			    $hash_list_to{lc($list_address)} = 1;
128492bca398SDaniel Schwierzeck			    push(@list_to, [$list_address,
128592bca398SDaniel Schwierzeck					    "subscriber list${list_role}"]);
128692bca398SDaniel Schwierzeck			}
128792bca398SDaniel Schwierzeck		    }
128892bca398SDaniel Schwierzeck		} else {
128992bca398SDaniel Schwierzeck		    if ($email_list) {
129092bca398SDaniel Schwierzeck			if (!$hash_list_to{lc($list_address)}) {
129192bca398SDaniel Schwierzeck			    $hash_list_to{lc($list_address)} = 1;
129292bca398SDaniel Schwierzeck			    if ($list_additional =~ m/moderated/) {
129392bca398SDaniel Schwierzeck				push(@list_to, [$list_address,
129492bca398SDaniel Schwierzeck						"moderated list${list_role}"]);
129592bca398SDaniel Schwierzeck			    } else {
129692bca398SDaniel Schwierzeck				push(@list_to, [$list_address,
129792bca398SDaniel Schwierzeck						"open list${list_role}"]);
129892bca398SDaniel Schwierzeck			    }
129992bca398SDaniel Schwierzeck			}
130092bca398SDaniel Schwierzeck		    }
130192bca398SDaniel Schwierzeck		}
130292bca398SDaniel Schwierzeck	    } elsif ($ptype eq "M") {
130392bca398SDaniel Schwierzeck		my ($name, $address) = parse_email($pvalue);
130492bca398SDaniel Schwierzeck		if ($name eq "") {
130592bca398SDaniel Schwierzeck		    if ($i > 0) {
130692bca398SDaniel Schwierzeck			my $tv = $typevalue[$i - 1];
130735729218SHeiko Schocher			if ($tv =~ m/^([A-Z]):\s*(.*)/) {
130892bca398SDaniel Schwierzeck			    if ($1 eq "P") {
130992bca398SDaniel Schwierzeck				$name = $2;
131092bca398SDaniel Schwierzeck				$pvalue = format_email($name, $address, $email_usename);
131192bca398SDaniel Schwierzeck			    }
131292bca398SDaniel Schwierzeck			}
131392bca398SDaniel Schwierzeck		    }
131492bca398SDaniel Schwierzeck		}
131592bca398SDaniel Schwierzeck		if ($email_maintainer) {
131692bca398SDaniel Schwierzeck		    my $role = get_maintainer_role($i);
131792bca398SDaniel Schwierzeck		    push_email_addresses($pvalue, $role);
131892bca398SDaniel Schwierzeck		}
131959ab72d7SHeinrich Schuchardt	    } elsif ($ptype eq "R") {
132059ab72d7SHeinrich Schuchardt		my ($name, $address) = parse_email($pvalue);
132159ab72d7SHeinrich Schuchardt		if ($name eq "") {
132259ab72d7SHeinrich Schuchardt		    if ($i > 0) {
132359ab72d7SHeinrich Schuchardt			my $tv = $typevalue[$i - 1];
132459ab72d7SHeinrich Schuchardt			if ($tv =~ m/^([A-Z]):\s*(.*)/) {
132559ab72d7SHeinrich Schuchardt			    if ($1 eq "P") {
132659ab72d7SHeinrich Schuchardt				$name = $2;
132759ab72d7SHeinrich Schuchardt				$pvalue = format_email($name, $address, $email_usename);
132859ab72d7SHeinrich Schuchardt			    }
132959ab72d7SHeinrich Schuchardt			}
133059ab72d7SHeinrich Schuchardt		    }
133159ab72d7SHeinrich Schuchardt		}
133259ab72d7SHeinrich Schuchardt		if ($email_reviewer) {
133359ab72d7SHeinrich Schuchardt		    my $subsystem = get_subsystem_name($i);
133459ab72d7SHeinrich Schuchardt		    push_email_addresses($pvalue, "reviewer:$subsystem");
133559ab72d7SHeinrich Schuchardt		}
133692bca398SDaniel Schwierzeck	    } elsif ($ptype eq "T") {
133792bca398SDaniel Schwierzeck		push(@scm, $pvalue);
133892bca398SDaniel Schwierzeck	    } elsif ($ptype eq "W") {
133992bca398SDaniel Schwierzeck		push(@web, $pvalue);
134092bca398SDaniel Schwierzeck	    } elsif ($ptype eq "S") {
134192bca398SDaniel Schwierzeck		push(@status, $pvalue);
134292bca398SDaniel Schwierzeck	    }
134392bca398SDaniel Schwierzeck	}
134492bca398SDaniel Schwierzeck    }
134592bca398SDaniel Schwierzeck}
134692bca398SDaniel Schwierzeck
134792bca398SDaniel Schwierzecksub email_inuse {
134892bca398SDaniel Schwierzeck    my ($name, $address) = @_;
134992bca398SDaniel Schwierzeck
135092bca398SDaniel Schwierzeck    return 1 if (($name eq "") && ($address eq ""));
135192bca398SDaniel Schwierzeck    return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
135292bca398SDaniel Schwierzeck    return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
135392bca398SDaniel Schwierzeck
135492bca398SDaniel Schwierzeck    return 0;
135592bca398SDaniel Schwierzeck}
135692bca398SDaniel Schwierzeck
135792bca398SDaniel Schwierzecksub push_email_address {
135892bca398SDaniel Schwierzeck    my ($line, $role) = @_;
135992bca398SDaniel Schwierzeck
136092bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
136192bca398SDaniel Schwierzeck
136292bca398SDaniel Schwierzeck    if ($address eq "") {
136392bca398SDaniel Schwierzeck	return 0;
136492bca398SDaniel Schwierzeck    }
136592bca398SDaniel Schwierzeck
136692bca398SDaniel Schwierzeck    if (!$email_remove_duplicates) {
136792bca398SDaniel Schwierzeck	push(@email_to, [format_email($name, $address, $email_usename), $role]);
136892bca398SDaniel Schwierzeck    } elsif (!email_inuse($name, $address)) {
136992bca398SDaniel Schwierzeck	push(@email_to, [format_email($name, $address, $email_usename), $role]);
137092bca398SDaniel Schwierzeck	$email_hash_name{lc($name)}++ if ($name ne "");
137192bca398SDaniel Schwierzeck	$email_hash_address{lc($address)}++;
137292bca398SDaniel Schwierzeck    }
137392bca398SDaniel Schwierzeck
137492bca398SDaniel Schwierzeck    return 1;
137592bca398SDaniel Schwierzeck}
137692bca398SDaniel Schwierzeck
137792bca398SDaniel Schwierzecksub push_email_addresses {
137892bca398SDaniel Schwierzeck    my ($address, $role) = @_;
137992bca398SDaniel Schwierzeck
138092bca398SDaniel Schwierzeck    my @address_list = ();
138192bca398SDaniel Schwierzeck
138292bca398SDaniel Schwierzeck    if (rfc822_valid($address)) {
138392bca398SDaniel Schwierzeck	push_email_address($address, $role);
138492bca398SDaniel Schwierzeck    } elsif (@address_list = rfc822_validlist($address)) {
138592bca398SDaniel Schwierzeck	my $array_count = shift(@address_list);
138692bca398SDaniel Schwierzeck	while (my $entry = shift(@address_list)) {
138792bca398SDaniel Schwierzeck	    push_email_address($entry, $role);
138892bca398SDaniel Schwierzeck	}
138992bca398SDaniel Schwierzeck    } else {
139092bca398SDaniel Schwierzeck	if (!push_email_address($address, $role)) {
139192bca398SDaniel Schwierzeck	    warn("Invalid MAINTAINERS address: '" . $address . "'\n");
139292bca398SDaniel Schwierzeck	}
139392bca398SDaniel Schwierzeck    }
139492bca398SDaniel Schwierzeck}
139592bca398SDaniel Schwierzeck
139692bca398SDaniel Schwierzecksub add_role {
139792bca398SDaniel Schwierzeck    my ($line, $role) = @_;
139892bca398SDaniel Schwierzeck
139992bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
140092bca398SDaniel Schwierzeck    my $email = format_email($name, $address, $email_usename);
140192bca398SDaniel Schwierzeck
140292bca398SDaniel Schwierzeck    foreach my $entry (@email_to) {
140392bca398SDaniel Schwierzeck	if ($email_remove_duplicates) {
140492bca398SDaniel Schwierzeck	    my ($entry_name, $entry_address) = parse_email($entry->[0]);
140592bca398SDaniel Schwierzeck	    if (($name eq $entry_name || $address eq $entry_address)
140692bca398SDaniel Schwierzeck		&& ($role eq "" || !($entry->[1] =~ m/$role/))
140792bca398SDaniel Schwierzeck	    ) {
140892bca398SDaniel Schwierzeck		if ($entry->[1] eq "") {
140992bca398SDaniel Schwierzeck		    $entry->[1] = "$role";
141092bca398SDaniel Schwierzeck		} else {
141192bca398SDaniel Schwierzeck		    $entry->[1] = "$entry->[1],$role";
141292bca398SDaniel Schwierzeck		}
141392bca398SDaniel Schwierzeck	    }
141492bca398SDaniel Schwierzeck	} else {
141592bca398SDaniel Schwierzeck	    if ($email eq $entry->[0]
141692bca398SDaniel Schwierzeck		&& ($role eq "" || !($entry->[1] =~ m/$role/))
141792bca398SDaniel Schwierzeck	    ) {
141892bca398SDaniel Schwierzeck		if ($entry->[1] eq "") {
141992bca398SDaniel Schwierzeck		    $entry->[1] = "$role";
142092bca398SDaniel Schwierzeck		} else {
142192bca398SDaniel Schwierzeck		    $entry->[1] = "$entry->[1],$role";
142292bca398SDaniel Schwierzeck		}
142392bca398SDaniel Schwierzeck	    }
142492bca398SDaniel Schwierzeck	}
142592bca398SDaniel Schwierzeck    }
142692bca398SDaniel Schwierzeck}
142792bca398SDaniel Schwierzeck
142892bca398SDaniel Schwierzecksub which {
142992bca398SDaniel Schwierzeck    my ($bin) = @_;
143092bca398SDaniel Schwierzeck
143192bca398SDaniel Schwierzeck    foreach my $path (split(/:/, $ENV{PATH})) {
143292bca398SDaniel Schwierzeck	if (-e "$path/$bin") {
143392bca398SDaniel Schwierzeck	    return "$path/$bin";
143492bca398SDaniel Schwierzeck	}
143592bca398SDaniel Schwierzeck    }
143692bca398SDaniel Schwierzeck
143792bca398SDaniel Schwierzeck    return "";
143892bca398SDaniel Schwierzeck}
143992bca398SDaniel Schwierzeck
144092bca398SDaniel Schwierzecksub which_conf {
144192bca398SDaniel Schwierzeck    my ($conf) = @_;
144292bca398SDaniel Schwierzeck
144392bca398SDaniel Schwierzeck    foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
144492bca398SDaniel Schwierzeck	if (-e "$path/$conf") {
144592bca398SDaniel Schwierzeck	    return "$path/$conf";
144692bca398SDaniel Schwierzeck	}
144792bca398SDaniel Schwierzeck    }
144892bca398SDaniel Schwierzeck
144992bca398SDaniel Schwierzeck    return "";
145092bca398SDaniel Schwierzeck}
145192bca398SDaniel Schwierzeck
145292bca398SDaniel Schwierzecksub mailmap_email {
145392bca398SDaniel Schwierzeck    my ($line) = @_;
145492bca398SDaniel Schwierzeck
145592bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($line);
145692bca398SDaniel Schwierzeck    my $email = format_email($name, $address, 1);
145792bca398SDaniel Schwierzeck    my $real_name = $name;
145892bca398SDaniel Schwierzeck    my $real_address = $address;
145992bca398SDaniel Schwierzeck
146092bca398SDaniel Schwierzeck    if (exists $mailmap->{names}->{$email} ||
146192bca398SDaniel Schwierzeck	exists $mailmap->{addresses}->{$email}) {
146292bca398SDaniel Schwierzeck	if (exists $mailmap->{names}->{$email}) {
146392bca398SDaniel Schwierzeck	    $real_name = $mailmap->{names}->{$email};
146492bca398SDaniel Schwierzeck	}
146592bca398SDaniel Schwierzeck	if (exists $mailmap->{addresses}->{$email}) {
146692bca398SDaniel Schwierzeck	    $real_address = $mailmap->{addresses}->{$email};
146792bca398SDaniel Schwierzeck	}
146892bca398SDaniel Schwierzeck    } else {
146992bca398SDaniel Schwierzeck	if (exists $mailmap->{names}->{$address}) {
147092bca398SDaniel Schwierzeck	    $real_name = $mailmap->{names}->{$address};
147192bca398SDaniel Schwierzeck	}
147292bca398SDaniel Schwierzeck	if (exists $mailmap->{addresses}->{$address}) {
147392bca398SDaniel Schwierzeck	    $real_address = $mailmap->{addresses}->{$address};
147492bca398SDaniel Schwierzeck	}
147592bca398SDaniel Schwierzeck    }
147692bca398SDaniel Schwierzeck    return format_email($real_name, $real_address, 1);
147792bca398SDaniel Schwierzeck}
147892bca398SDaniel Schwierzeck
147992bca398SDaniel Schwierzecksub mailmap {
148092bca398SDaniel Schwierzeck    my (@addresses) = @_;
148192bca398SDaniel Schwierzeck
148292bca398SDaniel Schwierzeck    my @mapped_emails = ();
148392bca398SDaniel Schwierzeck    foreach my $line (@addresses) {
148492bca398SDaniel Schwierzeck	push(@mapped_emails, mailmap_email($line));
148592bca398SDaniel Schwierzeck    }
148692bca398SDaniel Schwierzeck    merge_by_realname(@mapped_emails) if ($email_use_mailmap);
148792bca398SDaniel Schwierzeck    return @mapped_emails;
148892bca398SDaniel Schwierzeck}
148992bca398SDaniel Schwierzeck
149092bca398SDaniel Schwierzecksub merge_by_realname {
149192bca398SDaniel Schwierzeck    my %address_map;
149292bca398SDaniel Schwierzeck    my (@emails) = @_;
149392bca398SDaniel Schwierzeck
149492bca398SDaniel Schwierzeck    foreach my $email (@emails) {
149592bca398SDaniel Schwierzeck	my ($name, $address) = parse_email($email);
149692bca398SDaniel Schwierzeck	if (exists $address_map{$name}) {
149792bca398SDaniel Schwierzeck	    $address = $address_map{$name};
149892bca398SDaniel Schwierzeck	    $email = format_email($name, $address, 1);
149992bca398SDaniel Schwierzeck	} else {
150092bca398SDaniel Schwierzeck	    $address_map{$name} = $address;
150192bca398SDaniel Schwierzeck	}
150292bca398SDaniel Schwierzeck    }
150392bca398SDaniel Schwierzeck}
150492bca398SDaniel Schwierzeck
150592bca398SDaniel Schwierzecksub git_execute_cmd {
150692bca398SDaniel Schwierzeck    my ($cmd) = @_;
150792bca398SDaniel Schwierzeck    my @lines = ();
150892bca398SDaniel Schwierzeck
150992bca398SDaniel Schwierzeck    my $output = `$cmd`;
151092bca398SDaniel Schwierzeck    $output =~ s/^\s*//gm;
151192bca398SDaniel Schwierzeck    @lines = split("\n", $output);
151292bca398SDaniel Schwierzeck
151392bca398SDaniel Schwierzeck    return @lines;
151492bca398SDaniel Schwierzeck}
151592bca398SDaniel Schwierzeck
151692bca398SDaniel Schwierzecksub hg_execute_cmd {
151792bca398SDaniel Schwierzeck    my ($cmd) = @_;
151892bca398SDaniel Schwierzeck    my @lines = ();
151992bca398SDaniel Schwierzeck
152092bca398SDaniel Schwierzeck    my $output = `$cmd`;
152192bca398SDaniel Schwierzeck    @lines = split("\n", $output);
152292bca398SDaniel Schwierzeck
152392bca398SDaniel Schwierzeck    return @lines;
152492bca398SDaniel Schwierzeck}
152592bca398SDaniel Schwierzeck
152692bca398SDaniel Schwierzecksub extract_formatted_signatures {
152792bca398SDaniel Schwierzeck    my (@signature_lines) = @_;
152892bca398SDaniel Schwierzeck
152992bca398SDaniel Schwierzeck    my @type = @signature_lines;
153092bca398SDaniel Schwierzeck
153192bca398SDaniel Schwierzeck    s/\s*(.*):.*/$1/ for (@type);
153292bca398SDaniel Schwierzeck
153392bca398SDaniel Schwierzeck    # cut -f2- -d":"
153492bca398SDaniel Schwierzeck    s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
153592bca398SDaniel Schwierzeck
153692bca398SDaniel Schwierzeck## Reformat email addresses (with names) to avoid badly written signatures
153792bca398SDaniel Schwierzeck
153892bca398SDaniel Schwierzeck    foreach my $signer (@signature_lines) {
153992bca398SDaniel Schwierzeck	$signer = deduplicate_email($signer);
154092bca398SDaniel Schwierzeck    }
154192bca398SDaniel Schwierzeck
154292bca398SDaniel Schwierzeck    return (\@type, \@signature_lines);
154392bca398SDaniel Schwierzeck}
154492bca398SDaniel Schwierzeck
154592bca398SDaniel Schwierzecksub vcs_find_signers {
154692bca398SDaniel Schwierzeck    my ($cmd, $file) = @_;
154792bca398SDaniel Schwierzeck    my $commits;
154892bca398SDaniel Schwierzeck    my @lines = ();
154992bca398SDaniel Schwierzeck    my @signatures = ();
155092bca398SDaniel Schwierzeck    my @authors = ();
155192bca398SDaniel Schwierzeck    my @stats = ();
155292bca398SDaniel Schwierzeck
155392bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
155492bca398SDaniel Schwierzeck
155592bca398SDaniel Schwierzeck    my $pattern = $VCS_cmds{"commit_pattern"};
155692bca398SDaniel Schwierzeck    my $author_pattern = $VCS_cmds{"author_pattern"};
155792bca398SDaniel Schwierzeck    my $stat_pattern = $VCS_cmds{"stat_pattern"};
155892bca398SDaniel Schwierzeck
155992bca398SDaniel Schwierzeck    $stat_pattern =~ s/(\$\w+)/$1/eeg;		#interpolate $stat_pattern
156092bca398SDaniel Schwierzeck
156192bca398SDaniel Schwierzeck    $commits = grep(/$pattern/, @lines);	# of commits
156292bca398SDaniel Schwierzeck
156392bca398SDaniel Schwierzeck    @authors = grep(/$author_pattern/, @lines);
156492bca398SDaniel Schwierzeck    @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
156592bca398SDaniel Schwierzeck    @stats = grep(/$stat_pattern/, @lines);
156692bca398SDaniel Schwierzeck
156792bca398SDaniel Schwierzeck#    print("stats: <@stats>\n");
156892bca398SDaniel Schwierzeck
156992bca398SDaniel Schwierzeck    return (0, \@signatures, \@authors, \@stats) if !@signatures;
157092bca398SDaniel Schwierzeck
157192bca398SDaniel Schwierzeck    save_commits_by_author(@lines) if ($interactive);
157292bca398SDaniel Schwierzeck    save_commits_by_signer(@lines) if ($interactive);
157392bca398SDaniel Schwierzeck
157492bca398SDaniel Schwierzeck    if (!$email_git_penguin_chiefs) {
157592bca398SDaniel Schwierzeck	@signatures = grep(!/${penguin_chiefs}/i, @signatures);
157692bca398SDaniel Schwierzeck    }
157792bca398SDaniel Schwierzeck
157892bca398SDaniel Schwierzeck    my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
157992bca398SDaniel Schwierzeck    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
158092bca398SDaniel Schwierzeck
158192bca398SDaniel Schwierzeck    return ($commits, $signers_ref, $authors_ref, \@stats);
158292bca398SDaniel Schwierzeck}
158392bca398SDaniel Schwierzeck
158492bca398SDaniel Schwierzecksub vcs_find_author {
158592bca398SDaniel Schwierzeck    my ($cmd) = @_;
158692bca398SDaniel Schwierzeck    my @lines = ();
158792bca398SDaniel Schwierzeck
158892bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
158992bca398SDaniel Schwierzeck
159092bca398SDaniel Schwierzeck    if (!$email_git_penguin_chiefs) {
159192bca398SDaniel Schwierzeck	@lines = grep(!/${penguin_chiefs}/i, @lines);
159292bca398SDaniel Schwierzeck    }
159392bca398SDaniel Schwierzeck
159492bca398SDaniel Schwierzeck    return @lines if !@lines;
159592bca398SDaniel Schwierzeck
159692bca398SDaniel Schwierzeck    my @authors = ();
159792bca398SDaniel Schwierzeck    foreach my $line (@lines) {
159892bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
159992bca398SDaniel Schwierzeck	    my $author = $1;
160092bca398SDaniel Schwierzeck	    my ($name, $address) = parse_email($author);
160192bca398SDaniel Schwierzeck	    $author = format_email($name, $address, 1);
160292bca398SDaniel Schwierzeck	    push(@authors, $author);
160392bca398SDaniel Schwierzeck	}
160492bca398SDaniel Schwierzeck    }
160592bca398SDaniel Schwierzeck
160692bca398SDaniel Schwierzeck    save_commits_by_author(@lines) if ($interactive);
160792bca398SDaniel Schwierzeck    save_commits_by_signer(@lines) if ($interactive);
160892bca398SDaniel Schwierzeck
160992bca398SDaniel Schwierzeck    return @authors;
161092bca398SDaniel Schwierzeck}
161192bca398SDaniel Schwierzeck
161292bca398SDaniel Schwierzecksub vcs_save_commits {
161392bca398SDaniel Schwierzeck    my ($cmd) = @_;
161492bca398SDaniel Schwierzeck    my @lines = ();
161592bca398SDaniel Schwierzeck    my @commits = ();
161692bca398SDaniel Schwierzeck
161792bca398SDaniel Schwierzeck    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
161892bca398SDaniel Schwierzeck
161992bca398SDaniel Schwierzeck    foreach my $line (@lines) {
162092bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
162192bca398SDaniel Schwierzeck	    push(@commits, $1);
162292bca398SDaniel Schwierzeck	}
162392bca398SDaniel Schwierzeck    }
162492bca398SDaniel Schwierzeck
162592bca398SDaniel Schwierzeck    return @commits;
162692bca398SDaniel Schwierzeck}
162792bca398SDaniel Schwierzeck
162892bca398SDaniel Schwierzecksub vcs_blame {
162992bca398SDaniel Schwierzeck    my ($file) = @_;
163092bca398SDaniel Schwierzeck    my $cmd;
163192bca398SDaniel Schwierzeck    my @commits = ();
163292bca398SDaniel Schwierzeck
163392bca398SDaniel Schwierzeck    return @commits if (!(-f $file));
163492bca398SDaniel Schwierzeck
163592bca398SDaniel Schwierzeck    if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
163692bca398SDaniel Schwierzeck	my @all_commits = ();
163792bca398SDaniel Schwierzeck
163892bca398SDaniel Schwierzeck	$cmd = $VCS_cmds{"blame_file_cmd"};
163992bca398SDaniel Schwierzeck	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
164092bca398SDaniel Schwierzeck	@all_commits = vcs_save_commits($cmd);
164192bca398SDaniel Schwierzeck
164292bca398SDaniel Schwierzeck	foreach my $file_range_diff (@range) {
164392bca398SDaniel Schwierzeck	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
164492bca398SDaniel Schwierzeck	    my $diff_file = $1;
164592bca398SDaniel Schwierzeck	    my $diff_start = $2;
164692bca398SDaniel Schwierzeck	    my $diff_length = $3;
164792bca398SDaniel Schwierzeck	    next if ("$file" ne "$diff_file");
164892bca398SDaniel Schwierzeck	    for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
164992bca398SDaniel Schwierzeck		push(@commits, $all_commits[$i]);
165092bca398SDaniel Schwierzeck	    }
165192bca398SDaniel Schwierzeck	}
165292bca398SDaniel Schwierzeck    } elsif (@range) {
165392bca398SDaniel Schwierzeck	foreach my $file_range_diff (@range) {
165492bca398SDaniel Schwierzeck	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
165592bca398SDaniel Schwierzeck	    my $diff_file = $1;
165692bca398SDaniel Schwierzeck	    my $diff_start = $2;
165792bca398SDaniel Schwierzeck	    my $diff_length = $3;
165892bca398SDaniel Schwierzeck	    next if ("$file" ne "$diff_file");
165992bca398SDaniel Schwierzeck	    $cmd = $VCS_cmds{"blame_range_cmd"};
166092bca398SDaniel Schwierzeck	    $cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
166192bca398SDaniel Schwierzeck	    push(@commits, vcs_save_commits($cmd));
166292bca398SDaniel Schwierzeck	}
166392bca398SDaniel Schwierzeck    } else {
166492bca398SDaniel Schwierzeck	$cmd = $VCS_cmds{"blame_file_cmd"};
166592bca398SDaniel Schwierzeck	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
166692bca398SDaniel Schwierzeck	@commits = vcs_save_commits($cmd);
166792bca398SDaniel Schwierzeck    }
166892bca398SDaniel Schwierzeck
166992bca398SDaniel Schwierzeck    foreach my $commit (@commits) {
167092bca398SDaniel Schwierzeck	$commit =~ s/^\^//g;
167192bca398SDaniel Schwierzeck    }
167292bca398SDaniel Schwierzeck
167392bca398SDaniel Schwierzeck    return @commits;
167492bca398SDaniel Schwierzeck}
167592bca398SDaniel Schwierzeck
167692bca398SDaniel Schwierzeckmy $printed_novcs = 0;
167792bca398SDaniel Schwierzecksub vcs_exists {
167892bca398SDaniel Schwierzeck    %VCS_cmds = %VCS_cmds_git;
167992bca398SDaniel Schwierzeck    return 1 if eval $VCS_cmds{"available"};
168092bca398SDaniel Schwierzeck    %VCS_cmds = %VCS_cmds_hg;
168192bca398SDaniel Schwierzeck    return 2 if eval $VCS_cmds{"available"};
168292bca398SDaniel Schwierzeck    %VCS_cmds = ();
168392bca398SDaniel Schwierzeck    if (!$printed_novcs) {
168492bca398SDaniel Schwierzeck	warn("$P: No supported VCS found.  Add --nogit to options?\n");
168592bca398SDaniel Schwierzeck	warn("Using a git repository produces better results.\n");
168692bca398SDaniel Schwierzeck	warn("Try Linus Torvalds' latest git repository using:\n");
168792bca398SDaniel Schwierzeck	warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
168892bca398SDaniel Schwierzeck	$printed_novcs = 1;
168992bca398SDaniel Schwierzeck    }
169092bca398SDaniel Schwierzeck    return 0;
169192bca398SDaniel Schwierzeck}
169292bca398SDaniel Schwierzeck
169392bca398SDaniel Schwierzecksub vcs_is_git {
169492bca398SDaniel Schwierzeck    vcs_exists();
169592bca398SDaniel Schwierzeck    return $vcs_used == 1;
169692bca398SDaniel Schwierzeck}
169792bca398SDaniel Schwierzeck
169892bca398SDaniel Schwierzecksub vcs_is_hg {
169992bca398SDaniel Schwierzeck    return $vcs_used == 2;
170092bca398SDaniel Schwierzeck}
170192bca398SDaniel Schwierzeck
170292bca398SDaniel Schwierzecksub interactive_get_maintainers {
170392bca398SDaniel Schwierzeck    my ($list_ref) = @_;
170492bca398SDaniel Schwierzeck    my @list = @$list_ref;
170592bca398SDaniel Schwierzeck
170692bca398SDaniel Schwierzeck    vcs_exists();
170792bca398SDaniel Schwierzeck
170892bca398SDaniel Schwierzeck    my %selected;
170992bca398SDaniel Schwierzeck    my %authored;
171092bca398SDaniel Schwierzeck    my %signed;
171192bca398SDaniel Schwierzeck    my $count = 0;
171292bca398SDaniel Schwierzeck    my $maintained = 0;
171392bca398SDaniel Schwierzeck    foreach my $entry (@list) {
171492bca398SDaniel Schwierzeck	$maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
171592bca398SDaniel Schwierzeck	$selected{$count} = 1;
171692bca398SDaniel Schwierzeck	$authored{$count} = 0;
171792bca398SDaniel Schwierzeck	$signed{$count} = 0;
171892bca398SDaniel Schwierzeck	$count++;
171992bca398SDaniel Schwierzeck    }
172092bca398SDaniel Schwierzeck
172192bca398SDaniel Schwierzeck    #menu loop
172292bca398SDaniel Schwierzeck    my $done = 0;
172392bca398SDaniel Schwierzeck    my $print_options = 0;
172492bca398SDaniel Schwierzeck    my $redraw = 1;
172592bca398SDaniel Schwierzeck    while (!$done) {
172692bca398SDaniel Schwierzeck	$count = 0;
172792bca398SDaniel Schwierzeck	if ($redraw) {
172892bca398SDaniel Schwierzeck	    printf STDERR "\n%1s %2s %-65s",
172992bca398SDaniel Schwierzeck			  "*", "#", "email/list and role:stats";
173092bca398SDaniel Schwierzeck	    if ($email_git ||
173192bca398SDaniel Schwierzeck		($email_git_fallback && !$maintained) ||
173292bca398SDaniel Schwierzeck		$email_git_blame) {
173392bca398SDaniel Schwierzeck		print STDERR "auth sign";
173492bca398SDaniel Schwierzeck	    }
173592bca398SDaniel Schwierzeck	    print STDERR "\n";
173692bca398SDaniel Schwierzeck	    foreach my $entry (@list) {
173792bca398SDaniel Schwierzeck		my $email = $entry->[0];
173892bca398SDaniel Schwierzeck		my $role = $entry->[1];
173992bca398SDaniel Schwierzeck		my $sel = "";
174092bca398SDaniel Schwierzeck		$sel = "*" if ($selected{$count});
174192bca398SDaniel Schwierzeck		my $commit_author = $commit_author_hash{$email};
174292bca398SDaniel Schwierzeck		my $commit_signer = $commit_signer_hash{$email};
174392bca398SDaniel Schwierzeck		my $authored = 0;
174492bca398SDaniel Schwierzeck		my $signed = 0;
174592bca398SDaniel Schwierzeck		$authored++ for (@{$commit_author});
174692bca398SDaniel Schwierzeck		$signed++ for (@{$commit_signer});
174792bca398SDaniel Schwierzeck		printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
174892bca398SDaniel Schwierzeck		printf STDERR "%4d %4d", $authored, $signed
174992bca398SDaniel Schwierzeck		    if ($authored > 0 || $signed > 0);
175092bca398SDaniel Schwierzeck		printf STDERR "\n     %s\n", $role;
175192bca398SDaniel Schwierzeck		if ($authored{$count}) {
175292bca398SDaniel Schwierzeck		    my $commit_author = $commit_author_hash{$email};
175392bca398SDaniel Schwierzeck		    foreach my $ref (@{$commit_author}) {
175492bca398SDaniel Schwierzeck			print STDERR "     Author: @{$ref}[1]\n";
175592bca398SDaniel Schwierzeck		    }
175692bca398SDaniel Schwierzeck		}
175792bca398SDaniel Schwierzeck		if ($signed{$count}) {
175892bca398SDaniel Schwierzeck		    my $commit_signer = $commit_signer_hash{$email};
175992bca398SDaniel Schwierzeck		    foreach my $ref (@{$commit_signer}) {
176092bca398SDaniel Schwierzeck			print STDERR "     @{$ref}[2]: @{$ref}[1]\n";
176192bca398SDaniel Schwierzeck		    }
176292bca398SDaniel Schwierzeck		}
176392bca398SDaniel Schwierzeck
176492bca398SDaniel Schwierzeck		$count++;
176592bca398SDaniel Schwierzeck	    }
176692bca398SDaniel Schwierzeck	}
176792bca398SDaniel Schwierzeck	my $date_ref = \$email_git_since;
176892bca398SDaniel Schwierzeck	$date_ref = \$email_hg_since if (vcs_is_hg());
176992bca398SDaniel Schwierzeck	if ($print_options) {
177092bca398SDaniel Schwierzeck	    $print_options = 0;
177192bca398SDaniel Schwierzeck	    if (vcs_exists()) {
177292bca398SDaniel Schwierzeck		print STDERR <<EOT
177392bca398SDaniel Schwierzeck
177492bca398SDaniel SchwierzeckVersion Control options:
177592bca398SDaniel Schwierzeckg  use git history      [$email_git]
177692bca398SDaniel Schwierzeckgf use git-fallback     [$email_git_fallback]
177792bca398SDaniel Schwierzeckb  use git blame        [$email_git_blame]
177892bca398SDaniel Schwierzeckbs use blame signatures [$email_git_blame_signatures]
177992bca398SDaniel Schwierzeckc# minimum commits      [$email_git_min_signatures]
178092bca398SDaniel Schwierzeck%# min percent          [$email_git_min_percent]
178192bca398SDaniel Schwierzeckd# history to use       [$$date_ref]
178292bca398SDaniel Schwierzeckx# max maintainers      [$email_git_max_maintainers]
178392bca398SDaniel Schwierzeckt  all signature types  [$email_git_all_signature_types]
178492bca398SDaniel Schwierzeckm  use .mailmap         [$email_use_mailmap]
178592bca398SDaniel SchwierzeckEOT
178692bca398SDaniel Schwierzeck	    }
178792bca398SDaniel Schwierzeck	    print STDERR <<EOT
178892bca398SDaniel Schwierzeck
178992bca398SDaniel SchwierzeckAdditional options:
179092bca398SDaniel Schwierzeck0  toggle all
179192bca398SDaniel Schwierzecktm toggle maintainers
179292bca398SDaniel Schwierzecktg toggle git entries
179392bca398SDaniel Schwierzecktl toggle open list entries
179492bca398SDaniel Schwierzeckts toggle subscriber list entries
179592bca398SDaniel Schwierzeckf  emails in file       [$file_emails]
179692bca398SDaniel Schwierzeckk  keywords in file     [$keywords]
179792bca398SDaniel Schwierzeckr  remove duplicates    [$email_remove_duplicates]
179892bca398SDaniel Schwierzeckp# pattern match depth  [$pattern_depth]
179992bca398SDaniel SchwierzeckEOT
180092bca398SDaniel Schwierzeck	}
180192bca398SDaniel Schwierzeck	print STDERR
180292bca398SDaniel Schwierzeck"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
180392bca398SDaniel Schwierzeck
180492bca398SDaniel Schwierzeck	my $input = <STDIN>;
180592bca398SDaniel Schwierzeck	chomp($input);
180692bca398SDaniel Schwierzeck
180792bca398SDaniel Schwierzeck	$redraw = 1;
180892bca398SDaniel Schwierzeck	my $rerun = 0;
180992bca398SDaniel Schwierzeck	my @wish = split(/[, ]+/, $input);
181092bca398SDaniel Schwierzeck	foreach my $nr (@wish) {
181192bca398SDaniel Schwierzeck	    $nr = lc($nr);
181292bca398SDaniel Schwierzeck	    my $sel = substr($nr, 0, 1);
181392bca398SDaniel Schwierzeck	    my $str = substr($nr, 1);
181492bca398SDaniel Schwierzeck	    my $val = 0;
181592bca398SDaniel Schwierzeck	    $val = $1 if $str =~ /^(\d+)$/;
181692bca398SDaniel Schwierzeck
181792bca398SDaniel Schwierzeck	    if ($sel eq "y") {
181892bca398SDaniel Schwierzeck		$interactive = 0;
181992bca398SDaniel Schwierzeck		$done = 1;
182092bca398SDaniel Schwierzeck		$output_rolestats = 0;
182192bca398SDaniel Schwierzeck		$output_roles = 0;
182292bca398SDaniel Schwierzeck		last;
182392bca398SDaniel Schwierzeck	    } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
182492bca398SDaniel Schwierzeck		$selected{$nr - 1} = !$selected{$nr - 1};
182592bca398SDaniel Schwierzeck	    } elsif ($sel eq "*" || $sel eq '^') {
182692bca398SDaniel Schwierzeck		my $toggle = 0;
182792bca398SDaniel Schwierzeck		$toggle = 1 if ($sel eq '*');
182892bca398SDaniel Schwierzeck		for (my $i = 0; $i < $count; $i++) {
182992bca398SDaniel Schwierzeck		    $selected{$i} = $toggle;
183092bca398SDaniel Schwierzeck		}
183192bca398SDaniel Schwierzeck	    } elsif ($sel eq "0") {
183292bca398SDaniel Schwierzeck		for (my $i = 0; $i < $count; $i++) {
183392bca398SDaniel Schwierzeck		    $selected{$i} = !$selected{$i};
183492bca398SDaniel Schwierzeck		}
183592bca398SDaniel Schwierzeck	    } elsif ($sel eq "t") {
183692bca398SDaniel Schwierzeck		if (lc($str) eq "m") {
183792bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
183892bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
183992bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
184092bca398SDaniel Schwierzeck		    }
184192bca398SDaniel Schwierzeck		} elsif (lc($str) eq "g") {
184292bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
184392bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
184492bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
184592bca398SDaniel Schwierzeck		    }
184692bca398SDaniel Schwierzeck		} elsif (lc($str) eq "l") {
184792bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
184892bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
184992bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(open list)/i);
185092bca398SDaniel Schwierzeck		    }
185192bca398SDaniel Schwierzeck		} elsif (lc($str) eq "s") {
185292bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
185392bca398SDaniel Schwierzeck			$selected{$i} = !$selected{$i}
185492bca398SDaniel Schwierzeck			    if ($list[$i]->[1] =~ /^(subscriber list)/i);
185592bca398SDaniel Schwierzeck		    }
185692bca398SDaniel Schwierzeck		}
185792bca398SDaniel Schwierzeck	    } elsif ($sel eq "a") {
185892bca398SDaniel Schwierzeck		if ($val > 0 && $val <= $count) {
185992bca398SDaniel Schwierzeck		    $authored{$val - 1} = !$authored{$val - 1};
186092bca398SDaniel Schwierzeck		} elsif ($str eq '*' || $str eq '^') {
186192bca398SDaniel Schwierzeck		    my $toggle = 0;
186292bca398SDaniel Schwierzeck		    $toggle = 1 if ($str eq '*');
186392bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
186492bca398SDaniel Schwierzeck			$authored{$i} = $toggle;
186592bca398SDaniel Schwierzeck		    }
186692bca398SDaniel Schwierzeck		}
186792bca398SDaniel Schwierzeck	    } elsif ($sel eq "s") {
186892bca398SDaniel Schwierzeck		if ($val > 0 && $val <= $count) {
186992bca398SDaniel Schwierzeck		    $signed{$val - 1} = !$signed{$val - 1};
187092bca398SDaniel Schwierzeck		} elsif ($str eq '*' || $str eq '^') {
187192bca398SDaniel Schwierzeck		    my $toggle = 0;
187292bca398SDaniel Schwierzeck		    $toggle = 1 if ($str eq '*');
187392bca398SDaniel Schwierzeck		    for (my $i = 0; $i < $count; $i++) {
187492bca398SDaniel Schwierzeck			$signed{$i} = $toggle;
187592bca398SDaniel Schwierzeck		    }
187692bca398SDaniel Schwierzeck		}
187792bca398SDaniel Schwierzeck	    } elsif ($sel eq "o") {
187892bca398SDaniel Schwierzeck		$print_options = 1;
187992bca398SDaniel Schwierzeck		$redraw = 1;
188092bca398SDaniel Schwierzeck	    } elsif ($sel eq "g") {
188192bca398SDaniel Schwierzeck		if ($str eq "f") {
188292bca398SDaniel Schwierzeck		    bool_invert(\$email_git_fallback);
188392bca398SDaniel Schwierzeck		} else {
188492bca398SDaniel Schwierzeck		    bool_invert(\$email_git);
188592bca398SDaniel Schwierzeck		}
188692bca398SDaniel Schwierzeck		$rerun = 1;
188792bca398SDaniel Schwierzeck	    } elsif ($sel eq "b") {
188892bca398SDaniel Schwierzeck		if ($str eq "s") {
188992bca398SDaniel Schwierzeck		    bool_invert(\$email_git_blame_signatures);
189092bca398SDaniel Schwierzeck		} else {
189192bca398SDaniel Schwierzeck		    bool_invert(\$email_git_blame);
189292bca398SDaniel Schwierzeck		}
189392bca398SDaniel Schwierzeck		$rerun = 1;
189492bca398SDaniel Schwierzeck	    } elsif ($sel eq "c") {
189592bca398SDaniel Schwierzeck		if ($val > 0) {
189692bca398SDaniel Schwierzeck		    $email_git_min_signatures = $val;
189792bca398SDaniel Schwierzeck		    $rerun = 1;
189892bca398SDaniel Schwierzeck		}
189992bca398SDaniel Schwierzeck	    } elsif ($sel eq "x") {
190092bca398SDaniel Schwierzeck		if ($val > 0) {
190192bca398SDaniel Schwierzeck		    $email_git_max_maintainers = $val;
190292bca398SDaniel Schwierzeck		    $rerun = 1;
190392bca398SDaniel Schwierzeck		}
190492bca398SDaniel Schwierzeck	    } elsif ($sel eq "%") {
190592bca398SDaniel Schwierzeck		if ($str ne "" && $val >= 0) {
190692bca398SDaniel Schwierzeck		    $email_git_min_percent = $val;
190792bca398SDaniel Schwierzeck		    $rerun = 1;
190892bca398SDaniel Schwierzeck		}
190992bca398SDaniel Schwierzeck	    } elsif ($sel eq "d") {
191092bca398SDaniel Schwierzeck		if (vcs_is_git()) {
191192bca398SDaniel Schwierzeck		    $email_git_since = $str;
191292bca398SDaniel Schwierzeck		} elsif (vcs_is_hg()) {
191392bca398SDaniel Schwierzeck		    $email_hg_since = $str;
191492bca398SDaniel Schwierzeck		}
191592bca398SDaniel Schwierzeck		$rerun = 1;
191692bca398SDaniel Schwierzeck	    } elsif ($sel eq "t") {
191792bca398SDaniel Schwierzeck		bool_invert(\$email_git_all_signature_types);
191892bca398SDaniel Schwierzeck		$rerun = 1;
191992bca398SDaniel Schwierzeck	    } elsif ($sel eq "f") {
192092bca398SDaniel Schwierzeck		bool_invert(\$file_emails);
192192bca398SDaniel Schwierzeck		$rerun = 1;
192292bca398SDaniel Schwierzeck	    } elsif ($sel eq "r") {
192392bca398SDaniel Schwierzeck		bool_invert(\$email_remove_duplicates);
192492bca398SDaniel Schwierzeck		$rerun = 1;
192592bca398SDaniel Schwierzeck	    } elsif ($sel eq "m") {
192692bca398SDaniel Schwierzeck		bool_invert(\$email_use_mailmap);
192792bca398SDaniel Schwierzeck		read_mailmap();
192892bca398SDaniel Schwierzeck		$rerun = 1;
192992bca398SDaniel Schwierzeck	    } elsif ($sel eq "k") {
193092bca398SDaniel Schwierzeck		bool_invert(\$keywords);
193192bca398SDaniel Schwierzeck		$rerun = 1;
193292bca398SDaniel Schwierzeck	    } elsif ($sel eq "p") {
193392bca398SDaniel Schwierzeck		if ($str ne "" && $val >= 0) {
193492bca398SDaniel Schwierzeck		    $pattern_depth = $val;
193592bca398SDaniel Schwierzeck		    $rerun = 1;
193692bca398SDaniel Schwierzeck		}
193792bca398SDaniel Schwierzeck	    } elsif ($sel eq "h" || $sel eq "?") {
193892bca398SDaniel Schwierzeck		print STDERR <<EOT
193992bca398SDaniel Schwierzeck
194092bca398SDaniel SchwierzeckInteractive mode allows you to select the various maintainers, submitters,
194192bca398SDaniel Schwierzeckcommit signers and mailing lists that could be CC'd on a patch.
194292bca398SDaniel Schwierzeck
194392bca398SDaniel SchwierzeckAny *'d entry is selected.
194492bca398SDaniel Schwierzeck
194592bca398SDaniel SchwierzeckIf you have git or hg installed, you can choose to summarize the commit
194692bca398SDaniel Schwierzeckhistory of files in the patch.  Also, each line of the current file can
194792bca398SDaniel Schwierzeckbe matched to its commit author and that commits signers with blame.
194892bca398SDaniel Schwierzeck
194992bca398SDaniel SchwierzeckVarious knobs exist to control the length of time for active commit
195092bca398SDaniel Schwierzecktracking, the maximum number of commit authors and signers to add,
195192bca398SDaniel Schwierzeckand such.
195292bca398SDaniel Schwierzeck
195392bca398SDaniel SchwierzeckEnter selections at the prompt until you are satisfied that the selected
195492bca398SDaniel Schwierzeckmaintainers are appropriate.  You may enter multiple selections separated
195592bca398SDaniel Schwierzeckby either commas or spaces.
195692bca398SDaniel Schwierzeck
195792bca398SDaniel SchwierzeckEOT
195892bca398SDaniel Schwierzeck	    } else {
195992bca398SDaniel Schwierzeck		print STDERR "invalid option: '$nr'\n";
196092bca398SDaniel Schwierzeck		$redraw = 0;
196192bca398SDaniel Schwierzeck	    }
196292bca398SDaniel Schwierzeck	}
196392bca398SDaniel Schwierzeck	if ($rerun) {
196492bca398SDaniel Schwierzeck	    print STDERR "git-blame can be very slow, please have patience..."
196592bca398SDaniel Schwierzeck		if ($email_git_blame);
196692bca398SDaniel Schwierzeck	    goto &get_maintainers;
196792bca398SDaniel Schwierzeck	}
196892bca398SDaniel Schwierzeck    }
196992bca398SDaniel Schwierzeck
197092bca398SDaniel Schwierzeck    #drop not selected entries
197192bca398SDaniel Schwierzeck    $count = 0;
197292bca398SDaniel Schwierzeck    my @new_emailto = ();
197392bca398SDaniel Schwierzeck    foreach my $entry (@list) {
197492bca398SDaniel Schwierzeck	if ($selected{$count}) {
197592bca398SDaniel Schwierzeck	    push(@new_emailto, $list[$count]);
197692bca398SDaniel Schwierzeck	}
197792bca398SDaniel Schwierzeck	$count++;
197892bca398SDaniel Schwierzeck    }
197992bca398SDaniel Schwierzeck    return @new_emailto;
198092bca398SDaniel Schwierzeck}
198192bca398SDaniel Schwierzeck
198292bca398SDaniel Schwierzecksub bool_invert {
198392bca398SDaniel Schwierzeck    my ($bool_ref) = @_;
198492bca398SDaniel Schwierzeck
198592bca398SDaniel Schwierzeck    if ($$bool_ref) {
198692bca398SDaniel Schwierzeck	$$bool_ref = 0;
198792bca398SDaniel Schwierzeck    } else {
198892bca398SDaniel Schwierzeck	$$bool_ref = 1;
198992bca398SDaniel Schwierzeck    }
199092bca398SDaniel Schwierzeck}
199192bca398SDaniel Schwierzeck
199292bca398SDaniel Schwierzecksub deduplicate_email {
199392bca398SDaniel Schwierzeck    my ($email) = @_;
199492bca398SDaniel Schwierzeck
199592bca398SDaniel Schwierzeck    my $matched = 0;
199692bca398SDaniel Schwierzeck    my ($name, $address) = parse_email($email);
199792bca398SDaniel Schwierzeck    $email = format_email($name, $address, 1);
199892bca398SDaniel Schwierzeck    $email = mailmap_email($email);
199992bca398SDaniel Schwierzeck
200092bca398SDaniel Schwierzeck    return $email if (!$email_remove_duplicates);
200192bca398SDaniel Schwierzeck
200292bca398SDaniel Schwierzeck    ($name, $address) = parse_email($email);
200392bca398SDaniel Schwierzeck
200492bca398SDaniel Schwierzeck    if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
200592bca398SDaniel Schwierzeck	$name = $deduplicate_name_hash{lc($name)}->[0];
200692bca398SDaniel Schwierzeck	$address = $deduplicate_name_hash{lc($name)}->[1];
200792bca398SDaniel Schwierzeck	$matched = 1;
200892bca398SDaniel Schwierzeck    } elsif ($deduplicate_address_hash{lc($address)}) {
200992bca398SDaniel Schwierzeck	$name = $deduplicate_address_hash{lc($address)}->[0];
201092bca398SDaniel Schwierzeck	$address = $deduplicate_address_hash{lc($address)}->[1];
201192bca398SDaniel Schwierzeck	$matched = 1;
201292bca398SDaniel Schwierzeck    }
201392bca398SDaniel Schwierzeck    if (!$matched) {
201492bca398SDaniel Schwierzeck	$deduplicate_name_hash{lc($name)} = [ $name, $address ];
201592bca398SDaniel Schwierzeck	$deduplicate_address_hash{lc($address)} = [ $name, $address ];
201692bca398SDaniel Schwierzeck    }
201792bca398SDaniel Schwierzeck    $email = format_email($name, $address, 1);
201892bca398SDaniel Schwierzeck    $email = mailmap_email($email);
201992bca398SDaniel Schwierzeck    return $email;
202092bca398SDaniel Schwierzeck}
202192bca398SDaniel Schwierzeck
202292bca398SDaniel Schwierzecksub save_commits_by_author {
202392bca398SDaniel Schwierzeck    my (@lines) = @_;
202492bca398SDaniel Schwierzeck
202592bca398SDaniel Schwierzeck    my @authors = ();
202692bca398SDaniel Schwierzeck    my @commits = ();
202792bca398SDaniel Schwierzeck    my @subjects = ();
202892bca398SDaniel Schwierzeck
202992bca398SDaniel Schwierzeck    foreach my $line (@lines) {
203092bca398SDaniel Schwierzeck	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
203192bca398SDaniel Schwierzeck	    my $author = $1;
203292bca398SDaniel Schwierzeck	    $author = deduplicate_email($author);
203392bca398SDaniel Schwierzeck	    push(@authors, $author);
203492bca398SDaniel Schwierzeck	}
203592bca398SDaniel Schwierzeck	push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
203692bca398SDaniel Schwierzeck	push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
203792bca398SDaniel Schwierzeck    }
203892bca398SDaniel Schwierzeck
203992bca398SDaniel Schwierzeck    for (my $i = 0; $i < @authors; $i++) {
204092bca398SDaniel Schwierzeck	my $exists = 0;
204192bca398SDaniel Schwierzeck	foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
204292bca398SDaniel Schwierzeck	    if (@{$ref}[0] eq $commits[$i] &&
204392bca398SDaniel Schwierzeck		@{$ref}[1] eq $subjects[$i]) {
204492bca398SDaniel Schwierzeck		$exists = 1;
204592bca398SDaniel Schwierzeck		last;
204692bca398SDaniel Schwierzeck	    }
204792bca398SDaniel Schwierzeck	}
204892bca398SDaniel Schwierzeck	if (!$exists) {
204992bca398SDaniel Schwierzeck	    push(@{$commit_author_hash{$authors[$i]}},
205092bca398SDaniel Schwierzeck		 [ ($commits[$i], $subjects[$i]) ]);
205192bca398SDaniel Schwierzeck	}
205292bca398SDaniel Schwierzeck    }
205392bca398SDaniel Schwierzeck}
205492bca398SDaniel Schwierzeck
205592bca398SDaniel Schwierzecksub save_commits_by_signer {
205692bca398SDaniel Schwierzeck    my (@lines) = @_;
205792bca398SDaniel Schwierzeck
205892bca398SDaniel Schwierzeck    my $commit = "";
205992bca398SDaniel Schwierzeck    my $subject = "";
206092bca398SDaniel Schwierzeck
206192bca398SDaniel Schwierzeck    foreach my $line (@lines) {
206292bca398SDaniel Schwierzeck	$commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
206392bca398SDaniel Schwierzeck	$subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
206492bca398SDaniel Schwierzeck	if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
206592bca398SDaniel Schwierzeck	    my @signatures = ($line);
206692bca398SDaniel Schwierzeck	    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
206792bca398SDaniel Schwierzeck	    my @types = @$types_ref;
206892bca398SDaniel Schwierzeck	    my @signers = @$signers_ref;
206992bca398SDaniel Schwierzeck
207092bca398SDaniel Schwierzeck	    my $type = $types[0];
207192bca398SDaniel Schwierzeck	    my $signer = $signers[0];
207292bca398SDaniel Schwierzeck
207392bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
207492bca398SDaniel Schwierzeck
207592bca398SDaniel Schwierzeck	    my $exists = 0;
207692bca398SDaniel Schwierzeck	    foreach my $ref(@{$commit_signer_hash{$signer}}) {
207792bca398SDaniel Schwierzeck		if (@{$ref}[0] eq $commit &&
207892bca398SDaniel Schwierzeck		    @{$ref}[1] eq $subject &&
207992bca398SDaniel Schwierzeck		    @{$ref}[2] eq $type) {
208092bca398SDaniel Schwierzeck		    $exists = 1;
208192bca398SDaniel Schwierzeck		    last;
208292bca398SDaniel Schwierzeck		}
208392bca398SDaniel Schwierzeck	    }
208492bca398SDaniel Schwierzeck	    if (!$exists) {
208592bca398SDaniel Schwierzeck		push(@{$commit_signer_hash{$signer}},
208692bca398SDaniel Schwierzeck		     [ ($commit, $subject, $type) ]);
208792bca398SDaniel Schwierzeck	    }
208892bca398SDaniel Schwierzeck	}
208992bca398SDaniel Schwierzeck    }
209092bca398SDaniel Schwierzeck}
209192bca398SDaniel Schwierzeck
209292bca398SDaniel Schwierzecksub vcs_assign {
209392bca398SDaniel Schwierzeck    my ($role, $divisor, @lines) = @_;
209492bca398SDaniel Schwierzeck
209592bca398SDaniel Schwierzeck    my %hash;
209692bca398SDaniel Schwierzeck    my $count = 0;
209792bca398SDaniel Schwierzeck
209892bca398SDaniel Schwierzeck    return if (@lines <= 0);
209992bca398SDaniel Schwierzeck
210092bca398SDaniel Schwierzeck    if ($divisor <= 0) {
210192bca398SDaniel Schwierzeck	warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
210292bca398SDaniel Schwierzeck	$divisor = 1;
210392bca398SDaniel Schwierzeck    }
210492bca398SDaniel Schwierzeck
210592bca398SDaniel Schwierzeck    @lines = mailmap(@lines);
210692bca398SDaniel Schwierzeck
210792bca398SDaniel Schwierzeck    return if (@lines <= 0);
210892bca398SDaniel Schwierzeck
210992bca398SDaniel Schwierzeck    @lines = sort(@lines);
211092bca398SDaniel Schwierzeck
211192bca398SDaniel Schwierzeck    # uniq -c
211292bca398SDaniel Schwierzeck    $hash{$_}++ for @lines;
211392bca398SDaniel Schwierzeck
211492bca398SDaniel Schwierzeck    # sort -rn
211592bca398SDaniel Schwierzeck    foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
211692bca398SDaniel Schwierzeck	my $sign_offs = $hash{$line};
211792bca398SDaniel Schwierzeck	my $percent = $sign_offs * 100 / $divisor;
211892bca398SDaniel Schwierzeck
211992bca398SDaniel Schwierzeck	$percent = 100 if ($percent > 100);
212059ab72d7SHeinrich Schuchardt	next if (ignore_email_address($line));
212192bca398SDaniel Schwierzeck	$count++;
212292bca398SDaniel Schwierzeck	last if ($sign_offs < $email_git_min_signatures ||
212392bca398SDaniel Schwierzeck		 $count > $email_git_max_maintainers ||
212492bca398SDaniel Schwierzeck		 $percent < $email_git_min_percent);
212592bca398SDaniel Schwierzeck	push_email_address($line, '');
212692bca398SDaniel Schwierzeck	if ($output_rolestats) {
212792bca398SDaniel Schwierzeck	    my $fmt_percent = sprintf("%.0f", $percent);
212892bca398SDaniel Schwierzeck	    add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
212992bca398SDaniel Schwierzeck	} else {
213092bca398SDaniel Schwierzeck	    add_role($line, $role);
213192bca398SDaniel Schwierzeck	}
213292bca398SDaniel Schwierzeck    }
213392bca398SDaniel Schwierzeck}
213492bca398SDaniel Schwierzeck
213592bca398SDaniel Schwierzecksub vcs_file_signoffs {
213692bca398SDaniel Schwierzeck    my ($file) = @_;
213792bca398SDaniel Schwierzeck
213892bca398SDaniel Schwierzeck    my $authors_ref;
213992bca398SDaniel Schwierzeck    my $signers_ref;
214092bca398SDaniel Schwierzeck    my $stats_ref;
214192bca398SDaniel Schwierzeck    my @authors = ();
214292bca398SDaniel Schwierzeck    my @signers = ();
214392bca398SDaniel Schwierzeck    my @stats = ();
214492bca398SDaniel Schwierzeck    my $commits;
214592bca398SDaniel Schwierzeck
214692bca398SDaniel Schwierzeck    $vcs_used = vcs_exists();
214792bca398SDaniel Schwierzeck    return if (!$vcs_used);
214892bca398SDaniel Schwierzeck
214992bca398SDaniel Schwierzeck    my $cmd = $VCS_cmds{"find_signers_cmd"};
215092bca398SDaniel Schwierzeck    $cmd =~ s/(\$\w+)/$1/eeg;		# interpolate $cmd
215192bca398SDaniel Schwierzeck
215292bca398SDaniel Schwierzeck    ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
215392bca398SDaniel Schwierzeck
215492bca398SDaniel Schwierzeck    @signers = @{$signers_ref} if defined $signers_ref;
215592bca398SDaniel Schwierzeck    @authors = @{$authors_ref} if defined $authors_ref;
215692bca398SDaniel Schwierzeck    @stats = @{$stats_ref} if defined $stats_ref;
215792bca398SDaniel Schwierzeck
215892bca398SDaniel Schwierzeck#    print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
215992bca398SDaniel Schwierzeck
216092bca398SDaniel Schwierzeck    foreach my $signer (@signers) {
216192bca398SDaniel Schwierzeck	$signer = deduplicate_email($signer);
216292bca398SDaniel Schwierzeck    }
216392bca398SDaniel Schwierzeck
216492bca398SDaniel Schwierzeck    vcs_assign("commit_signer", $commits, @signers);
216592bca398SDaniel Schwierzeck    vcs_assign("authored", $commits, @authors);
216692bca398SDaniel Schwierzeck    if ($#authors == $#stats) {
216792bca398SDaniel Schwierzeck	my $stat_pattern = $VCS_cmds{"stat_pattern"};
216892bca398SDaniel Schwierzeck	$stat_pattern =~ s/(\$\w+)/$1/eeg;	#interpolate $stat_pattern
216992bca398SDaniel Schwierzeck
217092bca398SDaniel Schwierzeck	my $added = 0;
217192bca398SDaniel Schwierzeck	my $deleted = 0;
217292bca398SDaniel Schwierzeck	for (my $i = 0; $i <= $#stats; $i++) {
217392bca398SDaniel Schwierzeck	    if ($stats[$i] =~ /$stat_pattern/) {
217492bca398SDaniel Schwierzeck		$added += $1;
217592bca398SDaniel Schwierzeck		$deleted += $2;
217692bca398SDaniel Schwierzeck	    }
217792bca398SDaniel Schwierzeck	}
217892bca398SDaniel Schwierzeck	my @tmp_authors = uniq(@authors);
217992bca398SDaniel Schwierzeck	foreach my $author (@tmp_authors) {
218092bca398SDaniel Schwierzeck	    $author = deduplicate_email($author);
218192bca398SDaniel Schwierzeck	}
218292bca398SDaniel Schwierzeck	@tmp_authors = uniq(@tmp_authors);
218392bca398SDaniel Schwierzeck	my @list_added = ();
218492bca398SDaniel Schwierzeck	my @list_deleted = ();
218592bca398SDaniel Schwierzeck	foreach my $author (@tmp_authors) {
218692bca398SDaniel Schwierzeck	    my $auth_added = 0;
218792bca398SDaniel Schwierzeck	    my $auth_deleted = 0;
218892bca398SDaniel Schwierzeck	    for (my $i = 0; $i <= $#stats; $i++) {
218992bca398SDaniel Schwierzeck		if ($author eq deduplicate_email($authors[$i]) &&
219092bca398SDaniel Schwierzeck		    $stats[$i] =~ /$stat_pattern/) {
219192bca398SDaniel Schwierzeck		    $auth_added += $1;
219292bca398SDaniel Schwierzeck		    $auth_deleted += $2;
219392bca398SDaniel Schwierzeck		}
219492bca398SDaniel Schwierzeck	    }
219592bca398SDaniel Schwierzeck	    for (my $i = 0; $i < $auth_added; $i++) {
219692bca398SDaniel Schwierzeck		push(@list_added, $author);
219792bca398SDaniel Schwierzeck	    }
219892bca398SDaniel Schwierzeck	    for (my $i = 0; $i < $auth_deleted; $i++) {
219992bca398SDaniel Schwierzeck		push(@list_deleted, $author);
220092bca398SDaniel Schwierzeck	    }
220192bca398SDaniel Schwierzeck	}
220292bca398SDaniel Schwierzeck	vcs_assign("added_lines", $added, @list_added);
220392bca398SDaniel Schwierzeck	vcs_assign("removed_lines", $deleted, @list_deleted);
220492bca398SDaniel Schwierzeck    }
220592bca398SDaniel Schwierzeck}
220692bca398SDaniel Schwierzeck
220792bca398SDaniel Schwierzecksub vcs_file_blame {
220892bca398SDaniel Schwierzeck    my ($file) = @_;
220992bca398SDaniel Schwierzeck
221092bca398SDaniel Schwierzeck    my @signers = ();
221192bca398SDaniel Schwierzeck    my @all_commits = ();
221292bca398SDaniel Schwierzeck    my @commits = ();
221392bca398SDaniel Schwierzeck    my $total_commits;
221492bca398SDaniel Schwierzeck    my $total_lines;
221592bca398SDaniel Schwierzeck
221692bca398SDaniel Schwierzeck    $vcs_used = vcs_exists();
221792bca398SDaniel Schwierzeck    return if (!$vcs_used);
221892bca398SDaniel Schwierzeck
221992bca398SDaniel Schwierzeck    @all_commits = vcs_blame($file);
222092bca398SDaniel Schwierzeck    @commits = uniq(@all_commits);
222192bca398SDaniel Schwierzeck    $total_commits = @commits;
222292bca398SDaniel Schwierzeck    $total_lines = @all_commits;
222392bca398SDaniel Schwierzeck
222492bca398SDaniel Schwierzeck    if ($email_git_blame_signatures) {
222592bca398SDaniel Schwierzeck	if (vcs_is_hg()) {
222692bca398SDaniel Schwierzeck	    my $commit_count;
222792bca398SDaniel Schwierzeck	    my $commit_authors_ref;
222892bca398SDaniel Schwierzeck	    my $commit_signers_ref;
222992bca398SDaniel Schwierzeck	    my $stats_ref;
223092bca398SDaniel Schwierzeck	    my @commit_authors = ();
223192bca398SDaniel Schwierzeck	    my @commit_signers = ();
223292bca398SDaniel Schwierzeck	    my $commit = join(" -r ", @commits);
223392bca398SDaniel Schwierzeck	    my $cmd;
223492bca398SDaniel Schwierzeck
223592bca398SDaniel Schwierzeck	    $cmd = $VCS_cmds{"find_commit_signers_cmd"};
223692bca398SDaniel Schwierzeck	    $cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
223792bca398SDaniel Schwierzeck
223892bca398SDaniel Schwierzeck	    ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
223992bca398SDaniel Schwierzeck	    @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
224092bca398SDaniel Schwierzeck	    @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
224192bca398SDaniel Schwierzeck
224292bca398SDaniel Schwierzeck	    push(@signers, @commit_signers);
224392bca398SDaniel Schwierzeck	} else {
224492bca398SDaniel Schwierzeck	    foreach my $commit (@commits) {
224592bca398SDaniel Schwierzeck		my $commit_count;
224692bca398SDaniel Schwierzeck		my $commit_authors_ref;
224792bca398SDaniel Schwierzeck		my $commit_signers_ref;
224892bca398SDaniel Schwierzeck		my $stats_ref;
224992bca398SDaniel Schwierzeck		my @commit_authors = ();
225092bca398SDaniel Schwierzeck		my @commit_signers = ();
225192bca398SDaniel Schwierzeck		my $cmd;
225292bca398SDaniel Schwierzeck
225392bca398SDaniel Schwierzeck		$cmd = $VCS_cmds{"find_commit_signers_cmd"};
225492bca398SDaniel Schwierzeck		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
225592bca398SDaniel Schwierzeck
225692bca398SDaniel Schwierzeck		($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
225792bca398SDaniel Schwierzeck		@commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
225892bca398SDaniel Schwierzeck		@commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
225992bca398SDaniel Schwierzeck
226092bca398SDaniel Schwierzeck		push(@signers, @commit_signers);
226192bca398SDaniel Schwierzeck	    }
226292bca398SDaniel Schwierzeck	}
226392bca398SDaniel Schwierzeck    }
226492bca398SDaniel Schwierzeck
226592bca398SDaniel Schwierzeck    if ($from_filename) {
226692bca398SDaniel Schwierzeck	if ($output_rolestats) {
226792bca398SDaniel Schwierzeck	    my @blame_signers;
226892bca398SDaniel Schwierzeck	    if (vcs_is_hg()) {{		# Double brace for last exit
226992bca398SDaniel Schwierzeck		my $commit_count;
227092bca398SDaniel Schwierzeck		my @commit_signers = ();
227192bca398SDaniel Schwierzeck		@commits = uniq(@commits);
227292bca398SDaniel Schwierzeck		@commits = sort(@commits);
227392bca398SDaniel Schwierzeck		my $commit = join(" -r ", @commits);
227492bca398SDaniel Schwierzeck		my $cmd;
227592bca398SDaniel Schwierzeck
227692bca398SDaniel Schwierzeck		$cmd = $VCS_cmds{"find_commit_author_cmd"};
227792bca398SDaniel Schwierzeck		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
227892bca398SDaniel Schwierzeck
227992bca398SDaniel Schwierzeck		my @lines = ();
228092bca398SDaniel Schwierzeck
228192bca398SDaniel Schwierzeck		@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
228292bca398SDaniel Schwierzeck
228392bca398SDaniel Schwierzeck		if (!$email_git_penguin_chiefs) {
228492bca398SDaniel Schwierzeck		    @lines = grep(!/${penguin_chiefs}/i, @lines);
228592bca398SDaniel Schwierzeck		}
228692bca398SDaniel Schwierzeck
228792bca398SDaniel Schwierzeck		last if !@lines;
228892bca398SDaniel Schwierzeck
228992bca398SDaniel Schwierzeck		my @authors = ();
229092bca398SDaniel Schwierzeck		foreach my $line (@lines) {
229192bca398SDaniel Schwierzeck		    if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
229292bca398SDaniel Schwierzeck			my $author = $1;
229392bca398SDaniel Schwierzeck			$author = deduplicate_email($author);
229492bca398SDaniel Schwierzeck			push(@authors, $author);
229592bca398SDaniel Schwierzeck		    }
229692bca398SDaniel Schwierzeck		}
229792bca398SDaniel Schwierzeck
229892bca398SDaniel Schwierzeck		save_commits_by_author(@lines) if ($interactive);
229992bca398SDaniel Schwierzeck		save_commits_by_signer(@lines) if ($interactive);
230092bca398SDaniel Schwierzeck
230192bca398SDaniel Schwierzeck		push(@signers, @authors);
230292bca398SDaniel Schwierzeck	    }}
230392bca398SDaniel Schwierzeck	    else {
230492bca398SDaniel Schwierzeck		foreach my $commit (@commits) {
230592bca398SDaniel Schwierzeck		    my $i;
230692bca398SDaniel Schwierzeck		    my $cmd = $VCS_cmds{"find_commit_author_cmd"};
230792bca398SDaniel Schwierzeck		    $cmd =~ s/(\$\w+)/$1/eeg;	#interpolate $cmd
230892bca398SDaniel Schwierzeck		    my @author = vcs_find_author($cmd);
230992bca398SDaniel Schwierzeck		    next if !@author;
231092bca398SDaniel Schwierzeck
231192bca398SDaniel Schwierzeck		    my $formatted_author = deduplicate_email($author[0]);
231292bca398SDaniel Schwierzeck
231392bca398SDaniel Schwierzeck		    my $count = grep(/$commit/, @all_commits);
231492bca398SDaniel Schwierzeck		    for ($i = 0; $i < $count ; $i++) {
231592bca398SDaniel Schwierzeck			push(@blame_signers, $formatted_author);
231692bca398SDaniel Schwierzeck		    }
231792bca398SDaniel Schwierzeck		}
231892bca398SDaniel Schwierzeck	    }
231992bca398SDaniel Schwierzeck	    if (@blame_signers) {
232092bca398SDaniel Schwierzeck		vcs_assign("authored lines", $total_lines, @blame_signers);
232192bca398SDaniel Schwierzeck	    }
232292bca398SDaniel Schwierzeck	}
232392bca398SDaniel Schwierzeck	foreach my $signer (@signers) {
232492bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
232592bca398SDaniel Schwierzeck	}
232692bca398SDaniel Schwierzeck	vcs_assign("commits", $total_commits, @signers);
232792bca398SDaniel Schwierzeck    } else {
232892bca398SDaniel Schwierzeck	foreach my $signer (@signers) {
232992bca398SDaniel Schwierzeck	    $signer = deduplicate_email($signer);
233092bca398SDaniel Schwierzeck	}
233192bca398SDaniel Schwierzeck	vcs_assign("modified commits", $total_commits, @signers);
233292bca398SDaniel Schwierzeck    }
233392bca398SDaniel Schwierzeck}
233492bca398SDaniel Schwierzeck
233559ab72d7SHeinrich Schuchardtsub vcs_file_exists {
233659ab72d7SHeinrich Schuchardt    my ($file) = @_;
233759ab72d7SHeinrich Schuchardt
233859ab72d7SHeinrich Schuchardt    my $exists;
233959ab72d7SHeinrich Schuchardt
234059ab72d7SHeinrich Schuchardt    my $vcs_used = vcs_exists();
234159ab72d7SHeinrich Schuchardt    return 0 if (!$vcs_used);
234259ab72d7SHeinrich Schuchardt
234359ab72d7SHeinrich Schuchardt    my $cmd = $VCS_cmds{"file_exists_cmd"};
234459ab72d7SHeinrich Schuchardt    $cmd =~ s/(\$\w+)/$1/eeg;		# interpolate $cmd
234559ab72d7SHeinrich Schuchardt    $cmd .= " 2>&1";
234659ab72d7SHeinrich Schuchardt    $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
234759ab72d7SHeinrich Schuchardt
234859ab72d7SHeinrich Schuchardt    return 0 if ($? != 0);
234959ab72d7SHeinrich Schuchardt
235059ab72d7SHeinrich Schuchardt    return $exists;
235159ab72d7SHeinrich Schuchardt}
235259ab72d7SHeinrich Schuchardt
2353*2f8ffb06SHeinrich Schuchardtsub vcs_list_files {
2354*2f8ffb06SHeinrich Schuchardt    my ($file) = @_;
2355*2f8ffb06SHeinrich Schuchardt
2356*2f8ffb06SHeinrich Schuchardt    my @lsfiles = ();
2357*2f8ffb06SHeinrich Schuchardt
2358*2f8ffb06SHeinrich Schuchardt    my $vcs_used = vcs_exists();
2359*2f8ffb06SHeinrich Schuchardt    return 0 if (!$vcs_used);
2360*2f8ffb06SHeinrich Schuchardt
2361*2f8ffb06SHeinrich Schuchardt    my $cmd = $VCS_cmds{"list_files_cmd"};
2362*2f8ffb06SHeinrich Schuchardt    $cmd =~ s/(\$\w+)/$1/eeg;   # interpolate $cmd
2363*2f8ffb06SHeinrich Schuchardt    @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
2364*2f8ffb06SHeinrich Schuchardt
2365*2f8ffb06SHeinrich Schuchardt    return () if ($? != 0);
2366*2f8ffb06SHeinrich Schuchardt
2367*2f8ffb06SHeinrich Schuchardt    return @lsfiles;
2368*2f8ffb06SHeinrich Schuchardt}
2369*2f8ffb06SHeinrich Schuchardt
237092bca398SDaniel Schwierzecksub uniq {
237192bca398SDaniel Schwierzeck    my (@parms) = @_;
237292bca398SDaniel Schwierzeck
237392bca398SDaniel Schwierzeck    my %saw;
237492bca398SDaniel Schwierzeck    @parms = grep(!$saw{$_}++, @parms);
237592bca398SDaniel Schwierzeck    return @parms;
237692bca398SDaniel Schwierzeck}
237792bca398SDaniel Schwierzeck
237892bca398SDaniel Schwierzecksub sort_and_uniq {
237992bca398SDaniel Schwierzeck    my (@parms) = @_;
238092bca398SDaniel Schwierzeck
238192bca398SDaniel Schwierzeck    my %saw;
238292bca398SDaniel Schwierzeck    @parms = sort @parms;
238392bca398SDaniel Schwierzeck    @parms = grep(!$saw{$_}++, @parms);
238492bca398SDaniel Schwierzeck    return @parms;
238592bca398SDaniel Schwierzeck}
238692bca398SDaniel Schwierzeck
238792bca398SDaniel Schwierzecksub clean_file_emails {
238892bca398SDaniel Schwierzeck    my (@file_emails) = @_;
238992bca398SDaniel Schwierzeck    my @fmt_emails = ();
239092bca398SDaniel Schwierzeck
239192bca398SDaniel Schwierzeck    foreach my $email (@file_emails) {
239292bca398SDaniel Schwierzeck	$email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
239392bca398SDaniel Schwierzeck	my ($name, $address) = parse_email($email);
239492bca398SDaniel Schwierzeck	if ($name eq '"[,\.]"') {
239592bca398SDaniel Schwierzeck	    $name = "";
239692bca398SDaniel Schwierzeck	}
239792bca398SDaniel Schwierzeck
239892bca398SDaniel Schwierzeck	my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
239992bca398SDaniel Schwierzeck	if (@nw > 2) {
240092bca398SDaniel Schwierzeck	    my $first = $nw[@nw - 3];
240192bca398SDaniel Schwierzeck	    my $middle = $nw[@nw - 2];
240292bca398SDaniel Schwierzeck	    my $last = $nw[@nw - 1];
240392bca398SDaniel Schwierzeck
240492bca398SDaniel Schwierzeck	    if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
240592bca398SDaniel Schwierzeck		 (length($first) == 2 && substr($first, -1) eq ".")) ||
240692bca398SDaniel Schwierzeck		(length($middle) == 1 ||
240792bca398SDaniel Schwierzeck		 (length($middle) == 2 && substr($middle, -1) eq "."))) {
240892bca398SDaniel Schwierzeck		$name = "$first $middle $last";
240992bca398SDaniel Schwierzeck	    } else {
241092bca398SDaniel Schwierzeck		$name = "$middle $last";
241192bca398SDaniel Schwierzeck	    }
241292bca398SDaniel Schwierzeck	}
241392bca398SDaniel Schwierzeck
241492bca398SDaniel Schwierzeck	if (substr($name, -1) =~ /[,\.]/) {
241592bca398SDaniel Schwierzeck	    $name = substr($name, 0, length($name) - 1);
241692bca398SDaniel Schwierzeck	} elsif (substr($name, -2) =~ /[,\.]"/) {
241792bca398SDaniel Schwierzeck	    $name = substr($name, 0, length($name) - 2) . '"';
241892bca398SDaniel Schwierzeck	}
241992bca398SDaniel Schwierzeck
242092bca398SDaniel Schwierzeck	if (substr($name, 0, 1) =~ /[,\.]/) {
242192bca398SDaniel Schwierzeck	    $name = substr($name, 1, length($name) - 1);
242292bca398SDaniel Schwierzeck	} elsif (substr($name, 0, 2) =~ /"[,\.]/) {
242392bca398SDaniel Schwierzeck	    $name = '"' . substr($name, 2, length($name) - 2);
242492bca398SDaniel Schwierzeck	}
242592bca398SDaniel Schwierzeck
242692bca398SDaniel Schwierzeck	my $fmt_email = format_email($name, $address, $email_usename);
242792bca398SDaniel Schwierzeck	push(@fmt_emails, $fmt_email);
242892bca398SDaniel Schwierzeck    }
242992bca398SDaniel Schwierzeck    return @fmt_emails;
243092bca398SDaniel Schwierzeck}
243192bca398SDaniel Schwierzeck
243292bca398SDaniel Schwierzecksub merge_email {
243392bca398SDaniel Schwierzeck    my @lines;
243492bca398SDaniel Schwierzeck    my %saw;
243592bca398SDaniel Schwierzeck
243692bca398SDaniel Schwierzeck    for (@_) {
243792bca398SDaniel Schwierzeck	my ($address, $role) = @$_;
243892bca398SDaniel Schwierzeck	if (!$saw{$address}) {
243992bca398SDaniel Schwierzeck	    if ($output_roles) {
244092bca398SDaniel Schwierzeck		push(@lines, "$address ($role)");
244192bca398SDaniel Schwierzeck	    } else {
244292bca398SDaniel Schwierzeck		push(@lines, $address);
244392bca398SDaniel Schwierzeck	    }
244492bca398SDaniel Schwierzeck	    $saw{$address} = 1;
244592bca398SDaniel Schwierzeck	}
244692bca398SDaniel Schwierzeck    }
244792bca398SDaniel Schwierzeck
244892bca398SDaniel Schwierzeck    return @lines;
244992bca398SDaniel Schwierzeck}
245092bca398SDaniel Schwierzeck
245192bca398SDaniel Schwierzecksub output {
245292bca398SDaniel Schwierzeck    my (@parms) = @_;
245392bca398SDaniel Schwierzeck
245492bca398SDaniel Schwierzeck    if ($output_multiline) {
245592bca398SDaniel Schwierzeck	foreach my $line (@parms) {
245692bca398SDaniel Schwierzeck	    print("${line}\n");
245792bca398SDaniel Schwierzeck	}
245892bca398SDaniel Schwierzeck    } else {
245992bca398SDaniel Schwierzeck	print(join($output_separator, @parms));
246092bca398SDaniel Schwierzeck	print("\n");
246192bca398SDaniel Schwierzeck    }
246292bca398SDaniel Schwierzeck}
246392bca398SDaniel Schwierzeck
246492bca398SDaniel Schwierzeckmy $rfc822re;
246592bca398SDaniel Schwierzeck
246692bca398SDaniel Schwierzecksub make_rfc822re {
246792bca398SDaniel Schwierzeck#   Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
246892bca398SDaniel Schwierzeck#   comment.  We must allow for rfc822_lwsp (or comments) after each of these.
246992bca398SDaniel Schwierzeck#   This regexp will only work on addresses which have had comments stripped
247092bca398SDaniel Schwierzeck#   and replaced with rfc822_lwsp.
247192bca398SDaniel Schwierzeck
247292bca398SDaniel Schwierzeck    my $specials = '()<>@,;:\\\\".\\[\\]';
247392bca398SDaniel Schwierzeck    my $controls = '\\000-\\037\\177';
247492bca398SDaniel Schwierzeck
247592bca398SDaniel Schwierzeck    my $dtext = "[^\\[\\]\\r\\\\]";
247692bca398SDaniel Schwierzeck    my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
247792bca398SDaniel Schwierzeck
247892bca398SDaniel Schwierzeck    my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
247992bca398SDaniel Schwierzeck
248092bca398SDaniel Schwierzeck#   Use zero-width assertion to spot the limit of an atom.  A simple
248192bca398SDaniel Schwierzeck#   $rfc822_lwsp* causes the regexp engine to hang occasionally.
248292bca398SDaniel Schwierzeck    my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
248392bca398SDaniel Schwierzeck    my $word = "(?:$atom|$quoted_string)";
248492bca398SDaniel Schwierzeck    my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
248592bca398SDaniel Schwierzeck
248692bca398SDaniel Schwierzeck    my $sub_domain = "(?:$atom|$domain_literal)";
248792bca398SDaniel Schwierzeck    my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
248892bca398SDaniel Schwierzeck
248992bca398SDaniel Schwierzeck    my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
249092bca398SDaniel Schwierzeck
249192bca398SDaniel Schwierzeck    my $phrase = "$word*";
249292bca398SDaniel Schwierzeck    my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
249392bca398SDaniel Schwierzeck    my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
249492bca398SDaniel Schwierzeck    my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
249592bca398SDaniel Schwierzeck
249692bca398SDaniel Schwierzeck    my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
249792bca398SDaniel Schwierzeck    my $address = "(?:$mailbox|$group)";
249892bca398SDaniel Schwierzeck
249992bca398SDaniel Schwierzeck    return "$rfc822_lwsp*$address";
250092bca398SDaniel Schwierzeck}
250192bca398SDaniel Schwierzeck
250292bca398SDaniel Schwierzecksub rfc822_strip_comments {
250392bca398SDaniel Schwierzeck    my $s = shift;
250492bca398SDaniel Schwierzeck#   Recursively remove comments, and replace with a single space.  The simpler
250592bca398SDaniel Schwierzeck#   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
250692bca398SDaniel Schwierzeck#   chars in atoms, for example.
250792bca398SDaniel Schwierzeck
250892bca398SDaniel Schwierzeck    while ($s =~ s/^((?:[^"\\]|\\.)*
250992bca398SDaniel Schwierzeck                    (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
251092bca398SDaniel Schwierzeck                    \((?:[^()\\]|\\.)*\)/$1 /osx) {}
251192bca398SDaniel Schwierzeck    return $s;
251292bca398SDaniel Schwierzeck}
251392bca398SDaniel Schwierzeck
251492bca398SDaniel Schwierzeck#   valid: returns true if the parameter is an RFC822 valid address
251592bca398SDaniel Schwierzeck#
251692bca398SDaniel Schwierzecksub rfc822_valid {
251792bca398SDaniel Schwierzeck    my $s = rfc822_strip_comments(shift);
251892bca398SDaniel Schwierzeck
251992bca398SDaniel Schwierzeck    if (!$rfc822re) {
252092bca398SDaniel Schwierzeck        $rfc822re = make_rfc822re();
252192bca398SDaniel Schwierzeck    }
252292bca398SDaniel Schwierzeck
252392bca398SDaniel Schwierzeck    return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
252492bca398SDaniel Schwierzeck}
252592bca398SDaniel Schwierzeck
252692bca398SDaniel Schwierzeck#   validlist: In scalar context, returns true if the parameter is an RFC822
252792bca398SDaniel Schwierzeck#              valid list of addresses.
252892bca398SDaniel Schwierzeck#
252992bca398SDaniel Schwierzeck#              In list context, returns an empty list on failure (an invalid
253092bca398SDaniel Schwierzeck#              address was found); otherwise a list whose first element is the
253192bca398SDaniel Schwierzeck#              number of addresses found and whose remaining elements are the
253292bca398SDaniel Schwierzeck#              addresses.  This is needed to disambiguate failure (invalid)
253392bca398SDaniel Schwierzeck#              from success with no addresses found, because an empty string is
253492bca398SDaniel Schwierzeck#              a valid list.
253592bca398SDaniel Schwierzeck
253692bca398SDaniel Schwierzecksub rfc822_validlist {
253792bca398SDaniel Schwierzeck    my $s = rfc822_strip_comments(shift);
253892bca398SDaniel Schwierzeck
253992bca398SDaniel Schwierzeck    if (!$rfc822re) {
254092bca398SDaniel Schwierzeck        $rfc822re = make_rfc822re();
254192bca398SDaniel Schwierzeck    }
254292bca398SDaniel Schwierzeck    # * null list items are valid according to the RFC
254392bca398SDaniel Schwierzeck    # * the '1' business is to aid in distinguishing failure from no results
254492bca398SDaniel Schwierzeck
254592bca398SDaniel Schwierzeck    my @r;
254692bca398SDaniel Schwierzeck    if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
254792bca398SDaniel Schwierzeck	$s =~ m/^$rfc822_char*$/) {
254892bca398SDaniel Schwierzeck        while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
254992bca398SDaniel Schwierzeck            push(@r, $1);
255092bca398SDaniel Schwierzeck        }
255192bca398SDaniel Schwierzeck        return wantarray ? (scalar(@r), @r) : 1;
255292bca398SDaniel Schwierzeck    }
255392bca398SDaniel Schwierzeck    return wantarray ? () : 0;
255492bca398SDaniel Schwierzeck}
2555