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