1*92bca398SDaniel Schwierzeck#!/usr/bin/perl -w 2*92bca398SDaniel Schwierzeck# (c) 2007, Joe Perches <joe@perches.com> 3*92bca398SDaniel Schwierzeck# created from checkpatch.pl 4*92bca398SDaniel Schwierzeck# 5*92bca398SDaniel Schwierzeck# Print selected MAINTAINERS information for 6*92bca398SDaniel Schwierzeck# the files modified in a patch or for a file 7*92bca398SDaniel Schwierzeck# 8*92bca398SDaniel Schwierzeck# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch> 9*92bca398SDaniel Schwierzeck# perl scripts/get_maintainer.pl [OPTIONS] -f <file> 10*92bca398SDaniel Schwierzeck# 11*92bca398SDaniel Schwierzeck# Licensed under the terms of the GNU GPL License version 2 12*92bca398SDaniel Schwierzeck 13*92bca398SDaniel Schwierzeckuse strict; 14*92bca398SDaniel Schwierzeck 15*92bca398SDaniel Schwierzeckmy $P = $0; 16*92bca398SDaniel Schwierzeckmy $V = '0.26'; 17*92bca398SDaniel Schwierzeck 18*92bca398SDaniel Schwierzeckuse Getopt::Long qw(:config no_auto_abbrev); 19*92bca398SDaniel Schwierzeck 20*92bca398SDaniel Schwierzeckmy $lk_path = "./"; 21*92bca398SDaniel Schwierzeckmy $email = 1; 22*92bca398SDaniel Schwierzeckmy $email_usename = 1; 23*92bca398SDaniel Schwierzeckmy $email_maintainer = 1; 24*92bca398SDaniel Schwierzeckmy $email_list = 1; 25*92bca398SDaniel Schwierzeckmy $email_subscriber_list = 0; 26*92bca398SDaniel Schwierzeckmy $email_git_penguin_chiefs = 0; 27*92bca398SDaniel Schwierzeckmy $email_git = 0; 28*92bca398SDaniel Schwierzeckmy $email_git_all_signature_types = 0; 29*92bca398SDaniel Schwierzeckmy $email_git_blame = 0; 30*92bca398SDaniel Schwierzeckmy $email_git_blame_signatures = 1; 31*92bca398SDaniel Schwierzeckmy $email_git_fallback = 1; 32*92bca398SDaniel Schwierzeckmy $email_git_min_signatures = 1; 33*92bca398SDaniel Schwierzeckmy $email_git_max_maintainers = 5; 34*92bca398SDaniel Schwierzeckmy $email_git_min_percent = 5; 35*92bca398SDaniel Schwierzeckmy $email_git_since = "1-year-ago"; 36*92bca398SDaniel Schwierzeckmy $email_hg_since = "-365"; 37*92bca398SDaniel Schwierzeckmy $interactive = 0; 38*92bca398SDaniel Schwierzeckmy $email_remove_duplicates = 1; 39*92bca398SDaniel Schwierzeckmy $email_use_mailmap = 1; 40*92bca398SDaniel Schwierzeckmy $output_multiline = 1; 41*92bca398SDaniel Schwierzeckmy $output_separator = ", "; 42*92bca398SDaniel Schwierzeckmy $output_roles = 0; 43*92bca398SDaniel Schwierzeckmy $output_rolestats = 1; 44*92bca398SDaniel Schwierzeckmy $scm = 0; 45*92bca398SDaniel Schwierzeckmy $web = 0; 46*92bca398SDaniel Schwierzeckmy $subsystem = 0; 47*92bca398SDaniel Schwierzeckmy $status = 0; 48*92bca398SDaniel Schwierzeckmy $keywords = 1; 49*92bca398SDaniel Schwierzeckmy $sections = 0; 50*92bca398SDaniel Schwierzeckmy $file_emails = 0; 51*92bca398SDaniel Schwierzeckmy $from_filename = 0; 52*92bca398SDaniel Schwierzeckmy $pattern_depth = 0; 53*92bca398SDaniel Schwierzeckmy $version = 0; 54*92bca398SDaniel Schwierzeckmy $help = 0; 55*92bca398SDaniel Schwierzeck 56*92bca398SDaniel Schwierzeckmy $vcs_used = 0; 57*92bca398SDaniel Schwierzeck 58*92bca398SDaniel Schwierzeckmy $exit = 0; 59*92bca398SDaniel Schwierzeck 60*92bca398SDaniel Schwierzeckmy %commit_author_hash; 61*92bca398SDaniel Schwierzeckmy %commit_signer_hash; 62*92bca398SDaniel Schwierzeck 63*92bca398SDaniel Schwierzeckmy @penguin_chief = (); 64*92bca398SDaniel Schwierzeckpush(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org"); 65*92bca398SDaniel Schwierzeck#Andrew wants in on most everything - 2009/01/14 66*92bca398SDaniel Schwierzeck#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org"); 67*92bca398SDaniel Schwierzeck 68*92bca398SDaniel Schwierzeckmy @penguin_chief_names = (); 69*92bca398SDaniel Schwierzeckforeach my $chief (@penguin_chief) { 70*92bca398SDaniel Schwierzeck if ($chief =~ m/^(.*):(.*)/) { 71*92bca398SDaniel Schwierzeck my $chief_name = $1; 72*92bca398SDaniel Schwierzeck my $chief_addr = $2; 73*92bca398SDaniel Schwierzeck push(@penguin_chief_names, $chief_name); 74*92bca398SDaniel Schwierzeck } 75*92bca398SDaniel Schwierzeck} 76*92bca398SDaniel Schwierzeckmy $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)"; 77*92bca398SDaniel Schwierzeck 78*92bca398SDaniel Schwierzeck# Signature types of people who are either 79*92bca398SDaniel Schwierzeck# a) responsible for the code in question, or 80*92bca398SDaniel Schwierzeck# b) familiar enough with it to give relevant feedback 81*92bca398SDaniel Schwierzeckmy @signature_tags = (); 82*92bca398SDaniel Schwierzeckpush(@signature_tags, "Signed-off-by:"); 83*92bca398SDaniel Schwierzeckpush(@signature_tags, "Reviewed-by:"); 84*92bca398SDaniel Schwierzeckpush(@signature_tags, "Acked-by:"); 85*92bca398SDaniel Schwierzeck 86*92bca398SDaniel Schwierzeckmy $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; 87*92bca398SDaniel Schwierzeck 88*92bca398SDaniel Schwierzeck# rfc822 email address - preloaded methods go here. 89*92bca398SDaniel Schwierzeckmy $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])"; 90*92bca398SDaniel Schwierzeckmy $rfc822_char = '[\\000-\\377]'; 91*92bca398SDaniel Schwierzeck 92*92bca398SDaniel Schwierzeck# VCS command support: class-like functions and strings 93*92bca398SDaniel Schwierzeck 94*92bca398SDaniel Schwierzeckmy %VCS_cmds; 95*92bca398SDaniel Schwierzeck 96*92bca398SDaniel Schwierzeckmy %VCS_cmds_git = ( 97*92bca398SDaniel Schwierzeck "execute_cmd" => \&git_execute_cmd, 98*92bca398SDaniel Schwierzeck "available" => '(which("git") ne "") && (-e ".git")', 99*92bca398SDaniel Schwierzeck "find_signers_cmd" => 100*92bca398SDaniel Schwierzeck "git log --no-color --follow --since=\$email_git_since " . 101*92bca398SDaniel Schwierzeck '--numstat --no-merges ' . 102*92bca398SDaniel Schwierzeck '--format="GitCommit: %H%n' . 103*92bca398SDaniel Schwierzeck 'GitAuthor: %an <%ae>%n' . 104*92bca398SDaniel Schwierzeck 'GitDate: %aD%n' . 105*92bca398SDaniel Schwierzeck 'GitSubject: %s%n' . 106*92bca398SDaniel Schwierzeck '%b%n"' . 107*92bca398SDaniel Schwierzeck " -- \$file", 108*92bca398SDaniel Schwierzeck "find_commit_signers_cmd" => 109*92bca398SDaniel Schwierzeck "git log --no-color " . 110*92bca398SDaniel Schwierzeck '--numstat ' . 111*92bca398SDaniel Schwierzeck '--format="GitCommit: %H%n' . 112*92bca398SDaniel Schwierzeck 'GitAuthor: %an <%ae>%n' . 113*92bca398SDaniel Schwierzeck 'GitDate: %aD%n' . 114*92bca398SDaniel Schwierzeck 'GitSubject: %s%n' . 115*92bca398SDaniel Schwierzeck '%b%n"' . 116*92bca398SDaniel Schwierzeck " -1 \$commit", 117*92bca398SDaniel Schwierzeck "find_commit_author_cmd" => 118*92bca398SDaniel Schwierzeck "git log --no-color " . 119*92bca398SDaniel Schwierzeck '--numstat ' . 120*92bca398SDaniel Schwierzeck '--format="GitCommit: %H%n' . 121*92bca398SDaniel Schwierzeck 'GitAuthor: %an <%ae>%n' . 122*92bca398SDaniel Schwierzeck 'GitDate: %aD%n' . 123*92bca398SDaniel Schwierzeck 'GitSubject: %s%n"' . 124*92bca398SDaniel Schwierzeck " -1 \$commit", 125*92bca398SDaniel Schwierzeck "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file", 126*92bca398SDaniel Schwierzeck "blame_file_cmd" => "git blame -l \$file", 127*92bca398SDaniel Schwierzeck "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})", 128*92bca398SDaniel Schwierzeck "blame_commit_pattern" => "^([0-9a-f]+) ", 129*92bca398SDaniel Schwierzeck "author_pattern" => "^GitAuthor: (.*)", 130*92bca398SDaniel Schwierzeck "subject_pattern" => "^GitSubject: (.*)", 131*92bca398SDaniel Schwierzeck "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$", 132*92bca398SDaniel Schwierzeck); 133*92bca398SDaniel Schwierzeck 134*92bca398SDaniel Schwierzeckmy %VCS_cmds_hg = ( 135*92bca398SDaniel Schwierzeck "execute_cmd" => \&hg_execute_cmd, 136*92bca398SDaniel Schwierzeck "available" => '(which("hg") ne "") && (-d ".hg")', 137*92bca398SDaniel Schwierzeck "find_signers_cmd" => 138*92bca398SDaniel Schwierzeck "hg log --date=\$email_hg_since " . 139*92bca398SDaniel Schwierzeck "--template='HgCommit: {node}\\n" . 140*92bca398SDaniel Schwierzeck "HgAuthor: {author}\\n" . 141*92bca398SDaniel Schwierzeck "HgSubject: {desc}\\n'" . 142*92bca398SDaniel Schwierzeck " -- \$file", 143*92bca398SDaniel Schwierzeck "find_commit_signers_cmd" => 144*92bca398SDaniel Schwierzeck "hg log " . 145*92bca398SDaniel Schwierzeck "--template='HgSubject: {desc}\\n'" . 146*92bca398SDaniel Schwierzeck " -r \$commit", 147*92bca398SDaniel Schwierzeck "find_commit_author_cmd" => 148*92bca398SDaniel Schwierzeck "hg log " . 149*92bca398SDaniel Schwierzeck "--template='HgCommit: {node}\\n" . 150*92bca398SDaniel Schwierzeck "HgAuthor: {author}\\n" . 151*92bca398SDaniel Schwierzeck "HgSubject: {desc|firstline}\\n'" . 152*92bca398SDaniel Schwierzeck " -r \$commit", 153*92bca398SDaniel Schwierzeck "blame_range_cmd" => "", # not supported 154*92bca398SDaniel Schwierzeck "blame_file_cmd" => "hg blame -n \$file", 155*92bca398SDaniel Schwierzeck "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})", 156*92bca398SDaniel Schwierzeck "blame_commit_pattern" => "^([ 0-9a-f]+):", 157*92bca398SDaniel Schwierzeck "author_pattern" => "^HgAuthor: (.*)", 158*92bca398SDaniel Schwierzeck "subject_pattern" => "^HgSubject: (.*)", 159*92bca398SDaniel Schwierzeck "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$", 160*92bca398SDaniel Schwierzeck); 161*92bca398SDaniel Schwierzeck 162*92bca398SDaniel Schwierzeckmy $conf = which_conf(".get_maintainer.conf"); 163*92bca398SDaniel Schwierzeckif (-f $conf) { 164*92bca398SDaniel Schwierzeck my @conf_args; 165*92bca398SDaniel Schwierzeck open(my $conffile, '<', "$conf") 166*92bca398SDaniel Schwierzeck or warn "$P: Can't find a readable .get_maintainer.conf file $!\n"; 167*92bca398SDaniel Schwierzeck 168*92bca398SDaniel Schwierzeck while (<$conffile>) { 169*92bca398SDaniel Schwierzeck my $line = $_; 170*92bca398SDaniel Schwierzeck 171*92bca398SDaniel Schwierzeck $line =~ s/\s*\n?$//g; 172*92bca398SDaniel Schwierzeck $line =~ s/^\s*//g; 173*92bca398SDaniel Schwierzeck $line =~ s/\s+/ /g; 174*92bca398SDaniel Schwierzeck 175*92bca398SDaniel Schwierzeck next if ($line =~ m/^\s*#/); 176*92bca398SDaniel Schwierzeck next if ($line =~ m/^\s*$/); 177*92bca398SDaniel Schwierzeck 178*92bca398SDaniel Schwierzeck my @words = split(" ", $line); 179*92bca398SDaniel Schwierzeck foreach my $word (@words) { 180*92bca398SDaniel Schwierzeck last if ($word =~ m/^#/); 181*92bca398SDaniel Schwierzeck push (@conf_args, $word); 182*92bca398SDaniel Schwierzeck } 183*92bca398SDaniel Schwierzeck } 184*92bca398SDaniel Schwierzeck close($conffile); 185*92bca398SDaniel Schwierzeck unshift(@ARGV, @conf_args) if @conf_args; 186*92bca398SDaniel Schwierzeck} 187*92bca398SDaniel Schwierzeck 188*92bca398SDaniel Schwierzeckif (!GetOptions( 189*92bca398SDaniel Schwierzeck 'email!' => \$email, 190*92bca398SDaniel Schwierzeck 'git!' => \$email_git, 191*92bca398SDaniel Schwierzeck 'git-all-signature-types!' => \$email_git_all_signature_types, 192*92bca398SDaniel Schwierzeck 'git-blame!' => \$email_git_blame, 193*92bca398SDaniel Schwierzeck 'git-blame-signatures!' => \$email_git_blame_signatures, 194*92bca398SDaniel Schwierzeck 'git-fallback!' => \$email_git_fallback, 195*92bca398SDaniel Schwierzeck 'git-chief-penguins!' => \$email_git_penguin_chiefs, 196*92bca398SDaniel Schwierzeck 'git-min-signatures=i' => \$email_git_min_signatures, 197*92bca398SDaniel Schwierzeck 'git-max-maintainers=i' => \$email_git_max_maintainers, 198*92bca398SDaniel Schwierzeck 'git-min-percent=i' => \$email_git_min_percent, 199*92bca398SDaniel Schwierzeck 'git-since=s' => \$email_git_since, 200*92bca398SDaniel Schwierzeck 'hg-since=s' => \$email_hg_since, 201*92bca398SDaniel Schwierzeck 'i|interactive!' => \$interactive, 202*92bca398SDaniel Schwierzeck 'remove-duplicates!' => \$email_remove_duplicates, 203*92bca398SDaniel Schwierzeck 'mailmap!' => \$email_use_mailmap, 204*92bca398SDaniel Schwierzeck 'm!' => \$email_maintainer, 205*92bca398SDaniel Schwierzeck 'n!' => \$email_usename, 206*92bca398SDaniel Schwierzeck 'l!' => \$email_list, 207*92bca398SDaniel Schwierzeck 's!' => \$email_subscriber_list, 208*92bca398SDaniel Schwierzeck 'multiline!' => \$output_multiline, 209*92bca398SDaniel Schwierzeck 'roles!' => \$output_roles, 210*92bca398SDaniel Schwierzeck 'rolestats!' => \$output_rolestats, 211*92bca398SDaniel Schwierzeck 'separator=s' => \$output_separator, 212*92bca398SDaniel Schwierzeck 'subsystem!' => \$subsystem, 213*92bca398SDaniel Schwierzeck 'status!' => \$status, 214*92bca398SDaniel Schwierzeck 'scm!' => \$scm, 215*92bca398SDaniel Schwierzeck 'web!' => \$web, 216*92bca398SDaniel Schwierzeck 'pattern-depth=i' => \$pattern_depth, 217*92bca398SDaniel Schwierzeck 'k|keywords!' => \$keywords, 218*92bca398SDaniel Schwierzeck 'sections!' => \$sections, 219*92bca398SDaniel Schwierzeck 'fe|file-emails!' => \$file_emails, 220*92bca398SDaniel Schwierzeck 'f|file' => \$from_filename, 221*92bca398SDaniel Schwierzeck 'v|version' => \$version, 222*92bca398SDaniel Schwierzeck 'h|help|usage' => \$help, 223*92bca398SDaniel Schwierzeck )) { 224*92bca398SDaniel Schwierzeck die "$P: invalid argument - use --help if necessary\n"; 225*92bca398SDaniel Schwierzeck} 226*92bca398SDaniel Schwierzeck 227*92bca398SDaniel Schwierzeckif ($help != 0) { 228*92bca398SDaniel Schwierzeck usage(); 229*92bca398SDaniel Schwierzeck exit 0; 230*92bca398SDaniel Schwierzeck} 231*92bca398SDaniel Schwierzeck 232*92bca398SDaniel Schwierzeckif ($version != 0) { 233*92bca398SDaniel Schwierzeck print("${P} ${V}\n"); 234*92bca398SDaniel Schwierzeck exit 0; 235*92bca398SDaniel Schwierzeck} 236*92bca398SDaniel Schwierzeck 237*92bca398SDaniel Schwierzeckif (-t STDIN && !@ARGV) { 238*92bca398SDaniel Schwierzeck # We're talking to a terminal, but have no command line arguments. 239*92bca398SDaniel Schwierzeck die "$P: missing patchfile or -f file - use --help if necessary\n"; 240*92bca398SDaniel Schwierzeck} 241*92bca398SDaniel Schwierzeck 242*92bca398SDaniel Schwierzeck$output_multiline = 0 if ($output_separator ne ", "); 243*92bca398SDaniel Schwierzeck$output_rolestats = 1 if ($interactive); 244*92bca398SDaniel Schwierzeck$output_roles = 1 if ($output_rolestats); 245*92bca398SDaniel Schwierzeck 246*92bca398SDaniel Schwierzeckif ($sections) { 247*92bca398SDaniel Schwierzeck $email = 0; 248*92bca398SDaniel Schwierzeck $email_list = 0; 249*92bca398SDaniel Schwierzeck $scm = 0; 250*92bca398SDaniel Schwierzeck $status = 0; 251*92bca398SDaniel Schwierzeck $subsystem = 0; 252*92bca398SDaniel Schwierzeck $web = 0; 253*92bca398SDaniel Schwierzeck $keywords = 0; 254*92bca398SDaniel Schwierzeck $interactive = 0; 255*92bca398SDaniel Schwierzeck} else { 256*92bca398SDaniel Schwierzeck my $selections = $email + $scm + $status + $subsystem + $web; 257*92bca398SDaniel Schwierzeck if ($selections == 0) { 258*92bca398SDaniel Schwierzeck die "$P: Missing required option: email, scm, status, subsystem or web\n"; 259*92bca398SDaniel Schwierzeck } 260*92bca398SDaniel Schwierzeck} 261*92bca398SDaniel Schwierzeck 262*92bca398SDaniel Schwierzeckif ($email && 263*92bca398SDaniel Schwierzeck ($email_maintainer + $email_list + $email_subscriber_list + 264*92bca398SDaniel Schwierzeck $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) { 265*92bca398SDaniel Schwierzeck die "$P: Please select at least 1 email option\n"; 266*92bca398SDaniel Schwierzeck} 267*92bca398SDaniel Schwierzeck 268*92bca398SDaniel Schwierzeckif (!top_of_kernel_tree($lk_path)) { 269*92bca398SDaniel Schwierzeck die "$P: The current directory does not appear to be " 270*92bca398SDaniel Schwierzeck . "a linux kernel source tree.\n"; 271*92bca398SDaniel Schwierzeck} 272*92bca398SDaniel Schwierzeck 273*92bca398SDaniel Schwierzeck## Read MAINTAINERS for type/value pairs 274*92bca398SDaniel Schwierzeck 275*92bca398SDaniel Schwierzeckmy @typevalue = (); 276*92bca398SDaniel Schwierzeckmy %keyword_hash; 277*92bca398SDaniel Schwierzeck 278*92bca398SDaniel Schwierzeckopen (my $maint, '<', "${lk_path}MAINTAINERS") 279*92bca398SDaniel Schwierzeck or die "$P: Can't open MAINTAINERS: $!\n"; 280*92bca398SDaniel Schwierzeckwhile (<$maint>) { 281*92bca398SDaniel Schwierzeck my $line = $_; 282*92bca398SDaniel Schwierzeck 283*92bca398SDaniel Schwierzeck if ($line =~ m/^(\C):\s*(.*)/) { 284*92bca398SDaniel Schwierzeck my $type = $1; 285*92bca398SDaniel Schwierzeck my $value = $2; 286*92bca398SDaniel Schwierzeck 287*92bca398SDaniel Schwierzeck ##Filename pattern matching 288*92bca398SDaniel Schwierzeck if ($type eq "F" || $type eq "X") { 289*92bca398SDaniel Schwierzeck $value =~ s@\.@\\\.@g; ##Convert . to \. 290*92bca398SDaniel Schwierzeck $value =~ s/\*/\.\*/g; ##Convert * to .* 291*92bca398SDaniel Schwierzeck $value =~ s/\?/\./g; ##Convert ? to . 292*92bca398SDaniel Schwierzeck ##if pattern is a directory and it lacks a trailing slash, add one 293*92bca398SDaniel Schwierzeck if ((-d $value)) { 294*92bca398SDaniel Schwierzeck $value =~ s@([^/])$@$1/@; 295*92bca398SDaniel Schwierzeck } 296*92bca398SDaniel Schwierzeck } elsif ($type eq "K") { 297*92bca398SDaniel Schwierzeck $keyword_hash{@typevalue} = $value; 298*92bca398SDaniel Schwierzeck } 299*92bca398SDaniel Schwierzeck push(@typevalue, "$type:$value"); 300*92bca398SDaniel Schwierzeck } elsif (!/^(\s)*$/) { 301*92bca398SDaniel Schwierzeck $line =~ s/\n$//g; 302*92bca398SDaniel Schwierzeck push(@typevalue, $line); 303*92bca398SDaniel Schwierzeck } 304*92bca398SDaniel Schwierzeck} 305*92bca398SDaniel Schwierzeckclose($maint); 306*92bca398SDaniel Schwierzeck 307*92bca398SDaniel Schwierzeck 308*92bca398SDaniel Schwierzeck# 309*92bca398SDaniel Schwierzeck# Read mail address map 310*92bca398SDaniel Schwierzeck# 311*92bca398SDaniel Schwierzeck 312*92bca398SDaniel Schwierzeckmy $mailmap; 313*92bca398SDaniel Schwierzeck 314*92bca398SDaniel Schwierzeckread_mailmap(); 315*92bca398SDaniel Schwierzeck 316*92bca398SDaniel Schwierzecksub read_mailmap { 317*92bca398SDaniel Schwierzeck $mailmap = { 318*92bca398SDaniel Schwierzeck names => {}, 319*92bca398SDaniel Schwierzeck addresses => {} 320*92bca398SDaniel Schwierzeck }; 321*92bca398SDaniel Schwierzeck 322*92bca398SDaniel Schwierzeck return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap")); 323*92bca398SDaniel Schwierzeck 324*92bca398SDaniel Schwierzeck open(my $mailmap_file, '<', "${lk_path}.mailmap") 325*92bca398SDaniel Schwierzeck or warn "$P: Can't open .mailmap: $!\n"; 326*92bca398SDaniel Schwierzeck 327*92bca398SDaniel Schwierzeck while (<$mailmap_file>) { 328*92bca398SDaniel Schwierzeck s/#.*$//; #strip comments 329*92bca398SDaniel Schwierzeck s/^\s+|\s+$//g; #trim 330*92bca398SDaniel Schwierzeck 331*92bca398SDaniel Schwierzeck next if (/^\s*$/); #skip empty lines 332*92bca398SDaniel Schwierzeck #entries have one of the following formats: 333*92bca398SDaniel Schwierzeck # name1 <mail1> 334*92bca398SDaniel Schwierzeck # <mail1> <mail2> 335*92bca398SDaniel Schwierzeck # name1 <mail1> <mail2> 336*92bca398SDaniel Schwierzeck # name1 <mail1> name2 <mail2> 337*92bca398SDaniel Schwierzeck # (see man git-shortlog) 338*92bca398SDaniel Schwierzeck 339*92bca398SDaniel Schwierzeck if (/^([^<]+)<([^>]+)>$/) { 340*92bca398SDaniel Schwierzeck my $real_name = $1; 341*92bca398SDaniel Schwierzeck my $address = $2; 342*92bca398SDaniel Schwierzeck 343*92bca398SDaniel Schwierzeck $real_name =~ s/\s+$//; 344*92bca398SDaniel Schwierzeck ($real_name, $address) = parse_email("$real_name <$address>"); 345*92bca398SDaniel Schwierzeck $mailmap->{names}->{$address} = $real_name; 346*92bca398SDaniel Schwierzeck 347*92bca398SDaniel Schwierzeck } elsif (/^<([^>]+)>\s*<([^>]+)>$/) { 348*92bca398SDaniel Schwierzeck my $real_address = $1; 349*92bca398SDaniel Schwierzeck my $wrong_address = $2; 350*92bca398SDaniel Schwierzeck 351*92bca398SDaniel Schwierzeck $mailmap->{addresses}->{$wrong_address} = $real_address; 352*92bca398SDaniel Schwierzeck 353*92bca398SDaniel Schwierzeck } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) { 354*92bca398SDaniel Schwierzeck my $real_name = $1; 355*92bca398SDaniel Schwierzeck my $real_address = $2; 356*92bca398SDaniel Schwierzeck my $wrong_address = $3; 357*92bca398SDaniel Schwierzeck 358*92bca398SDaniel Schwierzeck $real_name =~ s/\s+$//; 359*92bca398SDaniel Schwierzeck ($real_name, $real_address) = 360*92bca398SDaniel Schwierzeck parse_email("$real_name <$real_address>"); 361*92bca398SDaniel Schwierzeck $mailmap->{names}->{$wrong_address} = $real_name; 362*92bca398SDaniel Schwierzeck $mailmap->{addresses}->{$wrong_address} = $real_address; 363*92bca398SDaniel Schwierzeck 364*92bca398SDaniel Schwierzeck } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) { 365*92bca398SDaniel Schwierzeck my $real_name = $1; 366*92bca398SDaniel Schwierzeck my $real_address = $2; 367*92bca398SDaniel Schwierzeck my $wrong_name = $3; 368*92bca398SDaniel Schwierzeck my $wrong_address = $4; 369*92bca398SDaniel Schwierzeck 370*92bca398SDaniel Schwierzeck $real_name =~ s/\s+$//; 371*92bca398SDaniel Schwierzeck ($real_name, $real_address) = 372*92bca398SDaniel Schwierzeck parse_email("$real_name <$real_address>"); 373*92bca398SDaniel Schwierzeck 374*92bca398SDaniel Schwierzeck $wrong_name =~ s/\s+$//; 375*92bca398SDaniel Schwierzeck ($wrong_name, $wrong_address) = 376*92bca398SDaniel Schwierzeck parse_email("$wrong_name <$wrong_address>"); 377*92bca398SDaniel Schwierzeck 378*92bca398SDaniel Schwierzeck my $wrong_email = format_email($wrong_name, $wrong_address, 1); 379*92bca398SDaniel Schwierzeck $mailmap->{names}->{$wrong_email} = $real_name; 380*92bca398SDaniel Schwierzeck $mailmap->{addresses}->{$wrong_email} = $real_address; 381*92bca398SDaniel Schwierzeck } 382*92bca398SDaniel Schwierzeck } 383*92bca398SDaniel Schwierzeck close($mailmap_file); 384*92bca398SDaniel Schwierzeck} 385*92bca398SDaniel Schwierzeck 386*92bca398SDaniel Schwierzeck## use the filenames on the command line or find the filenames in the patchfiles 387*92bca398SDaniel Schwierzeck 388*92bca398SDaniel Schwierzeckmy @files = (); 389*92bca398SDaniel Schwierzeckmy @range = (); 390*92bca398SDaniel Schwierzeckmy @keyword_tvi = (); 391*92bca398SDaniel Schwierzeckmy @file_emails = (); 392*92bca398SDaniel Schwierzeck 393*92bca398SDaniel Schwierzeckif (!@ARGV) { 394*92bca398SDaniel Schwierzeck push(@ARGV, "&STDIN"); 395*92bca398SDaniel Schwierzeck} 396*92bca398SDaniel Schwierzeck 397*92bca398SDaniel Schwierzeckforeach my $file (@ARGV) { 398*92bca398SDaniel Schwierzeck if ($file ne "&STDIN") { 399*92bca398SDaniel Schwierzeck ##if $file is a directory and it lacks a trailing slash, add one 400*92bca398SDaniel Schwierzeck if ((-d $file)) { 401*92bca398SDaniel Schwierzeck $file =~ s@([^/])$@$1/@; 402*92bca398SDaniel Schwierzeck } elsif (!(-f $file)) { 403*92bca398SDaniel Schwierzeck die "$P: file '${file}' not found\n"; 404*92bca398SDaniel Schwierzeck } 405*92bca398SDaniel Schwierzeck } 406*92bca398SDaniel Schwierzeck if ($from_filename) { 407*92bca398SDaniel Schwierzeck push(@files, $file); 408*92bca398SDaniel Schwierzeck if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) { 409*92bca398SDaniel Schwierzeck open(my $f, '<', $file) 410*92bca398SDaniel Schwierzeck or die "$P: Can't open $file: $!\n"; 411*92bca398SDaniel Schwierzeck my $text = do { local($/) ; <$f> }; 412*92bca398SDaniel Schwierzeck close($f); 413*92bca398SDaniel Schwierzeck if ($keywords) { 414*92bca398SDaniel Schwierzeck foreach my $line (keys %keyword_hash) { 415*92bca398SDaniel Schwierzeck if ($text =~ m/$keyword_hash{$line}/x) { 416*92bca398SDaniel Schwierzeck push(@keyword_tvi, $line); 417*92bca398SDaniel Schwierzeck } 418*92bca398SDaniel Schwierzeck } 419*92bca398SDaniel Schwierzeck } 420*92bca398SDaniel Schwierzeck if ($file_emails) { 421*92bca398SDaniel 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; 422*92bca398SDaniel Schwierzeck push(@file_emails, clean_file_emails(@poss_addr)); 423*92bca398SDaniel Schwierzeck } 424*92bca398SDaniel Schwierzeck } 425*92bca398SDaniel Schwierzeck } else { 426*92bca398SDaniel Schwierzeck my $file_cnt = @files; 427*92bca398SDaniel Schwierzeck my $lastfile; 428*92bca398SDaniel Schwierzeck 429*92bca398SDaniel Schwierzeck open(my $patch, "< $file") 430*92bca398SDaniel Schwierzeck or die "$P: Can't open $file: $!\n"; 431*92bca398SDaniel Schwierzeck 432*92bca398SDaniel Schwierzeck # We can check arbitrary information before the patch 433*92bca398SDaniel Schwierzeck # like the commit message, mail headers, etc... 434*92bca398SDaniel Schwierzeck # This allows us to match arbitrary keywords against any part 435*92bca398SDaniel Schwierzeck # of a git format-patch generated file (subject tags, etc...) 436*92bca398SDaniel Schwierzeck 437*92bca398SDaniel Schwierzeck my $patch_prefix = ""; #Parsing the intro 438*92bca398SDaniel Schwierzeck 439*92bca398SDaniel Schwierzeck while (<$patch>) { 440*92bca398SDaniel Schwierzeck my $patch_line = $_; 441*92bca398SDaniel Schwierzeck if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) { 442*92bca398SDaniel Schwierzeck my $filename = $1; 443*92bca398SDaniel Schwierzeck $filename =~ s@^[^/]*/@@; 444*92bca398SDaniel Schwierzeck $filename =~ s@\n@@; 445*92bca398SDaniel Schwierzeck $lastfile = $filename; 446*92bca398SDaniel Schwierzeck push(@files, $filename); 447*92bca398SDaniel Schwierzeck $patch_prefix = "^[+-].*"; #Now parsing the actual patch 448*92bca398SDaniel Schwierzeck } elsif (m/^\@\@ -(\d+),(\d+)/) { 449*92bca398SDaniel Schwierzeck if ($email_git_blame) { 450*92bca398SDaniel Schwierzeck push(@range, "$lastfile:$1:$2"); 451*92bca398SDaniel Schwierzeck } 452*92bca398SDaniel Schwierzeck } elsif ($keywords) { 453*92bca398SDaniel Schwierzeck foreach my $line (keys %keyword_hash) { 454*92bca398SDaniel Schwierzeck if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) { 455*92bca398SDaniel Schwierzeck push(@keyword_tvi, $line); 456*92bca398SDaniel Schwierzeck } 457*92bca398SDaniel Schwierzeck } 458*92bca398SDaniel Schwierzeck } 459*92bca398SDaniel Schwierzeck } 460*92bca398SDaniel Schwierzeck close($patch); 461*92bca398SDaniel Schwierzeck 462*92bca398SDaniel Schwierzeck if ($file_cnt == @files) { 463*92bca398SDaniel Schwierzeck warn "$P: file '${file}' doesn't appear to be a patch. " 464*92bca398SDaniel Schwierzeck . "Add -f to options?\n"; 465*92bca398SDaniel Schwierzeck } 466*92bca398SDaniel Schwierzeck @files = sort_and_uniq(@files); 467*92bca398SDaniel Schwierzeck } 468*92bca398SDaniel Schwierzeck} 469*92bca398SDaniel Schwierzeck 470*92bca398SDaniel Schwierzeck@file_emails = uniq(@file_emails); 471*92bca398SDaniel Schwierzeck 472*92bca398SDaniel Schwierzeckmy %email_hash_name; 473*92bca398SDaniel Schwierzeckmy %email_hash_address; 474*92bca398SDaniel Schwierzeckmy @email_to = (); 475*92bca398SDaniel Schwierzeckmy %hash_list_to; 476*92bca398SDaniel Schwierzeckmy @list_to = (); 477*92bca398SDaniel Schwierzeckmy @scm = (); 478*92bca398SDaniel Schwierzeckmy @web = (); 479*92bca398SDaniel Schwierzeckmy @subsystem = (); 480*92bca398SDaniel Schwierzeckmy @status = (); 481*92bca398SDaniel Schwierzeckmy %deduplicate_name_hash = (); 482*92bca398SDaniel Schwierzeckmy %deduplicate_address_hash = (); 483*92bca398SDaniel Schwierzeck 484*92bca398SDaniel Schwierzeckmy @maintainers = get_maintainers(); 485*92bca398SDaniel Schwierzeck 486*92bca398SDaniel Schwierzeckif (@maintainers) { 487*92bca398SDaniel Schwierzeck @maintainers = merge_email(@maintainers); 488*92bca398SDaniel Schwierzeck output(@maintainers); 489*92bca398SDaniel Schwierzeck} 490*92bca398SDaniel Schwierzeck 491*92bca398SDaniel Schwierzeckif ($scm) { 492*92bca398SDaniel Schwierzeck @scm = uniq(@scm); 493*92bca398SDaniel Schwierzeck output(@scm); 494*92bca398SDaniel Schwierzeck} 495*92bca398SDaniel Schwierzeck 496*92bca398SDaniel Schwierzeckif ($status) { 497*92bca398SDaniel Schwierzeck @status = uniq(@status); 498*92bca398SDaniel Schwierzeck output(@status); 499*92bca398SDaniel Schwierzeck} 500*92bca398SDaniel Schwierzeck 501*92bca398SDaniel Schwierzeckif ($subsystem) { 502*92bca398SDaniel Schwierzeck @subsystem = uniq(@subsystem); 503*92bca398SDaniel Schwierzeck output(@subsystem); 504*92bca398SDaniel Schwierzeck} 505*92bca398SDaniel Schwierzeck 506*92bca398SDaniel Schwierzeckif ($web) { 507*92bca398SDaniel Schwierzeck @web = uniq(@web); 508*92bca398SDaniel Schwierzeck output(@web); 509*92bca398SDaniel Schwierzeck} 510*92bca398SDaniel Schwierzeck 511*92bca398SDaniel Schwierzeckexit($exit); 512*92bca398SDaniel Schwierzeck 513*92bca398SDaniel Schwierzecksub range_is_maintained { 514*92bca398SDaniel Schwierzeck my ($start, $end) = @_; 515*92bca398SDaniel Schwierzeck 516*92bca398SDaniel Schwierzeck for (my $i = $start; $i < $end; $i++) { 517*92bca398SDaniel Schwierzeck my $line = $typevalue[$i]; 518*92bca398SDaniel Schwierzeck if ($line =~ m/^(\C):\s*(.*)/) { 519*92bca398SDaniel Schwierzeck my $type = $1; 520*92bca398SDaniel Schwierzeck my $value = $2; 521*92bca398SDaniel Schwierzeck if ($type eq 'S') { 522*92bca398SDaniel Schwierzeck if ($value =~ /(maintain|support)/i) { 523*92bca398SDaniel Schwierzeck return 1; 524*92bca398SDaniel Schwierzeck } 525*92bca398SDaniel Schwierzeck } 526*92bca398SDaniel Schwierzeck } 527*92bca398SDaniel Schwierzeck } 528*92bca398SDaniel Schwierzeck return 0; 529*92bca398SDaniel Schwierzeck} 530*92bca398SDaniel Schwierzeck 531*92bca398SDaniel Schwierzecksub range_has_maintainer { 532*92bca398SDaniel Schwierzeck my ($start, $end) = @_; 533*92bca398SDaniel Schwierzeck 534*92bca398SDaniel Schwierzeck for (my $i = $start; $i < $end; $i++) { 535*92bca398SDaniel Schwierzeck my $line = $typevalue[$i]; 536*92bca398SDaniel Schwierzeck if ($line =~ m/^(\C):\s*(.*)/) { 537*92bca398SDaniel Schwierzeck my $type = $1; 538*92bca398SDaniel Schwierzeck my $value = $2; 539*92bca398SDaniel Schwierzeck if ($type eq 'M') { 540*92bca398SDaniel Schwierzeck return 1; 541*92bca398SDaniel Schwierzeck } 542*92bca398SDaniel Schwierzeck } 543*92bca398SDaniel Schwierzeck } 544*92bca398SDaniel Schwierzeck return 0; 545*92bca398SDaniel Schwierzeck} 546*92bca398SDaniel Schwierzeck 547*92bca398SDaniel Schwierzecksub get_maintainers { 548*92bca398SDaniel Schwierzeck %email_hash_name = (); 549*92bca398SDaniel Schwierzeck %email_hash_address = (); 550*92bca398SDaniel Schwierzeck %commit_author_hash = (); 551*92bca398SDaniel Schwierzeck %commit_signer_hash = (); 552*92bca398SDaniel Schwierzeck @email_to = (); 553*92bca398SDaniel Schwierzeck %hash_list_to = (); 554*92bca398SDaniel Schwierzeck @list_to = (); 555*92bca398SDaniel Schwierzeck @scm = (); 556*92bca398SDaniel Schwierzeck @web = (); 557*92bca398SDaniel Schwierzeck @subsystem = (); 558*92bca398SDaniel Schwierzeck @status = (); 559*92bca398SDaniel Schwierzeck %deduplicate_name_hash = (); 560*92bca398SDaniel Schwierzeck %deduplicate_address_hash = (); 561*92bca398SDaniel Schwierzeck if ($email_git_all_signature_types) { 562*92bca398SDaniel Schwierzeck $signature_pattern = "(.+?)[Bb][Yy]:"; 563*92bca398SDaniel Schwierzeck } else { 564*92bca398SDaniel Schwierzeck $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; 565*92bca398SDaniel Schwierzeck } 566*92bca398SDaniel Schwierzeck 567*92bca398SDaniel Schwierzeck # Find responsible parties 568*92bca398SDaniel Schwierzeck 569*92bca398SDaniel Schwierzeck my %exact_pattern_match_hash = (); 570*92bca398SDaniel Schwierzeck 571*92bca398SDaniel Schwierzeck foreach my $file (@files) { 572*92bca398SDaniel Schwierzeck 573*92bca398SDaniel Schwierzeck my %hash; 574*92bca398SDaniel Schwierzeck my $tvi = find_first_section(); 575*92bca398SDaniel Schwierzeck while ($tvi < @typevalue) { 576*92bca398SDaniel Schwierzeck my $start = find_starting_index($tvi); 577*92bca398SDaniel Schwierzeck my $end = find_ending_index($tvi); 578*92bca398SDaniel Schwierzeck my $exclude = 0; 579*92bca398SDaniel Schwierzeck my $i; 580*92bca398SDaniel Schwierzeck 581*92bca398SDaniel Schwierzeck #Do not match excluded file patterns 582*92bca398SDaniel Schwierzeck 583*92bca398SDaniel Schwierzeck for ($i = $start; $i < $end; $i++) { 584*92bca398SDaniel Schwierzeck my $line = $typevalue[$i]; 585*92bca398SDaniel Schwierzeck if ($line =~ m/^(\C):\s*(.*)/) { 586*92bca398SDaniel Schwierzeck my $type = $1; 587*92bca398SDaniel Schwierzeck my $value = $2; 588*92bca398SDaniel Schwierzeck if ($type eq 'X') { 589*92bca398SDaniel Schwierzeck if (file_match_pattern($file, $value)) { 590*92bca398SDaniel Schwierzeck $exclude = 1; 591*92bca398SDaniel Schwierzeck last; 592*92bca398SDaniel Schwierzeck } 593*92bca398SDaniel Schwierzeck } 594*92bca398SDaniel Schwierzeck } 595*92bca398SDaniel Schwierzeck } 596*92bca398SDaniel Schwierzeck 597*92bca398SDaniel Schwierzeck if (!$exclude) { 598*92bca398SDaniel Schwierzeck for ($i = $start; $i < $end; $i++) { 599*92bca398SDaniel Schwierzeck my $line = $typevalue[$i]; 600*92bca398SDaniel Schwierzeck if ($line =~ m/^(\C):\s*(.*)/) { 601*92bca398SDaniel Schwierzeck my $type = $1; 602*92bca398SDaniel Schwierzeck my $value = $2; 603*92bca398SDaniel Schwierzeck if ($type eq 'F') { 604*92bca398SDaniel Schwierzeck if (file_match_pattern($file, $value)) { 605*92bca398SDaniel Schwierzeck my $value_pd = ($value =~ tr@/@@); 606*92bca398SDaniel Schwierzeck my $file_pd = ($file =~ tr@/@@); 607*92bca398SDaniel Schwierzeck $value_pd++ if (substr($value,-1,1) ne "/"); 608*92bca398SDaniel Schwierzeck $value_pd = -1 if ($value =~ /^\.\*/); 609*92bca398SDaniel Schwierzeck if ($value_pd >= $file_pd && 610*92bca398SDaniel Schwierzeck range_is_maintained($start, $end) && 611*92bca398SDaniel Schwierzeck range_has_maintainer($start, $end)) { 612*92bca398SDaniel Schwierzeck $exact_pattern_match_hash{$file} = 1; 613*92bca398SDaniel Schwierzeck } 614*92bca398SDaniel Schwierzeck if ($pattern_depth == 0 || 615*92bca398SDaniel Schwierzeck (($file_pd - $value_pd) < $pattern_depth)) { 616*92bca398SDaniel Schwierzeck $hash{$tvi} = $value_pd; 617*92bca398SDaniel Schwierzeck } 618*92bca398SDaniel Schwierzeck } 619*92bca398SDaniel Schwierzeck } elsif ($type eq 'N') { 620*92bca398SDaniel Schwierzeck if ($file =~ m/$value/x) { 621*92bca398SDaniel Schwierzeck $hash{$tvi} = 0; 622*92bca398SDaniel Schwierzeck } 623*92bca398SDaniel Schwierzeck } 624*92bca398SDaniel Schwierzeck } 625*92bca398SDaniel Schwierzeck } 626*92bca398SDaniel Schwierzeck } 627*92bca398SDaniel Schwierzeck $tvi = $end + 1; 628*92bca398SDaniel Schwierzeck } 629*92bca398SDaniel Schwierzeck 630*92bca398SDaniel Schwierzeck foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { 631*92bca398SDaniel Schwierzeck add_categories($line); 632*92bca398SDaniel Schwierzeck if ($sections) { 633*92bca398SDaniel Schwierzeck my $i; 634*92bca398SDaniel Schwierzeck my $start = find_starting_index($line); 635*92bca398SDaniel Schwierzeck my $end = find_ending_index($line); 636*92bca398SDaniel Schwierzeck for ($i = $start; $i < $end; $i++) { 637*92bca398SDaniel Schwierzeck my $line = $typevalue[$i]; 638*92bca398SDaniel Schwierzeck if ($line =~ /^[FX]:/) { ##Restore file patterns 639*92bca398SDaniel Schwierzeck $line =~ s/([^\\])\.([^\*])/$1\?$2/g; 640*92bca398SDaniel Schwierzeck $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ? 641*92bca398SDaniel Schwierzeck $line =~ s/\\\./\./g; ##Convert \. to . 642*92bca398SDaniel Schwierzeck $line =~ s/\.\*/\*/g; ##Convert .* to * 643*92bca398SDaniel Schwierzeck } 644*92bca398SDaniel Schwierzeck $line =~ s/^([A-Z]):/$1:\t/g; 645*92bca398SDaniel Schwierzeck print("$line\n"); 646*92bca398SDaniel Schwierzeck } 647*92bca398SDaniel Schwierzeck print("\n"); 648*92bca398SDaniel Schwierzeck } 649*92bca398SDaniel Schwierzeck } 650*92bca398SDaniel Schwierzeck } 651*92bca398SDaniel Schwierzeck 652*92bca398SDaniel Schwierzeck if ($keywords) { 653*92bca398SDaniel Schwierzeck @keyword_tvi = sort_and_uniq(@keyword_tvi); 654*92bca398SDaniel Schwierzeck foreach my $line (@keyword_tvi) { 655*92bca398SDaniel Schwierzeck add_categories($line); 656*92bca398SDaniel Schwierzeck } 657*92bca398SDaniel Schwierzeck } 658*92bca398SDaniel Schwierzeck 659*92bca398SDaniel Schwierzeck foreach my $email (@email_to, @list_to) { 660*92bca398SDaniel Schwierzeck $email->[0] = deduplicate_email($email->[0]); 661*92bca398SDaniel Schwierzeck } 662*92bca398SDaniel Schwierzeck 663*92bca398SDaniel Schwierzeck foreach my $file (@files) { 664*92bca398SDaniel Schwierzeck if ($email && 665*92bca398SDaniel Schwierzeck ($email_git || ($email_git_fallback && 666*92bca398SDaniel Schwierzeck !$exact_pattern_match_hash{$file}))) { 667*92bca398SDaniel Schwierzeck vcs_file_signoffs($file); 668*92bca398SDaniel Schwierzeck } 669*92bca398SDaniel Schwierzeck if ($email && $email_git_blame) { 670*92bca398SDaniel Schwierzeck vcs_file_blame($file); 671*92bca398SDaniel Schwierzeck } 672*92bca398SDaniel Schwierzeck } 673*92bca398SDaniel Schwierzeck 674*92bca398SDaniel Schwierzeck if ($email) { 675*92bca398SDaniel Schwierzeck foreach my $chief (@penguin_chief) { 676*92bca398SDaniel Schwierzeck if ($chief =~ m/^(.*):(.*)/) { 677*92bca398SDaniel Schwierzeck my $email_address; 678*92bca398SDaniel Schwierzeck 679*92bca398SDaniel Schwierzeck $email_address = format_email($1, $2, $email_usename); 680*92bca398SDaniel Schwierzeck if ($email_git_penguin_chiefs) { 681*92bca398SDaniel Schwierzeck push(@email_to, [$email_address, 'chief penguin']); 682*92bca398SDaniel Schwierzeck } else { 683*92bca398SDaniel Schwierzeck @email_to = grep($_->[0] !~ /${email_address}/, @email_to); 684*92bca398SDaniel Schwierzeck } 685*92bca398SDaniel Schwierzeck } 686*92bca398SDaniel Schwierzeck } 687*92bca398SDaniel Schwierzeck 688*92bca398SDaniel Schwierzeck foreach my $email (@file_emails) { 689*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($email); 690*92bca398SDaniel Schwierzeck 691*92bca398SDaniel Schwierzeck my $tmp_email = format_email($name, $address, $email_usename); 692*92bca398SDaniel Schwierzeck push_email_address($tmp_email, ''); 693*92bca398SDaniel Schwierzeck add_role($tmp_email, 'in file'); 694*92bca398SDaniel Schwierzeck } 695*92bca398SDaniel Schwierzeck } 696*92bca398SDaniel Schwierzeck 697*92bca398SDaniel Schwierzeck my @to = (); 698*92bca398SDaniel Schwierzeck if ($email || $email_list) { 699*92bca398SDaniel Schwierzeck if ($email) { 700*92bca398SDaniel Schwierzeck @to = (@to, @email_to); 701*92bca398SDaniel Schwierzeck } 702*92bca398SDaniel Schwierzeck if ($email_list) { 703*92bca398SDaniel Schwierzeck @to = (@to, @list_to); 704*92bca398SDaniel Schwierzeck } 705*92bca398SDaniel Schwierzeck } 706*92bca398SDaniel Schwierzeck 707*92bca398SDaniel Schwierzeck if ($interactive) { 708*92bca398SDaniel Schwierzeck @to = interactive_get_maintainers(\@to); 709*92bca398SDaniel Schwierzeck } 710*92bca398SDaniel Schwierzeck 711*92bca398SDaniel Schwierzeck return @to; 712*92bca398SDaniel Schwierzeck} 713*92bca398SDaniel Schwierzeck 714*92bca398SDaniel Schwierzecksub file_match_pattern { 715*92bca398SDaniel Schwierzeck my ($file, $pattern) = @_; 716*92bca398SDaniel Schwierzeck if (substr($pattern, -1) eq "/") { 717*92bca398SDaniel Schwierzeck if ($file =~ m@^$pattern@) { 718*92bca398SDaniel Schwierzeck return 1; 719*92bca398SDaniel Schwierzeck } 720*92bca398SDaniel Schwierzeck } else { 721*92bca398SDaniel Schwierzeck if ($file =~ m@^$pattern@) { 722*92bca398SDaniel Schwierzeck my $s1 = ($file =~ tr@/@@); 723*92bca398SDaniel Schwierzeck my $s2 = ($pattern =~ tr@/@@); 724*92bca398SDaniel Schwierzeck if ($s1 == $s2) { 725*92bca398SDaniel Schwierzeck return 1; 726*92bca398SDaniel Schwierzeck } 727*92bca398SDaniel Schwierzeck } 728*92bca398SDaniel Schwierzeck } 729*92bca398SDaniel Schwierzeck return 0; 730*92bca398SDaniel Schwierzeck} 731*92bca398SDaniel Schwierzeck 732*92bca398SDaniel Schwierzecksub usage { 733*92bca398SDaniel Schwierzeck print <<EOT; 734*92bca398SDaniel Schwierzeckusage: $P [options] patchfile 735*92bca398SDaniel Schwierzeck $P [options] -f file|directory 736*92bca398SDaniel Schwierzeckversion: $V 737*92bca398SDaniel Schwierzeck 738*92bca398SDaniel SchwierzeckMAINTAINER field selection options: 739*92bca398SDaniel Schwierzeck --email => print email address(es) if any 740*92bca398SDaniel Schwierzeck --git => include recent git \*-by: signers 741*92bca398SDaniel Schwierzeck --git-all-signature-types => include signers regardless of signature type 742*92bca398SDaniel Schwierzeck or use only ${signature_pattern} signers (default: $email_git_all_signature_types) 743*92bca398SDaniel Schwierzeck --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback) 744*92bca398SDaniel Schwierzeck --git-chief-penguins => include ${penguin_chiefs} 745*92bca398SDaniel Schwierzeck --git-min-signatures => number of signatures required (default: $email_git_min_signatures) 746*92bca398SDaniel Schwierzeck --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers) 747*92bca398SDaniel Schwierzeck --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent) 748*92bca398SDaniel Schwierzeck --git-blame => use git blame to find modified commits for patch or file 749*92bca398SDaniel Schwierzeck --git-since => git history to use (default: $email_git_since) 750*92bca398SDaniel Schwierzeck --hg-since => hg history to use (default: $email_hg_since) 751*92bca398SDaniel Schwierzeck --interactive => display a menu (mostly useful if used with the --git option) 752*92bca398SDaniel Schwierzeck --m => include maintainer(s) if any 753*92bca398SDaniel Schwierzeck --n => include name 'Full Name <addr\@domain.tld>' 754*92bca398SDaniel Schwierzeck --l => include list(s) if any 755*92bca398SDaniel Schwierzeck --s => include subscriber only list(s) if any 756*92bca398SDaniel Schwierzeck --remove-duplicates => minimize duplicate email names/addresses 757*92bca398SDaniel Schwierzeck --roles => show roles (status:subsystem, git-signer, list, etc...) 758*92bca398SDaniel Schwierzeck --rolestats => show roles and statistics (commits/total_commits, %) 759*92bca398SDaniel Schwierzeck --file-emails => add email addresses found in -f file (default: 0 (off)) 760*92bca398SDaniel Schwierzeck --scm => print SCM tree(s) if any 761*92bca398SDaniel Schwierzeck --status => print status if any 762*92bca398SDaniel Schwierzeck --subsystem => print subsystem name if any 763*92bca398SDaniel Schwierzeck --web => print website(s) if any 764*92bca398SDaniel Schwierzeck 765*92bca398SDaniel SchwierzeckOutput type options: 766*92bca398SDaniel Schwierzeck --separator [, ] => separator for multiple entries on 1 line 767*92bca398SDaniel Schwierzeck using --separator also sets --nomultiline if --separator is not [, ] 768*92bca398SDaniel Schwierzeck --multiline => print 1 entry per line 769*92bca398SDaniel Schwierzeck 770*92bca398SDaniel SchwierzeckOther options: 771*92bca398SDaniel Schwierzeck --pattern-depth => Number of pattern directory traversals (default: 0 (all)) 772*92bca398SDaniel Schwierzeck --keywords => scan patch for keywords (default: $keywords) 773*92bca398SDaniel Schwierzeck --sections => print all of the subsystem sections with pattern matches 774*92bca398SDaniel Schwierzeck --mailmap => use .mailmap file (default: $email_use_mailmap) 775*92bca398SDaniel Schwierzeck --version => show version 776*92bca398SDaniel Schwierzeck --help => show this help information 777*92bca398SDaniel Schwierzeck 778*92bca398SDaniel SchwierzeckDefault options: 779*92bca398SDaniel Schwierzeck [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0 780*92bca398SDaniel Schwierzeck --remove-duplicates --rolestats] 781*92bca398SDaniel Schwierzeck 782*92bca398SDaniel SchwierzeckNotes: 783*92bca398SDaniel Schwierzeck Using "-f directory" may give unexpected results: 784*92bca398SDaniel Schwierzeck Used with "--git", git signators for _all_ files in and below 785*92bca398SDaniel Schwierzeck directory are examined as git recurses directories. 786*92bca398SDaniel Schwierzeck Any specified X: (exclude) pattern matches are _not_ ignored. 787*92bca398SDaniel Schwierzeck Used with "--nogit", directory is used as a pattern match, 788*92bca398SDaniel Schwierzeck no individual file within the directory or subdirectory 789*92bca398SDaniel Schwierzeck is matched. 790*92bca398SDaniel Schwierzeck Used with "--git-blame", does not iterate all files in directory 791*92bca398SDaniel Schwierzeck Using "--git-blame" is slow and may add old committers and authors 792*92bca398SDaniel Schwierzeck that are no longer active maintainers to the output. 793*92bca398SDaniel Schwierzeck Using "--roles" or "--rolestats" with git send-email --cc-cmd or any 794*92bca398SDaniel Schwierzeck other automated tools that expect only ["name"] <email address> 795*92bca398SDaniel Schwierzeck may not work because of additional output after <email address>. 796*92bca398SDaniel Schwierzeck Using "--rolestats" and "--git-blame" shows the #/total=% commits, 797*92bca398SDaniel Schwierzeck not the percentage of the entire file authored. # of commits is 798*92bca398SDaniel Schwierzeck not a good measure of amount of code authored. 1 major commit may 799*92bca398SDaniel Schwierzeck contain a thousand lines, 5 trivial commits may modify a single line. 800*92bca398SDaniel Schwierzeck If git is not installed, but mercurial (hg) is installed and an .hg 801*92bca398SDaniel Schwierzeck repository exists, the following options apply to mercurial: 802*92bca398SDaniel Schwierzeck --git, 803*92bca398SDaniel Schwierzeck --git-min-signatures, --git-max-maintainers, --git-min-percent, and 804*92bca398SDaniel Schwierzeck --git-blame 805*92bca398SDaniel Schwierzeck Use --hg-since not --git-since to control date selection 806*92bca398SDaniel Schwierzeck File ".get_maintainer.conf", if it exists in the linux kernel source root 807*92bca398SDaniel Schwierzeck directory, can change whatever get_maintainer defaults are desired. 808*92bca398SDaniel Schwierzeck Entries in this file can be any command line argument. 809*92bca398SDaniel Schwierzeck This file is prepended to any additional command line arguments. 810*92bca398SDaniel Schwierzeck Multiple lines and # comments are allowed. 811*92bca398SDaniel SchwierzeckEOT 812*92bca398SDaniel Schwierzeck} 813*92bca398SDaniel Schwierzeck 814*92bca398SDaniel Schwierzecksub top_of_kernel_tree { 815*92bca398SDaniel Schwierzeck my ($lk_path) = @_; 816*92bca398SDaniel Schwierzeck 817*92bca398SDaniel Schwierzeck if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") { 818*92bca398SDaniel Schwierzeck $lk_path .= "/"; 819*92bca398SDaniel Schwierzeck } 820*92bca398SDaniel Schwierzeck if ( (-f "${lk_path}COPYING") 821*92bca398SDaniel Schwierzeck && (-f "${lk_path}CREDITS") 822*92bca398SDaniel Schwierzeck && (-f "${lk_path}Kbuild") 823*92bca398SDaniel Schwierzeck && (-f "${lk_path}MAINTAINERS") 824*92bca398SDaniel Schwierzeck && (-f "${lk_path}Makefile") 825*92bca398SDaniel Schwierzeck && (-f "${lk_path}README") 826*92bca398SDaniel Schwierzeck && (-d "${lk_path}Documentation") 827*92bca398SDaniel Schwierzeck && (-d "${lk_path}arch") 828*92bca398SDaniel Schwierzeck && (-d "${lk_path}include") 829*92bca398SDaniel Schwierzeck && (-d "${lk_path}drivers") 830*92bca398SDaniel Schwierzeck && (-d "${lk_path}fs") 831*92bca398SDaniel Schwierzeck && (-d "${lk_path}init") 832*92bca398SDaniel Schwierzeck && (-d "${lk_path}ipc") 833*92bca398SDaniel Schwierzeck && (-d "${lk_path}kernel") 834*92bca398SDaniel Schwierzeck && (-d "${lk_path}lib") 835*92bca398SDaniel Schwierzeck && (-d "${lk_path}scripts")) { 836*92bca398SDaniel Schwierzeck return 1; 837*92bca398SDaniel Schwierzeck } 838*92bca398SDaniel Schwierzeck return 0; 839*92bca398SDaniel Schwierzeck} 840*92bca398SDaniel Schwierzeck 841*92bca398SDaniel Schwierzecksub parse_email { 842*92bca398SDaniel Schwierzeck my ($formatted_email) = @_; 843*92bca398SDaniel Schwierzeck 844*92bca398SDaniel Schwierzeck my $name = ""; 845*92bca398SDaniel Schwierzeck my $address = ""; 846*92bca398SDaniel Schwierzeck 847*92bca398SDaniel Schwierzeck if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) { 848*92bca398SDaniel Schwierzeck $name = $1; 849*92bca398SDaniel Schwierzeck $address = $2; 850*92bca398SDaniel Schwierzeck } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) { 851*92bca398SDaniel Schwierzeck $address = $1; 852*92bca398SDaniel Schwierzeck } elsif ($formatted_email =~ /^(.+\@\S*).*$/) { 853*92bca398SDaniel Schwierzeck $address = $1; 854*92bca398SDaniel Schwierzeck } 855*92bca398SDaniel Schwierzeck 856*92bca398SDaniel Schwierzeck $name =~ s/^\s+|\s+$//g; 857*92bca398SDaniel Schwierzeck $name =~ s/^\"|\"$//g; 858*92bca398SDaniel Schwierzeck $address =~ s/^\s+|\s+$//g; 859*92bca398SDaniel Schwierzeck 860*92bca398SDaniel Schwierzeck if ($name =~ /[^\w \-]/i) { ##has "must quote" chars 861*92bca398SDaniel Schwierzeck $name =~ s/(?<!\\)"/\\"/g; ##escape quotes 862*92bca398SDaniel Schwierzeck $name = "\"$name\""; 863*92bca398SDaniel Schwierzeck } 864*92bca398SDaniel Schwierzeck 865*92bca398SDaniel Schwierzeck return ($name, $address); 866*92bca398SDaniel Schwierzeck} 867*92bca398SDaniel Schwierzeck 868*92bca398SDaniel Schwierzecksub format_email { 869*92bca398SDaniel Schwierzeck my ($name, $address, $usename) = @_; 870*92bca398SDaniel Schwierzeck 871*92bca398SDaniel Schwierzeck my $formatted_email; 872*92bca398SDaniel Schwierzeck 873*92bca398SDaniel Schwierzeck $name =~ s/^\s+|\s+$//g; 874*92bca398SDaniel Schwierzeck $name =~ s/^\"|\"$//g; 875*92bca398SDaniel Schwierzeck $address =~ s/^\s+|\s+$//g; 876*92bca398SDaniel Schwierzeck 877*92bca398SDaniel Schwierzeck if ($name =~ /[^\w \-]/i) { ##has "must quote" chars 878*92bca398SDaniel Schwierzeck $name =~ s/(?<!\\)"/\\"/g; ##escape quotes 879*92bca398SDaniel Schwierzeck $name = "\"$name\""; 880*92bca398SDaniel Schwierzeck } 881*92bca398SDaniel Schwierzeck 882*92bca398SDaniel Schwierzeck if ($usename) { 883*92bca398SDaniel Schwierzeck if ("$name" eq "") { 884*92bca398SDaniel Schwierzeck $formatted_email = "$address"; 885*92bca398SDaniel Schwierzeck } else { 886*92bca398SDaniel Schwierzeck $formatted_email = "$name <$address>"; 887*92bca398SDaniel Schwierzeck } 888*92bca398SDaniel Schwierzeck } else { 889*92bca398SDaniel Schwierzeck $formatted_email = $address; 890*92bca398SDaniel Schwierzeck } 891*92bca398SDaniel Schwierzeck 892*92bca398SDaniel Schwierzeck return $formatted_email; 893*92bca398SDaniel Schwierzeck} 894*92bca398SDaniel Schwierzeck 895*92bca398SDaniel Schwierzecksub find_first_section { 896*92bca398SDaniel Schwierzeck my $index = 0; 897*92bca398SDaniel Schwierzeck 898*92bca398SDaniel Schwierzeck while ($index < @typevalue) { 899*92bca398SDaniel Schwierzeck my $tv = $typevalue[$index]; 900*92bca398SDaniel Schwierzeck if (($tv =~ m/^(\C):\s*(.*)/)) { 901*92bca398SDaniel Schwierzeck last; 902*92bca398SDaniel Schwierzeck } 903*92bca398SDaniel Schwierzeck $index++; 904*92bca398SDaniel Schwierzeck } 905*92bca398SDaniel Schwierzeck 906*92bca398SDaniel Schwierzeck return $index; 907*92bca398SDaniel Schwierzeck} 908*92bca398SDaniel Schwierzeck 909*92bca398SDaniel Schwierzecksub find_starting_index { 910*92bca398SDaniel Schwierzeck my ($index) = @_; 911*92bca398SDaniel Schwierzeck 912*92bca398SDaniel Schwierzeck while ($index > 0) { 913*92bca398SDaniel Schwierzeck my $tv = $typevalue[$index]; 914*92bca398SDaniel Schwierzeck if (!($tv =~ m/^(\C):\s*(.*)/)) { 915*92bca398SDaniel Schwierzeck last; 916*92bca398SDaniel Schwierzeck } 917*92bca398SDaniel Schwierzeck $index--; 918*92bca398SDaniel Schwierzeck } 919*92bca398SDaniel Schwierzeck 920*92bca398SDaniel Schwierzeck return $index; 921*92bca398SDaniel Schwierzeck} 922*92bca398SDaniel Schwierzeck 923*92bca398SDaniel Schwierzecksub find_ending_index { 924*92bca398SDaniel Schwierzeck my ($index) = @_; 925*92bca398SDaniel Schwierzeck 926*92bca398SDaniel Schwierzeck while ($index < @typevalue) { 927*92bca398SDaniel Schwierzeck my $tv = $typevalue[$index]; 928*92bca398SDaniel Schwierzeck if (!($tv =~ m/^(\C):\s*(.*)/)) { 929*92bca398SDaniel Schwierzeck last; 930*92bca398SDaniel Schwierzeck } 931*92bca398SDaniel Schwierzeck $index++; 932*92bca398SDaniel Schwierzeck } 933*92bca398SDaniel Schwierzeck 934*92bca398SDaniel Schwierzeck return $index; 935*92bca398SDaniel Schwierzeck} 936*92bca398SDaniel Schwierzeck 937*92bca398SDaniel Schwierzecksub get_maintainer_role { 938*92bca398SDaniel Schwierzeck my ($index) = @_; 939*92bca398SDaniel Schwierzeck 940*92bca398SDaniel Schwierzeck my $i; 941*92bca398SDaniel Schwierzeck my $start = find_starting_index($index); 942*92bca398SDaniel Schwierzeck my $end = find_ending_index($index); 943*92bca398SDaniel Schwierzeck 944*92bca398SDaniel Schwierzeck my $role = "unknown"; 945*92bca398SDaniel Schwierzeck my $subsystem = $typevalue[$start]; 946*92bca398SDaniel Schwierzeck if (length($subsystem) > 20) { 947*92bca398SDaniel Schwierzeck $subsystem = substr($subsystem, 0, 17); 948*92bca398SDaniel Schwierzeck $subsystem =~ s/\s*$//; 949*92bca398SDaniel Schwierzeck $subsystem = $subsystem . "..."; 950*92bca398SDaniel Schwierzeck } 951*92bca398SDaniel Schwierzeck 952*92bca398SDaniel Schwierzeck for ($i = $start + 1; $i < $end; $i++) { 953*92bca398SDaniel Schwierzeck my $tv = $typevalue[$i]; 954*92bca398SDaniel Schwierzeck if ($tv =~ m/^(\C):\s*(.*)/) { 955*92bca398SDaniel Schwierzeck my $ptype = $1; 956*92bca398SDaniel Schwierzeck my $pvalue = $2; 957*92bca398SDaniel Schwierzeck if ($ptype eq "S") { 958*92bca398SDaniel Schwierzeck $role = $pvalue; 959*92bca398SDaniel Schwierzeck } 960*92bca398SDaniel Schwierzeck } 961*92bca398SDaniel Schwierzeck } 962*92bca398SDaniel Schwierzeck 963*92bca398SDaniel Schwierzeck $role = lc($role); 964*92bca398SDaniel Schwierzeck if ($role eq "supported") { 965*92bca398SDaniel Schwierzeck $role = "supporter"; 966*92bca398SDaniel Schwierzeck } elsif ($role eq "maintained") { 967*92bca398SDaniel Schwierzeck $role = "maintainer"; 968*92bca398SDaniel Schwierzeck } elsif ($role eq "odd fixes") { 969*92bca398SDaniel Schwierzeck $role = "odd fixer"; 970*92bca398SDaniel Schwierzeck } elsif ($role eq "orphan") { 971*92bca398SDaniel Schwierzeck $role = "orphan minder"; 972*92bca398SDaniel Schwierzeck } elsif ($role eq "obsolete") { 973*92bca398SDaniel Schwierzeck $role = "obsolete minder"; 974*92bca398SDaniel Schwierzeck } elsif ($role eq "buried alive in reporters") { 975*92bca398SDaniel Schwierzeck $role = "chief penguin"; 976*92bca398SDaniel Schwierzeck } 977*92bca398SDaniel Schwierzeck 978*92bca398SDaniel Schwierzeck return $role . ":" . $subsystem; 979*92bca398SDaniel Schwierzeck} 980*92bca398SDaniel Schwierzeck 981*92bca398SDaniel Schwierzecksub get_list_role { 982*92bca398SDaniel Schwierzeck my ($index) = @_; 983*92bca398SDaniel Schwierzeck 984*92bca398SDaniel Schwierzeck my $i; 985*92bca398SDaniel Schwierzeck my $start = find_starting_index($index); 986*92bca398SDaniel Schwierzeck my $end = find_ending_index($index); 987*92bca398SDaniel Schwierzeck 988*92bca398SDaniel Schwierzeck my $subsystem = $typevalue[$start]; 989*92bca398SDaniel Schwierzeck if (length($subsystem) > 20) { 990*92bca398SDaniel Schwierzeck $subsystem = substr($subsystem, 0, 17); 991*92bca398SDaniel Schwierzeck $subsystem =~ s/\s*$//; 992*92bca398SDaniel Schwierzeck $subsystem = $subsystem . "..."; 993*92bca398SDaniel Schwierzeck } 994*92bca398SDaniel Schwierzeck 995*92bca398SDaniel Schwierzeck if ($subsystem eq "THE REST") { 996*92bca398SDaniel Schwierzeck $subsystem = ""; 997*92bca398SDaniel Schwierzeck } 998*92bca398SDaniel Schwierzeck 999*92bca398SDaniel Schwierzeck return $subsystem; 1000*92bca398SDaniel Schwierzeck} 1001*92bca398SDaniel Schwierzeck 1002*92bca398SDaniel Schwierzecksub add_categories { 1003*92bca398SDaniel Schwierzeck my ($index) = @_; 1004*92bca398SDaniel Schwierzeck 1005*92bca398SDaniel Schwierzeck my $i; 1006*92bca398SDaniel Schwierzeck my $start = find_starting_index($index); 1007*92bca398SDaniel Schwierzeck my $end = find_ending_index($index); 1008*92bca398SDaniel Schwierzeck 1009*92bca398SDaniel Schwierzeck push(@subsystem, $typevalue[$start]); 1010*92bca398SDaniel Schwierzeck 1011*92bca398SDaniel Schwierzeck for ($i = $start + 1; $i < $end; $i++) { 1012*92bca398SDaniel Schwierzeck my $tv = $typevalue[$i]; 1013*92bca398SDaniel Schwierzeck if ($tv =~ m/^(\C):\s*(.*)/) { 1014*92bca398SDaniel Schwierzeck my $ptype = $1; 1015*92bca398SDaniel Schwierzeck my $pvalue = $2; 1016*92bca398SDaniel Schwierzeck if ($ptype eq "L") { 1017*92bca398SDaniel Schwierzeck my $list_address = $pvalue; 1018*92bca398SDaniel Schwierzeck my $list_additional = ""; 1019*92bca398SDaniel Schwierzeck my $list_role = get_list_role($i); 1020*92bca398SDaniel Schwierzeck 1021*92bca398SDaniel Schwierzeck if ($list_role ne "") { 1022*92bca398SDaniel Schwierzeck $list_role = ":" . $list_role; 1023*92bca398SDaniel Schwierzeck } 1024*92bca398SDaniel Schwierzeck if ($list_address =~ m/([^\s]+)\s+(.*)$/) { 1025*92bca398SDaniel Schwierzeck $list_address = $1; 1026*92bca398SDaniel Schwierzeck $list_additional = $2; 1027*92bca398SDaniel Schwierzeck } 1028*92bca398SDaniel Schwierzeck if ($list_additional =~ m/subscribers-only/) { 1029*92bca398SDaniel Schwierzeck if ($email_subscriber_list) { 1030*92bca398SDaniel Schwierzeck if (!$hash_list_to{lc($list_address)}) { 1031*92bca398SDaniel Schwierzeck $hash_list_to{lc($list_address)} = 1; 1032*92bca398SDaniel Schwierzeck push(@list_to, [$list_address, 1033*92bca398SDaniel Schwierzeck "subscriber list${list_role}"]); 1034*92bca398SDaniel Schwierzeck } 1035*92bca398SDaniel Schwierzeck } 1036*92bca398SDaniel Schwierzeck } else { 1037*92bca398SDaniel Schwierzeck if ($email_list) { 1038*92bca398SDaniel Schwierzeck if (!$hash_list_to{lc($list_address)}) { 1039*92bca398SDaniel Schwierzeck $hash_list_to{lc($list_address)} = 1; 1040*92bca398SDaniel Schwierzeck if ($list_additional =~ m/moderated/) { 1041*92bca398SDaniel Schwierzeck push(@list_to, [$list_address, 1042*92bca398SDaniel Schwierzeck "moderated list${list_role}"]); 1043*92bca398SDaniel Schwierzeck } else { 1044*92bca398SDaniel Schwierzeck push(@list_to, [$list_address, 1045*92bca398SDaniel Schwierzeck "open list${list_role}"]); 1046*92bca398SDaniel Schwierzeck } 1047*92bca398SDaniel Schwierzeck } 1048*92bca398SDaniel Schwierzeck } 1049*92bca398SDaniel Schwierzeck } 1050*92bca398SDaniel Schwierzeck } elsif ($ptype eq "M") { 1051*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($pvalue); 1052*92bca398SDaniel Schwierzeck if ($name eq "") { 1053*92bca398SDaniel Schwierzeck if ($i > 0) { 1054*92bca398SDaniel Schwierzeck my $tv = $typevalue[$i - 1]; 1055*92bca398SDaniel Schwierzeck if ($tv =~ m/^(\C):\s*(.*)/) { 1056*92bca398SDaniel Schwierzeck if ($1 eq "P") { 1057*92bca398SDaniel Schwierzeck $name = $2; 1058*92bca398SDaniel Schwierzeck $pvalue = format_email($name, $address, $email_usename); 1059*92bca398SDaniel Schwierzeck } 1060*92bca398SDaniel Schwierzeck } 1061*92bca398SDaniel Schwierzeck } 1062*92bca398SDaniel Schwierzeck } 1063*92bca398SDaniel Schwierzeck if ($email_maintainer) { 1064*92bca398SDaniel Schwierzeck my $role = get_maintainer_role($i); 1065*92bca398SDaniel Schwierzeck push_email_addresses($pvalue, $role); 1066*92bca398SDaniel Schwierzeck } 1067*92bca398SDaniel Schwierzeck } elsif ($ptype eq "T") { 1068*92bca398SDaniel Schwierzeck push(@scm, $pvalue); 1069*92bca398SDaniel Schwierzeck } elsif ($ptype eq "W") { 1070*92bca398SDaniel Schwierzeck push(@web, $pvalue); 1071*92bca398SDaniel Schwierzeck } elsif ($ptype eq "S") { 1072*92bca398SDaniel Schwierzeck push(@status, $pvalue); 1073*92bca398SDaniel Schwierzeck } 1074*92bca398SDaniel Schwierzeck } 1075*92bca398SDaniel Schwierzeck } 1076*92bca398SDaniel Schwierzeck} 1077*92bca398SDaniel Schwierzeck 1078*92bca398SDaniel Schwierzecksub email_inuse { 1079*92bca398SDaniel Schwierzeck my ($name, $address) = @_; 1080*92bca398SDaniel Schwierzeck 1081*92bca398SDaniel Schwierzeck return 1 if (($name eq "") && ($address eq "")); 1082*92bca398SDaniel Schwierzeck return 1 if (($name ne "") && exists($email_hash_name{lc($name)})); 1083*92bca398SDaniel Schwierzeck return 1 if (($address ne "") && exists($email_hash_address{lc($address)})); 1084*92bca398SDaniel Schwierzeck 1085*92bca398SDaniel Schwierzeck return 0; 1086*92bca398SDaniel Schwierzeck} 1087*92bca398SDaniel Schwierzeck 1088*92bca398SDaniel Schwierzecksub push_email_address { 1089*92bca398SDaniel Schwierzeck my ($line, $role) = @_; 1090*92bca398SDaniel Schwierzeck 1091*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($line); 1092*92bca398SDaniel Schwierzeck 1093*92bca398SDaniel Schwierzeck if ($address eq "") { 1094*92bca398SDaniel Schwierzeck return 0; 1095*92bca398SDaniel Schwierzeck } 1096*92bca398SDaniel Schwierzeck 1097*92bca398SDaniel Schwierzeck if (!$email_remove_duplicates) { 1098*92bca398SDaniel Schwierzeck push(@email_to, [format_email($name, $address, $email_usename), $role]); 1099*92bca398SDaniel Schwierzeck } elsif (!email_inuse($name, $address)) { 1100*92bca398SDaniel Schwierzeck push(@email_to, [format_email($name, $address, $email_usename), $role]); 1101*92bca398SDaniel Schwierzeck $email_hash_name{lc($name)}++ if ($name ne ""); 1102*92bca398SDaniel Schwierzeck $email_hash_address{lc($address)}++; 1103*92bca398SDaniel Schwierzeck } 1104*92bca398SDaniel Schwierzeck 1105*92bca398SDaniel Schwierzeck return 1; 1106*92bca398SDaniel Schwierzeck} 1107*92bca398SDaniel Schwierzeck 1108*92bca398SDaniel Schwierzecksub push_email_addresses { 1109*92bca398SDaniel Schwierzeck my ($address, $role) = @_; 1110*92bca398SDaniel Schwierzeck 1111*92bca398SDaniel Schwierzeck my @address_list = (); 1112*92bca398SDaniel Schwierzeck 1113*92bca398SDaniel Schwierzeck if (rfc822_valid($address)) { 1114*92bca398SDaniel Schwierzeck push_email_address($address, $role); 1115*92bca398SDaniel Schwierzeck } elsif (@address_list = rfc822_validlist($address)) { 1116*92bca398SDaniel Schwierzeck my $array_count = shift(@address_list); 1117*92bca398SDaniel Schwierzeck while (my $entry = shift(@address_list)) { 1118*92bca398SDaniel Schwierzeck push_email_address($entry, $role); 1119*92bca398SDaniel Schwierzeck } 1120*92bca398SDaniel Schwierzeck } else { 1121*92bca398SDaniel Schwierzeck if (!push_email_address($address, $role)) { 1122*92bca398SDaniel Schwierzeck warn("Invalid MAINTAINERS address: '" . $address . "'\n"); 1123*92bca398SDaniel Schwierzeck } 1124*92bca398SDaniel Schwierzeck } 1125*92bca398SDaniel Schwierzeck} 1126*92bca398SDaniel Schwierzeck 1127*92bca398SDaniel Schwierzecksub add_role { 1128*92bca398SDaniel Schwierzeck my ($line, $role) = @_; 1129*92bca398SDaniel Schwierzeck 1130*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($line); 1131*92bca398SDaniel Schwierzeck my $email = format_email($name, $address, $email_usename); 1132*92bca398SDaniel Schwierzeck 1133*92bca398SDaniel Schwierzeck foreach my $entry (@email_to) { 1134*92bca398SDaniel Schwierzeck if ($email_remove_duplicates) { 1135*92bca398SDaniel Schwierzeck my ($entry_name, $entry_address) = parse_email($entry->[0]); 1136*92bca398SDaniel Schwierzeck if (($name eq $entry_name || $address eq $entry_address) 1137*92bca398SDaniel Schwierzeck && ($role eq "" || !($entry->[1] =~ m/$role/)) 1138*92bca398SDaniel Schwierzeck ) { 1139*92bca398SDaniel Schwierzeck if ($entry->[1] eq "") { 1140*92bca398SDaniel Schwierzeck $entry->[1] = "$role"; 1141*92bca398SDaniel Schwierzeck } else { 1142*92bca398SDaniel Schwierzeck $entry->[1] = "$entry->[1],$role"; 1143*92bca398SDaniel Schwierzeck } 1144*92bca398SDaniel Schwierzeck } 1145*92bca398SDaniel Schwierzeck } else { 1146*92bca398SDaniel Schwierzeck if ($email eq $entry->[0] 1147*92bca398SDaniel Schwierzeck && ($role eq "" || !($entry->[1] =~ m/$role/)) 1148*92bca398SDaniel Schwierzeck ) { 1149*92bca398SDaniel Schwierzeck if ($entry->[1] eq "") { 1150*92bca398SDaniel Schwierzeck $entry->[1] = "$role"; 1151*92bca398SDaniel Schwierzeck } else { 1152*92bca398SDaniel Schwierzeck $entry->[1] = "$entry->[1],$role"; 1153*92bca398SDaniel Schwierzeck } 1154*92bca398SDaniel Schwierzeck } 1155*92bca398SDaniel Schwierzeck } 1156*92bca398SDaniel Schwierzeck } 1157*92bca398SDaniel Schwierzeck} 1158*92bca398SDaniel Schwierzeck 1159*92bca398SDaniel Schwierzecksub which { 1160*92bca398SDaniel Schwierzeck my ($bin) = @_; 1161*92bca398SDaniel Schwierzeck 1162*92bca398SDaniel Schwierzeck foreach my $path (split(/:/, $ENV{PATH})) { 1163*92bca398SDaniel Schwierzeck if (-e "$path/$bin") { 1164*92bca398SDaniel Schwierzeck return "$path/$bin"; 1165*92bca398SDaniel Schwierzeck } 1166*92bca398SDaniel Schwierzeck } 1167*92bca398SDaniel Schwierzeck 1168*92bca398SDaniel Schwierzeck return ""; 1169*92bca398SDaniel Schwierzeck} 1170*92bca398SDaniel Schwierzeck 1171*92bca398SDaniel Schwierzecksub which_conf { 1172*92bca398SDaniel Schwierzeck my ($conf) = @_; 1173*92bca398SDaniel Schwierzeck 1174*92bca398SDaniel Schwierzeck foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { 1175*92bca398SDaniel Schwierzeck if (-e "$path/$conf") { 1176*92bca398SDaniel Schwierzeck return "$path/$conf"; 1177*92bca398SDaniel Schwierzeck } 1178*92bca398SDaniel Schwierzeck } 1179*92bca398SDaniel Schwierzeck 1180*92bca398SDaniel Schwierzeck return ""; 1181*92bca398SDaniel Schwierzeck} 1182*92bca398SDaniel Schwierzeck 1183*92bca398SDaniel Schwierzecksub mailmap_email { 1184*92bca398SDaniel Schwierzeck my ($line) = @_; 1185*92bca398SDaniel Schwierzeck 1186*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($line); 1187*92bca398SDaniel Schwierzeck my $email = format_email($name, $address, 1); 1188*92bca398SDaniel Schwierzeck my $real_name = $name; 1189*92bca398SDaniel Schwierzeck my $real_address = $address; 1190*92bca398SDaniel Schwierzeck 1191*92bca398SDaniel Schwierzeck if (exists $mailmap->{names}->{$email} || 1192*92bca398SDaniel Schwierzeck exists $mailmap->{addresses}->{$email}) { 1193*92bca398SDaniel Schwierzeck if (exists $mailmap->{names}->{$email}) { 1194*92bca398SDaniel Schwierzeck $real_name = $mailmap->{names}->{$email}; 1195*92bca398SDaniel Schwierzeck } 1196*92bca398SDaniel Schwierzeck if (exists $mailmap->{addresses}->{$email}) { 1197*92bca398SDaniel Schwierzeck $real_address = $mailmap->{addresses}->{$email}; 1198*92bca398SDaniel Schwierzeck } 1199*92bca398SDaniel Schwierzeck } else { 1200*92bca398SDaniel Schwierzeck if (exists $mailmap->{names}->{$address}) { 1201*92bca398SDaniel Schwierzeck $real_name = $mailmap->{names}->{$address}; 1202*92bca398SDaniel Schwierzeck } 1203*92bca398SDaniel Schwierzeck if (exists $mailmap->{addresses}->{$address}) { 1204*92bca398SDaniel Schwierzeck $real_address = $mailmap->{addresses}->{$address}; 1205*92bca398SDaniel Schwierzeck } 1206*92bca398SDaniel Schwierzeck } 1207*92bca398SDaniel Schwierzeck return format_email($real_name, $real_address, 1); 1208*92bca398SDaniel Schwierzeck} 1209*92bca398SDaniel Schwierzeck 1210*92bca398SDaniel Schwierzecksub mailmap { 1211*92bca398SDaniel Schwierzeck my (@addresses) = @_; 1212*92bca398SDaniel Schwierzeck 1213*92bca398SDaniel Schwierzeck my @mapped_emails = (); 1214*92bca398SDaniel Schwierzeck foreach my $line (@addresses) { 1215*92bca398SDaniel Schwierzeck push(@mapped_emails, mailmap_email($line)); 1216*92bca398SDaniel Schwierzeck } 1217*92bca398SDaniel Schwierzeck merge_by_realname(@mapped_emails) if ($email_use_mailmap); 1218*92bca398SDaniel Schwierzeck return @mapped_emails; 1219*92bca398SDaniel Schwierzeck} 1220*92bca398SDaniel Schwierzeck 1221*92bca398SDaniel Schwierzecksub merge_by_realname { 1222*92bca398SDaniel Schwierzeck my %address_map; 1223*92bca398SDaniel Schwierzeck my (@emails) = @_; 1224*92bca398SDaniel Schwierzeck 1225*92bca398SDaniel Schwierzeck foreach my $email (@emails) { 1226*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($email); 1227*92bca398SDaniel Schwierzeck if (exists $address_map{$name}) { 1228*92bca398SDaniel Schwierzeck $address = $address_map{$name}; 1229*92bca398SDaniel Schwierzeck $email = format_email($name, $address, 1); 1230*92bca398SDaniel Schwierzeck } else { 1231*92bca398SDaniel Schwierzeck $address_map{$name} = $address; 1232*92bca398SDaniel Schwierzeck } 1233*92bca398SDaniel Schwierzeck } 1234*92bca398SDaniel Schwierzeck} 1235*92bca398SDaniel Schwierzeck 1236*92bca398SDaniel Schwierzecksub git_execute_cmd { 1237*92bca398SDaniel Schwierzeck my ($cmd) = @_; 1238*92bca398SDaniel Schwierzeck my @lines = (); 1239*92bca398SDaniel Schwierzeck 1240*92bca398SDaniel Schwierzeck my $output = `$cmd`; 1241*92bca398SDaniel Schwierzeck $output =~ s/^\s*//gm; 1242*92bca398SDaniel Schwierzeck @lines = split("\n", $output); 1243*92bca398SDaniel Schwierzeck 1244*92bca398SDaniel Schwierzeck return @lines; 1245*92bca398SDaniel Schwierzeck} 1246*92bca398SDaniel Schwierzeck 1247*92bca398SDaniel Schwierzecksub hg_execute_cmd { 1248*92bca398SDaniel Schwierzeck my ($cmd) = @_; 1249*92bca398SDaniel Schwierzeck my @lines = (); 1250*92bca398SDaniel Schwierzeck 1251*92bca398SDaniel Schwierzeck my $output = `$cmd`; 1252*92bca398SDaniel Schwierzeck @lines = split("\n", $output); 1253*92bca398SDaniel Schwierzeck 1254*92bca398SDaniel Schwierzeck return @lines; 1255*92bca398SDaniel Schwierzeck} 1256*92bca398SDaniel Schwierzeck 1257*92bca398SDaniel Schwierzecksub extract_formatted_signatures { 1258*92bca398SDaniel Schwierzeck my (@signature_lines) = @_; 1259*92bca398SDaniel Schwierzeck 1260*92bca398SDaniel Schwierzeck my @type = @signature_lines; 1261*92bca398SDaniel Schwierzeck 1262*92bca398SDaniel Schwierzeck s/\s*(.*):.*/$1/ for (@type); 1263*92bca398SDaniel Schwierzeck 1264*92bca398SDaniel Schwierzeck # cut -f2- -d":" 1265*92bca398SDaniel Schwierzeck s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines); 1266*92bca398SDaniel Schwierzeck 1267*92bca398SDaniel Schwierzeck## Reformat email addresses (with names) to avoid badly written signatures 1268*92bca398SDaniel Schwierzeck 1269*92bca398SDaniel Schwierzeck foreach my $signer (@signature_lines) { 1270*92bca398SDaniel Schwierzeck $signer = deduplicate_email($signer); 1271*92bca398SDaniel Schwierzeck } 1272*92bca398SDaniel Schwierzeck 1273*92bca398SDaniel Schwierzeck return (\@type, \@signature_lines); 1274*92bca398SDaniel Schwierzeck} 1275*92bca398SDaniel Schwierzeck 1276*92bca398SDaniel Schwierzecksub vcs_find_signers { 1277*92bca398SDaniel Schwierzeck my ($cmd, $file) = @_; 1278*92bca398SDaniel Schwierzeck my $commits; 1279*92bca398SDaniel Schwierzeck my @lines = (); 1280*92bca398SDaniel Schwierzeck my @signatures = (); 1281*92bca398SDaniel Schwierzeck my @authors = (); 1282*92bca398SDaniel Schwierzeck my @stats = (); 1283*92bca398SDaniel Schwierzeck 1284*92bca398SDaniel Schwierzeck @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 1285*92bca398SDaniel Schwierzeck 1286*92bca398SDaniel Schwierzeck my $pattern = $VCS_cmds{"commit_pattern"}; 1287*92bca398SDaniel Schwierzeck my $author_pattern = $VCS_cmds{"author_pattern"}; 1288*92bca398SDaniel Schwierzeck my $stat_pattern = $VCS_cmds{"stat_pattern"}; 1289*92bca398SDaniel Schwierzeck 1290*92bca398SDaniel Schwierzeck $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern 1291*92bca398SDaniel Schwierzeck 1292*92bca398SDaniel Schwierzeck $commits = grep(/$pattern/, @lines); # of commits 1293*92bca398SDaniel Schwierzeck 1294*92bca398SDaniel Schwierzeck @authors = grep(/$author_pattern/, @lines); 1295*92bca398SDaniel Schwierzeck @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines); 1296*92bca398SDaniel Schwierzeck @stats = grep(/$stat_pattern/, @lines); 1297*92bca398SDaniel Schwierzeck 1298*92bca398SDaniel Schwierzeck# print("stats: <@stats>\n"); 1299*92bca398SDaniel Schwierzeck 1300*92bca398SDaniel Schwierzeck return (0, \@signatures, \@authors, \@stats) if !@signatures; 1301*92bca398SDaniel Schwierzeck 1302*92bca398SDaniel Schwierzeck save_commits_by_author(@lines) if ($interactive); 1303*92bca398SDaniel Schwierzeck save_commits_by_signer(@lines) if ($interactive); 1304*92bca398SDaniel Schwierzeck 1305*92bca398SDaniel Schwierzeck if (!$email_git_penguin_chiefs) { 1306*92bca398SDaniel Schwierzeck @signatures = grep(!/${penguin_chiefs}/i, @signatures); 1307*92bca398SDaniel Schwierzeck } 1308*92bca398SDaniel Schwierzeck 1309*92bca398SDaniel Schwierzeck my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors); 1310*92bca398SDaniel Schwierzeck my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); 1311*92bca398SDaniel Schwierzeck 1312*92bca398SDaniel Schwierzeck return ($commits, $signers_ref, $authors_ref, \@stats); 1313*92bca398SDaniel Schwierzeck} 1314*92bca398SDaniel Schwierzeck 1315*92bca398SDaniel Schwierzecksub vcs_find_author { 1316*92bca398SDaniel Schwierzeck my ($cmd) = @_; 1317*92bca398SDaniel Schwierzeck my @lines = (); 1318*92bca398SDaniel Schwierzeck 1319*92bca398SDaniel Schwierzeck @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 1320*92bca398SDaniel Schwierzeck 1321*92bca398SDaniel Schwierzeck if (!$email_git_penguin_chiefs) { 1322*92bca398SDaniel Schwierzeck @lines = grep(!/${penguin_chiefs}/i, @lines); 1323*92bca398SDaniel Schwierzeck } 1324*92bca398SDaniel Schwierzeck 1325*92bca398SDaniel Schwierzeck return @lines if !@lines; 1326*92bca398SDaniel Schwierzeck 1327*92bca398SDaniel Schwierzeck my @authors = (); 1328*92bca398SDaniel Schwierzeck foreach my $line (@lines) { 1329*92bca398SDaniel Schwierzeck if ($line =~ m/$VCS_cmds{"author_pattern"}/) { 1330*92bca398SDaniel Schwierzeck my $author = $1; 1331*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($author); 1332*92bca398SDaniel Schwierzeck $author = format_email($name, $address, 1); 1333*92bca398SDaniel Schwierzeck push(@authors, $author); 1334*92bca398SDaniel Schwierzeck } 1335*92bca398SDaniel Schwierzeck } 1336*92bca398SDaniel Schwierzeck 1337*92bca398SDaniel Schwierzeck save_commits_by_author(@lines) if ($interactive); 1338*92bca398SDaniel Schwierzeck save_commits_by_signer(@lines) if ($interactive); 1339*92bca398SDaniel Schwierzeck 1340*92bca398SDaniel Schwierzeck return @authors; 1341*92bca398SDaniel Schwierzeck} 1342*92bca398SDaniel Schwierzeck 1343*92bca398SDaniel Schwierzecksub vcs_save_commits { 1344*92bca398SDaniel Schwierzeck my ($cmd) = @_; 1345*92bca398SDaniel Schwierzeck my @lines = (); 1346*92bca398SDaniel Schwierzeck my @commits = (); 1347*92bca398SDaniel Schwierzeck 1348*92bca398SDaniel Schwierzeck @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 1349*92bca398SDaniel Schwierzeck 1350*92bca398SDaniel Schwierzeck foreach my $line (@lines) { 1351*92bca398SDaniel Schwierzeck if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) { 1352*92bca398SDaniel Schwierzeck push(@commits, $1); 1353*92bca398SDaniel Schwierzeck } 1354*92bca398SDaniel Schwierzeck } 1355*92bca398SDaniel Schwierzeck 1356*92bca398SDaniel Schwierzeck return @commits; 1357*92bca398SDaniel Schwierzeck} 1358*92bca398SDaniel Schwierzeck 1359*92bca398SDaniel Schwierzecksub vcs_blame { 1360*92bca398SDaniel Schwierzeck my ($file) = @_; 1361*92bca398SDaniel Schwierzeck my $cmd; 1362*92bca398SDaniel Schwierzeck my @commits = (); 1363*92bca398SDaniel Schwierzeck 1364*92bca398SDaniel Schwierzeck return @commits if (!(-f $file)); 1365*92bca398SDaniel Schwierzeck 1366*92bca398SDaniel Schwierzeck if (@range && $VCS_cmds{"blame_range_cmd"} eq "") { 1367*92bca398SDaniel Schwierzeck my @all_commits = (); 1368*92bca398SDaniel Schwierzeck 1369*92bca398SDaniel Schwierzeck $cmd = $VCS_cmds{"blame_file_cmd"}; 1370*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 1371*92bca398SDaniel Schwierzeck @all_commits = vcs_save_commits($cmd); 1372*92bca398SDaniel Schwierzeck 1373*92bca398SDaniel Schwierzeck foreach my $file_range_diff (@range) { 1374*92bca398SDaniel Schwierzeck next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); 1375*92bca398SDaniel Schwierzeck my $diff_file = $1; 1376*92bca398SDaniel Schwierzeck my $diff_start = $2; 1377*92bca398SDaniel Schwierzeck my $diff_length = $3; 1378*92bca398SDaniel Schwierzeck next if ("$file" ne "$diff_file"); 1379*92bca398SDaniel Schwierzeck for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) { 1380*92bca398SDaniel Schwierzeck push(@commits, $all_commits[$i]); 1381*92bca398SDaniel Schwierzeck } 1382*92bca398SDaniel Schwierzeck } 1383*92bca398SDaniel Schwierzeck } elsif (@range) { 1384*92bca398SDaniel Schwierzeck foreach my $file_range_diff (@range) { 1385*92bca398SDaniel Schwierzeck next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); 1386*92bca398SDaniel Schwierzeck my $diff_file = $1; 1387*92bca398SDaniel Schwierzeck my $diff_start = $2; 1388*92bca398SDaniel Schwierzeck my $diff_length = $3; 1389*92bca398SDaniel Schwierzeck next if ("$file" ne "$diff_file"); 1390*92bca398SDaniel Schwierzeck $cmd = $VCS_cmds{"blame_range_cmd"}; 1391*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 1392*92bca398SDaniel Schwierzeck push(@commits, vcs_save_commits($cmd)); 1393*92bca398SDaniel Schwierzeck } 1394*92bca398SDaniel Schwierzeck } else { 1395*92bca398SDaniel Schwierzeck $cmd = $VCS_cmds{"blame_file_cmd"}; 1396*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 1397*92bca398SDaniel Schwierzeck @commits = vcs_save_commits($cmd); 1398*92bca398SDaniel Schwierzeck } 1399*92bca398SDaniel Schwierzeck 1400*92bca398SDaniel Schwierzeck foreach my $commit (@commits) { 1401*92bca398SDaniel Schwierzeck $commit =~ s/^\^//g; 1402*92bca398SDaniel Schwierzeck } 1403*92bca398SDaniel Schwierzeck 1404*92bca398SDaniel Schwierzeck return @commits; 1405*92bca398SDaniel Schwierzeck} 1406*92bca398SDaniel Schwierzeck 1407*92bca398SDaniel Schwierzeckmy $printed_novcs = 0; 1408*92bca398SDaniel Schwierzecksub vcs_exists { 1409*92bca398SDaniel Schwierzeck %VCS_cmds = %VCS_cmds_git; 1410*92bca398SDaniel Schwierzeck return 1 if eval $VCS_cmds{"available"}; 1411*92bca398SDaniel Schwierzeck %VCS_cmds = %VCS_cmds_hg; 1412*92bca398SDaniel Schwierzeck return 2 if eval $VCS_cmds{"available"}; 1413*92bca398SDaniel Schwierzeck %VCS_cmds = (); 1414*92bca398SDaniel Schwierzeck if (!$printed_novcs) { 1415*92bca398SDaniel Schwierzeck warn("$P: No supported VCS found. Add --nogit to options?\n"); 1416*92bca398SDaniel Schwierzeck warn("Using a git repository produces better results.\n"); 1417*92bca398SDaniel Schwierzeck warn("Try Linus Torvalds' latest git repository using:\n"); 1418*92bca398SDaniel Schwierzeck warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n"); 1419*92bca398SDaniel Schwierzeck $printed_novcs = 1; 1420*92bca398SDaniel Schwierzeck } 1421*92bca398SDaniel Schwierzeck return 0; 1422*92bca398SDaniel Schwierzeck} 1423*92bca398SDaniel Schwierzeck 1424*92bca398SDaniel Schwierzecksub vcs_is_git { 1425*92bca398SDaniel Schwierzeck vcs_exists(); 1426*92bca398SDaniel Schwierzeck return $vcs_used == 1; 1427*92bca398SDaniel Schwierzeck} 1428*92bca398SDaniel Schwierzeck 1429*92bca398SDaniel Schwierzecksub vcs_is_hg { 1430*92bca398SDaniel Schwierzeck return $vcs_used == 2; 1431*92bca398SDaniel Schwierzeck} 1432*92bca398SDaniel Schwierzeck 1433*92bca398SDaniel Schwierzecksub interactive_get_maintainers { 1434*92bca398SDaniel Schwierzeck my ($list_ref) = @_; 1435*92bca398SDaniel Schwierzeck my @list = @$list_ref; 1436*92bca398SDaniel Schwierzeck 1437*92bca398SDaniel Schwierzeck vcs_exists(); 1438*92bca398SDaniel Schwierzeck 1439*92bca398SDaniel Schwierzeck my %selected; 1440*92bca398SDaniel Schwierzeck my %authored; 1441*92bca398SDaniel Schwierzeck my %signed; 1442*92bca398SDaniel Schwierzeck my $count = 0; 1443*92bca398SDaniel Schwierzeck my $maintained = 0; 1444*92bca398SDaniel Schwierzeck foreach my $entry (@list) { 1445*92bca398SDaniel Schwierzeck $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i); 1446*92bca398SDaniel Schwierzeck $selected{$count} = 1; 1447*92bca398SDaniel Schwierzeck $authored{$count} = 0; 1448*92bca398SDaniel Schwierzeck $signed{$count} = 0; 1449*92bca398SDaniel Schwierzeck $count++; 1450*92bca398SDaniel Schwierzeck } 1451*92bca398SDaniel Schwierzeck 1452*92bca398SDaniel Schwierzeck #menu loop 1453*92bca398SDaniel Schwierzeck my $done = 0; 1454*92bca398SDaniel Schwierzeck my $print_options = 0; 1455*92bca398SDaniel Schwierzeck my $redraw = 1; 1456*92bca398SDaniel Schwierzeck while (!$done) { 1457*92bca398SDaniel Schwierzeck $count = 0; 1458*92bca398SDaniel Schwierzeck if ($redraw) { 1459*92bca398SDaniel Schwierzeck printf STDERR "\n%1s %2s %-65s", 1460*92bca398SDaniel Schwierzeck "*", "#", "email/list and role:stats"; 1461*92bca398SDaniel Schwierzeck if ($email_git || 1462*92bca398SDaniel Schwierzeck ($email_git_fallback && !$maintained) || 1463*92bca398SDaniel Schwierzeck $email_git_blame) { 1464*92bca398SDaniel Schwierzeck print STDERR "auth sign"; 1465*92bca398SDaniel Schwierzeck } 1466*92bca398SDaniel Schwierzeck print STDERR "\n"; 1467*92bca398SDaniel Schwierzeck foreach my $entry (@list) { 1468*92bca398SDaniel Schwierzeck my $email = $entry->[0]; 1469*92bca398SDaniel Schwierzeck my $role = $entry->[1]; 1470*92bca398SDaniel Schwierzeck my $sel = ""; 1471*92bca398SDaniel Schwierzeck $sel = "*" if ($selected{$count}); 1472*92bca398SDaniel Schwierzeck my $commit_author = $commit_author_hash{$email}; 1473*92bca398SDaniel Schwierzeck my $commit_signer = $commit_signer_hash{$email}; 1474*92bca398SDaniel Schwierzeck my $authored = 0; 1475*92bca398SDaniel Schwierzeck my $signed = 0; 1476*92bca398SDaniel Schwierzeck $authored++ for (@{$commit_author}); 1477*92bca398SDaniel Schwierzeck $signed++ for (@{$commit_signer}); 1478*92bca398SDaniel Schwierzeck printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email; 1479*92bca398SDaniel Schwierzeck printf STDERR "%4d %4d", $authored, $signed 1480*92bca398SDaniel Schwierzeck if ($authored > 0 || $signed > 0); 1481*92bca398SDaniel Schwierzeck printf STDERR "\n %s\n", $role; 1482*92bca398SDaniel Schwierzeck if ($authored{$count}) { 1483*92bca398SDaniel Schwierzeck my $commit_author = $commit_author_hash{$email}; 1484*92bca398SDaniel Schwierzeck foreach my $ref (@{$commit_author}) { 1485*92bca398SDaniel Schwierzeck print STDERR " Author: @{$ref}[1]\n"; 1486*92bca398SDaniel Schwierzeck } 1487*92bca398SDaniel Schwierzeck } 1488*92bca398SDaniel Schwierzeck if ($signed{$count}) { 1489*92bca398SDaniel Schwierzeck my $commit_signer = $commit_signer_hash{$email}; 1490*92bca398SDaniel Schwierzeck foreach my $ref (@{$commit_signer}) { 1491*92bca398SDaniel Schwierzeck print STDERR " @{$ref}[2]: @{$ref}[1]\n"; 1492*92bca398SDaniel Schwierzeck } 1493*92bca398SDaniel Schwierzeck } 1494*92bca398SDaniel Schwierzeck 1495*92bca398SDaniel Schwierzeck $count++; 1496*92bca398SDaniel Schwierzeck } 1497*92bca398SDaniel Schwierzeck } 1498*92bca398SDaniel Schwierzeck my $date_ref = \$email_git_since; 1499*92bca398SDaniel Schwierzeck $date_ref = \$email_hg_since if (vcs_is_hg()); 1500*92bca398SDaniel Schwierzeck if ($print_options) { 1501*92bca398SDaniel Schwierzeck $print_options = 0; 1502*92bca398SDaniel Schwierzeck if (vcs_exists()) { 1503*92bca398SDaniel Schwierzeck print STDERR <<EOT 1504*92bca398SDaniel Schwierzeck 1505*92bca398SDaniel SchwierzeckVersion Control options: 1506*92bca398SDaniel Schwierzeckg use git history [$email_git] 1507*92bca398SDaniel Schwierzeckgf use git-fallback [$email_git_fallback] 1508*92bca398SDaniel Schwierzeckb use git blame [$email_git_blame] 1509*92bca398SDaniel Schwierzeckbs use blame signatures [$email_git_blame_signatures] 1510*92bca398SDaniel Schwierzeckc# minimum commits [$email_git_min_signatures] 1511*92bca398SDaniel Schwierzeck%# min percent [$email_git_min_percent] 1512*92bca398SDaniel Schwierzeckd# history to use [$$date_ref] 1513*92bca398SDaniel Schwierzeckx# max maintainers [$email_git_max_maintainers] 1514*92bca398SDaniel Schwierzeckt all signature types [$email_git_all_signature_types] 1515*92bca398SDaniel Schwierzeckm use .mailmap [$email_use_mailmap] 1516*92bca398SDaniel SchwierzeckEOT 1517*92bca398SDaniel Schwierzeck } 1518*92bca398SDaniel Schwierzeck print STDERR <<EOT 1519*92bca398SDaniel Schwierzeck 1520*92bca398SDaniel SchwierzeckAdditional options: 1521*92bca398SDaniel Schwierzeck0 toggle all 1522*92bca398SDaniel Schwierzecktm toggle maintainers 1523*92bca398SDaniel Schwierzecktg toggle git entries 1524*92bca398SDaniel Schwierzecktl toggle open list entries 1525*92bca398SDaniel Schwierzeckts toggle subscriber list entries 1526*92bca398SDaniel Schwierzeckf emails in file [$file_emails] 1527*92bca398SDaniel Schwierzeckk keywords in file [$keywords] 1528*92bca398SDaniel Schwierzeckr remove duplicates [$email_remove_duplicates] 1529*92bca398SDaniel Schwierzeckp# pattern match depth [$pattern_depth] 1530*92bca398SDaniel SchwierzeckEOT 1531*92bca398SDaniel Schwierzeck } 1532*92bca398SDaniel Schwierzeck print STDERR 1533*92bca398SDaniel Schwierzeck"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): "; 1534*92bca398SDaniel Schwierzeck 1535*92bca398SDaniel Schwierzeck my $input = <STDIN>; 1536*92bca398SDaniel Schwierzeck chomp($input); 1537*92bca398SDaniel Schwierzeck 1538*92bca398SDaniel Schwierzeck $redraw = 1; 1539*92bca398SDaniel Schwierzeck my $rerun = 0; 1540*92bca398SDaniel Schwierzeck my @wish = split(/[, ]+/, $input); 1541*92bca398SDaniel Schwierzeck foreach my $nr (@wish) { 1542*92bca398SDaniel Schwierzeck $nr = lc($nr); 1543*92bca398SDaniel Schwierzeck my $sel = substr($nr, 0, 1); 1544*92bca398SDaniel Schwierzeck my $str = substr($nr, 1); 1545*92bca398SDaniel Schwierzeck my $val = 0; 1546*92bca398SDaniel Schwierzeck $val = $1 if $str =~ /^(\d+)$/; 1547*92bca398SDaniel Schwierzeck 1548*92bca398SDaniel Schwierzeck if ($sel eq "y") { 1549*92bca398SDaniel Schwierzeck $interactive = 0; 1550*92bca398SDaniel Schwierzeck $done = 1; 1551*92bca398SDaniel Schwierzeck $output_rolestats = 0; 1552*92bca398SDaniel Schwierzeck $output_roles = 0; 1553*92bca398SDaniel Schwierzeck last; 1554*92bca398SDaniel Schwierzeck } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) { 1555*92bca398SDaniel Schwierzeck $selected{$nr - 1} = !$selected{$nr - 1}; 1556*92bca398SDaniel Schwierzeck } elsif ($sel eq "*" || $sel eq '^') { 1557*92bca398SDaniel Schwierzeck my $toggle = 0; 1558*92bca398SDaniel Schwierzeck $toggle = 1 if ($sel eq '*'); 1559*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1560*92bca398SDaniel Schwierzeck $selected{$i} = $toggle; 1561*92bca398SDaniel Schwierzeck } 1562*92bca398SDaniel Schwierzeck } elsif ($sel eq "0") { 1563*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1564*92bca398SDaniel Schwierzeck $selected{$i} = !$selected{$i}; 1565*92bca398SDaniel Schwierzeck } 1566*92bca398SDaniel Schwierzeck } elsif ($sel eq "t") { 1567*92bca398SDaniel Schwierzeck if (lc($str) eq "m") { 1568*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1569*92bca398SDaniel Schwierzeck $selected{$i} = !$selected{$i} 1570*92bca398SDaniel Schwierzeck if ($list[$i]->[1] =~ /^(maintainer|supporter)/i); 1571*92bca398SDaniel Schwierzeck } 1572*92bca398SDaniel Schwierzeck } elsif (lc($str) eq "g") { 1573*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1574*92bca398SDaniel Schwierzeck $selected{$i} = !$selected{$i} 1575*92bca398SDaniel Schwierzeck if ($list[$i]->[1] =~ /^(author|commit|signer)/i); 1576*92bca398SDaniel Schwierzeck } 1577*92bca398SDaniel Schwierzeck } elsif (lc($str) eq "l") { 1578*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1579*92bca398SDaniel Schwierzeck $selected{$i} = !$selected{$i} 1580*92bca398SDaniel Schwierzeck if ($list[$i]->[1] =~ /^(open list)/i); 1581*92bca398SDaniel Schwierzeck } 1582*92bca398SDaniel Schwierzeck } elsif (lc($str) eq "s") { 1583*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1584*92bca398SDaniel Schwierzeck $selected{$i} = !$selected{$i} 1585*92bca398SDaniel Schwierzeck if ($list[$i]->[1] =~ /^(subscriber list)/i); 1586*92bca398SDaniel Schwierzeck } 1587*92bca398SDaniel Schwierzeck } 1588*92bca398SDaniel Schwierzeck } elsif ($sel eq "a") { 1589*92bca398SDaniel Schwierzeck if ($val > 0 && $val <= $count) { 1590*92bca398SDaniel Schwierzeck $authored{$val - 1} = !$authored{$val - 1}; 1591*92bca398SDaniel Schwierzeck } elsif ($str eq '*' || $str eq '^') { 1592*92bca398SDaniel Schwierzeck my $toggle = 0; 1593*92bca398SDaniel Schwierzeck $toggle = 1 if ($str eq '*'); 1594*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1595*92bca398SDaniel Schwierzeck $authored{$i} = $toggle; 1596*92bca398SDaniel Schwierzeck } 1597*92bca398SDaniel Schwierzeck } 1598*92bca398SDaniel Schwierzeck } elsif ($sel eq "s") { 1599*92bca398SDaniel Schwierzeck if ($val > 0 && $val <= $count) { 1600*92bca398SDaniel Schwierzeck $signed{$val - 1} = !$signed{$val - 1}; 1601*92bca398SDaniel Schwierzeck } elsif ($str eq '*' || $str eq '^') { 1602*92bca398SDaniel Schwierzeck my $toggle = 0; 1603*92bca398SDaniel Schwierzeck $toggle = 1 if ($str eq '*'); 1604*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $count; $i++) { 1605*92bca398SDaniel Schwierzeck $signed{$i} = $toggle; 1606*92bca398SDaniel Schwierzeck } 1607*92bca398SDaniel Schwierzeck } 1608*92bca398SDaniel Schwierzeck } elsif ($sel eq "o") { 1609*92bca398SDaniel Schwierzeck $print_options = 1; 1610*92bca398SDaniel Schwierzeck $redraw = 1; 1611*92bca398SDaniel Schwierzeck } elsif ($sel eq "g") { 1612*92bca398SDaniel Schwierzeck if ($str eq "f") { 1613*92bca398SDaniel Schwierzeck bool_invert(\$email_git_fallback); 1614*92bca398SDaniel Schwierzeck } else { 1615*92bca398SDaniel Schwierzeck bool_invert(\$email_git); 1616*92bca398SDaniel Schwierzeck } 1617*92bca398SDaniel Schwierzeck $rerun = 1; 1618*92bca398SDaniel Schwierzeck } elsif ($sel eq "b") { 1619*92bca398SDaniel Schwierzeck if ($str eq "s") { 1620*92bca398SDaniel Schwierzeck bool_invert(\$email_git_blame_signatures); 1621*92bca398SDaniel Schwierzeck } else { 1622*92bca398SDaniel Schwierzeck bool_invert(\$email_git_blame); 1623*92bca398SDaniel Schwierzeck } 1624*92bca398SDaniel Schwierzeck $rerun = 1; 1625*92bca398SDaniel Schwierzeck } elsif ($sel eq "c") { 1626*92bca398SDaniel Schwierzeck if ($val > 0) { 1627*92bca398SDaniel Schwierzeck $email_git_min_signatures = $val; 1628*92bca398SDaniel Schwierzeck $rerun = 1; 1629*92bca398SDaniel Schwierzeck } 1630*92bca398SDaniel Schwierzeck } elsif ($sel eq "x") { 1631*92bca398SDaniel Schwierzeck if ($val > 0) { 1632*92bca398SDaniel Schwierzeck $email_git_max_maintainers = $val; 1633*92bca398SDaniel Schwierzeck $rerun = 1; 1634*92bca398SDaniel Schwierzeck } 1635*92bca398SDaniel Schwierzeck } elsif ($sel eq "%") { 1636*92bca398SDaniel Schwierzeck if ($str ne "" && $val >= 0) { 1637*92bca398SDaniel Schwierzeck $email_git_min_percent = $val; 1638*92bca398SDaniel Schwierzeck $rerun = 1; 1639*92bca398SDaniel Schwierzeck } 1640*92bca398SDaniel Schwierzeck } elsif ($sel eq "d") { 1641*92bca398SDaniel Schwierzeck if (vcs_is_git()) { 1642*92bca398SDaniel Schwierzeck $email_git_since = $str; 1643*92bca398SDaniel Schwierzeck } elsif (vcs_is_hg()) { 1644*92bca398SDaniel Schwierzeck $email_hg_since = $str; 1645*92bca398SDaniel Schwierzeck } 1646*92bca398SDaniel Schwierzeck $rerun = 1; 1647*92bca398SDaniel Schwierzeck } elsif ($sel eq "t") { 1648*92bca398SDaniel Schwierzeck bool_invert(\$email_git_all_signature_types); 1649*92bca398SDaniel Schwierzeck $rerun = 1; 1650*92bca398SDaniel Schwierzeck } elsif ($sel eq "f") { 1651*92bca398SDaniel Schwierzeck bool_invert(\$file_emails); 1652*92bca398SDaniel Schwierzeck $rerun = 1; 1653*92bca398SDaniel Schwierzeck } elsif ($sel eq "r") { 1654*92bca398SDaniel Schwierzeck bool_invert(\$email_remove_duplicates); 1655*92bca398SDaniel Schwierzeck $rerun = 1; 1656*92bca398SDaniel Schwierzeck } elsif ($sel eq "m") { 1657*92bca398SDaniel Schwierzeck bool_invert(\$email_use_mailmap); 1658*92bca398SDaniel Schwierzeck read_mailmap(); 1659*92bca398SDaniel Schwierzeck $rerun = 1; 1660*92bca398SDaniel Schwierzeck } elsif ($sel eq "k") { 1661*92bca398SDaniel Schwierzeck bool_invert(\$keywords); 1662*92bca398SDaniel Schwierzeck $rerun = 1; 1663*92bca398SDaniel Schwierzeck } elsif ($sel eq "p") { 1664*92bca398SDaniel Schwierzeck if ($str ne "" && $val >= 0) { 1665*92bca398SDaniel Schwierzeck $pattern_depth = $val; 1666*92bca398SDaniel Schwierzeck $rerun = 1; 1667*92bca398SDaniel Schwierzeck } 1668*92bca398SDaniel Schwierzeck } elsif ($sel eq "h" || $sel eq "?") { 1669*92bca398SDaniel Schwierzeck print STDERR <<EOT 1670*92bca398SDaniel Schwierzeck 1671*92bca398SDaniel SchwierzeckInteractive mode allows you to select the various maintainers, submitters, 1672*92bca398SDaniel Schwierzeckcommit signers and mailing lists that could be CC'd on a patch. 1673*92bca398SDaniel Schwierzeck 1674*92bca398SDaniel SchwierzeckAny *'d entry is selected. 1675*92bca398SDaniel Schwierzeck 1676*92bca398SDaniel SchwierzeckIf you have git or hg installed, you can choose to summarize the commit 1677*92bca398SDaniel Schwierzeckhistory of files in the patch. Also, each line of the current file can 1678*92bca398SDaniel Schwierzeckbe matched to its commit author and that commits signers with blame. 1679*92bca398SDaniel Schwierzeck 1680*92bca398SDaniel SchwierzeckVarious knobs exist to control the length of time for active commit 1681*92bca398SDaniel Schwierzecktracking, the maximum number of commit authors and signers to add, 1682*92bca398SDaniel Schwierzeckand such. 1683*92bca398SDaniel Schwierzeck 1684*92bca398SDaniel SchwierzeckEnter selections at the prompt until you are satisfied that the selected 1685*92bca398SDaniel Schwierzeckmaintainers are appropriate. You may enter multiple selections separated 1686*92bca398SDaniel Schwierzeckby either commas or spaces. 1687*92bca398SDaniel Schwierzeck 1688*92bca398SDaniel SchwierzeckEOT 1689*92bca398SDaniel Schwierzeck } else { 1690*92bca398SDaniel Schwierzeck print STDERR "invalid option: '$nr'\n"; 1691*92bca398SDaniel Schwierzeck $redraw = 0; 1692*92bca398SDaniel Schwierzeck } 1693*92bca398SDaniel Schwierzeck } 1694*92bca398SDaniel Schwierzeck if ($rerun) { 1695*92bca398SDaniel Schwierzeck print STDERR "git-blame can be very slow, please have patience..." 1696*92bca398SDaniel Schwierzeck if ($email_git_blame); 1697*92bca398SDaniel Schwierzeck goto &get_maintainers; 1698*92bca398SDaniel Schwierzeck } 1699*92bca398SDaniel Schwierzeck } 1700*92bca398SDaniel Schwierzeck 1701*92bca398SDaniel Schwierzeck #drop not selected entries 1702*92bca398SDaniel Schwierzeck $count = 0; 1703*92bca398SDaniel Schwierzeck my @new_emailto = (); 1704*92bca398SDaniel Schwierzeck foreach my $entry (@list) { 1705*92bca398SDaniel Schwierzeck if ($selected{$count}) { 1706*92bca398SDaniel Schwierzeck push(@new_emailto, $list[$count]); 1707*92bca398SDaniel Schwierzeck } 1708*92bca398SDaniel Schwierzeck $count++; 1709*92bca398SDaniel Schwierzeck } 1710*92bca398SDaniel Schwierzeck return @new_emailto; 1711*92bca398SDaniel Schwierzeck} 1712*92bca398SDaniel Schwierzeck 1713*92bca398SDaniel Schwierzecksub bool_invert { 1714*92bca398SDaniel Schwierzeck my ($bool_ref) = @_; 1715*92bca398SDaniel Schwierzeck 1716*92bca398SDaniel Schwierzeck if ($$bool_ref) { 1717*92bca398SDaniel Schwierzeck $$bool_ref = 0; 1718*92bca398SDaniel Schwierzeck } else { 1719*92bca398SDaniel Schwierzeck $$bool_ref = 1; 1720*92bca398SDaniel Schwierzeck } 1721*92bca398SDaniel Schwierzeck} 1722*92bca398SDaniel Schwierzeck 1723*92bca398SDaniel Schwierzecksub deduplicate_email { 1724*92bca398SDaniel Schwierzeck my ($email) = @_; 1725*92bca398SDaniel Schwierzeck 1726*92bca398SDaniel Schwierzeck my $matched = 0; 1727*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($email); 1728*92bca398SDaniel Schwierzeck $email = format_email($name, $address, 1); 1729*92bca398SDaniel Schwierzeck $email = mailmap_email($email); 1730*92bca398SDaniel Schwierzeck 1731*92bca398SDaniel Schwierzeck return $email if (!$email_remove_duplicates); 1732*92bca398SDaniel Schwierzeck 1733*92bca398SDaniel Schwierzeck ($name, $address) = parse_email($email); 1734*92bca398SDaniel Schwierzeck 1735*92bca398SDaniel Schwierzeck if ($name ne "" && $deduplicate_name_hash{lc($name)}) { 1736*92bca398SDaniel Schwierzeck $name = $deduplicate_name_hash{lc($name)}->[0]; 1737*92bca398SDaniel Schwierzeck $address = $deduplicate_name_hash{lc($name)}->[1]; 1738*92bca398SDaniel Schwierzeck $matched = 1; 1739*92bca398SDaniel Schwierzeck } elsif ($deduplicate_address_hash{lc($address)}) { 1740*92bca398SDaniel Schwierzeck $name = $deduplicate_address_hash{lc($address)}->[0]; 1741*92bca398SDaniel Schwierzeck $address = $deduplicate_address_hash{lc($address)}->[1]; 1742*92bca398SDaniel Schwierzeck $matched = 1; 1743*92bca398SDaniel Schwierzeck } 1744*92bca398SDaniel Schwierzeck if (!$matched) { 1745*92bca398SDaniel Schwierzeck $deduplicate_name_hash{lc($name)} = [ $name, $address ]; 1746*92bca398SDaniel Schwierzeck $deduplicate_address_hash{lc($address)} = [ $name, $address ]; 1747*92bca398SDaniel Schwierzeck } 1748*92bca398SDaniel Schwierzeck $email = format_email($name, $address, 1); 1749*92bca398SDaniel Schwierzeck $email = mailmap_email($email); 1750*92bca398SDaniel Schwierzeck return $email; 1751*92bca398SDaniel Schwierzeck} 1752*92bca398SDaniel Schwierzeck 1753*92bca398SDaniel Schwierzecksub save_commits_by_author { 1754*92bca398SDaniel Schwierzeck my (@lines) = @_; 1755*92bca398SDaniel Schwierzeck 1756*92bca398SDaniel Schwierzeck my @authors = (); 1757*92bca398SDaniel Schwierzeck my @commits = (); 1758*92bca398SDaniel Schwierzeck my @subjects = (); 1759*92bca398SDaniel Schwierzeck 1760*92bca398SDaniel Schwierzeck foreach my $line (@lines) { 1761*92bca398SDaniel Schwierzeck if ($line =~ m/$VCS_cmds{"author_pattern"}/) { 1762*92bca398SDaniel Schwierzeck my $author = $1; 1763*92bca398SDaniel Schwierzeck $author = deduplicate_email($author); 1764*92bca398SDaniel Schwierzeck push(@authors, $author); 1765*92bca398SDaniel Schwierzeck } 1766*92bca398SDaniel Schwierzeck push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/); 1767*92bca398SDaniel Schwierzeck push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/); 1768*92bca398SDaniel Schwierzeck } 1769*92bca398SDaniel Schwierzeck 1770*92bca398SDaniel Schwierzeck for (my $i = 0; $i < @authors; $i++) { 1771*92bca398SDaniel Schwierzeck my $exists = 0; 1772*92bca398SDaniel Schwierzeck foreach my $ref(@{$commit_author_hash{$authors[$i]}}) { 1773*92bca398SDaniel Schwierzeck if (@{$ref}[0] eq $commits[$i] && 1774*92bca398SDaniel Schwierzeck @{$ref}[1] eq $subjects[$i]) { 1775*92bca398SDaniel Schwierzeck $exists = 1; 1776*92bca398SDaniel Schwierzeck last; 1777*92bca398SDaniel Schwierzeck } 1778*92bca398SDaniel Schwierzeck } 1779*92bca398SDaniel Schwierzeck if (!$exists) { 1780*92bca398SDaniel Schwierzeck push(@{$commit_author_hash{$authors[$i]}}, 1781*92bca398SDaniel Schwierzeck [ ($commits[$i], $subjects[$i]) ]); 1782*92bca398SDaniel Schwierzeck } 1783*92bca398SDaniel Schwierzeck } 1784*92bca398SDaniel Schwierzeck} 1785*92bca398SDaniel Schwierzeck 1786*92bca398SDaniel Schwierzecksub save_commits_by_signer { 1787*92bca398SDaniel Schwierzeck my (@lines) = @_; 1788*92bca398SDaniel Schwierzeck 1789*92bca398SDaniel Schwierzeck my $commit = ""; 1790*92bca398SDaniel Schwierzeck my $subject = ""; 1791*92bca398SDaniel Schwierzeck 1792*92bca398SDaniel Schwierzeck foreach my $line (@lines) { 1793*92bca398SDaniel Schwierzeck $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/); 1794*92bca398SDaniel Schwierzeck $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/); 1795*92bca398SDaniel Schwierzeck if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) { 1796*92bca398SDaniel Schwierzeck my @signatures = ($line); 1797*92bca398SDaniel Schwierzeck my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); 1798*92bca398SDaniel Schwierzeck my @types = @$types_ref; 1799*92bca398SDaniel Schwierzeck my @signers = @$signers_ref; 1800*92bca398SDaniel Schwierzeck 1801*92bca398SDaniel Schwierzeck my $type = $types[0]; 1802*92bca398SDaniel Schwierzeck my $signer = $signers[0]; 1803*92bca398SDaniel Schwierzeck 1804*92bca398SDaniel Schwierzeck $signer = deduplicate_email($signer); 1805*92bca398SDaniel Schwierzeck 1806*92bca398SDaniel Schwierzeck my $exists = 0; 1807*92bca398SDaniel Schwierzeck foreach my $ref(@{$commit_signer_hash{$signer}}) { 1808*92bca398SDaniel Schwierzeck if (@{$ref}[0] eq $commit && 1809*92bca398SDaniel Schwierzeck @{$ref}[1] eq $subject && 1810*92bca398SDaniel Schwierzeck @{$ref}[2] eq $type) { 1811*92bca398SDaniel Schwierzeck $exists = 1; 1812*92bca398SDaniel Schwierzeck last; 1813*92bca398SDaniel Schwierzeck } 1814*92bca398SDaniel Schwierzeck } 1815*92bca398SDaniel Schwierzeck if (!$exists) { 1816*92bca398SDaniel Schwierzeck push(@{$commit_signer_hash{$signer}}, 1817*92bca398SDaniel Schwierzeck [ ($commit, $subject, $type) ]); 1818*92bca398SDaniel Schwierzeck } 1819*92bca398SDaniel Schwierzeck } 1820*92bca398SDaniel Schwierzeck } 1821*92bca398SDaniel Schwierzeck} 1822*92bca398SDaniel Schwierzeck 1823*92bca398SDaniel Schwierzecksub vcs_assign { 1824*92bca398SDaniel Schwierzeck my ($role, $divisor, @lines) = @_; 1825*92bca398SDaniel Schwierzeck 1826*92bca398SDaniel Schwierzeck my %hash; 1827*92bca398SDaniel Schwierzeck my $count = 0; 1828*92bca398SDaniel Schwierzeck 1829*92bca398SDaniel Schwierzeck return if (@lines <= 0); 1830*92bca398SDaniel Schwierzeck 1831*92bca398SDaniel Schwierzeck if ($divisor <= 0) { 1832*92bca398SDaniel Schwierzeck warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n"); 1833*92bca398SDaniel Schwierzeck $divisor = 1; 1834*92bca398SDaniel Schwierzeck } 1835*92bca398SDaniel Schwierzeck 1836*92bca398SDaniel Schwierzeck @lines = mailmap(@lines); 1837*92bca398SDaniel Schwierzeck 1838*92bca398SDaniel Schwierzeck return if (@lines <= 0); 1839*92bca398SDaniel Schwierzeck 1840*92bca398SDaniel Schwierzeck @lines = sort(@lines); 1841*92bca398SDaniel Schwierzeck 1842*92bca398SDaniel Schwierzeck # uniq -c 1843*92bca398SDaniel Schwierzeck $hash{$_}++ for @lines; 1844*92bca398SDaniel Schwierzeck 1845*92bca398SDaniel Schwierzeck # sort -rn 1846*92bca398SDaniel Schwierzeck foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { 1847*92bca398SDaniel Schwierzeck my $sign_offs = $hash{$line}; 1848*92bca398SDaniel Schwierzeck my $percent = $sign_offs * 100 / $divisor; 1849*92bca398SDaniel Schwierzeck 1850*92bca398SDaniel Schwierzeck $percent = 100 if ($percent > 100); 1851*92bca398SDaniel Schwierzeck $count++; 1852*92bca398SDaniel Schwierzeck last if ($sign_offs < $email_git_min_signatures || 1853*92bca398SDaniel Schwierzeck $count > $email_git_max_maintainers || 1854*92bca398SDaniel Schwierzeck $percent < $email_git_min_percent); 1855*92bca398SDaniel Schwierzeck push_email_address($line, ''); 1856*92bca398SDaniel Schwierzeck if ($output_rolestats) { 1857*92bca398SDaniel Schwierzeck my $fmt_percent = sprintf("%.0f", $percent); 1858*92bca398SDaniel Schwierzeck add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%"); 1859*92bca398SDaniel Schwierzeck } else { 1860*92bca398SDaniel Schwierzeck add_role($line, $role); 1861*92bca398SDaniel Schwierzeck } 1862*92bca398SDaniel Schwierzeck } 1863*92bca398SDaniel Schwierzeck} 1864*92bca398SDaniel Schwierzeck 1865*92bca398SDaniel Schwierzecksub vcs_file_signoffs { 1866*92bca398SDaniel Schwierzeck my ($file) = @_; 1867*92bca398SDaniel Schwierzeck 1868*92bca398SDaniel Schwierzeck my $authors_ref; 1869*92bca398SDaniel Schwierzeck my $signers_ref; 1870*92bca398SDaniel Schwierzeck my $stats_ref; 1871*92bca398SDaniel Schwierzeck my @authors = (); 1872*92bca398SDaniel Schwierzeck my @signers = (); 1873*92bca398SDaniel Schwierzeck my @stats = (); 1874*92bca398SDaniel Schwierzeck my $commits; 1875*92bca398SDaniel Schwierzeck 1876*92bca398SDaniel Schwierzeck $vcs_used = vcs_exists(); 1877*92bca398SDaniel Schwierzeck return if (!$vcs_used); 1878*92bca398SDaniel Schwierzeck 1879*92bca398SDaniel Schwierzeck my $cmd = $VCS_cmds{"find_signers_cmd"}; 1880*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd 1881*92bca398SDaniel Schwierzeck 1882*92bca398SDaniel Schwierzeck ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); 1883*92bca398SDaniel Schwierzeck 1884*92bca398SDaniel Schwierzeck @signers = @{$signers_ref} if defined $signers_ref; 1885*92bca398SDaniel Schwierzeck @authors = @{$authors_ref} if defined $authors_ref; 1886*92bca398SDaniel Schwierzeck @stats = @{$stats_ref} if defined $stats_ref; 1887*92bca398SDaniel Schwierzeck 1888*92bca398SDaniel Schwierzeck# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n"); 1889*92bca398SDaniel Schwierzeck 1890*92bca398SDaniel Schwierzeck foreach my $signer (@signers) { 1891*92bca398SDaniel Schwierzeck $signer = deduplicate_email($signer); 1892*92bca398SDaniel Schwierzeck } 1893*92bca398SDaniel Schwierzeck 1894*92bca398SDaniel Schwierzeck vcs_assign("commit_signer", $commits, @signers); 1895*92bca398SDaniel Schwierzeck vcs_assign("authored", $commits, @authors); 1896*92bca398SDaniel Schwierzeck if ($#authors == $#stats) { 1897*92bca398SDaniel Schwierzeck my $stat_pattern = $VCS_cmds{"stat_pattern"}; 1898*92bca398SDaniel Schwierzeck $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern 1899*92bca398SDaniel Schwierzeck 1900*92bca398SDaniel Schwierzeck my $added = 0; 1901*92bca398SDaniel Schwierzeck my $deleted = 0; 1902*92bca398SDaniel Schwierzeck for (my $i = 0; $i <= $#stats; $i++) { 1903*92bca398SDaniel Schwierzeck if ($stats[$i] =~ /$stat_pattern/) { 1904*92bca398SDaniel Schwierzeck $added += $1; 1905*92bca398SDaniel Schwierzeck $deleted += $2; 1906*92bca398SDaniel Schwierzeck } 1907*92bca398SDaniel Schwierzeck } 1908*92bca398SDaniel Schwierzeck my @tmp_authors = uniq(@authors); 1909*92bca398SDaniel Schwierzeck foreach my $author (@tmp_authors) { 1910*92bca398SDaniel Schwierzeck $author = deduplicate_email($author); 1911*92bca398SDaniel Schwierzeck } 1912*92bca398SDaniel Schwierzeck @tmp_authors = uniq(@tmp_authors); 1913*92bca398SDaniel Schwierzeck my @list_added = (); 1914*92bca398SDaniel Schwierzeck my @list_deleted = (); 1915*92bca398SDaniel Schwierzeck foreach my $author (@tmp_authors) { 1916*92bca398SDaniel Schwierzeck my $auth_added = 0; 1917*92bca398SDaniel Schwierzeck my $auth_deleted = 0; 1918*92bca398SDaniel Schwierzeck for (my $i = 0; $i <= $#stats; $i++) { 1919*92bca398SDaniel Schwierzeck if ($author eq deduplicate_email($authors[$i]) && 1920*92bca398SDaniel Schwierzeck $stats[$i] =~ /$stat_pattern/) { 1921*92bca398SDaniel Schwierzeck $auth_added += $1; 1922*92bca398SDaniel Schwierzeck $auth_deleted += $2; 1923*92bca398SDaniel Schwierzeck } 1924*92bca398SDaniel Schwierzeck } 1925*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $auth_added; $i++) { 1926*92bca398SDaniel Schwierzeck push(@list_added, $author); 1927*92bca398SDaniel Schwierzeck } 1928*92bca398SDaniel Schwierzeck for (my $i = 0; $i < $auth_deleted; $i++) { 1929*92bca398SDaniel Schwierzeck push(@list_deleted, $author); 1930*92bca398SDaniel Schwierzeck } 1931*92bca398SDaniel Schwierzeck } 1932*92bca398SDaniel Schwierzeck vcs_assign("added_lines", $added, @list_added); 1933*92bca398SDaniel Schwierzeck vcs_assign("removed_lines", $deleted, @list_deleted); 1934*92bca398SDaniel Schwierzeck } 1935*92bca398SDaniel Schwierzeck} 1936*92bca398SDaniel Schwierzeck 1937*92bca398SDaniel Schwierzecksub vcs_file_blame { 1938*92bca398SDaniel Schwierzeck my ($file) = @_; 1939*92bca398SDaniel Schwierzeck 1940*92bca398SDaniel Schwierzeck my @signers = (); 1941*92bca398SDaniel Schwierzeck my @all_commits = (); 1942*92bca398SDaniel Schwierzeck my @commits = (); 1943*92bca398SDaniel Schwierzeck my $total_commits; 1944*92bca398SDaniel Schwierzeck my $total_lines; 1945*92bca398SDaniel Schwierzeck 1946*92bca398SDaniel Schwierzeck $vcs_used = vcs_exists(); 1947*92bca398SDaniel Schwierzeck return if (!$vcs_used); 1948*92bca398SDaniel Schwierzeck 1949*92bca398SDaniel Schwierzeck @all_commits = vcs_blame($file); 1950*92bca398SDaniel Schwierzeck @commits = uniq(@all_commits); 1951*92bca398SDaniel Schwierzeck $total_commits = @commits; 1952*92bca398SDaniel Schwierzeck $total_lines = @all_commits; 1953*92bca398SDaniel Schwierzeck 1954*92bca398SDaniel Schwierzeck if ($email_git_blame_signatures) { 1955*92bca398SDaniel Schwierzeck if (vcs_is_hg()) { 1956*92bca398SDaniel Schwierzeck my $commit_count; 1957*92bca398SDaniel Schwierzeck my $commit_authors_ref; 1958*92bca398SDaniel Schwierzeck my $commit_signers_ref; 1959*92bca398SDaniel Schwierzeck my $stats_ref; 1960*92bca398SDaniel Schwierzeck my @commit_authors = (); 1961*92bca398SDaniel Schwierzeck my @commit_signers = (); 1962*92bca398SDaniel Schwierzeck my $commit = join(" -r ", @commits); 1963*92bca398SDaniel Schwierzeck my $cmd; 1964*92bca398SDaniel Schwierzeck 1965*92bca398SDaniel Schwierzeck $cmd = $VCS_cmds{"find_commit_signers_cmd"}; 1966*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 1967*92bca398SDaniel Schwierzeck 1968*92bca398SDaniel Schwierzeck ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); 1969*92bca398SDaniel Schwierzeck @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; 1970*92bca398SDaniel Schwierzeck @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; 1971*92bca398SDaniel Schwierzeck 1972*92bca398SDaniel Schwierzeck push(@signers, @commit_signers); 1973*92bca398SDaniel Schwierzeck } else { 1974*92bca398SDaniel Schwierzeck foreach my $commit (@commits) { 1975*92bca398SDaniel Schwierzeck my $commit_count; 1976*92bca398SDaniel Schwierzeck my $commit_authors_ref; 1977*92bca398SDaniel Schwierzeck my $commit_signers_ref; 1978*92bca398SDaniel Schwierzeck my $stats_ref; 1979*92bca398SDaniel Schwierzeck my @commit_authors = (); 1980*92bca398SDaniel Schwierzeck my @commit_signers = (); 1981*92bca398SDaniel Schwierzeck my $cmd; 1982*92bca398SDaniel Schwierzeck 1983*92bca398SDaniel Schwierzeck $cmd = $VCS_cmds{"find_commit_signers_cmd"}; 1984*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 1985*92bca398SDaniel Schwierzeck 1986*92bca398SDaniel Schwierzeck ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); 1987*92bca398SDaniel Schwierzeck @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; 1988*92bca398SDaniel Schwierzeck @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; 1989*92bca398SDaniel Schwierzeck 1990*92bca398SDaniel Schwierzeck push(@signers, @commit_signers); 1991*92bca398SDaniel Schwierzeck } 1992*92bca398SDaniel Schwierzeck } 1993*92bca398SDaniel Schwierzeck } 1994*92bca398SDaniel Schwierzeck 1995*92bca398SDaniel Schwierzeck if ($from_filename) { 1996*92bca398SDaniel Schwierzeck if ($output_rolestats) { 1997*92bca398SDaniel Schwierzeck my @blame_signers; 1998*92bca398SDaniel Schwierzeck if (vcs_is_hg()) {{ # Double brace for last exit 1999*92bca398SDaniel Schwierzeck my $commit_count; 2000*92bca398SDaniel Schwierzeck my @commit_signers = (); 2001*92bca398SDaniel Schwierzeck @commits = uniq(@commits); 2002*92bca398SDaniel Schwierzeck @commits = sort(@commits); 2003*92bca398SDaniel Schwierzeck my $commit = join(" -r ", @commits); 2004*92bca398SDaniel Schwierzeck my $cmd; 2005*92bca398SDaniel Schwierzeck 2006*92bca398SDaniel Schwierzeck $cmd = $VCS_cmds{"find_commit_author_cmd"}; 2007*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 2008*92bca398SDaniel Schwierzeck 2009*92bca398SDaniel Schwierzeck my @lines = (); 2010*92bca398SDaniel Schwierzeck 2011*92bca398SDaniel Schwierzeck @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 2012*92bca398SDaniel Schwierzeck 2013*92bca398SDaniel Schwierzeck if (!$email_git_penguin_chiefs) { 2014*92bca398SDaniel Schwierzeck @lines = grep(!/${penguin_chiefs}/i, @lines); 2015*92bca398SDaniel Schwierzeck } 2016*92bca398SDaniel Schwierzeck 2017*92bca398SDaniel Schwierzeck last if !@lines; 2018*92bca398SDaniel Schwierzeck 2019*92bca398SDaniel Schwierzeck my @authors = (); 2020*92bca398SDaniel Schwierzeck foreach my $line (@lines) { 2021*92bca398SDaniel Schwierzeck if ($line =~ m/$VCS_cmds{"author_pattern"}/) { 2022*92bca398SDaniel Schwierzeck my $author = $1; 2023*92bca398SDaniel Schwierzeck $author = deduplicate_email($author); 2024*92bca398SDaniel Schwierzeck push(@authors, $author); 2025*92bca398SDaniel Schwierzeck } 2026*92bca398SDaniel Schwierzeck } 2027*92bca398SDaniel Schwierzeck 2028*92bca398SDaniel Schwierzeck save_commits_by_author(@lines) if ($interactive); 2029*92bca398SDaniel Schwierzeck save_commits_by_signer(@lines) if ($interactive); 2030*92bca398SDaniel Schwierzeck 2031*92bca398SDaniel Schwierzeck push(@signers, @authors); 2032*92bca398SDaniel Schwierzeck }} 2033*92bca398SDaniel Schwierzeck else { 2034*92bca398SDaniel Schwierzeck foreach my $commit (@commits) { 2035*92bca398SDaniel Schwierzeck my $i; 2036*92bca398SDaniel Schwierzeck my $cmd = $VCS_cmds{"find_commit_author_cmd"}; 2037*92bca398SDaniel Schwierzeck $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 2038*92bca398SDaniel Schwierzeck my @author = vcs_find_author($cmd); 2039*92bca398SDaniel Schwierzeck next if !@author; 2040*92bca398SDaniel Schwierzeck 2041*92bca398SDaniel Schwierzeck my $formatted_author = deduplicate_email($author[0]); 2042*92bca398SDaniel Schwierzeck 2043*92bca398SDaniel Schwierzeck my $count = grep(/$commit/, @all_commits); 2044*92bca398SDaniel Schwierzeck for ($i = 0; $i < $count ; $i++) { 2045*92bca398SDaniel Schwierzeck push(@blame_signers, $formatted_author); 2046*92bca398SDaniel Schwierzeck } 2047*92bca398SDaniel Schwierzeck } 2048*92bca398SDaniel Schwierzeck } 2049*92bca398SDaniel Schwierzeck if (@blame_signers) { 2050*92bca398SDaniel Schwierzeck vcs_assign("authored lines", $total_lines, @blame_signers); 2051*92bca398SDaniel Schwierzeck } 2052*92bca398SDaniel Schwierzeck } 2053*92bca398SDaniel Schwierzeck foreach my $signer (@signers) { 2054*92bca398SDaniel Schwierzeck $signer = deduplicate_email($signer); 2055*92bca398SDaniel Schwierzeck } 2056*92bca398SDaniel Schwierzeck vcs_assign("commits", $total_commits, @signers); 2057*92bca398SDaniel Schwierzeck } else { 2058*92bca398SDaniel Schwierzeck foreach my $signer (@signers) { 2059*92bca398SDaniel Schwierzeck $signer = deduplicate_email($signer); 2060*92bca398SDaniel Schwierzeck } 2061*92bca398SDaniel Schwierzeck vcs_assign("modified commits", $total_commits, @signers); 2062*92bca398SDaniel Schwierzeck } 2063*92bca398SDaniel Schwierzeck} 2064*92bca398SDaniel Schwierzeck 2065*92bca398SDaniel Schwierzecksub uniq { 2066*92bca398SDaniel Schwierzeck my (@parms) = @_; 2067*92bca398SDaniel Schwierzeck 2068*92bca398SDaniel Schwierzeck my %saw; 2069*92bca398SDaniel Schwierzeck @parms = grep(!$saw{$_}++, @parms); 2070*92bca398SDaniel Schwierzeck return @parms; 2071*92bca398SDaniel Schwierzeck} 2072*92bca398SDaniel Schwierzeck 2073*92bca398SDaniel Schwierzecksub sort_and_uniq { 2074*92bca398SDaniel Schwierzeck my (@parms) = @_; 2075*92bca398SDaniel Schwierzeck 2076*92bca398SDaniel Schwierzeck my %saw; 2077*92bca398SDaniel Schwierzeck @parms = sort @parms; 2078*92bca398SDaniel Schwierzeck @parms = grep(!$saw{$_}++, @parms); 2079*92bca398SDaniel Schwierzeck return @parms; 2080*92bca398SDaniel Schwierzeck} 2081*92bca398SDaniel Schwierzeck 2082*92bca398SDaniel Schwierzecksub clean_file_emails { 2083*92bca398SDaniel Schwierzeck my (@file_emails) = @_; 2084*92bca398SDaniel Schwierzeck my @fmt_emails = (); 2085*92bca398SDaniel Schwierzeck 2086*92bca398SDaniel Schwierzeck foreach my $email (@file_emails) { 2087*92bca398SDaniel Schwierzeck $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g; 2088*92bca398SDaniel Schwierzeck my ($name, $address) = parse_email($email); 2089*92bca398SDaniel Schwierzeck if ($name eq '"[,\.]"') { 2090*92bca398SDaniel Schwierzeck $name = ""; 2091*92bca398SDaniel Schwierzeck } 2092*92bca398SDaniel Schwierzeck 2093*92bca398SDaniel Schwierzeck my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name); 2094*92bca398SDaniel Schwierzeck if (@nw > 2) { 2095*92bca398SDaniel Schwierzeck my $first = $nw[@nw - 3]; 2096*92bca398SDaniel Schwierzeck my $middle = $nw[@nw - 2]; 2097*92bca398SDaniel Schwierzeck my $last = $nw[@nw - 1]; 2098*92bca398SDaniel Schwierzeck 2099*92bca398SDaniel Schwierzeck if (((length($first) == 1 && $first =~ m/[A-Za-z]/) || 2100*92bca398SDaniel Schwierzeck (length($first) == 2 && substr($first, -1) eq ".")) || 2101*92bca398SDaniel Schwierzeck (length($middle) == 1 || 2102*92bca398SDaniel Schwierzeck (length($middle) == 2 && substr($middle, -1) eq "."))) { 2103*92bca398SDaniel Schwierzeck $name = "$first $middle $last"; 2104*92bca398SDaniel Schwierzeck } else { 2105*92bca398SDaniel Schwierzeck $name = "$middle $last"; 2106*92bca398SDaniel Schwierzeck } 2107*92bca398SDaniel Schwierzeck } 2108*92bca398SDaniel Schwierzeck 2109*92bca398SDaniel Schwierzeck if (substr($name, -1) =~ /[,\.]/) { 2110*92bca398SDaniel Schwierzeck $name = substr($name, 0, length($name) - 1); 2111*92bca398SDaniel Schwierzeck } elsif (substr($name, -2) =~ /[,\.]"/) { 2112*92bca398SDaniel Schwierzeck $name = substr($name, 0, length($name) - 2) . '"'; 2113*92bca398SDaniel Schwierzeck } 2114*92bca398SDaniel Schwierzeck 2115*92bca398SDaniel Schwierzeck if (substr($name, 0, 1) =~ /[,\.]/) { 2116*92bca398SDaniel Schwierzeck $name = substr($name, 1, length($name) - 1); 2117*92bca398SDaniel Schwierzeck } elsif (substr($name, 0, 2) =~ /"[,\.]/) { 2118*92bca398SDaniel Schwierzeck $name = '"' . substr($name, 2, length($name) - 2); 2119*92bca398SDaniel Schwierzeck } 2120*92bca398SDaniel Schwierzeck 2121*92bca398SDaniel Schwierzeck my $fmt_email = format_email($name, $address, $email_usename); 2122*92bca398SDaniel Schwierzeck push(@fmt_emails, $fmt_email); 2123*92bca398SDaniel Schwierzeck } 2124*92bca398SDaniel Schwierzeck return @fmt_emails; 2125*92bca398SDaniel Schwierzeck} 2126*92bca398SDaniel Schwierzeck 2127*92bca398SDaniel Schwierzecksub merge_email { 2128*92bca398SDaniel Schwierzeck my @lines; 2129*92bca398SDaniel Schwierzeck my %saw; 2130*92bca398SDaniel Schwierzeck 2131*92bca398SDaniel Schwierzeck for (@_) { 2132*92bca398SDaniel Schwierzeck my ($address, $role) = @$_; 2133*92bca398SDaniel Schwierzeck if (!$saw{$address}) { 2134*92bca398SDaniel Schwierzeck if ($output_roles) { 2135*92bca398SDaniel Schwierzeck push(@lines, "$address ($role)"); 2136*92bca398SDaniel Schwierzeck } else { 2137*92bca398SDaniel Schwierzeck push(@lines, $address); 2138*92bca398SDaniel Schwierzeck } 2139*92bca398SDaniel Schwierzeck $saw{$address} = 1; 2140*92bca398SDaniel Schwierzeck } 2141*92bca398SDaniel Schwierzeck } 2142*92bca398SDaniel Schwierzeck 2143*92bca398SDaniel Schwierzeck return @lines; 2144*92bca398SDaniel Schwierzeck} 2145*92bca398SDaniel Schwierzeck 2146*92bca398SDaniel Schwierzecksub output { 2147*92bca398SDaniel Schwierzeck my (@parms) = @_; 2148*92bca398SDaniel Schwierzeck 2149*92bca398SDaniel Schwierzeck if ($output_multiline) { 2150*92bca398SDaniel Schwierzeck foreach my $line (@parms) { 2151*92bca398SDaniel Schwierzeck print("${line}\n"); 2152*92bca398SDaniel Schwierzeck } 2153*92bca398SDaniel Schwierzeck } else { 2154*92bca398SDaniel Schwierzeck print(join($output_separator, @parms)); 2155*92bca398SDaniel Schwierzeck print("\n"); 2156*92bca398SDaniel Schwierzeck } 2157*92bca398SDaniel Schwierzeck} 2158*92bca398SDaniel Schwierzeck 2159*92bca398SDaniel Schwierzeckmy $rfc822re; 2160*92bca398SDaniel Schwierzeck 2161*92bca398SDaniel Schwierzecksub make_rfc822re { 2162*92bca398SDaniel Schwierzeck# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and 2163*92bca398SDaniel Schwierzeck# comment. We must allow for rfc822_lwsp (or comments) after each of these. 2164*92bca398SDaniel Schwierzeck# This regexp will only work on addresses which have had comments stripped 2165*92bca398SDaniel Schwierzeck# and replaced with rfc822_lwsp. 2166*92bca398SDaniel Schwierzeck 2167*92bca398SDaniel Schwierzeck my $specials = '()<>@,;:\\\\".\\[\\]'; 2168*92bca398SDaniel Schwierzeck my $controls = '\\000-\\037\\177'; 2169*92bca398SDaniel Schwierzeck 2170*92bca398SDaniel Schwierzeck my $dtext = "[^\\[\\]\\r\\\\]"; 2171*92bca398SDaniel Schwierzeck my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*"; 2172*92bca398SDaniel Schwierzeck 2173*92bca398SDaniel Schwierzeck my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*"; 2174*92bca398SDaniel Schwierzeck 2175*92bca398SDaniel Schwierzeck# Use zero-width assertion to spot the limit of an atom. A simple 2176*92bca398SDaniel Schwierzeck# $rfc822_lwsp* causes the regexp engine to hang occasionally. 2177*92bca398SDaniel Schwierzeck my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))"; 2178*92bca398SDaniel Schwierzeck my $word = "(?:$atom|$quoted_string)"; 2179*92bca398SDaniel Schwierzeck my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*"; 2180*92bca398SDaniel Schwierzeck 2181*92bca398SDaniel Schwierzeck my $sub_domain = "(?:$atom|$domain_literal)"; 2182*92bca398SDaniel Schwierzeck my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*"; 2183*92bca398SDaniel Schwierzeck 2184*92bca398SDaniel Schwierzeck my $addr_spec = "$localpart\@$rfc822_lwsp*$domain"; 2185*92bca398SDaniel Schwierzeck 2186*92bca398SDaniel Schwierzeck my $phrase = "$word*"; 2187*92bca398SDaniel Schwierzeck my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)"; 2188*92bca398SDaniel Schwierzeck my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*"; 2189*92bca398SDaniel Schwierzeck my $mailbox = "(?:$addr_spec|$phrase$route_addr)"; 2190*92bca398SDaniel Schwierzeck 2191*92bca398SDaniel Schwierzeck my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*"; 2192*92bca398SDaniel Schwierzeck my $address = "(?:$mailbox|$group)"; 2193*92bca398SDaniel Schwierzeck 2194*92bca398SDaniel Schwierzeck return "$rfc822_lwsp*$address"; 2195*92bca398SDaniel Schwierzeck} 2196*92bca398SDaniel Schwierzeck 2197*92bca398SDaniel Schwierzecksub rfc822_strip_comments { 2198*92bca398SDaniel Schwierzeck my $s = shift; 2199*92bca398SDaniel Schwierzeck# Recursively remove comments, and replace with a single space. The simpler 2200*92bca398SDaniel Schwierzeck# regexps in the Email Addressing FAQ are imperfect - they will miss escaped 2201*92bca398SDaniel Schwierzeck# chars in atoms, for example. 2202*92bca398SDaniel Schwierzeck 2203*92bca398SDaniel Schwierzeck while ($s =~ s/^((?:[^"\\]|\\.)* 2204*92bca398SDaniel Schwierzeck (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*) 2205*92bca398SDaniel Schwierzeck \((?:[^()\\]|\\.)*\)/$1 /osx) {} 2206*92bca398SDaniel Schwierzeck return $s; 2207*92bca398SDaniel Schwierzeck} 2208*92bca398SDaniel Schwierzeck 2209*92bca398SDaniel Schwierzeck# valid: returns true if the parameter is an RFC822 valid address 2210*92bca398SDaniel Schwierzeck# 2211*92bca398SDaniel Schwierzecksub rfc822_valid { 2212*92bca398SDaniel Schwierzeck my $s = rfc822_strip_comments(shift); 2213*92bca398SDaniel Schwierzeck 2214*92bca398SDaniel Schwierzeck if (!$rfc822re) { 2215*92bca398SDaniel Schwierzeck $rfc822re = make_rfc822re(); 2216*92bca398SDaniel Schwierzeck } 2217*92bca398SDaniel Schwierzeck 2218*92bca398SDaniel Schwierzeck return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/; 2219*92bca398SDaniel Schwierzeck} 2220*92bca398SDaniel Schwierzeck 2221*92bca398SDaniel Schwierzeck# validlist: In scalar context, returns true if the parameter is an RFC822 2222*92bca398SDaniel Schwierzeck# valid list of addresses. 2223*92bca398SDaniel Schwierzeck# 2224*92bca398SDaniel Schwierzeck# In list context, returns an empty list on failure (an invalid 2225*92bca398SDaniel Schwierzeck# address was found); otherwise a list whose first element is the 2226*92bca398SDaniel Schwierzeck# number of addresses found and whose remaining elements are the 2227*92bca398SDaniel Schwierzeck# addresses. This is needed to disambiguate failure (invalid) 2228*92bca398SDaniel Schwierzeck# from success with no addresses found, because an empty string is 2229*92bca398SDaniel Schwierzeck# a valid list. 2230*92bca398SDaniel Schwierzeck 2231*92bca398SDaniel Schwierzecksub rfc822_validlist { 2232*92bca398SDaniel Schwierzeck my $s = rfc822_strip_comments(shift); 2233*92bca398SDaniel Schwierzeck 2234*92bca398SDaniel Schwierzeck if (!$rfc822re) { 2235*92bca398SDaniel Schwierzeck $rfc822re = make_rfc822re(); 2236*92bca398SDaniel Schwierzeck } 2237*92bca398SDaniel Schwierzeck # * null list items are valid according to the RFC 2238*92bca398SDaniel Schwierzeck # * the '1' business is to aid in distinguishing failure from no results 2239*92bca398SDaniel Schwierzeck 2240*92bca398SDaniel Schwierzeck my @r; 2241*92bca398SDaniel Schwierzeck if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so && 2242*92bca398SDaniel Schwierzeck $s =~ m/^$rfc822_char*$/) { 2243*92bca398SDaniel Schwierzeck while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) { 2244*92bca398SDaniel Schwierzeck push(@r, $1); 2245*92bca398SDaniel Schwierzeck } 2246*92bca398SDaniel Schwierzeck return wantarray ? (scalar(@r), @r) : 1; 2247*92bca398SDaniel Schwierzeck } 2248*92bca398SDaniel Schwierzeck return wantarray ? () : 0; 2249*92bca398SDaniel Schwierzeck} 2250