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